Data Validation
Introduction
Data validation ensures that input from the client follows business rules before it is processed. Spring Boot supports Jakarta Validation (Bean Validation), which makes it easy to declare validation rules directly on your models.
Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Common Validation Annotations
| Annotation | Purpose | Example |
|---|---|---|
@NotNull |
Must not be null | @NotNull Long id |
@NotBlank |
Must not be null/empty/spaces | @NotBlank String name |
@Size(min, max) |
Length of a string/list | @Size(min = 3, max = 50) |
@Min(value) |
Minimum value | @Min(0) int age |
@Max(value) |
Maximum value | @Max(100) int score |
@Email |
Valid email format | @Email String email |
@Pattern(regex) |
Matches a regex | @Pattern(regexp = "^[0-9]+$") |
Entity Example with Validation
public class UserRequest {
@NotBlank(message = "Name is required")
@Size(min = 3, max = 100, message = "Name must be 3-100 characters")
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
private String email;
@NotBlank(message = "Password is required")
@Size(min = 8, message = "Password must be at least 8 characters")
private String password;
@Min(value = 17, message = "Age must be at least 17 years")
private int age;
}
Using @Valid in a Controller
The @Valid annotation on a controller parameter triggers automatic validation.
@PostMapping
public ResponseEntity<User> create(@Valid @RequestBody UserRequest request) {
User user = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@PutMapping("/{id}")
public ResponseEntity<User> update(
@PathVariable Long id,
@Valid @RequestBody UserRequest request) {
User user = userService.update(id, request);
return ResponseEntity.ok(user);
}
Handling Validation Errors
Spring automatically returns a MethodArgumentNotValidException when validation fails. Handle it with @ExceptionHandler.
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, List<String>>> handleValidation(
MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
Map<String, List<String>> response = new HashMap<>();
response.put("errors", errors);
return ResponseEntity.badRequest().body(response);
}
}
Response:
{
"errors": [
"name: Name is required",
"email: Invalid email format",
"password: Password must be at least 8 characters"
]
}
Custom Validators
Create custom validation rules using an annotation and a ConstraintValidator.
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueEmailValidator.class)
public @interface UniqueEmail {
String message() default "Email is already registered";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
@Autowired
private UserRepository userRepository;
@Override
public boolean isValid(String email, ConstraintValidatorContext context) {
return email != null && !userRepository.existsByEmail(email);
}
}
Summary
- Use
@Validin controllers to trigger validation. - Jakarta Validation provides ready-to-use annotations.
- Handle errors with
@RestControllerAdvice.
Next chapter: We'll cover Exception Handling — how to handle errors globally so your API returns consistent error responses.