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

    Alejandro Gervasio
    Share

    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.

    CSS Master, 3rd Edition