Master Spring Bean Validation: Quick Start, Custom Constraints, and Advanced Techniques

This article walks through Spring Bean Validation fundamentals, demonstrating how to apply built‑in constraints, create custom validators, use group validation, perform cascading request validation, and enable method‑level checks, all with clear code examples and best‑practice recommendations.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Master Spring Bean Validation: Quick Start, Custom Constraints, and Advanced Techniques

01 Bean Validation Basics: Quick Start and Efficient Validation

Spring Validation integrates the JSR‑380 (Bean Validation 2.0) specification and provides a set of ready‑to‑use validation annotations that help developers quickly enforce basic data checks.

@Data
public class UserDTO {
    @NotNull(message = "User ID cannot be null")
    private Long id;

    @NotBlank(message = "Username cannot be blank")
    @Size(min = 4, max = 20, message = "Username length must be between 4 and 20 characters")
    private String username;

    @Email(message = "Invalid email format")
    private String email;

    @Min(value = 18, message = "Age must be at least 18")
    @Max(value = 120, message = "Age must be at most 120")
    private Integer age;

    @Past(message = "Birth date must be in the past")
    private LocalDate birthDate;

    @Pattern(regexp = "^1[3-9]\d{9}$", message = "Invalid phone number format")
    private String phoneNumber;
}
@RestController
@RequestMapping("/api/users")
public class UserController {
    @PostMapping
    public ResponseEntity createUser(@RequestBody @Valid UserDTO userDTO, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            throw new ValidationException(bindingResult);
        }
        return ResponseEntity.ok(userDTO);
    }
}

Annotations: Use @NotNull, @NotBlank, @Email, etc., on DTO fields to perform quick basic validation.

In the controller, apply @Valid on the request body and retrieve validation results via BindingResult. If validation fails, throw a custom ValidationException to inform the client.

Best Practices: Provide meaningful error messages, keep a consistent naming style, and place validation annotations on DTOs rather than entity classes to improve separation of concerns and maintainability.

02 Custom Constraint Validators: Tailored Business Rules

Spring Validation allows developers to create custom constraints to satisfy specific business validation needs.

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueUsernameValidator.class)
public @interface UniqueUsername {
    String message() default "Username already exists";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
    private UserRepository userRepository;

    @Override
    public boolean isValid(String username, ConstraintValidatorContext context) {
        if (username == null) {
            return true;
        }
        return !userRepository.existsByUsername(username);
    }
}
public class UserRegistrationDTO {
    @NotBlank
    @Size(min = 4, max = 20)
    @UniqueUsername
    private String username;
}

Apply @UniqueUsername on the username field of UserRegistrationDTO to enforce uniqueness.

03 Group Validation: Switching Rules per Scenario

Group validation enables different validation rules for different use cases, such as create versus update operations.

public interface ValidationGroups {
    interface Create {}
    interface Update {}
}
@Data
public class ProductDTO {
    @Null(groups = ValidationGroups.Create.class, message = "ID must be null when creating")
    @NotNull(groups = ValidationGroups.Update.class, message = "ID cannot be null when updating")
    private Long id;

    @NotBlank(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class})
    private String name;

    @PositiveOrZero(groups = ValidationGroups.Create.class)
    @Positive(groups = ValidationGroups.Update.class)
    private BigDecimal price;
}
@RestController
@RequestMapping("/api/products")
public class ProductController {
    @PostMapping
    public ResponseEntity createProduct(@RequestBody @Validated(ValidationGroups.Create.class) ProductDTO productDTO) {
        return ResponseEntity.ok(productDTO);
    }

    @PutMapping("/{id}")
    public ResponseEntity<ProductDTO> updateProduct(@PathVariable Long id,
            @RequestBody @Validated(ValidationGroups.Update.class) ProductDTO productDTO) {
        return ResponseEntity.ok(productDTO);
    }
}

Use @Validated with the appropriate group to trigger the correct set of constraints.

04 Cascading Validation: Deep Object Graph Checks

Use @Valid on nested objects to ensure validation traverses the entire request structure.

@Data
public class OrderDTO {
    @NotNull
    private Long id;

    @NotNull
    @Valid
    private CustomerDTO customer;

    @NotEmpty
    @Valid
    private List<OrderItemDTO> items;
}

@Data
public class CustomerDTO {
    @NotNull
    private Long id;

    @NotBlank
    private String name;

    @Email
    private String email;

    @Valid
    private AddressDTO address;
}

Annotate fields like customer and items with @Valid so that their internal constraints are also evaluated.

05 Method‑Level Validation: Service Layer Robustness

Enable method‑level validation to verify parameters and return values of service methods.

@Configuration
@EnableMethodValidation
public class ValidationConfig {}
@Service
public class UserService {
    @Validated
    public User createUser(@Valid UserDTO userDTO) {
        return new User();
    }

    @NotNull
    public User findById(@Min(1) Long id) {
        return new User();
    }

    @Validated(ValidationGroups.Update.class)
    public void updateUser(@Valid UserDTO userDTO) {}
}

Annotate the configuration class with @EnableMethodValidation and service methods with @Validated, @Valid, @NotNull, @Min, etc., to enforce constraints at the service layer.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaspringBean ValidationvalidationJSR-380
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.