HelloKoding

Practical coding guides

Java Bean Validation API and Hibernate Validator Tutorial with Example

Java Bean Validation API is a specification which

  • Providing data validation model via built-in and custom defined constraint annotations placed on class, field and method
  • Providing APIs to validate objects, object graphs, parameters and return values of methods and constructors
  • Defined in javax.validation package and available as a part of Java EE since Java 6

Hibernate Validator is an implementation of the API

In this tutorial, you will learn to implement an Hibernate Validator example to validate a data model

What you’ll need

  • JDK 8+ or OpenJDK 8+
  • Maven 3+

Project structure

├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── hellokoding
│       │           └── validation
│       │               ├── Main.java
│       │               ├── Product.java
│       │               ├── ProductCodeExisting.java
│       │               └── ProductCodeExistingValidator.java
│       └── resources
│           └── ValidationMessages.properties
└── pom.xml

Dependencies

Include hibernate-validator into your pom.xml

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.16.Final</version>
</dependency>

Unified Expression Language (EL) is also required for evaluating dynamic expressions in constraint violation messages when you run your application in Java SE. In Java EE, it is already provided by container

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.1-b09</version>
</dependency>

Full content of pom.xml as below

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hellokoding</groupId>
    <artifactId>java.bean-validation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <exec.mainClass>com.hellokoding.validation.Main</exec.mainClass>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.16.Final</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.el</artifactId>
            <version>3.0.1-b09</version>
        </dependency>

    </dependencies>
</project>

Define data model and validation constraints

Define data model

Product.java

package com.hellokoding.validation;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;

public class Product {
    @NotNull
    private Long id;

    @ProductCodeExisting
    private String code;

    @Size(min=1, max = 10, message = "{Size.name}")
    private String name;

    @NotNull(message = "must be not null")
    private String description;

    @Min(value = 1, message = "{Min.price}")
    private BigDecimal price;

    public Product(Long id, String code, String name, String description, BigDecimal price) {
        this.id = id;
        this.code = code;
        this.name = name;
        this.description = description;
        this.price = price;
    }
}

@NotNull, @Size and @Min are built-in constraints

@ProductCodeExisting is a custom constraint defined as below

{Size.name} and {Min.price} are message templates, theirs value will be replaced at run time by the below ValidationMessages.properties

Define custom constraint

ProductCodeExisting.java

package com.hellokoding.validation;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(validatedBy = ProductCodeExistingValidator.class)
public @interface ProductCodeExisting {
    String message() default "{ProductCodeExisting}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };
}

An annotation type is defined using the @interface keyword

All attributes of an annotation type are declared in a method-like manner, the following are required

  • an attribute message that returns the default violation messages
  • an attribute groups to restrict the set of constraints applied during validation
  • an attribute payload used by clients of the Bean Validation API to assign custom payload objects to a constraint

{ProductCodeExisting} is a message template. Its value will be replaced at run time by the below ValidationMessages.properties

Define custom constraint validator

ProductCodeExistingValidator.java

package com.hellokoding.validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class ProductCodeExistingValidator implements ConstraintValidator<ProductCodeExisting, String> {
    @Override
    public boolean isValid(String productCode, ConstraintValidatorContext context) {
        return productCode.equals("P1") || productCode.equals("P2");
    }
}

Interpolate / customize the validation messages

Provide the message value when you express the constraint on defining data model, for example the constraint @NotNull(message = "must be not null") of Product.java, or add a file named ValidationMessages.properties to the classpath

ValidationMessages.properties

javax.validation.constraints.NotNull.message=is required
Size.name=must be between {min} and {max} character long
ProductCodeExisting="${validatedValue}" is not existing
Min.price=${formatter.format('%1$.2f', validatedValue)} is invalid, must be greater than or equal to {value}

Bean Validation API uses the Unified Expression Language (EL) in constraint violation messages. The following objects available in the EL context

  • the attribute values of the constraint mapped to the attribute names, for example {min} and {max} of the message Size.name mapped to @Size constraint annotation in Product.java
  • ${validatedValue} mapped to the current validated value of field, bean or method parameter
  • a bean named formatter exposing the var-arg method format(String format, Object…​ args) which behaves like java.util.Formatter.format(String format, Object…​ args)

Validating the constraints

Use the built-in ValidatorFactory of javax.validation to get a Validator instance

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

Use validator to validate all constraints on object, for example

Set<ConstraintViolation<Product>> constraintViolations = validator.validate(product);

Main.java

package com.hellokoding.validation;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Product product = new Product(null, "Hello Koding", "Coding Courses", null, new BigDecimal(0.789));
        Set<ConstraintViolation<Product>> constraintViolations = validator.validate(product);

        for(ConstraintViolation constraintViolation : constraintViolations) {
            String fieldName = constraintViolation.getPropertyPath().toString().toUpperCase();
            System.out.println(fieldName + " " + constraintViolation.getMessage());
        }
    }
}

Type command mvn clean package exec:java at your project root directory to run, expected output

PRICE 0.79 is less than 1
ID is required
NAME must be between 1 and 10 character long
DESCRIPTION must be not null
CODE "Hello Koding" is not existing

Source code

https://github.com/hellokoding/hellokoding-courses/tree/master/java-examples/java-bean-validation

Follow HelloKoding