Hibernate Validator是基于Hibernate Core的一套Annotation的验证框架。可以和Hibernate 的GenerDdl结合使用,来生成数据表时添加规则,也可以通过代码的形式完成对象属性的有效性验证,并且抛出验证中产生的错误,使用简单。下面详细介绍:
首先,下载Hibernate Validator:
http://nchc.dl.sourceforge.net/sourceforge/hibernate/hibernate-validator-3.0.0.GA.zip
然后开始Hibernate Validator之旅。
1,最基本的验证:
直接开始一个最简单的验证测试:
首先准备一个待验证的对象:User:
public class User {
@NotEmpty
@Length(min = 2, max = 6)
private String userName;
@NotNull
@Range(min = 1, max = 120)
private Integer age;
@Valid
private Address address;
@NotNull
@Email
private String email;
@Pattern(regex = "//d{3}-//d{3}-//d{6,9}")
private String number;
//Getter&&setter
验证标签可以放在属性之上,也可以放在Getter方法之上,比如:
@Length(min = 2, max = 6)
public String getUserName(){
return this.userName;
}
注意User对象中还有一个复合对象Address,我们使用@ Valid标签标记,表示在验证User对象的时候,自动的也去验证Address对象,并把验证信息统一返回。
下面是Address对象:
public class Address {
@Pattern(regex = "//d{6}")
private String zip;
@NotEmpty
private String name;
public final String getZip() {
return zip;
}
public final void setZip(String zip) {
this.zip = zip;
}
public final String getName() {
return name;
}
public final void setName(String name) {
this.name = name;
}
}
然后我们来写一个简单的测试,对User进行验证:
可以看到,在测试用例中的所有User属性都是错误的,关于各个标签的详细含义和使用,将在下面讲解。我们运行该测试,测试通过,控制台输出:
信息: Hibernate Annotations 3.2.1.GA
=========================
长度必须介于 2 与 6 之间
class com.thtf.ezone.ezframework.validator.User:userName
wwwwwww
=========================
必须介于 1 与 120 之间
class com.thtf.ezone.ezframework.validator.User:age
-1
=========================
not a well-formed email address
class com.thtf.ezone.ezframework.validator.User:email
www.com
=========================
必须符合 "/d{3}-/d{3}-/d{6,9}"
class com.thtf.ezone.ezframework.validator.User:number
222-222-33
=========================
必须符合 "/d{6}"
class com.thtf.ezone.ezframework.validator.Address:zip
wwwwwww
=========================
may not be null or empty
class com.thtf.ezone.ezframework.validator.Address:name
=========================
不能为空白
class com.thtf.ezone.ezframework.validator.User:userName
=========================
buenng wei kongbai haha
class com.thtf.ezone.ezframework.validator.User:userName
=========================
buenng wei kongbai haha
class com.thtf.ezone.ezframework.validator.User:userName
依次输出了所有的错误信息,包括Address里面的验证错误都统一输出。Hibernate Validator使用InvalidValue对象来包装一个验证的错误,使用ClassValidator来作为验证处理器。关于这两个对象的方法请参见API Doc。
下表列出了Hibernate Validator提供的内置的验证器:
Annotation | 施加于 | 检查项 | Hibernate生成相关表字段 |
@Length(min=, max=) | 属性(String类型) | 检查String长度是否在范围之内 | 字段的长度会被设置为最大长度所定义的值 |
@Max(value=) | 属性(数字,或者内容为数字的String) | 检查值是否小于等于Max值 | 在该字段上添加一个check约束 |
@Min(value=) | 属性(数字,或者内容为数字的String) | 检查值是否大于等于Max值 | 在该字段上添加一个check约束 |
@NotNull | 属性 | 属性不能为null | 字段设置为 not null |
@NotEmpty | 属性 | 属性不能为null且不能为空(比如String就不能为"")如果是集合类型的话,不能为null且不能为空(size()==0) | 字段设置为 not null(String的话) |
@Past | 属性(Date或者calendar) | 检查时间为过去时间 | 在该字段上添加一个check约束 |
@Future | 属性(Date或者calendar) | 检查时间为将来时间 | 无 |
@Pattern(regex="regexp" , flag=) or @Patterns( {@Pattern(...)} ) | 属性(String类型) | 检查属性根据匹配标识(flag)来判断是否要求匹配(java.util.regex.Pattern) | 无 |
@Range(min=, max=) | 属性(数字,或者内容为数字的String) | 检查属性值是否在范围之内,包括相等的情况 | 在该字段上添加一个check约束 |
@Size(min=, max=) | 属性(array,collection,map) | 检查元素个数是否在范围之内,包括相等的情况 | 无 |
@AssertFalse | 属性 | 检查返回值为false | 无 |
@AssertTrue | 属性 | 检查返回值为true | 无 |
@Valid | 属性(Object) | 级联的检查关联的对象。如果对象是一个Collection或者数组,则检查其中的每一个对象。如果对象是Map,则检查每一个Value域对象。 | 无 |
@Email | 属性(String) | 检查属性是否符合Email规则 | |
@Digits | 属性(数字,或者内容为数字的String) | 检查值是否是一个数字 | |
创建自己的验证规则
要在Hibernate Validator里面创建一个自定义的验证规则是很简单的。下面就来创建一个类似于JWeb里面@Validator(name="string",value="blank;trim",required")的验证器NotBlank。
首先创建一个NotBlank标签:
@ValidatorClass(NotBlankValidator.class)
@Target( { java.lang.annotation.ElementType.METHOD,
java.lang.annotation.ElementType.FIELD })
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Documented
public @interface NotBlank {
String message() default "{validator.notBlank}";
}
注意这里要使用@ValidatorClass来指明该标签使用哪个验证器来完成具体的验证。标签会有一个Message属性,用来表示错误的信息提示。这里使用的是ResourceBundle来从资源文件里面读取。关于错误消息的自定义,将在下面讲解。
然后来创建具体的验证器:
public class NotBlankValidator implements Validator<NotBlank>,
PropertyConstraint, Serializable {
private static final long serialVersionUID = 3873258797919992347L;
public boolean isValid(Object value) {
if (value == null)
return false;
if (value instanceof String) {
return ((String) value).trim().length() > 0;
}
return false;
}
@SuppressWarnings("unchecked")
public void apply(Property property) {
if (!(property.getPersistentClass() instanceof SingleTableSubclass)
&& !(property.getValue() instanceof Collection)) {
// single table should not be forced to null
if (!property.isComposite()) {
Iterator<Column> iter = (Iterator<Column>) property
.getColumnIterator();
while (iter.hasNext()) {
iter.next().setNullable(false);
}
}
}
}
public void initialize(NotBlank parameters) {
}
}
首先,一般的验证器只需要实现Validator接口即可。如果需要验证器对Hibernate生成数据库表有影响的话,需要实现PropertyConstraint接口。
public boolean isValid(Object value)是Validator里面定义的方法,用来做具体的验证。public void apply(Property property)是PropertyConstraint接口里定义的方法,用来对Hibernate生成表的时候做一定的规则。该验证器在验证时只处理String类型比较,并且能在相应的表的字段上完成添加not null规则。最后public void initialize(NotBlank parameters)是Validator接口定义的,可以在这里面通过parameters.getXXX()来得到在Annotation里面定义的其他属性值。
下面来测试这个自定义的验证标签。首先修改User对象
public class User {
@NotBlank
@Length(min = 2, max = 6)
private String userName;
这里使用了@NotBlank来标记userName属性。
public void testSelfValidator() {
ClassValidator cv = new ClassValidator(User.class);
User user = new User();
Address address = new Address();
address.setName("address1");
address.setZip("610019");
user.setAddress(address);
user.setAge(20);
user.setEmail("wwww@www.com");
user.setNumber("222-222-3333333");
user.setUserName(" ");
InvalidValue[] vms = cv.getInvalidValues(user);
for (InvalidValue iv : vms) {
System.out.println("=========================");
System.out.println(iv.getMessage());
System.out.println(iv.getBean().getClass() + ":"
+ iv.getPropertyName());
System.out.println(iv.getValue());
}
assertEquals(1, vms.length);
}
接下来的测试,注意user.setUserName(" ")这行,他符合@@Length(min = 2, max = 6)的规则,但不符合@NotBlank的规则,执行该测试,控制台中输出:
不能为空白
class com.thtf.ezone.ezframework.validator.User:userName
' '
=========================
验证成功。
错误消息自定义
Hibernate Validator中的错误消息都是放在ValidatorMessages.properties文件里面的,也可以通过覆盖该文件里面的消息达到自定义错误消息的目的:
下面列出所有的Hibernate内建消息:
validator.assertFalse=assertion failed
validator.assertTrue=assertion failed
validator.future=must be a future date
validator.length=length must be between {min} and {max}
validator.max=must be less than or equal to {value}
validator.min=must be greater than or equal to {value}
validator.notNull=may not be null
validator.past=must be a past date
validator.pattern=must match "{regex}"
validator.range=must be between {min} and {max}
validator.size=size must be between {min} and {max}
刚才我们在建立NotBlank标签的时候使用了一个Message来作为默认的提示,这里,我们来看看这个是怎么完成的。
public @interface NotBlank {
String message() default "{validator.notBlank}";
}
首先在classpath下面创建一个ValidatorMessages.properties文件,
在其中添加一项:validator.notBlank=不能为空白,
通过转码得到:
validator.notBlank=/u4e0d/u80fd/u4e3a/u7a7a/u767d
OK,就这么简单。
也可以在使用标签是定义自定义的错误信息,
比如:修改User对象:
@NotBlank(message = "用户名不能为空")
@Length(min = 2, max = 6)
private String userName;
然后测试:
public void testSelfValidator() {
ClassValidator cv = new ClassValidator(User.class);
User user = new User();
Address address = new Address();
address.setName("address1");
address.setZip("610019");
user.setAddress(address);
user.setAge(20);
user.setEmail("wwww@www.com");
user.setNumber("222-222-3333333");
user.setUserName(" ");
InvalidValue[] vms = cv.getInvalidValues(user);
for (InvalidValue iv : vms) {
System.out.println("=========================");
System.out.println(iv.getMessage());
System.out.println(iv.getBean().getClass() + ":"
+ iv.getPropertyName());
System.out.println(iv.getValue());
}
assertEquals(1, vms.length);
}
控制台输出结果为:
信息: Hibernate Annotations 3.2.1.GA
=========================
用户名不能为空
class com.thtf.ezone.ezframework.validator.User:userName
同样,在这里我们也可以通过资源文件来达到国际化的目的:
创建资源文件:vmessages_zh.properties
在其中添加一行:
validator.notBlank=buenng wei kongbai haha
然后修改User对象:
@NotBlank(message = "{validator.notBlank}")
@Length(min = 2, max = 6)
private String userName;
完成测试:
public void testSelfValidator() {
ClassValidator cv = new ClassValidator(User.class, ResourceBundle.getBundle("vmessages"));
User user = new User();
Address address = new Address();
address.setName("address1");
address.setZip("610019");
user.setAddress(address);
user.setAge(20);
user.setEmail("wwww@www.com");
user.setNumber("222-222-3333333");
user.setUserName(" ");
InvalidValue[] vms = cv.getInvalidValues(user);
for (InvalidValue iv : vms) {
System.out.println("=========================");
System.out.println(iv.getMessage());
System.out.println(iv.getBean().getClass() + ":"
+ iv.getPropertyName());
System.out.println(iv.getValue());
}
assertEquals(1, vms.length);
}
控制台输出:
=========================
buenng wei kongbai haha
class com.thtf.ezone.ezframework.validator.User:userName
' '
注意这里的new ClassValidator(User.class, ResourceBundle.getBundle("vmessages"));完成了绑定国际化资源文件的动作。
验证一个对象中的某一个属性:
Hibernate Validator可以只测试一个对象中的某一个属性:下面是一个测试用例:
public void testObjectOneProperty() {
ClassValidator cv = new ClassValidator(User.class, ResourceBundle
.getBundle("vmessages"));
User user = new User();
Address address = new Address();
address.setName("address1");
address.setZip("610019");
user.setAddress(address);
user.setAge(20);
user.setEmail("wwww@www.com");
user.setNumber("222-222-3");
user.setUserName(" ");
InvalidValue[] vms = cv.getInvalidValues(user, "userName");
assertEquals(1, vms.length);
for (InvalidValue iv : vms) {
System.out.println("=========================");
System.out.println(iv.getMessage());
System.out.println(iv.getBean().getClass() + ":"
+ iv.getPropertyName());
System.out.println(iv.getValue());
}
assertEquals("buenng wei kongbai haha", vms[0].getMessage());
}
可以看到虽然user.setNumber("222-222-3");user.setUserName(" ");都不符合要求,但我们只通过方法cv.getInvalidValues(user, "userName");验证了userName一个属性,所以最后只有一个错误。
验证一个属性:
Hibernate Validator也可以只单独直接验证一个属性,看下面的测试用例:
public void testOneProperty() {
ClassValidator cv = new ClassValidator(User.class, ResourceBundle
.getBundle("vmessages"));
String userName = " ";
InvalidValue[] vms = cv.getPotentialInvalidValues("userName", userName);
assertEquals(1, vms.length);
assertEquals("buenng wei kongbai haha", vms[0].getMessage());
}
我们不需要创建User对象,就可以使用User对象里面设置的验证规则直接去验证一个值。很适合验证HttpServletRequest传入的单个参数到PO中的情况。