Mastering Bean Validation with Hibernate Validator: A Complete Guide for Spring Boot
This article explains how to use JSR‑380 Bean Validation 2.0 and Hibernate Validator in Spring Boot applications, covering built‑in constraints, custom annotations, group validation, method and container element validation, fast‑fail mode, and integration with @Valid and @Validated for clean, reusable data checks.
Overview
Data validation is a common task across all application layers. Embedding validation logic directly in domain models mixes business logic with metadata. JSR‑380 (Bean Validation 2.0) defines a metadata model and API for entity and method validation, and Hibernate Validator is its reference implementation, automatically integrated in Spring Boot starter web (up to version 2.3).
Why Use Hibernate Validator
Improves code cleanliness.
Separates validation logic from business logic, reducing coupling.
Provides a unified, standard validation approach, avoiding duplicate code.
Lets developers focus on business concerns.
Built‑in Constraints (JSR‑380)
@Null– element must be null. @NotNull – element must not be null. @AssertTrue – element must be true. @AssertFalse – element must be false. @Min(value) – numeric or string value must be ≥ value. @Max(value) – numeric or string value must be ≤ value. @DecimalMin(value) – decimal value must be ≥ value (optionally inclusive). @DecimalMax(value) – decimal value must be ≤ value (optionally inclusive). @Size(min, max) – size of string, collection, map or array must be within range. @Digits(integer, fraction) – numeric value must have specified integer and fraction digits. @Past – date must be in the past. @Future – date must be in the future. @Pattern(value) – must match the given regular expression. @Email – must be a valid email address. @NotBlank – string must be non‑null and contain at least one non‑whitespace character. @NotEmpty – string, collection, map or array must not be null or empty. @Valid – triggers cascaded validation.
Hibernate‑Validator Additional Constraints
@Length– string length within range. @URL – validates a URL according to RFC2396. @Range – numeric or string value within range. @UniqueElements – collection contains only unique elements. @SafeHtml – checks for unsafe HTML fragments such as <script/>; requires jsoup dependency.
Key Differences Between @Validated and @Valid
@Validated is a Spring annotation that supports validation groups; it can be placed on types, methods and method parameters but not on fields.
@Valid is the standard JSR‑303 annotation; it does not support groups but can be used on methods, parameters, constructors and fields.
When both are present, @Valid can be combined with @Validated to enable nested validation.
Usage in Spring Boot
Spring Boot starter web (up to 2.3) already includes Hibernate Validator, so no extra dependency is required. To use @SafeHtml, add the jsoup dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- HTML parsing -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.3</version>
</dependency>Validation Modes
Normal mode – validates all constraints and returns all violations.
Fail‑fast mode – stops after the first violation. Configure with:
@Bean
public Validator validator() {
ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
.configure()
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
return factory.getValidator();
}Object Validation Example
@Data
public class User {
@NotBlank(message = "Gender cannot be empty")
private static String sex; // static fields are ignored
@NotBlank(message = "Name cannot be empty")
@Size(min = 2, max = 5, message = "Name length is invalid")
private String name;
@NotNull(message = "Age cannot be null")
@Max(value = 30, message = "Age exceeds maximum 30")
@Range(min = 30, max = 60)
private Integer age;
@DecimalMax(value = "108.88", message = "Exceeds maximum 108.88", inclusive = false)
private Double price;
@Past(message = "Birthday cannot be in the future")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime birthday;
@Email(message = "Invalid email format")
private String email;
@SafeHtml(message = "Illegal request parameter")
private String content;
}In a controller method, add @Valid before the parameter and optionally a BindingResult to capture errors without throwing an exception.
@PostMapping("user")
public String person(@Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return bindingResult.getAllErrors()
.stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.joining(","));
}
return "OK";
}Cascaded Validation
Annotate a nested object with @Valid to trigger validation of its fields.
@Data
public class User2 {
@NotBlank(message = "Name cannot be empty")
private String name;
@Max(value = 50, message = "Age cannot be empty")
private Integer age;
@Valid
@NotNull(message = "Goods cannot be null")
private Goods goods;
}Container Element Validation
Validate elements inside a collection by placing constraints on the generic type.
@Data
public class User3 {
@NotBlank(message = "Name cannot be empty")
private String name;
@Max(value = 50, message = "Age cannot be empty")
private Integer age;
@Valid
@NotEmpty(message = "Goods list cannot be empty")
private List<@NotNull(message = "Goods cannot be null") Goods> goodsList;
}Method Validation
Enable method‑level validation by adding @Validated on the class and using constraint annotations on parameters or return values. Violations throw ConstraintViolationException.
@RestController
@RequestMapping("validator")
@Validated
public class ValidatorController {
@GetMapping("demo1")
public String test1(@Range(min = 1, max = 999, message = "Page index out of range") @RequestParam int pageIndex,
@Range(min = 1, max = 999, message = "Page size out of range") @RequestParam int pageSize) {
return "ok";
}
}Group Validation
Define marker interfaces for groups and assign them to constraints. Use @Validated(Group.class) to activate a specific group.
@Data
public class UserGroup {
@NotNull(message = "Id cannot be null", groups = UpdateUser.class)
private Integer id;
@NotBlank(message = "Name cannot be null", groups = AddUser.class)
private String name;
@NotNull(message = "Age cannot be null", groups = AddUser.class)
private Integer age;
public interface AddUser {}
public interface UpdateUser {}
}Controller example:
@PostMapping("addUser")
public String addUser(@Validated(UserGroup.AddUser.class) UserGroup user) {
return "OK";
}Custom Constraint Example
Create a custom annotation @Sex and its validator to allow only “男” or “女”.
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = SexConstraintValidator.class)
public @interface Sex {
String message() default "Invalid gender";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
} public class SexConstraintValidator implements ConstraintValidator<Sex, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext ctx) {
if (value == null) return true;
return "男".equals(value) || "女".equals(value);
}
}Summary
Hibernate Validator, as the reference implementation of JSR‑380, provides a powerful, declarative way to validate data in Spring Boot applications. By leveraging built‑in constraints, group validation, method validation, cascaded and container element validation, and custom constraints, developers can keep validation logic clean, reusable, and separate from business code.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
