Hibernate Validator JSR303示例教程

时间:2020-02-23 14:41:27  来源:igfitidea点击:

欢迎使用Hibernate Validator示例教程。
数据验证是任何应用程序不可或者缺的一部分。
您将在使用Javascript的表示层进行数据验证。
然后在服务器端编码之前处理客户端数据。
数据验证也将在持久化之前进行,以确保其遵循正确的格式。

Hibernate 验证器

验证是一项跨领域的任务,因此我们应尽量将其与业务逻辑分开。
因此,JSR303和JSR349提供了使用批注来验证bean的规范。
Hibernate Validator提供了这两个bean验证规范的参考实现。

使用Hibernate Validator非常容易,最重要的是,我们可以轻松扩展它并创建自己的自定义验证注释。
今天,我们将通过示例详细研究Hibernate 验证器。
最后,我们将有一个测试程序来检查验证。

我为所有Hibernate 验证示例创建了一个示例项目,下图显示了最终的项目结构。

Hibernate Validator Maven依赖关系

我们需要添加JSR303和Hibernate Validator依赖项才能使用它们。

<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>1.1.0.Final</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>5.1.1.Final</version>
</dependency>

Hibernate Validator还需要实现统一表达式语言(JSR 341)来评估约束违例消息中的动态表达式。

如果您的应用程序在Servlet容器(例如JBoss)中运行,则已经提供了它。
但是,如果要在像我的示例项目这样的独立应用程序中使用它,则需要手动添加它们。
所需的依赖关系是;

<dependency>
	<groupId>javax.el</groupId>
	<artifactId>javax.el-api</artifactId>
	<version>2.2.4</version>
</dependency>
<dependency>
	<groupId>org.glassfish.web</groupId>
	<artifactId>javax.el</artifactId>
	<version>2.2.4</version>
</dependency>

检查镜像中项目中所有的Maven依赖项。
现在,我们准备开始使用Hibernate 验证示例。

Hibernate 验证示例

让我们创建一个简单的类,并向其中添加一些验证。

Employee.java

package com.theitroad.validator.hibernate.model;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.CreditCardNumber;
import org.hibernate.validator.constraints.Email;

public class Employee {

	@Min(value=1, groups=EmpIdCheck.class)
	private int id;
	
	@NotNull(message="Name cannot be null")
	@Size(min=5, max=30)
	private String name;
	
	@Email
	private String email;
	
	@CreditCardNumber
	private String creditCardNumber;
	
	//default no-args constructor
	public Employee(){}
	
	public Employee(int id, String name, String email, String ccNum){
		this.id=id;
		this.name=name;
		this.email=email;
		this.creditCardNumber=ccNum;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getCreditCardNumber() {
		return creditCardNumber;
	}

	public void setCreditCardNumber(String creditCardNumber) {
		this.creditCardNumber = creditCardNumber;
	}
	
}

我们应该避免使用特定于实现的注释进行松散耦合。
但是Hibernate 验证器提供了一些非常好的验证注释,例如," @ Email"和" @CreditCardNumber"。

我们还可以提供自定义错误消息,以用于任何验证。
如果未定义任何消息,则将使用Hibernate 内置错误消息。

我们可以将组分配给任何验证,这仅对验证一组字段有用。
例如,如果我只需要验证Employee id字段,则可以使用EmpIdCheck组。
为此,我们需要定义一个类/接口。

EmpIdCheck.java

package com.theitroad.validator.hibernate.model;

public interface EmpIdCheck {
}

稍后我们将在测试程序中查看它的用法。

Hibernate Validator自定义错误消息

我们也可以定义自定义错误消息,我们所要做的就是在类路径中有" ValidationMessages.properties"文件。

ValidationMessages.properties

#Hibernate Bug for @CreditCardNumber Workaround - https://hibernate.atlassian.net/browse/HV-881
org.hibernate.validator.constraints.LuhnCheck.message=The check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed.

org.hibernate.validator.constraints.Email.message=Invalid email address

这些属性文件还支持本地化,在这种情况下,我们需要保留诸如ValidationMessages_tr_TR.properties之类的文件名。

消息的属性名称是完全分类的注释名称,末尾是message,您可以轻松地从以上示例中找出来。

基于XML的约束验证

有时我们想对第三方类进行验证,然后就不能对它们使用批注。
在这种情况下,基于xml配置的验证非常方便。
例如,假设我们有一个没有任何验证注释的类,如下所示。

EmployeeXMLValidation.java

package com.theitroad.validator.hibernate.model;

public class EmployeeXMLValidation {

	private int id;
	
	private String name;
	
	private String email;
	
	private String creditCardNumber;
	
	//default no-args constructor
	public EmployeeXMLValidation(){}
	
	public EmployeeXMLValidation(int id, String name, String email, String ccNum){
		this.id=id;
		this.name=name;
		this.email=email;
		this.creditCardNumber=ccNum;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getCreditCardNumber() {
		return creditCardNumber;
	}

	public void setCreditCardNumber(String creditCardNumber) {
		this.creditCardNumber = creditCardNumber;
	}
	
}

上面的beanHibernate 验证的一个简单示例如下所示。

employeeXMLValidation.xml

<constraint-mappings xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.1.xsd"
	xmlns="https://jboss.org/xml/ns/javax/validation/mapping" version="1.1">

	<default-package>com.theitroad.validator.hibernate.model</default-package>

	<bean class="EmployeeXMLValidation" ignore-annotations="true">
		<field name="id">
			<constraint annotation="javax.validation.constraints.Min">
				<element name="value">1</element>
			</constraint>
		</field>
		<field name="name">
			<constraint annotation="javax.validation.constraints.NotNull" 
			<constraint annotation="javax.validation.constraints.Size">
				<element name="min">5</element>
				<element name="max">30</element>
			</constraint>
		</field>
		<field name="email">
			<constraint annotation="org.hibernate.validator.constraints.Email" 
		</field>
		<field name="creditCardNumber">
			<constraint annotation="org.hibernate.validator.constraints.CreditCardNumber" 
		</field>
	</bean>

</constraint-mappings>

default-package用于定义基本软件包,因此我们可以跳过长软件包名称。

ignore-annotations用于告诉Hibernate验证程序忽略类中存在的用于验证目的的任何注释,仅执行xml文件中配置的验证。

我们可以在一个文件中进行多个bean验证,我们需要将此文件加载到验证器的工厂配置中,稍后将给出一个示例。

属性级别约束

我们也可以在getter方法上应用约束,我们不应该在setter方法上应用约束。
另外,我们应该避免在字段和getter方法上都应用约束,因为这样它将被两次验证。

MyBean.java

package com.theitroad.validator.hibernate.model;

import javax.validation.constraints.NotNull;

public class MyBean {

	private String name;

	@NotNull
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

具有继承的Hibernate 验证

Bean验证是继承的,因此,如果我们要验证子类的对象,则还将执行父类中的任何验证。

MyChildBean.java

package com.theitroad.validator.hibernate.model;

import javax.validation.constraints.NotNull;

public class MyChildBean extends MyBean {

	private String data;

	@NotNull
	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}
}

如果我们将验证MyChildBean的实例,则MyBean名称字段也将被验证。

使用@Valid批注进行组合验证

如果有合成,我们可以使用@ Valid批注,以便执行验证。

AnotherBean.java

package com.theitroad.validator.hibernate.model;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

public class AnotherBean {

	@NotNull
	@Valid
	private MyChildBean childBean;

	public MyChildBean getChildBean() {
		return childBean;
	}

	public void setChildBean(MyChildBean childBean) {
		this.childBean = childBean;
	}
}

现在,如果我们验证AnotherBean实例,则还将验证MyChildBean对象。

方法参数Hibernate 验证

我们也可以定义方法参数的约束,下面给出一个简单的例子。

ParamValidationBean.java

package com.theitroad.validator.hibernate.model;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class ParamValidationBean {

	private String name;
	
	//using @NotNull at constructor rather than at field
	public ParamValidationBean(@NotNull String name){
		this.name = name;
	}
	
	public void printData(@NotNull @Size(min=5) String data){
		System.out.println("Data is::"+data);
	}

	public String getName() {
		return name;
	}

}

Hibernate Validator示例测试程序

我们已经看到了很多验证方案,这里是测试程序,以显示验证它们的过程。

ValidatorTest.java

package com.theitroad.validator.hibernate.main;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Method;
import java.util.Set;

import javax.validation.Configuration;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;

import org.hibernate.validator.HibernateValidator;

import com.theitroad.validator.hibernate.model.AnotherBean;
import com.theitroad.validator.hibernate.model.EmpIdCheck;
import com.theitroad.validator.hibernate.model.Employee;
import com.theitroad.validator.hibernate.model.EmployeeXMLValidation;
import com.theitroad.validator.hibernate.model.MyChildBean;
import com.theitroad.validator.hibernate.model.ParamValidationBean;

public class ValidatorTest {

	public static void main(String[] args) throws FileNotFoundException, NoSuchMethodException, SecurityException {
		
		//Getting Validator instance with Annotations
		ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
		Validator validator = validatorFactory.getValidator();
		
		//If there are multiple JSR303 implementations in classpath
		//we can get HibernateValidator specifically too
		ValidatorFactory hibernateVF = Validation.byProvider(HibernateValidator.class)
									.configure().buildValidatorFactory();
		
		System.out.println("\nSimple field level validation example");
		Employee emp = new Employee(-1, "Name","email","123");
		Set<ConstraintViolation<Employee>> validationErrors = validator.validate(emp);
		
		if(!validationErrors.isEmpty()){
			for(ConstraintViolation<Employee> error : validationErrors){
				System.out.println(error.getMessageTemplate()+"::"+error.getPropertyPath()+"::"+error.getMessage());
				
			}
		}
		
		System.out.println("\nXML Based validation example");
		
		//XML Based validation
		Configuration<?> config = Validation.byDefaultProvider().configure();
		config.addMapping(new FileInputStream("employeeXMLValidation.xml"));
		ValidatorFactory validatorFactory1 = config.buildValidatorFactory();
		Validator validator1 = validatorFactory1.getValidator();
		
		EmployeeXMLValidation emp1 = new EmployeeXMLValidation(10, "Name","email","123");
		
		Set<ConstraintViolation<EmployeeXMLValidation>> validationErrors1 = validator1.validate(emp1);
		
		if(!validationErrors1.isEmpty()){
			for(ConstraintViolation<EmployeeXMLValidation> error : validationErrors1){
				System.out.println(error.getMessageTemplate()+"::"+error.getInvalidValue()+"::"+ error.getPropertyPath()+"::"+error.getMessage());
				
			}
		}
		
		System.out.println("\nValidation Group example");
		validationErrors = validator.validate(emp, EmpIdCheck.class);
		
		if(!validationErrors.isEmpty()){
			for(ConstraintViolation<Employee> error : validationErrors){
				System.out.println(error.getMessageTemplate()+"::"+error.getPropertyPath()+"::"+error.getMessage());
				
			}
		}
		
		System.out.println("\nValidation with inheritance example");

		//Validation inheritance and Property-level constraints example
		MyChildBean childBean = new MyChildBean();
		Set<ConstraintViolation<MyChildBean>> validationInheritanceErrors = validator.validate(childBean);
		
		if(!validationInheritanceErrors.isEmpty()){
			for(ConstraintViolation<MyChildBean> error : validationInheritanceErrors){
				System.out.println(error.getMessageTemplate()+"::"+error.getPropertyPath()+"::"+error.getMessage());
				
			}
		}
		
		System.out.println("\nValidation in composition using @Valid annotation");

		//@Valid annotation - validation in composition example
		AnotherBean compositionBean = new AnotherBean();
		compositionBean.setChildBean(new MyChildBean());
		Set<ConstraintViolation<AnotherBean>> validationCompositionErrors = validator.validate(compositionBean);
		
		if(!validationCompositionErrors.isEmpty()){
			for(ConstraintViolation<AnotherBean> error : validationCompositionErrors){
				System.out.println(error.getMessageTemplate()+"::"+error.getPropertyPath()+"::"+error.getMessage());
				
			}
		}
		
		System.out.println("\nParameter validation example");
		ParamValidationBean paramValidationBean = new ParamValidationBean("hyman");
		Method method = ParamValidationBean.class.getMethod("printData", String.class);
		Object[] params = {"1234"};
		ExecutableValidator executableValidator = validator.forExecutables();
		Set<ConstraintViolation<ParamValidationBean>> violations = 
				executableValidator.validateParameters(paramValidationBean, method, params);
		if(!violations.isEmpty()){
			for(ConstraintViolation<ParamValidationBean> error : violations){
				System.out.println(error.getMessageTemplate()+"::"+error.getPropertyPath()+"::"+error.getMessage());
				
			}
		}
		
	}

}

当我们在Hibernate 验证器示例程序之上运行时,我们得到以下输出。

May 25, 2014 3:51:56 AM org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 5.1.1.Final

Simple field level validation example
{javax.validation.constraints.Size.message}::name::size must be between 5 and 30
{org.hibernate.validator.constraints.CreditCardNumber.message}::creditCardNumber::invalid credit card number
{org.hibernate.validator.constraints.Email.message}::email::Invalid email address

XML Based validation example
{org.hibernate.validator.constraints.Email.message}::email::email::Invalid email address
{javax.validation.constraints.Size.message}::Name::name::size must be between 5 and 30
{org.hibernate.validator.constraints.CreditCardNumber.message}::123::creditCardNumber::invalid credit card number

Validation Group example
{javax.validation.constraints.Min.message}::id::must be greater than or equal to 1

Validation with inheritance example
{javax.validation.constraints.NotNull.message}::data::Jan not be null
{javax.validation.constraints.NotNull.message}::name::Jan not be null

Validation in composition using @Valid annotation
{javax.validation.constraints.NotNull.message}::childBean.data::Jan not be null
{javax.validation.constraints.NotNull.message}::childBean.name::Jan not be null

Parameter validation example
{javax.validation.constraints.Size.message}::printData.arg0::size must be between 5 and 2147483647

来自Hibernate 验证器测试程序的重要要点是:

  • Validator实例是线程安全的,因此我们可以对其进行缓存并重用。

  • 如果存在多个JSR303实现,则可以使用" Validation.byProvider()"方法获取Hibernate Validator实例。

  • 请注意,使用的是组验证,它仅验证组中的字段。

  • ExecutableValidator用于验证方法的参数。

  • ExecutableValidator提供了一些方法来验证构造函数的参数并返回值,这些方法是validateateReturnValue(),validateConstructorParameters()和validateConstructorReturnValue()。

Hibernate Validator自定义验证和Spring Framework集成

我们也可以创建自己的自定义验证器,请阅读Spring Hibernate Validator Example以更好地了解此功能。