验证数据是一项常见的任务发生在所有应用程序层,从展示层到持久层。通常在每一层中实现相同的验证逻辑耗时且容易出错,幸好hibernate提供了对object对象的校验,只需加上相应的注解即可。
1. Getting started
在Maven中使用Hibernate Validator进行Bean验证,需要以下条件:
- JDK的版本大于等于1.6
- Apache Maven
- 联网(Maven需要联网去下载依赖包)
1.1.项目设置
使用Hibernate Validator需要在pom.xml中进行以下配置:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.2.Final</version>
</dependency
这也将依赖Bean验证API(javax.validation:validation-api:1.1.0.Final)。
1.1.1. Unified EL
Hibernate验证器需要实现统一的表达式语言(JSR 341)来评估违反约束的消息中的动态表达式。当您的应用程序在Java EE容器(如JBoss)中运行时,容器已经提供了EL实现。然而,在Java SE环境中,必须将一个实现添加到POM文件中。例如:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b08</version>
</dependency>
1.1.2. CDI
Bean验证定义需要Java EE的上下文和依赖注入。如果您的应用程序运行的环境中没有提供功能,你可以使用Hibernate Validator CDI便携扩展,在pom.xml中添加以下依赖:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-cdi</artifactId>
<version>5.4.2.Final</version>
</dependency>
注意:需要添加此依赖的通常不是Java EE应用服务器上运行的应用程序。
1.1.3. Running with a security manager
Hibernate验证框架的支持运行在一个安全管理器中。要做到这一点,您必须为Hibernate验证器和Bean Validation API代码库分配几个权限。下面通过策略文件作为处理Java默认策略实现,显示了如何做到这一点:
grant codeBase "file:path/to/hibernate-validator-5.4.2.Final.jar" {
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "setContextClassLoader";
permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers";
// Only needed when working with XML descriptors (validation.xml or XML constraint mappings)
permission java.util.PropertyPermission "mapAnyUriToUri", "read";
};
grant codeBase "file:path/to/validation-api-1.1.0.Final.jar" {
permission java.io.FilePermission "path/to/hibernate-validator-5.4.2.Final.jar", "read";
};
1.2. 运用约束
直接通过例子来了解如何运用约束
package org.hibernate.validator.referenceguide.chapter01;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class Car {
@NotNull//不能为null
private String manufacturer;
@NotNull//不能为null
@Size(min = 2, max = 14)//在2~14个字符之间
private String licensePlate;
@Min(2)//最小为2
private int seatCount;
public Car(String manufacturer, String licencePlate, int seatCount) {
this.manufacturer = manufacturer;
this.licensePlate = licencePlate;
this.seatCount = seatCount;
}
//getters and setters ...
}
1.3. 验证约束
还是通过例子来了解:
package org.hibernate.validator.referenceguide.chapter01;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class CarTest {
//一个Validator实例是线程安全的,可能多次被重用。因此可以安全地存储在一个静态字段。
private static Validator validator;
@BeforeClass
public static void setUpValidator() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@Test
public void manufacturerIsNull() {
Car car = new Car( null, "DD-AB-123", 4 );
//validate()方法返回一组ConstraintViolation实例,可以遍历为了看到哪些验证错误发生,如果对象成功通过验证,则返回一个空Set。
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals( "may not be null", constraintViolations.iterator().next().getMessage() );
}
@Test
public void licensePlateTooShort() {
Car car = new Car( "Morris", "D", 4 );
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals(
"size must be between 2 and 14",
constraintViolations.iterator().next().getMessage()
);
}
@Test
public void seatCountTooLow() {
Car car = new Car( "Morris", "DD-AB-123", 1 );
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals(
"must be greater than or equal to 2",
constraintViolations.iterator().next().getMessage()
);
}
@Test
public void carIsValid() {
Car car = new Car( "Morris", "DD-AB-123", 2 );
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate( car );
assertEquals( 0, constraintViolations.size() );
}
}
注意:代码没有直接引用Hibernate Validator的类,而是使用javax.validation中的类或接口来实现验证,提高代码的可移植性。
1.4. Java 8 support
Java 8从Hibernate验证器的角度介绍了一些有价值的增强功能。本节简要介绍了基于Java 8的Hibernate验证器特性。它们只能在Hibernate Validator 5.2和更高版本中使用。
1.4.1. Type arguments constraints
在Java 8中,可以在任何位置使用任何类型的注解,这包括类型参数。Hibernate验证器支持定义在类型参数集合、映射和自定义的参数类型上的验证。The Type argument constraints章节提供了关于如何应用和使用类型参数约束的进一步信息。
1.4.2. Actual parameter names
现在,Java 8反射API可以检索方法或构造函数的实际参数名称。Hibernate验证器使用这种能力来告知实际的参数名称,而不是arg0、arg1等。The ParameterNameProvider 章节解释了如何使用新的基于反射的参数名称提供程序。
1.4.3. New date/time API
Java 8引入了一个新的日期/时间API。Hibernate验证器为新的API提供了充分的支持,在这些新API中,@ future和@ past约束可以应用于新类型。对@ future和@ past支持的类型的完整列表可以在Bean Validation constraints章节中找到。
1.4.4. Optional type
Hibernate验证器还提供了对Java 8可选类型的支持,通过展开可选实例并验证内部值。在Optional unwrapper provides 示例进一步的讨论。