Java Notes
Complete guide to input validation in Spring Boot - Jakarta Bean Validation, custom validators, group validation, and validation error handling.
Setup
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>Validation Annotations Reference
| Annotation | Applies To | Description |
|---|---|---|
@NotNull | Any | Must not be null |
@NotBlank | String | Not null, not empty, not whitespace |
@NotEmpty | String/Collection | Not null and not empty |
@Size(min, max) | String/Collection | Length/size bounds |
@Min(value) | Number | Minimum value |
@Max(value) | Number | Maximum value |
@Positive | Number | > 0 |
@PositiveOrZero | Number | >= 0 |
@Negative | Number | < 0 |
@Email | String | Valid email format |
@Pattern(regexp) | String | Matches regex |
@Past | Date/Time | Must be in past |
@Future | Date/Time | Must be in future |
@Digits(integer, fraction) | Number | Digit constraints |
@DecimalMin | Number | Minimum decimal value |
@DecimalMax | Number | Maximum decimal value |
Applying Validation in Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<?> createUser(@RequestBody @Valid UserRegistrationRequest request) {
// If validation fails, MethodArgumentNotValidException is thrown
// before this code executes
User user = userService.register(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
// Validate path variable
@GetMapping("/{id}")
public ResponseEntity<?> getUser(@PathVariable @Min(1) Long id) {
return ResponseEntity.ok(userService.getById(id));
}
// Validate request params
@GetMapping
public ResponseEntity<?> searchUsers(
@RequestParam @NotBlank String query,
@RequestParam @Min(0) int page,
@RequestParam @Min(1) @Max(100) int size) {
return ResponseEntity.ok(userService.search(query, page, size));
}
}Important: Add @Validated on the class for path variable / request param validation:@RestController
@Validated // Required for method-level validation
@RequestMapping("/api/users")
public class UserController { }Custom Validator
Step 1: Create Annotation
@Documented
@Constraint(validatedBy = UniqueEmailValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueEmail {
String message() default "Email already registered";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Step 2: Create Validator Class
@Component
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
@Autowired
private UserRepository userRepository;
@Override
public boolean isValid(String email, ConstraintValidatorContext context) {
if (email == null) return true; // Let @NotNull handle null check
return !userRepository.existsByEmail(email);
}
}Step 3: Use It
@Data
public class UserRequest {
@NotBlank
@Email
@UniqueEmail // Custom validation!
private String email;
}Cross-Field Validation
// Class-level validator
@Documented
@Constraint(validatedBy = PasswordMatchValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PasswordMatch {
String message() default "Passwords do not match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, Object> {
@Override
public boolean isValid(Object obj, ConstraintValidatorContext context) {
if (obj instanceof ChangePasswordRequest req) {
return req.getNewPassword().equals(req.getConfirmPassword());
}
return true;
}
}
@Data
@PasswordMatch
public class ChangePasswordRequest {
@NotBlank
private String currentPassword;
@NotBlank @Size(min = 8)
private String newPassword;
@NotBlank
private String confirmPassword;
}Nested Object Validation
Handling Validation Errors
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidationErrors(
MethodArgumentNotValidException ex) {
Map<String, String> fieldErrors = new LinkedHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
fieldErrors.put(error.getField(), error.getDefaultMessage()));
Map<String, Object> response = new LinkedHashMap<>();
response.put("success", false);
response.put("message", "Validation failed");
response.put("errors", fieldErrors);
response.put("timestamp", LocalDateTime.now());
return ResponseEntity.badRequest().body(response);
}
// For @Validated on path/query params
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Map<String, Object>> handleConstraintViolation(
ConstraintViolationException ex) {
Map<String, String> errors = new HashMap<>();
ex.getConstraintViolations().forEach(v ->
errors.put(v.getPropertyPath().toString(), v.getMessage()));
Map<String, Object> response = Map.of(
"success", false,
"message", "Constraint violation",
"errors", errors
);
return ResponseEntity.badRequest().body(response);
}
}Interview Key Points
@Validon@RequestBodytriggers validation before controller method runs@Validatedon class enables method-level validation (params, path vars)- Custom validators implement
ConstraintValidator<Annotation, Type> - Use
@Validon nested objects to trigger their validation - Validation errors throw
MethodArgumentNotValidException @NotNullvs@NotBlankvs@NotEmpty: Null → Empty → Whitespace- Custom class-level annotations enable cross-field validation
- Always return field-specific error messages for good UX
Summary
In this chapter, we learned about Validation in Spring Boot in detail. Here are the key points:
- Basic introduction to the concept and why it is needed
- Syntax and structure with complete examples
- Internal working and best practices
- Real-world applications and use cases
- Common mistakes and how to avoid them
- How this topic is asked in interviews
To practice these concepts, write and run the code in your IDE and verify the output. Modify each example and experiment so that you deeply understand the concept.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Validation in Spring Boot.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Java Master Course topic.
Search Terms
java-master-course, java master course, java, master, course, spring, framework, boot
Related Java Master Course Topics