Chapter 6 Tutorials

Required Validation

  1. Add the dependency to Spring Boot validation in the pom file.
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
            </dependency>
  2. Copy the interface and bean from the chapter 5 enhanced example to a new folder src/main/java/data/ch6/requiredValidation. Rename the interface to RequestDataRequired. Rename the implementation to RequestDataRequiredImpl.
  3. Remove the default validation from the implementation. The getters should simply return the property without doing any testing in code.
  4. Add a declaration for a prototype bean to the configuration file, SimpleBean.java
        @Bean("protoRequiredBean")
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        com.bytesizebook.data.ch6.requiredValidation.RequestDataRequired getProtoRequiredBean() {
            return new com.bytesizebook.data.ch6.requiredValidation.RequestDataRequiredImpl();
        }
  5. Extend the interface from the interface from chapter 5.
  6. Add the NotNull and Pattern annotations to the interface getters that return a string. Use appropriate messages. The patterns for the hobby and aversion should only allow three choices, ignoring case.
  7. Add an integer property for the days per week.
  8. Add the Min and Max annotations to the getter for the days. Use reasonable values and messages.
  9. Copy the JSPs to a new location named ch6/requiredValidation.
  10. Modify each view for the days per week field.
  11. Modify the edit page so that it uses the Spring tag library for forms to show error messages.
  12. Copy the controller from the last example to a new class named ControllerRequredValidation in the controller/ch6/requiredValidation folder.
  13. Change the import for the bean to com.bytesizebook.data.ch6.requiredValidation.
  14. Modify the request mapping annotation for the URL pattern /ch6/requiredValidation/collect/.
  15. Modify the instance variable of type ObjectFactory so that it wraps the RequestDataRequired interface. Annotate it with a qualifier for the prototype bean that was added to the configuration file for this example.
  16. Modify the viewLocation method for the new location of the JSPs.
  17. Add the Valid annotation to the confirm method that expects data.
  18. Add BindingResult and RedirectAttributes to the same method, after the parameter for the data.
  19. After testing for expired data, test if errors exist. If they do, add the errors and the data to the flash attributes and redirect to the edit view. If no errors exist, redirect to the edit page.

Custom Editor

  1. This example rewrites the interface with Integer but will still work with the previous bean due to automatic boxing and unboxing.
  2. Copy the interface and bean from the above example to the folder src/main/java/data/ch6/requiredValidation/editor. Rename the interface to RequestDataCustom. Rename the implementation to RequestDataCustomImpl.
  3. Change the type for the days per week property from int to Integer in the interface and implementation.
  4. Add a declaration for a prototype bean to the configuration file, SimpleBean.java
        @Bean("protoCustomBean")
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        RequestDataCustomImpl getProtoCustomBean() {
            return new requiredValidation.RequestDataCustomImpl();
        }
  5. Create a new folder for the controller named ch6/requiredValidation/editor.
  6. Define an editor class that extends CustomNumberEditor.
  7. Add a constructor to the editor that expects the type of the destination class and a boolean indicating if the field is required. Call the super class with those parameters.
  8. Override the setAsText method. Call the super class method of the same name. Catch an IllegalArgumentException and set the value to 0.
  9. Copy the controller from the last example into the folder.
  10. Add a method for initializing the binder. Annotate the method with InitBinder. Add a parameter for a WebDataBinder from Spring. Spring will autowire an instance for binding.
  11. On the binder parameter, call the registerCustomEditor method. Pass the Integer class, the name of the property and an instance of the custom editor.
  12. Add a method for showing the expired view.
  13. Run the application. If a non-number is entered into the days per week field, a zero will be entered by default.

Custom Validator

  1. A new interface is not needed for this example. The book declares one for the section but does not use it.
  2. Create a new folder for the controller named ch6/requiredValidation/validator.
  3. Define a validator class that extends Validator.
  4. Override the supports and validate methods.
  5. From the supports method, return the isAssignableFrom method on the class of the bean.
  6. From the validate method, call a helper method for each property.
  7. In the helper method for the hobby, test that it is not empty and not one of two words.
  8. In the helper method for the aversion, test that it is not empty and not one of two words.
  9. In the helper method for the days per week, test that it is in the correct range.
  10. Copy the controller from the last example into the folder. Give it a new name.
  11. Modify the request mapping annotation for the URL pattern /ch6/requiredValidation/validator/collect/.
  12. Modify the method for initializing the binder. On the binder parameter, call the setValidator method. Pass an instance of the validator.

Validation Groups

  1. Copy the interface and bean from the above example to the folder src/main/java/data/ch6/requiredValidation/groups. Rename the interface to RequestDataRequiredGroup. Rename the implementation to RequestDataRequiredGroupImpl.
  2. Change the NotNull annotations to NotEmpty.
  3. Add the group parameter to the annotations for NotEmpty, Min and Max.
  4. Add a declaration for a prototype bean to the configuration file, SimpleBean.java
        @Bean("protoGroupBean")
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        RequestDataRequiredGroupImpl getProtoGroupBean() {
            return new RequestDataRequiredGroupImpl();
        }
  5. Create a new folder for this application named ch6/requiredValidation/groups
  6. Create an interface named Common that is empty.
  7. Copy the validator from the previous example to this folder and modify it.
    1. Change the bean class to the one for this example, wherever the bean class is referenced.
    2. Remove the tests for empty hobby and aversion.
    3. Remove the helper method for the days per week.
  8. Copy the controller from the custom validator example to this folder.
    1. Modify the URL pattern.
    2. Modify the initBinder method and change setValidator to addValidators, passing an instance of the new validator.
    3. Replace all occurrences of the bean interface with the one for this application.
    4. Modify the qualifier for the bean to the one for this application.
    5. Replace the Valid annotation with the Validated annotation in the confirm method, including the parameter for the class of the new interface.

Database Configuration

  1. Add the dependencies for JPA and H2 to the pom file. Run maven with clean and install.
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <scope>runtime</scope>
            </dependency>
  2. Add three properties to the application properties to add the h2 console, set the path to the h2 console, clear the associated datasource on restart and set a static URL for the datasource at runtime.
    spring.h2.console.path=/h2-console
    spring.datasource.initialization-mode=always
    spring.datasource.url:h2:mem:mydb
  3. Run the application and access the path for the h2 console.

Data Persistence

  1. Copy the bean, but not the interface, from the required validation example to the folder src/main/java/data/ch6/persistent/. Rename the implementation to RequestDataPersistentImpl.
  2. Create an interface that extends the required validation interface and has a getter for the id property.
  3. The implementation should implement the interface and Serializable
  4. Add a property to the implementation for the id. Annotate the getter for the id with the Id and GeneratedValue annotations.
  5. Add a declaration for a prototype bean to the configuration file, SimpleBean.java
        @Bean("protoPersistentBean")
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        RequestDataPersistentImpl getProtoPersistentBean() {
            return new RequestDataPersistentImpl();
        }
  6. Create an interface for a CRUD repository for the bean and its id.
  7. Add a default method to the interface named saveWrappedData that inspects the source sent to the method and extracts the target object, whether the bean is Spring scoped or in the session attributes.
  8. Create a new folder for the controller, in the location for controllers, ch6/persistent/basic. This application introduces access to the database, but does not implement all features, so it is named basic.
  9. Copy the required validation controller to this folder.
    1. Modify the URL pattern. Do not include 'collect' in the pattern. This is the first application that will have paths other than 'collect'.
    2. Change the location of the JSPs.
    3. Preface all paths for the handler methods with collect/. The next iteration of the application will have additional paths available.
    4. Replace all occurrences of the bean interface with the one for this application.
    5. Modify the qualifier for the bean to the one for this application.
    6. Autowire an instance of the CRUD repo.
    7. Modify the process method so that it tests for errors. If no errors, then save the data using the CRUD repo.
  10. Run the application. Enter data and submit the process button.
  11. Go to the URL for the h2 console, h2-console.
  12. Connect to the datasource.
  13. Click on the name of the bean and run the query. You should see the record you just entered.
  14. No further editing can be done on the record since the conversational storage was cleared. Both the Start Over and Edit buttons on the process page perform the same action. The next application will introduce distinct actions for these buttons.

Data Persistence - Basic

  1. Copy the bean, but not the interface, from the required validation example to the folder src/main/java/data/ch6/persistent/. Rename the implementation to RequestDataPersistentImpl.
  2. Create an interface that extends the required validation interface and has a getter for the id property.
  3. The implementation should implement the interface and Serializable
  4. Add a property to the implementation for the id. Annotate the getter for the id with the Id and GeneratedValue annotations.
  5. Add a declaration for a prototype bean to the configuration file, SimpleBean.java
        @Bean("protoPersistentBean")
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        RequestDataPersistentImpl getProtoPersistentBean() {
            return new RequestDataPersistentImpl();
        }
  6. Create an interface for a CRUD repository for the bean and its id.
  7. Add a default method to the interface named saveWrappedData that inspects the source sent to the method and extracts the target object, whether the bean is Spring scoped or in the session attributes.
  8. Create a new folder for the controller, in the location for controllers, ch6/persistent/basic. This application introduces access to the database, but does not implement all features, so it is named basic.
  9. Copy the required validation controller to this folder.
    1. Modify the URL pattern. Do not include 'collect' in the pattern. This is the first application that will have paths other than 'collect'.
    2. Preface all paths for the handler methods with collect/. The next iteration of the application will have additional paths available.
    3. Replace all occurrences of the bean interface with the one for this application.
    4. Modify the qualifier for the bean to the one for this application.
    5. Autowire an instance of the CRUD repo.
    6. Modify the process method so that it tests for errors. If no errors, then save the data using the CRUD repo.
  10. Run the application. Enter data and submit the process button.
  11. Go to the URL for the h2 console, h2-console.
  12. Connect to the datasource.
  13. Click on the name of the bean and run the query. You should see the record you just entered.
  14. No further editing can be done on the record since the conversational storage was cleared. Both the Start Over and Edit buttons on the process page perform the same action. The next application will introduce distinct actions for these buttons.

Data Persistence - Complete

The last example correctly added records to the database, but did not allow the user to see them or edit them. This example will complete the application with these features.

  1. In the folder that contains the CRUD repository, create a new interface that has two, generic parameters: one for the bean type and one for the id type. Extend the interface from the CrudRepository class.
  2. Add a default method that is similar to the saveWrappedData method from the last example, but uses generic types.
  3. Create another interface that extends the generic one, supplying arguments.
  4. Copy the JSPs to a new location.
  5. Add a view that displays all the records from the database. The records will be available in the model.
    1. Include the taglib statement for the JSTL core library.
    2. Use the forEach tag to loop through the records and display each field.
    3. Add a link for each displayed ID. The path for the URL will start with view. These are new views that are distinct from the collect views.
  6. Add a view that displays one record. The record will be in the model. Add buttons to enter a new record and to view all records.
  7. Modify the process view so that it has a button that returns to the edit view and one that links to the new view for viewing all records.
  8. Copy the required validation controller to the ch6/persistent folder.
    1. Modify the URL pattern.
    2. Update the return value from the viewLocation method.
    3. Replace the actual class for the previous repo with the new repo. Include a qualifier annotation to identify the repo.
    4. Add a handler method for viewing all the records that is mapped to the URL view. Do not include collect in the URL.
      1. Include a parameter for the model.
      2. In the method, retrieve all the records and add them to the model.
      3. Return the name of the view that displays all the records.
    5. Change the return value from the default handler so it redirects to the edit view, instead of forwarding to the edit view.
    6. Add a handler to view one record.
      1. The record id will be in the path information, so add a parameter that has the PathVariable attribute.
      2. Retrieve the id from the repo calling the findById method.
      3. If the data is present, forward to the page to view one record. Pass the record as an attribute.
      4. If the data is not present, show an appropriate page with an error message.

Testing

The only addition needed to test the database is to use the same repo as the controller.

  1. Start with the test file for the enhanced controller from Chapter 5.
  2. Add the same repo to the test file as was used in the controller.
  3. Modify the process method so it tests the database before and after the request. A simple test is that the number of records has increased. Be creative in adding other tests, but remember that after the request is done, the values are no longer in the session.

Contact the author