Java Bean Validation Basics

1. Overview

In this quick article, we’ll go over the basics of validating a Java bean with the standard framework – JSR 380, also known as Bean Validation 2.0.

Validating user input is, of course, a super common requirement in most applications, and the Java Bean Validation framework has become the de-facto standard for handling this kind of logic.

Further reading:

Validation in Spring Boot

Learn how to validate domain objects in Spring Boot using Hibernate Validator, the reference implementation of the Bean Validation framework.

Read more

Method Constraints with Bean Validation 2.0

An introduction to method constraints using Bean Validation 2.0.

Read more

2. JSR 380

JSR 380 is a specification of the Java API for bean validation, part of JavaEE and JavaSE, which ensures that the properties of a bean meet specific criteria, using annotations such as @NotNull, @Min, and @Max.

This version requires Java 8 or higher, and takes advantage of new features added in Java 8 such as type annotations, and supports new types like Optional and LocalDate.

For full information on the specifications, go ahead and read through the JSR 380.

3. Dependencies

We’re going to use a Maven example to show the required dependencies, but of course, these jars can be added using various ways.

3.1. Validation API

Per the JSR 380 specification, the validation-api dependency contains the standard validation APIs:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.0.Final</version>
</dependency>

3.2. Validation API Reference Implementation

Hibernate Validator is the reference implementation of the validation API.

To use it, we must add the following dependencies:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.2.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>6.0.2.Final</version>
</dependency>

A quick note here is that hibernate-validator is entirely separate from the persistence aspects of Hibernate and by adding it as a dependency, we’re not adding these persistence aspects into the project.

3.3. Expression Language Dependencies

JSR 380 provides support for variable interpolation, allowing expressions inside the violation messages.

To parse these expressions, we must add a dependency on both the expression language API and an implementation of that API. GlassFish provides the reference implementation:

<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.0</version>
</dependency>

<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.6</version>
</dependency>

If these JARs are not added, you will get an error message at run time as shown below:

HV000183: Unable to load ‘javax.el.ExpressionFactory’. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead

4. Using Validation Annotations

We’ll use a User bean as the main example here and work on adding some simple validation to it:

import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.constraints.Email;

public class User {

    @NotNull(message = "Name cannot be null")
    private String name;

    @AssertTrue
    private boolean working;

    @Size(min = 10, max = 200, message
      = "About Me must be between 10 and 200 characters")
    private String aboutMe;

    @Min(value = 18, message = "Age should not be less than 18")
    @Max(value = 150, message = "Age should not be greater than 150")
    private int age;

    @Email(message = "Email should be valid")
    private String email;

    // standard setters and getters
}

All of the annotations used in the example are standard JSR annotations:

  • @NotNull – validates that the annotated property value is not null

  • @AssertTrue – validates that the annotated property value is true

  • @Size – validates that the annotated property value has a size between the attributes min and max; can be applied to String, Collection, Map, and array properties

  • @MinvValidates that the annotated property has a value no smaller than the value attribute

  • @Max – validates that the annotated property has a value no larger than the value attribute

  • @Email – validates that the annotated property is a valid email address

Some annotations accept additional attributes, but the message attribute is common to all of them. This is the message that will usually be rendered when the value of the respective property fails validation.

Some additional annotations that can be found in the JSR are:

  • @NotEmpty – validates that the property is not null or empty; can be applied to String, Collection, Map or Array values

  • @NotBlank – can be applied only to text values and validated that the property is not null or whitespace

  • @Positive and @PositiveOrZero – apply to numeric values and validate that they are strictly positive, or positive including 0

  • @Negative and @NegativeOrZero – apply to numeric values and validate that they are strictly negative, or negative including 0

  • @Past and @PastOrPresent – validate that a date value is in the past or the past including the present; can be applied to date types including those added in Java 8

  • *@Future* and *@FutureOrPresent* – validates that a date value is in the future, or in the future including the present

The validation annotations can also be applied to elements of a collection:

List<@NotBlank String> preferences;

In this case, any value added to the preferences list will be validated.

The specification also supports the new Optional type in Java 8:

private LocalDate dateOfBirth;

public Optional<@Past LocalDate> getDateOfBirth() {
    return Optional.of(dateOfBirth);
}

Here, the validation framework will automatically unwrap the LocalDate value and validate it.

5. Programmatic Validation

Some frameworks – such as Spring – have simple ways of triggering the validation process by just using annotations. This is mainly so that we don’t have to interact with the programmatic validation API.

Let’s now go the manual route and set things up programmatically:

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();

To validate a bean, we must first have a Validator object, which is constructed using a ValidatorFactory.

5.1. Defining the Bean

We’re now going to set up this invalid user – with a null name value:

User user = new User();
user.setWorking(true);
user.setAboutMe("Its all about me!");
user.setAge(50);

5.2. Validate the Bean

Now that we have a Validator, we can validate our bean by passing it to the validate method. Any violations of the constraints defined in the User object will be returned as a Set.

Set<ConstraintViolation<User>> violations = validator.validate(user);

By iterating over the violations, we can get all the violation messages by using the getMessage method.

for (ConstraintViolation<User> violation : violations) {
    log.error(violation.getMessage());
}

In our example (ifNameIsNull_nameValidationFails), the set would contain a single ConstraintViolation, with the message, “Name cannot be null”.

6. Conclusion

This tutorial focused on a simple pass through the standard Java Validation API and illustrated the basics of bean validation using javax.validation annotations and APIs.

An implementation of this the concepts in this article can be found in the GitHub project. We’ll feature more features of JSR 380 and Hibernate Validator in upcoming articles.

All code snippets can be found over on GitHub.

Leave a Reply

Your email address will not be published.