// Add dependency
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
// Domain model
public class User {
@NotNull
@Size(min = 2, max = 50)
private String username;
@Email
private String email;
@Min(18)
@Max(120)
private Integer age;
@Pattern(regexp = "^\\+?[1-9]\\d{1,14}$")
private String phone;
@Past
private LocalDate birthDate;
@Valid // Nested validation
private Address address;
}@Component
public class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
if (user.getUsername() == null || user.getUsername().isBlank()) {
errors.rejectValue("username", "username.required", "Username is required");
}
if (user.getEmail() != null && !user.getEmail().contains("@")) {
errors.rejectValue("email", "email.invalid", "Invalid email format");
}
// Cross-field validation
if (user.getPassword() != null && !user.getPassword().equals(user.getConfirmPassword())) {
errors.reject("password.mismatch", "Passwords do not match");
}
}
}@Service
@Validated
public class UserService {
@Autowired
private UserRepository repository;
// Parameter validation
public User createUser(@Valid User user) {
return repository.save(user);
}
// Method parameter validation
public User getUser(@NotNull @Min(1) Long id) {
return repository.findById(id);
}
// Return value validation
@Valid
public User updateUser(Long id, @Valid User user) {
return repository.save(user);
}
// Validation groups
public void processUser(@Validated(BasicValidation.class) User user) {
// Only validates BasicValidation group
}
}
// Exception handling
@ControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<?> handleConstraintViolation(ConstraintViolationException ex) {
Map<String, String> errors = ex.getConstraintViolations().stream()
.collect(Collectors.toMap(
v -> v.getPropertyPath().toString(),
ConstraintViolation::getMessage
));
return ResponseEntity.badRequest().body(errors);
}
}public interface BasicValidation {}
public interface AdvancedValidation {}
public class User {
@NotNull(groups = BasicValidation.class)
@Size(min = 2, max = 50, groups = BasicValidation.class)
private String username;
@NotNull(groups = AdvancedValidation.class)
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$",
groups = AdvancedValidation.class)
private String password;
@AssertTrue(groups = AdvancedValidation.class)
public boolean isAgeVerified() {
return age != null && age >= 18;
}
}
// Usage
@Service
public class UserService {
public void quickRegistration(@Validated(BasicValidation.class) User user) {}
public void fullRegistration(@Validated({BasicValidation.class, AdvancedValidation.class}) User user) {}
}// Annotation
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueUsernameValidator.class)
public @interface UniqueUsername {
String message() default "Username already exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// Validator
@Component
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
@Autowired
private UserRepository userRepository;
@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
if (username == null) return true;
return !userRepository.existsByUsername(username);
}
}
// Usage
public class User {
@UniqueUsername
private String username;
}@Service
public class ValidationService {
@Autowired
private Validator validator;
public void validateUser(User user) {
Set<ConstraintViolation<User>> violations = validator.validate(user);
if (!violations.isEmpty()) {
violations.forEach(v ->
System.out.println(v.getPropertyPath() + ": " + v.getMessage())
);
}
}
// Validate specific property
public void validateUsername(User user) {
Set<ConstraintViolation<User>> violations =
validator.validateProperty(user, "username");
}
// Validate specific group
public void validateBasic(User user) {
Set<ConstraintViolation<User>> violations =
validator.validate(user, BasicValidation.class);
}
}@Configuration
public class ValidationConfig {
@Bean
public LocalValidatorFactoryBean validator(MessageSource messageSource) {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource);
return validator;
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator);
return processor;
}
}public class ValidationExamples {
@NotNull // Must not be null
@NotEmpty // Not null and not empty (collections/strings)
@NotBlank // Not null and has non-whitespace
@Null // Must be null
@Size(min = 2, max = 100) // Size constraints
@Min(18) // Minimum numeric value
@Max(100) // Maximum numeric value
@DecimalMin("0.0") // Decimal minimum
@DecimalMax("100.0") // Decimal maximum
@Digits(integer = 3, fraction = 2) // Numeric format
@Past // Date in the past
@PastOrPresent // Past or present
@Future // Date in the future
@FutureOrPresent // Future or present
@Email // Valid email format
@Pattern(regexp = "...") // Regex pattern match
@Positive // Positive number
@PositiveOrZero // Positive or zero
@Negative // Negative number
@NegativeOrZero // Negative or zero
@AssertTrue // Boolean must be true
@AssertFalse // Boolean must be false
@Valid // Nested validation
private String field;
}