美文网首页
使用Hibernate validator进行model约束

使用Hibernate validator进行model约束

作者: 东本三月 | 来源:发表于2019-04-25 16:10 被阅读0次

1. 需求/目的

  • 减少代码量,提高model约束条件的可维护性

2.使用环境

  • 同之前笔记,没有额外引入

3.思路

  • 在model定义约束条件
  • 依据约束条件进行判断
  • 获取判断结果
  • 处理判断结果

4.定义约束条件

注解 作用
@Valid 被注释的元素是一个对象,需要检查此对象的所有字段值
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null( 任何对象的value不能为null)
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
  • Hibernate Validator 附加的 constraint
注解 作用
@Email 被注释的元素必须是电子邮箱地址
@Length(min=, max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空(可以用于集合对象的元素不为0,即集合不为空)
@Range(min=, max=) 被注释的元素必须在合适的范围内
@NotBlank 被注释的字符串的必须非空(字符串trim()以后length要大于0)
@Entity
@Table(name = "base_user")  //基本用户表
public class BaseUser extends AbstractModel implements Serializable {
    //主键
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    //  @Column(name = "id", unique = true, nullable = false, length = 20)
    private Integer id;

    //用户名
    @NotBlank(message = "用户名不能为空")
    @Length(max = 16,message = "用户名长度不能超过{max}位")
    @Column(name = "username", length = 64)
    private String username;

    //密码
    @NotBlank(message = "密码不能为空")
    @Column(name = "password", length = 128)
    private String password;

    //性别
    @Column(name = "sex_id", length = 32)
    private Integer sex_id;

    //邮箱
    @Email(message = "邮箱不符合规范")
    @Column(name = "email", length = 64)
    private String email;

    //手机号
    @Column(name = "phone", length = 32)
    private String phone;

    //备注
    @Length(max = 500,message = "备注长度不能超过{max}位")
    @Column(name = "remark", length = 512)
    private String remark;
    
    //状态
    @Column(name = "status_id", length = 32)
    private Integer status_id;
                 
    ...略...
                 
}
  • 约束条件也可以标注在属性的get方法上,但是显然,标注在属性上更方便些
  • 注解的message可不填,Hibernate有默认的提示文本

4.设计一个工具类,进行约束的判断与处理


/**
 * 提供使用Hibernate注解进行model的约束,获取约束验证的返回值
 * @time 2019年4月25日15:13:24
 * @author author
 */
@Component
public class HibernateValidatorUtils {
    private static Validator validator;

    //注解PostConstruct用于在项目启动的时候执行该方法
    @PostConstruct
    public synchronized void init() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    public static Validator getValidator() {
        return validator;
    }

    public static void validate(Object obj)throws MsgException {
        //依据约束条件进行判断,获取判断结果
        Set<ConstraintViolation<Object>> s = validator.validate(obj, new Class[0]);
        //处理判断结果
        if (s != null && !s.isEmpty()) {
            //throw new ConstraintViolationException(s);
            throw Assert.ThrowByViolationResult(s);
        }
    }
}
  • init方法会在spring boot 启动时获取一个validator对象,用于执行约束判断
  • validate方法用来接收一个model,并进行相关处理
  • validation包有提供相关的ConstraintViolationException异常类,可以直接使用约束的判断结果创建一个异常,用于抛出.
  • 但ConstraintViolationException异常无法方便的提供所有约束的判断信息,因此这里我进行自定义处理
    /**
     * 通过hibernate的model验证结果集,创建异常对象
     * @param constraintViolations  hibernate的model验证结果集
     * @return 创建的异常
     * @time 2019年4月23日11:36:51
     * @author authstr
     */
    public static MsgException ThrowByViolationResult(Set<? extends ConstraintViolation<?>> constraintViolations){
        //记录异常信息
        String message="";
        //用于存储所有的错误信息
        List<Map<String,String>> allError=new ArrayList<Map<String,String>>();
        int index=0;
        //遍历set
        for(ConstraintViolation con:constraintViolations){
            //将第一个验证结果作为异常的message
            if(index==0){
                message=con.getMessage();
            }
            index++;
            //记录其他异常信息
            Map<String,String> errInfo=new HashMap<String,String>();
            errInfo.put("message",con.getMessage());
            errInfo.put("value",String.valueOf(con.getInvalidValue()));
            errInfo.put("propertyPath",String.valueOf(con.getPropertyPath()));
            allError.add(errInfo);
        }
        return new MsgException(BasicException.DEFAULT_CODE,message,allError);
    }
  • Assert和MsgException类在之前有说明.
  • Set里包含了没通过判断的相关约束信息
  • 该方法进行的处理是:
    将第一个没有通过判断的约束的message作为MsgException异常对象的message
    每个没通过判断的约束信息转换为Map,将Map集合作为MsgException异常对象的data

5.调用约束判断

  • 约束的判断放在了数据库操作的上一步,也就是AbstractDao类的sava方法和update方法
  • 有时,也会有不需要进行约束判断的需求,因此对原有的方法进行了一些修改
  • 这里抛出的异常最终会被AbstractController类处理
      /**
      * 保存一个entity
     * @param entity 要保存的model
     * @return 主键值
     */
    @Override
    public Serializable save(Object entity) {
        return save(entity,true);
    }

    /**
     * 保存一个entity
     * @param entity 要保存的model
     * @param isValidation 是否验证model
     * @return 主键值
     */
    @Override
    public Serializable save(Object entity, Boolean isValidation) {
        // model验证
        if(isValidation){
            HibernateValidatorUtils.validate(entity);
        }
        return this.getSession().save(entity);
    }
     /**
      * 更新一个entity
     * @param entity 要更新的model
     * @time 2018年9月25日16:15:25
     * @author authstr
     */
    @Override
    public void update(Object entity) {
        update(entity,true);
    }
    /**
     * 更新一个entity
     * @param entity 要更新的model
     * @param isValidation 是否验证model
     * @return 主键值
     */
    @Override
    public void update(Object entity, Boolean isValidation) {
        // model验证
        if(isValidation){
            HibernateValidatorUtils.validate(entity);
        }
        this.getSession().update(entity);
    }

7.实际使用

user添加页面
  • 在不进行任何输入的情况下,进行保存,出现错误提示


    错误提示
  • 请求返回数据还包含其他错误信息,前端可以根据实际情况选择将多个错误信息显示到表单上


    返回值

8.创建自定义约束

  • java和Hibernate提供的约束注解不一定能满足全部需要,这时可以自行创建新的约束注解
  • 约束注解很方便,但是不够强大(自定义也不够方便),过于复杂的约束还需在Service层进行定义.
  • 以身份证验证自定义约束注解为例:
  • 第一步:创建注解类
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;

@Constraint(validatedBy = IDCardValidated.class)
@Target({METHOD,FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IDCard {

    String message() default "身份证号码不符合规范";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
  • Target,Retention,Documented都是元注解,这里不做说明,可参考:自定义注解入门
  • Constraint注解用来与注解的执行类进行关联
  • IDCard 注解执行类
import com.authstr.ff.utils.base.StringUtils;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

@Component
public class IDCardValidated implements ConstraintValidator<IDCard,String> {
    public void initialize(IDCard constraintAnnotation) { }
    public boolean isValid(String s, ConstraintValidatorContext context) {
        if(StringUtils.notText(s)){
            return false;
        }
        return IDCardUtil.verify(s);
    }
}
  • IDCardUtil是进行身份证号码验证的一个工具类,在其他笔记有说明.
  • 之后,就可以在model中进行使用.通过注解约束model作用有限,这里不做深入探究
  • 参考:Hibernate validator官方文档

相关文章

网友评论

      本文标题:使用Hibernate validator进行model约束

      本文链接:https://www.haomeiwen.com/subject/bafzgqtx.html