1:于struts结合
1.1:下载struts2-fullhibernatecore-plugin-2.2.2(目前最新的是2.2.2),
它支持的hibernate validate中,最新的版本是4.0.2,也是使用这个版本的原因
1.2:struts配置文件中,包继承hibernate-default(需要同时继承多个包时,先后顺序对校验有影响)
<package name="recevaccount" extends="default,hibernate-default" namespace="">
1.3:action的属性上添加约束
@not null
private String name;
对一个对象的校验,需要使用Valid,它会校验Emp对象中的约束
@Valid
private Emp emp;
也可以添加自定义约束
@MyOwnValid
private User user;
1.4:校验不通过时,错误信息会由struts2-fullhibernatecore-plugin插件放入Action的fielderror和actionerror中。
前台使用struts标签传递数据时:<s:textfield>,错误信息存放在fielderror和actionerror中
前台使用一般的html表单时:<input>,错误信息存放在fielderror中。
1.5:如1.3所示,假如Emp中10个字段,全部添加相应的约束,jsp传递到action中的参数只有emp.name,emp.id,则只会校验name和id上对应的约束,即:request parameter中的键所对应的约束。
2:校验的错误信息如何存入Action中
以1.3中的emp为例,前台通过emp.name,emp.age传递2个参数到后台,如果数据校验不通过,对应的错误信息存放在一个键值对中,键即是emp.name,emp.age;类约束默认存放在emp.null中。在所有属性都校验完之后,struts2-fullhibernatecore-plugin插件会遍历request.parameter中所有参数的键(这里是emp.name和emp.age),将错误信息存放到fielderror和actionerror中。
所以,当类约束校验不通过时,由于没有从前台传递以emp.null为键的参数到后台,所以类约束的错误信息是没办法放入action中的。
3:如何指定错误信息对应的field
上面提到过,默认情况下类约束是没办法放入action的error中的,所以即便校验不通过,也不会跳转到input页面。可以通过如下方式指定一个field来存放错误信息,但是该field必须要是前台传递到后台数据的键才行,原因可参考2.
//禁用默认
arg1.disableDefaultConstraintViolation();
arg1.buildConstraintViolationWithTemplate("错误信息").addNode("field").addConstraintViolation();
校验不通过后,类约束的错误信息将会放入emp.field中。
4:一个自定义类约束的例子
//该注解可以应用在:方法,域,注解类型上
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})
//注解在运行期通过反射读取
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = LeaveHourCheckValidator.class)
//在生成JavaDoc时,该注解会被添加到java文档中
@Documented
public @interface LeaveHourCheck {
//定义错误消息
String message() default "请假小时验证不通过";
//定义所在的组
Class<?>[] groups() default {};
//定义级别条件的严重级别
Class<? extends Payload>[] payload() default {};
}
//3.6版本和4.0有所不同
public class LeaveHourCheckValidator implements ConstraintValidator<LeaveHourCheck, HrLeave>{
private LeaveService leaveService;
public void initialize(LeaveHourCheck arg0) {
WebApplicationContext wc = null;
if (wc == null) {
wc = WebApplicationContextUtils
.getRequiredWebApplicationContext(ServletActionContext
.getServletContext());
}
leaveService = wc.getBean(LeaveService.class);
}
public boolean isValid(HrLeave leave, ConstraintValidatorContext arg1) {
……………………
//禁用默认
arg1.disableDefaultConstraintViolation();
arg1.buildConstraintViolationWithTemplate(message).addNode("hour").addConstraintViolation();
return flag;
}
}
5:同一个实体类的同一个field,不同操作进行不同的校验
假如有如下新需求,在保存时,年龄数必须小于20小时,修改时年龄必须大于25小时。由于在约束的Annotation中是没办法指定一个变量的,所以无法在使用约束时,通过参数进行区分。可以通过ActionContext.getContext().getName()获得Action的名字,不同的操作进行不同的校验
6:如何对数组对象进行校验
考虑如下情况:假如需要一次保存多个员工,前台通过js将数据封装成emp[0].name,emp[0].age,emp[1].name,emp[1].age……传递到后台,后台通过List<Emp> list进行接收,想通过hibernate validate进行校验,校验不通过时返回input页面。
假如我们使用默认情况:校验不通过时,错误信息存放在list.null中;指定一个field:校验不通过时,存放在list.field中。由此可见,没有哪一种情况对应的键出现在request的parameter中,所以错误信息无法放入Action中。
解决办法,在校验不通过时,自己手动将错误信息放入Action中,如下所示:
Object obj = ActionContext.getContext().getActionInvocation().getAction();
if (!(obj instanceof ActionSupport)) {
LOG.warn(obj.getClass().getPackage()+obj.getClass().getName() + "转换Action方法出错");
}
ActionSupport support = (ActionSupport)obj;
另一种校验思路
上面的方法虽然可以有效的进行数据校验,将校验代码从Action中分离出来,但是不同的Action,不同的封装Bean需要不同的类约束(业务逻辑的需要,自带的约束远远不够用),这就导致了需要创建很多的自定义约束。到最后每个约束是干什么的自己都不清楚,维护起来也比较麻烦。可以通过如下方式解决。
1:自定义一个接口,所有的封装Bean都继承该接口。
public interface IValidate {
public ReturnValue validate(RecevParameters parameter);
}
2:封装Bean,每个Bean,在自己的封装Bean中,写自己的校验方法。
public class RecevAccountBean implements IValidate {
……field……
……get,set……
//========================进行数据校验=========================
public ReturnValue validate(RecevParameters parameter) {
String actionName = parameter.getActionName();
if ("saveRecevAccountDeal".equals(actionName)) {
}
return null;
}
}
3:自定义约束类,通过回调,每个封装Bean处理自己的校验,将错误信息封装成对象,统一返回到约束中进行处理。约束只对接口进行校验
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
//注解在运行期通过反射读取
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = BeanCheckValidator.class)
//在生成JavaDoc时,该注解会被添加到java文档中
@Documented
public @interface BeanCheck {
//定义错误消息
String message() default "单据校验不通过";
//定义所在的组
Class<?>[] groups() default {};
//定义级别条件的严重级别
Class<? extends Payload>[] payload() default {};
}
public class BeanCheckValidator implements ConstraintValidator<BeanCheck, IValidate> {
private BeanCheck check;
public void initialize(BeanCheck constraintAnnotation) {
this.check = constraintAnnotation;
}
public boolean isValid(IValidate validator, ConstraintValidatorContext context) {
if (validator==null) {
return true;
}
ActionContext actionContext = ActionContext.getContext();
//存放Action的名字
RecevParameters parameter = new RecevParameters();
parameter.setActionName(actionContext.getName());
parameter.setAnnotation(check);
ReturnValue value = validator.validate(parameter);
if (value==null || value.getErrors()==null) {
return true;
}
boolean keepInAction = value.isKeepInAction(); //是否需要将错误信息存入ActionError
boolean keepInField = value.isKeepInFields(); //是否需要将错误信息存入FieldError
Map<String, String> map = value.getErrors();
for (String string : value.getErrors().keySet()) {
if(keepInAction) {
ActionErrorUtil.addActionError(actionContext, map.get(string));
}
if(keepInField) {
ActionErrorUtil.addFieldError(actionContext, string, map.get(string));
}
}
return true;
}
}