Implementing Data Validation
1. Using Bean Validation API
| Detail | Value |
|---|---|
| Spec | Jakarta Bean Validation 3.0 |
| Default impl | Hibernate Validator 8 |
| Starter | spring-boot-starter-validation |
| Package | jakarta.validation.constraints.* |
2. Validating Request Body (@Valid, @Validated)
Example: Validate request body
@PostMapping
public ResponseEntity<Void> create(@Valid @RequestBody NewUser body) { /* … */ }
| Annotation | Scope |
|---|---|
@Valid (Jakarta) |
Cascade validation, basic groups via Default |
@Validated (Spring) |
Supports validation groups + class-level on @Service |
3. Using Standard Constraints
| Annotation | Applies To | Rule |
|---|---|---|
@NotNull |
Any | Not null (allows empty) |
@NotEmpty |
String/Coll | Not null + size > 0 |
@NotBlank |
String | Not null + trim() not empty |
@Size(min,max) |
String/Coll | Length / size range |
@AssertTrue/@AssertFalse |
boolean | Boolean check |
@Null |
Any | Must be null |
4. Using Numeric Constraints
| Constraint | Description |
|---|---|
@Min(n) / @Max(n) |
Long range |
@DecimalMin / @DecimalMax |
BigDecimal range |
@Positive / @PositiveOrZero |
> 0 / ≥ 0 |
@Negative / @NegativeOrZero |
< 0 / ≤ 0 |
@Digits(integer, fraction) |
Numeric precision |
5. Using Pattern Matching
| Constraint | Use |
|---|---|
@Pattern(regexp="^[A-Z0-9]{6}$") |
Regex match |
@Email |
RFC email |
@URL (Hibernate) |
Valid URL |
@CreditCardNumber |
Luhn check |
@ISBN |
ISBN-10/13 |
6. Creating Custom Validators (ConstraintValidator)
Example: Custom @ValidSku constraint annotation
@Constraint(validatedBy = SkuValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidSku {
String message() default "Invalid SKU format";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class SkuValidator implements ConstraintValidator<ValidSku, String> {
private static final Pattern P = Pattern.compile("^[A-Z]{3}-\\d{6}$");
@Override public boolean isValid(String v, ConstraintValidatorContext c) {
return v == null || P.matcher(v).matches();
}
}
7. Handling Validation Errors (BindingResult)
Example: Return field validation errors as map
@PostMapping
public ResponseEntity<?> create(@Valid @RequestBody Form f, BindingResult br) {
if (br.hasErrors()) {
Map<String,String> errs = br.getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
return ResponseEntity.badRequest().body(errs);
}
return ResponseEntity.ok().build();
}
Warning:
BindingResult parameter MUST be immediately after the
validated argument.8. Customizing Validation Messages
Example: Custom validation message bundle
# src/main/resources/ValidationMessages.properties
NotBlank.user.email=Email is required
Size.user.password=Password must be {min} to {max} characters
javax.validation.constraints.Email.message=Provide a valid email address
| Format | Description |
|---|---|
{min}, {max}, {value} |
Constraint params |
{NotBlank.user.email} |
Inheritable message keys |
SpEL ${} |
Expression evaluation |
9. Using Validation Groups
Example: Validation groups for create vs update
public interface OnCreate {}
public interface OnUpdate {}
public record User(
@Null(groups=OnCreate.class) @NotNull(groups=OnUpdate.class) Long id,
@NotBlank String email
) {}
@PostMapping
ResponseEntity<?> create(@Validated(OnCreate.class) @RequestBody User u) { /* … */ }
10. Validating Nested Objects (@Valid on fields)
Example: Nested object and list validation
public record Order(
@NotNull @Valid Customer customer,
@NotEmpty @Valid List<@Valid LineItem> items
) {}
| Pattern | Effect |
|---|---|
@Valid on field |
Cascade into nested object |
List<@Valid X> |
Element-level cascade (Bean Val 2+) |
11. Using @Validated for Method-Level Validation
Example: Method-level validation on service
@Service
@Validated
public class UserService {
public User load(@NotBlank String email) { /* … */ }
public @NotNull User update(@Valid UserUpdate u) { /* … */ }
}
Note: Throws
ConstraintViolationException; handle in
@ControllerAdvice.