Validation with Hibernate Validator
|
This section documents Hibernate ORM 7.4.x (User Guide, Introduction, Query Language Guide, Data Repositories Guide), Jakarta Persistence 3.2, Hibernate Search 8.4.x, and the Hibernate Validator / Hibernate Reactive references — which are the references these pages are written and verified against. This content was generated with the assistance of AI and should be verified against those official docs before being relied on in production. Three older reference books were consulted as bibliography only while preparing these pages and are not the
primary or main source for any page. All three predate Jakarta Persistence 3.2 and Hibernate ORM 6/7 (the
This section’s bibliography lists the reference material consulted while preparing these pages. |
Hibernate Validator is the reference implementation of Jakarta Validation (Bean Validation) — despite the shared "Hibernate" name, it is a separate specification and library from Hibernate ORM, usable entirely on its own without any JPA/ORM involvement at all. This page covers it as a general validation library and its two points of contact with the rest of this section: JPA pre-persist/pre-update validation, and Spring MVC request validation.
Built-in constraints
public class BookRequest {
@NotBlank
private String title;
@NotNull
@Positive
private BigDecimal price;
@Size(min = 10, max = 13)
private String isbn;
@Email
private String contactEmail;
@Past
private LocalDate firstEditionDate;
}
The standard set covers most cases directly: @NotNull/@NotBlank/@NotEmpty, @Size, @Min/@Max,
@Positive/@Negative (and their OrZero variants), @Email, @Pattern, @Past/@Future, @Digits.
Hibernate Validator additionally ships a set of non-standard extensions (@URL, @Length, @Range,
@CreditCardNumber, @ISBN, @DurationMin/@DurationMax) not part of the Jakarta Validation specification
itself, so code depending on them is tied to Hibernate Validator specifically rather than portable to another
implementation.
@Valid cascading
@Valid (not @Validated — see the Spring-specific section below) triggers validation of a nested object
graph:
public class OrderRequest {
@NotNull
@Valid
private CustomerRequest customer; // CustomerRequest's own constraints are also checked
@NotEmpty
@Valid
private List<@Valid OrderLineRequest> lines; // each element validated too
}
Without @Valid on the nested field/collection element, only the container object’s own directly-declared
constraints are checked — constraints on the nested type are silently skipped.
Custom `ConstraintValidator`s
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IsbnFormatValidator.class)
public @interface ValidIsbnFormat {
String message() default "not a valid ISBN-13 format";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class IsbnFormatValidator implements ConstraintValidator<ValidIsbnFormat, String> {
private static final Pattern ISBN_13 = Pattern.compile("97[89]\\d{10}");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value == null || ISBN_13.matcher(value).matches();
}
}
A custom constraint pairs an annotation (declaring message/groups/payload, required by the
specification) with a ConstraintValidator<Annotation, Type> implementation containing the actual check logic.
Constraint groups
Groups let the same object be validated differently depending on context, without duplicating the object’s class:
public interface OnCreate {}
public interface OnUpdate {}
public class BookRequest {
@Null(groups = OnCreate.class) // must be absent on create ...
@NotNull(groups = OnUpdate.class) // ... but required on update
private Long id;
}
// ...
Set<ConstraintViolation<BookRequest>> violations = validator.validate(request, OnCreate.class);
A constraint with no explicit groups() belongs to the implicit Default group, checked whenever validation
runs without naming a specific group.
Method-level validation
Jakarta Validation also validates method parameters and return values directly, independent of any web framework:
public class BookService {
public Book findBook(@NotNull Long id) { /* ... */ }
@NotNull
public Book createBook(@Valid @NotNull BookRequest request) { /* ... */ }
}
This requires a method-validation-aware proxy around the bean (Hibernate Validator’s own
ExecutableValidator, or, under Spring, MethodValidationPostProcessor/@Validated at the class level — see below) to actually intercept calls and check the annotated parameters/return value; the annotations alone
do nothing without that interception layer in place.
How it plugs into JPA
Jakarta Persistence integrates Jakarta Validation directly: by default, every entity’s constraints are checked
automatically immediately before the corresponding INSERT/UPDATE is sent (JPA’s own
pre-persist/pre-update lifecycle validation, layered on top of
the JPA lifecycle callback mechanism) — ConstraintViolationException is thrown at flush time, before the SQL executes, if any managed entity fails
validation. This is a safety net, not a substitute for validating input at the API boundary — catching a
violation this late means the failing state has already flowed through the whole service layer.
jakarta.persistence.validation.mode (AUTO/CALLBACK/NONE) controls whether this automatic check runs at
all; AUTO (the default) enables it whenever a Jakarta Validation provider is present on the classpath.
How it plugs into Spring MVC
Two distinct annotations that are easy to conflate:
-
@Validon a@RequestBody/@ModelAttributecontroller parameter — triggers Spring MVC’s own request-body validation, throwingMethodArgumentNotValidExceptionon failure. -
@Validated(Spring’s own annotation, not part of Jakarta Validation) on a@Service/@Componentclass — enables Spring’s AOP-based method-level validation (the "method-level validation" mechanism above) for every method of that bean, and, with a group argument (@Validated(OnCreate.class)), selects which constraint group applies.
MethodArgumentNotValidException (from a failed @Valid request body) is the standard case
REST APIs' @ExceptionHandler/ProblemDetail machinery is built to
translate into a structured RFC 9457 error response — see that page’s
ProblemDetail and RFC 9457 section for the translation itself.
Links
-
REST APIs —
@Validrequest validation andProblemDetailmapping.