Validate Object Graphs with Java Bean Validation’s @Valid Annotation

Share this article

Validate Object Graphs with Java Bean Validation’s @Valid Annotation

The Java Bean Validation’s @Valid constraint annotation makes sure that when an object is validated, the validation recurses to all fields that are annotated with @Valid. This makes it really easy to perform the usually complex task of validating entire object graphs.

To make sense of this post you should know the basics of how to use Java Bean Validation.

Object Graph Validation in One Step With @Valid

To better understand why the @Valid annotation is such a valuable feature, I will start by showing you what you have to do to validate an object graph that doesn’t use it.

Bloated Code without @Valid

Consider the case of a constrained class A that declares a dependency on another set of constrained classes B and C. What we have here is just a simple object graph, where each collaborator on this association has its own set of validation constraints. Without the @Valid annotation, validating the graph is a pretty cumbersome task, as each instance of A, B, and C has to be validated separately, through multiple calls to the validate() method of the Validator interface implementer.

Consider the following classes:

public class AccountProcessor {

    @NotEmpty(message = "Account number may not be empty")
    @Size(min = 8, max = 8,
            message = "Account number must be an 8-digit string")
    private String accountNumber;

    public AccountProcessor(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public void processAccount() {
        // process the account here
    }

}

public class PaymentService {

    private AccountProcessor accountProcessor;

    @NotEmpty(message = "Payment method may not be empty")
    private String paymentMethod;

    public PaymentService(AccountProcessor accountProcessor, String paymentMethod) {
        this.accountProcessor = accountProcessor;
        this.paymentMethod = paymentMethod;
    }

}

At this point, we’ve managed to create a basic object graph with PaymentService using AccountProcessor. Without the @Valid annotation, the graph has to be validated on a per-object basis, as follows:

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
AccountProcessor accountProcessor = new AccountProcessor("");
PaymentService paymentService = new PaymentService(accountProcessor, "");
validator
        .validate(accountProcessor)
        .stream()
        .forEach(violation -> System.out.println(violation.getMessage()));
validator
        .validate(paymentService)
        .stream()
        .forEach(violation -> System.out.println(violation.getMessage()));

In this case, neither the AccountProcessor instance nor its PaymentService have been correctly initialized and taken together both will raise three constraint violations.

While the validation works as expected, the code is bloated with having to call validate separately for each instance.

Using @Valid to Validate an Object Graph

To improve the validation code, let’s refactor the PaymentService class and bind the @Valid annotation to its AccountProcessor field:

public class PaymentService {

    @Valid
    private AccountProcessor accountProcessor;

    // other class members and methods as before

}

The impact of using the @Valid annotation can be spotted in a snap here:

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
AccountProcessor defaultAccountProcessor = new DefaultAccountProcessor("");
PaymentService paymentService = new PaymentService(defaultAccountProcessor, "");
validator
        .validate(paymentService)
        .stream()
        .forEach(violation -> System.out.println(violation.getMessage()));

The code is more compact and streamlined but will result in the same validation result as the longer block before. Whenever an instance of PaymentService gets validated, AccountProcessor will be validated recursively. We kill two birds with one stone!

From a client code’s perspective, the validation is apparently performed through a single call to validate(), which is the right way to do it, as this neatly hides from the developer the complexities involved in scanning the whole object graph structure. Internally, though, the method is actually called recursively while the validator traverses the tree from the root object to the leaf objects. Moreover, the use of @Valid isn’t restricted to just class fields, as it can be utilized in collections and arrays as well.

This is object graph validation made easy.

Validate an entire object graph (did somebody say tree?) with the @Valid annotation

Summary

The @Valid annotation is a key feature of Bean Validation, as it allows to validate object graphs with a single call to the validator. To make use of it all fields that should be recursively checked should be annotated with @Valid.

Another interesting feature that goes beyond basic bean validation is validating individual fields and values with validateProperty() and validateValue(). The API has of course much more to offer, so make sure to check the official docs and keep up to date with the latest news.

Frequently Asked Questions (FAQs) about Java Bean Validation and Valid Annotation

What is the main difference between @Valid and @Validated annotations in Java?

The @Valid and @Validated annotations are used in Java for bean validation. The @Valid annotation is a standard Java annotation for bean validation, defined in the Java Specification Request (JSR) 380. It is used to validate an object before it is used. On the other hand, @Validated is a Spring-specific annotation that provides groups support for validation. It allows you to categorize validations so that you can apply them to different situations as needed.

How does the @Valid annotation work in Spring?

In Spring, the @Valid annotation is used in conjunction with the @RequestBody annotation in a method parameter to perform automatic validation. When a request is made, Spring checks the request body against the constraints defined in the bean. If any constraint is violated, a MethodArgumentNotValidException is thrown, which can be handled to return a meaningful error response.

How can I implement custom validation in Spring Boot?

Custom validation in Spring Boot can be implemented by creating a custom annotation and a corresponding ConstraintValidator. The custom annotation defines the constraint, and the ConstraintValidator implements the validation logic. Once created, the custom annotation can be used in the same way as built-in validation annotations.

What is the role of Bean Validation in Spring Boot?

Bean Validation is a standard validation specification that allows developers to express constraints on object models via annotations. In Spring Boot, Bean Validation is used to ensure that the data an application receives is in the correct format and meets specified constraints. This helps to prevent invalid or malicious data from being processed by the application.

How can I handle validation errors in Spring Boot?

Validation errors in Spring Boot can be handled using an exception handler. When a validation error occurs, a MethodArgumentNotValidException is thrown. This exception can be caught in a @ControllerAdvice class, where you can extract the validation errors from the exception and return a meaningful error response.

What is the purpose of the javax.validation.Valid annotation in Java?

The javax.validation.Valid annotation in Java is used to perform validation on an object. When an object is annotated with @Valid, the validation engine will validate the object’s properties based on the constraints defined in the object’s class. This helps to ensure that the object’s state is valid before it is used.

How can I use groups in Bean Validation?

Groups in Bean Validation allow you to categorize constraints so that you can validate only a subset of constraints at a time. To use groups, you first define an interface that represents the group. Then, you associate the group with a constraint by specifying the group in the constraint’s groups attribute. Finally, you validate the group by passing the group class to the validate method.

Can I use @Valid and @Validated annotations together?

Yes, you can use @Valid and @Validated annotations together. The @Valid annotation can be used to validate an object, while the @Validated annotation can be used to validate a group of constraints. However, keep in mind that @Validated is a Spring-specific annotation, while @Valid is a standard Java annotation.

How can I disable validation in Spring Boot?

Validation in Spring Boot can be disabled by removing the @Valid or @Validated annotations from your controllers. However, this is not recommended as it can lead to invalid or malicious data being processed by your application. Instead, consider adjusting your validation rules or handling validation errors more gracefully.

What is the difference between field-level and class-level constraints in Bean Validation?

Field-level constraints in Bean Validation are applied to individual fields in a class. They are used to validate the state of a single field. On the other hand, class-level constraints are applied to the class itself. They are used to validate the state of the object as a whole, often based on multiple fields.

Alejandro GervasioAlejandro Gervasio
View Author

Alejandro Gervasio is a senior System Analyst from Argentina who has been involved in software development since the mid-80's. He has more than 12 years of experience in PHP development, 10 years in Java Programming, Object-Oriented Design, and most of the client-side technologies available out there.

nicolaipquick-tipvalidation
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week