How to Configure Hibernate Bean Validation in a Spring 5 MVC with Service Layer JEE8 Web App
Configuring Hibernate Bean Validation in a Spring 5 MVC with a service layer in a JEE8 web application involves several steps. Here’s a basic guide on how to set it up:
-
Add Dependencies: First, ensure that you have the necessary dependencies in your project. Include Hibernate Validator and its dependencies in your Maven or Gradle build file, as mentioned earlier.
-
Enable Validation in Spring: In your Spring configuration class (usually annotated with
@Configuration
), enable Bean Validation by adding@EnableValidation
annotation. If you’re using XML-based configuration, you can configure it in your Spring XML configuration file. -
Configure Validator: Spring automatically configures a
LocalValidatorFactoryBean
bean, which acts as a bridge between Spring’s validation facilities and Hibernate Validator. You can inject this bean wherever validation is needed in your application. -
Apply Validation Annotations: Add validation annotations like
@NotNull
,@Size
,@Email
, etc., to your domain model classes or DTOs (Data Transfer Objects). These annotations define the validation constraints for the corresponding fields. -
Validate Input in Controller: In your Spring MVC controllers, use the
@Valid
annotation on method parameters or method-level annotations like@RequestBody
to trigger validation on incoming data. This will automatically validate the input against the constraints defined in your domain model classes. -
Handle Validation Errors: Spring MVC provides mechanisms to handle validation errors gracefully. You can use
BindingResult
orErrors
object to access validation errors and perform appropriate error handling, such as returning error responses or redirecting to error pages. -
Integrate with Service Layer: Your service layer can interact with validated data passed from the controller. Ensure that validation is applied at appropriate points in your service methods, especially when accepting input from external sources.
Here’s a simplified example:
@Configuration
@EnableValidation
public class AppConfig {
// Spring automatically configures LocalValidatorFactoryBean
}
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<?> createUser(@Valid @RequestBody UserDTO userDTO, BindingResult result) {
if (result.hasErrors()) {
// Handle validation errors
return ResponseEntity.badRequest().body("Validation failed");
}
userService.createUser(userDTO);
return ResponseEntity.ok("User created successfully");
}
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void createUser(UserDTO userDTO) {
// Business logic
// Persist user
userRepository.save(userDTO.toEntity());
}
}
In this example, UserDTO
is a data transfer object representing user input, and it is annotated with validation constraints. The createUser
method in the controller validates the incoming UserDTO
object using @Valid
, and any validation errors are captured in the BindingResult
. Finally, the validated data is passed to the service layer for further processing.
How to Configure the Spring Validator Bean
In addition to enabling Bean Validation support in Spring and using Hibernate Validator, you can also configure a custom Spring Validator
bean to handle validation beyond what Hibernate Validator provides. This allows you to add custom validation logic or to integrate with other validation frameworks if needed.
Here’s how you can configure a Spring Validator
bean:
-
Create a Validator Implementation: Implement the
Validator
interface provided by Spring. This interface has a single methodvalidate(Object target, Errors errors)
that you need to implement. Inside this method, perform custom validation logic and add any validation errors to theErrors
object.import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import org.springframework.validation.Validator; @Component public class CustomValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return YourDTOClass.class.isAssignableFrom(clazz); } @Override public void validate(Object target, Errors errors) { YourDTOClass dto = (YourDTOClass) target; // Custom validation logic if (/* your validation condition */) { errors.rejectValue("fieldName", "error.code", "Error message"); } } }
-
Configure the Validator Bean: In your Spring configuration class, register the custom validator bean using
@Bean
annotation or@ComponentScan
if it’s in a package scanned by Spring.import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public CustomValidator customValidator() { return new CustomValidator(); } }
-
Integrate with Spring MVC: Use the custom validator in your Spring MVC controllers by injecting it and adding it to the
@InitBinder
method or as a method parameter annotated with@Validated
.import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.http.ResponseEntity; @RestController @RequestMapping("/api") public class YourController { @Autowired private CustomValidator customValidator; @InitBinder protected void initBinder(WebDataBinder binder) { binder.addValidators(customValidator); } @PostMapping("/endpoint") public ResponseEntity<?> yourEndpoint(@Validated @RequestBody YourDTOClass dto, BindingResult result) { if (result.hasErrors()) { // Handle validation errors return ResponseEntity.badRequest().body("Validation failed"); } // Process valid input return ResponseEntity.ok("Valid input"); } }
With this setup, Spring will automatically invoke both Hibernate Validator and your custom validator during data binding and validation. This allows you to have a unified validation mechanism that combines both built-in and custom validation logic in your Spring MVC application.
Error Code Localization
To set up error code localization in Spring MVC with Hibernate Validator, you can leverage Spring’s support for message source and use ResourceBundle for managing localized error messages. Here’s how you can do it:
-
Create Message Properties Files: First, create property files containing error messages for each supported locale. These files typically have names like
messages.properties
for the default locale andmessages_<locale>.properties
for other locales.Example
messages.properties
:error.required=This field is required error.size=Size must be between {min} and {max}
Example
messages_fr.properties
:error.required=Ce champ est obligatoire error.size=La taille doit être entre {min} et {max}
-
Configure Message Source Bean: In your Spring configuration class, configure a
MessageSource
bean to load the message properties files.import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ReloadableResourceBundleMessageSource; @Configuration public class AppConfig { @Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename("classpath:messages"); messageSource.setDefaultEncoding("UTF-8"); return messageSource; } }
-
Configure Hibernate Validator to Use Spring Message Source: Configure Hibernate Validator to use the Spring message source for error message resolution by creating a custom
LocalValidatorFactoryBean
.import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; public class CustomValidatorFactoryBean extends LocalValidatorFactoryBean { @Autowired private MessageSource messageSource; @Override public void afterPropertiesSet() { super.afterPropertiesSet(); this.setValidationMessageSource(messageSource); } }
-
Use Custom Validator Factory Bean: Register the custom validator factory bean in your Spring configuration class.
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public CustomValidatorFactoryBean validator() { return new CustomValidatorFactoryBean(); } }
With these configurations in place, Hibernate Validator will use the Spring message source to resolve error messages, allowing for error code localization based on the user’s locale preference. When a validation error occurs, Hibernate Validator will search for error messages in the appropriate message properties file based on the locale set in the application context. This provides a seamless integration of error code localization with Spring MVC and Hibernate Validator in your application.