Hibernate Validator, a Java Bean validation tool

Why is data verification required

For robustness of the system, defensive programming makes the system more robust and prevents the generation of dirty data.

Besides, using parameter verification can make problems be thrown out at the beginning, which is in line with the concept of Fail-Fast and can help us locate and solve anomalies and bugs faster. For example, 🌰 can be taken to see Why Methods Should Check Their Arguments. The constructor created an object with the wrong two String arguments, but failed to verify the arguments in time to hide the problem. As a result, the code that uses this object in other places has an exception, instead of throwing an exception where the object is created, which increases the link and difficulty of locating the problem.

How to verify data correctly

Data validation is a common task across all application layers, from the presentation layer to the persistence layer. Often similar or identical data validation logic is implemented in each layer, which can be time-consuming and error-prone. For example, data verification code like the following is scattered in their own projects, which is definitely not a good thing for a long time:

if(a.size > 10 && a.size < 100){
    Result result = Reuslt.fail("Invalid parameter size, please check input!");return result;
}

if(xxx)
    return xxx ;
Copy the code

To avoid duplicate validation code (following the DRY principle), the recommended way to validate data is to place the constraint logic on the data directly on the data’s domain objects.

The advantage of this approach is that there is no need to write the constraint logic repeatedly each time the data is validated, and the constraint logic is clustered into the domain object, which is easy to maintain the code.

Constrain Once, Validate Everywhere on the first page of Java Bean Validation. “Constrain Once, validate everywhere.” 👇

Java Bean Validation is an official Java Validation specification that follows these principles. It has been updated from JSR 303 1.0 to JSR 349 1.1 to JSR 380 2.0 (2.0 completed in 2017.08). It’s been through three versions.

Java Bean Validation specification corresponding jar package is Jakarta. The Validation: Jakarta. Validation – API, inside the package defines for the verification of the entity and method annotations and apis, but there is no specific implementation of the API, It simply provides a specification for data validation that requires a third party to implement its API for data validation.

What data verification tools are available

  1. Hibernate Validator (JBoss)
  2. Apache BVal (Apache)

Hibernate Validator and Apache BVal are two validation tools that implement the latest Bean Validation2.0 specification as well as the latest Bean Validation2.0 specification. I just couldn’t find it. Other data validation tools not developed by a well-known “big factory” have found Oval. Oval does not implement data Validation according to the Bean Validation specification, but it does configure data constraints using annotations and provides a way to accommodate Bean Validation annotations. If you are interested, check out the documentation. More detailed than Apache BVal 🐶).

This is where the question arises, which data validation tool should we use?

Let’s compare the two tools in terms of “performance” and “community activity and documentation.”

First and foremost, performance, Hibernate Validator6.0.9 Hibernate Validator Hibernate Validator6.0.9 Hibernate Hibernate Validator Validator6.0.4.Final, Hibernate Validator 5.4.2.Final, Apache BVal 1.1.2

Two of the most intuitive conclusions can be drawn from the benchmark results above:

  1. Hibernate Validator performs significantly better than BVal.
  2. Hibernate Validator’s performance continues to improve with each release.

Bean Validation Benchmark (re) Revisited The relevant code for this benchmark is hibernate/ Beanvalidation-benchmark in the Github repository.

Hibernate Validator’s Github repository has far more PR activity, contributors, stars and forks than BVal’s. Hibernate Validator is usually the first thing that pops up when you search for a parameter validation tool in a search engine. Even the default validation tool for Springboot is Hibernate Validator.

So back to the question above: Which data validation tool should we use?

The answer is already there 🐶.

What features Hibernate Validator provides

  1. Constraint configuration and Validation mechanisms based on annotations (Bean Validation specification annotations and Hibernate Validator built-in annotations see here).
  2. Annotation constraint configurations at Class, Method (input and return values) (including constructors), Field, and Property levels are supported.
  3. If a Method has more than one input parameter, you can verify that the relationship between the input parameters complies with the constraint. Portal 🚪.
  4. Verification nested constraints are supported, and loop dependent scenarios are supported. Portal 🚪
  5. Supports validation of objects inside container classes (Set, List, Map, java.util.Optional, etc.) and can be extended (support self-written container classes). Portal 🚪
  6. A constraint definition can be passed to a subclass or implementation class of an interface by inheriting a parent class or implementation interface (overrides on method constraints need to comply with lee’s substitution rule) portal 🚪.
  7. Supports custom constraints. Portal 🚪
  8. You can set constraint groups. Portal 🚪
  9. Support for constraint choreography. Portal 🚪
  10. Constraint rules support the use of scripting languages (as long as the corresponding scripting engine is introduced in the project, the extension is implemented by implementing these two interfacesAbstractCachingScriptEvaluatorFactory.ScriptEvaluator.)Portal 🚪.
  11. Flexible prompt configuration when the verification fails. Portal 🚪
  12. Rich expandable HOOK TODO to provide portals

These are just a few of Hibernate Validator’s basic and interesting features. To learn more, check out the official documentation at 👉.

The most important thing you need to do when validating parameters is to flag the constraint stability that fails, especially when this is intended for users, so the next section describes the configuration of the Hiberbate Validator prompt.

Hibernate Validator configuration verification failure prompt

Configure the prompt in the Resource Bundle

On the constraint annotation of Bean Validation, there is a message attribute that configates a message indicating that the constraint Validation of the annotation is invalid. Such as @ NotNull:

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })
public @interface NotNull {

	String message(a) default "{javax.validation.constraints.NotNull.message}"; Class
      [] groups() default { }; Class
      [] payload() default { }; @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Documented @interface List { NotNull[] value(); }}Copy the code

When using the @notnull annotation, the default prompt is"{javax.validation.constraints.NotNull.message}"Hibernate Validator will find the value of the key in curly braces from its built-in Resource bundle as a prompt, and also supports internationalization:

In addition, we can under the classpath of your project to add a custom resource bundles, the default file called ValidationMessages. Properties. If you want to support internationalization, you can add configuration files for other languages, such as validationMessages_en_us.properties. By default, Hibernate Validator internally decides which language configuration file to use using the return value of Locale#getDefault().

Dynamic prompt expression

In addition to using resource bundles to configure prompt expressions, Hibernate Validator also supports dynamic prompt expressions of the following form:

    @Min( value = 2, message = "There must be at least {value} seat${value > 1 ? 's' : ''}" )
    private int seatCount;
Copy the code

{} can have the following values:

  1. Key in the Resource bundle (to get the value corresponding to the key).
  2. Attribute name in the annotation (to get the value of the attribute name).

${} is the Unified Expression Language (EL Expression) that conforms to the JSR 341 specification.

  1. Use the attribute name in the annotation (to get the value of the attribute name)
  2. throughvalidatedValueTo read the value of the currently validated object: ${validatedValue}.
  3. useThe formatter. The format (String format, the Object... args)The effect is similar to calling this method in Java codeJava. Util. The Formatter. The format (String format, the Object... args)

Here’s an example:

public class Car {

    @NotNull
    private String manufacturer;

    @Size( min = 2, max = 14, message = "The license plate '${validatedValue}' must be between {min} and {max} characters long" )
    private String licensePlate;

    @Min( value = 2, message = "There must be at least {value} seat${value > 1 ? 's' : ''}" )
    private int seatCount;

    @DecimalMax( value = "350", message = "The top speed ${formatter.format('%1$.2f', validatedValue)} is higher " + "than {value}" )
    private double topSpeed;

    @DecimalMax(value = "100000", message = "Price must not be higher than ${value}")
    private BigDecimal price;

    public Car(
            String manufacturer,
            String licensePlate,
            int seatCount,
            double topSpeed,
            BigDecimal price) {
        this.manufacturer = manufacturer;
        this.licensePlate = licensePlate;
        this.seatCount = seatCount;
        this.topSpeed = topSpeed;
        this.price = price;
    }

    //getters and setters ...
}
Copy the code

Custom prompt generator

If neither of the above configuration methods can satisfy your needs, you can customize the prompt generator.

Custom generator the clues that need to implement a Java Bean Validation specification javax.mail. Validation. MessageInterpolator interface, Then in the bootstrap (start) javax.mail. Validation. Will own custom implementations MessageInterpolator ValidatorFactory process set in, can take effect.

import javax.validation.Configuration;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.bootstrap.GenericBootstrap;
importjavax.validation.executable.ExecutableValidator; GenericBootstrap genericBootstrap = Validation.byDefaultProvider(); Configuration<? > configure = genericBootstrap.configure(); configure.messageInterpolator(new MyMessageInterpolator() ) // Custom implementation of MessageInterpolator
ValidatorFactory factory = configure.buildValidatorFactory();

// Validators are used to validate Java beans
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<Car>> constraintViolationExceptions = validator.validate(car);

ExecutableValidator (executableValidator
ExecutableValidator executableValidator = validator.forExecutables();

Copy the code

In addition to customizing the prompt generator, Java Bean Validation and Hibernate Validation have a number of interfaces that allow you to customize the implementation to interfere with the Validation process: Javax.mail. Validation. The Configuration, org. Hibernate. The validator. BaseHibernateValidatorConfiguration, and hibernate Validation this section of the official document for Bootstrapping

As an added bonus, to use Hibernate Validation in your project, you must import the Hibernate Validation package into your project as well as the Java Bean Validation specification package. Hibernate Validation validates the Java Bean using the Hibernate Validation API provided by Java Bean Validation, as shown in 👆 above. You do not need to directly manipulate the Hibernate Validation API. This is done through Java’s Service Provider Interface (SPI) mechanism, which implements known services and loads them in a plugin-like manner (Hibernate Validation). Called by the service definer (Bean Validation). There are many scenarios to use SPI, such as Spring Boot, Dubbo, JDBC, SLF4J, etc. Those who are interested can go to learn more about it.

Hibernate Validator is integrated with other frameworks

Hibernate Validator can be integrated with other frameworks in an AOP way, such as with Spring Boot, which is implemented by Spring’s AOP by default. Spring AOP is implemented through a run-time dynamic proxy approach. AspectJ can also be used if you want to implement AOP using compile-time bytecode weaving. Both are similar in nature, using the idea of “proxy”, so that users do not need to manually call the Bean Validator/Hibernat Validation API to verify parameters. The framework encapsulates the parameters for you.

reference

Why Methods Should Check Their Arguments

Hibernate Validator 6.0.22.Final – JSR 380 Reference Implementation: Reference Guide

Parameter verification is an elegant practice

Bean Validation benchmark (re)revisited

Validating Form Input

An integrated Hibernate-Validator validation framework for SpringBoot source code parsing

Java SPI mechanism in detail

SLF4J Implementation Principle (Simple Analysis)

Dubbo SPI and adaptive mechanisms

Spring AOP — Faceted programming in Spring

👍👍👍 Native AspectJ usage analysis and Spring-AOP principle analysis

Intro to AspectJ

Interviewer: What is AOP? What is the difference between Spring AOP and AspectJ?