zoukankan      html  css  js  c++  java
  • Hibernate验证器

    第 4 章 Hibernate验证器

     http://hibernate.org/validator/documentation/getting-started/#applying-constraints

    注解是一种为领域模型(domain model)指定不变约束的简洁而幽雅的方法。例如,你能 表示一个属性永远不为null,一个帐户余额一定是正值,等等。这些域模型约束通过为bean中的属性添加 注解来加以声明。随后一个验证器(validator)会读取并检查这些约束。验证机制可以执行于应用程序中的 不同层(表现层、数据访问层),而不必复述任何(前述)这些规则。Hibernate验证器正为这一目的而设计的。

    Hibernate验证器工作在两个层次上。第一层,它能检查内存中一个类的实例是否违反约束。 第二层,它能将约束应用于Hibernate元模型上,并将它们融入生成的数据库schema中。

    每个约束注解(constraint annotation)和一个验证器实现关联,该验证器负责检查位于实体实例上的约束。 一个验证器也能(可选地)将约束应用于Hibernate元模型上,让Hibernate生成表示这一约束的DDL。使用合适的事件监听器,你能 让Hibernate在插入和更新时执行检查操作。Hibernate验证器并不局限于同Hibernate一起使用。 你能在你应用程序的任何地方方便地使用它。

    在运行时检查实例时,Hibernate验证器返回违反约束的信息, 这些信息以一个InvalidValue数组的形式返回。 除了众多其他信息外,InvalidValue包含了一个错误描述消 息,该信息可以内嵌与注解相捆绑的参数值(例如长度限制),以及能被提取至ResourceBundle的消息字串。

    4.1. 约束

     

    4.1.1. 什么是约束?

     

    约束通过注解表示。一个约束通常有一些用来参数化约束限制的属性。约束应用于带注解的元素。

    4.1.2. 内建约束

     

    Hibernate验证器有些内建约束,这些约束覆盖了大多数的基本数据检查。随后我们会看到, 你不必受制于这些内置约束,因为一分钟内就可以写出你自己的约束。

    表 4.1. 内建约束

    注解应用目标运行时检查Hibernate元数据影响
    @Length(min=, max=) 属性(String) 检查字符串长度是否符合范围 列长度会被设到最大值
    @Max(value=) 属性 (以numeric或者string类型来表示一个数字) 检查值是否小于或等于最大值 对列增加一个检查约束
    @Min(value=) 属性(以numeric或者string类型来表示一个数字) 检查值是否大于或等于最小值 对列增加一个检查约束
    @NotNull 属性 检查值是否非空(not null) 列不为空
    @Past 属性(date或calendar) 检查日期是否是过去时 对列增加一个检查约束
    @Future 属性 (date 或 calendar) 检查日期是否是将来时
    @Pattern(regex="regexp", flag=) 属性 (string) 检查属性是否与给定匹配标志的正则表达式相匹配(见 java.util.regex.Pattern )
    @Range(min=, max=) 属性(以numeric或者string类型来表示一个数字) 检查值是否在最小和最大值之间(包括临界值) 对列增加一个检查约束
    @Size(min=, max=) 属性 (array, collection, map) 检查元素大小是否在最小和最大值之间(包括临界值)
    @AssertFalse 属性 检查方法的演算结果是否为false(对以代码方式而不是注解表示的约束很有用)
    @AssertTrue 属性 检查方法的演算结果是否为true(对以代码方式而不是注解表示的约束很有用)
    @Valid 属性 (object) 对关联对象递归的进行验证。如果对象是集合或数组,就递归地验证其元素。如果对象是Map,则递归验证其值元素。
    @Email 属性(String) 检查字符串是否符合有效的email地址规范。

    4.1.3. 错误信息

     

    Hibernate验证器提供了一组默认的错误提示信息,它们被翻译成多种语言(如果你的语言不在其中,请给 我们寄一个补丁)。你可以在org.hibernate.validator.resources.DefaultValidatorMessages.properties 之外创建ValidatorMessages.propertiesValidatorMessages_loc.properties 文件并改变相应的键值,籍此覆盖那些(默认)信息。你甚至可以在写自己的验证器 注解时添加你自己的附加消息集。

    或者你可以以编程方式检查bean的验证规则并提供相应的ResourceBundle

    4.1.4. 编写你自己的约束

     

    扩展内建约束集是极其方便的。任何约束都包括两部分:约束描述符(注解) 和约束验证器(实现类)。下面是一个简单的用户定义描述符:

    @ValidatorClass(CapitalizedValidator.class)
    @Target(METHOD) 
    @Retention(RUNTIME)
    @Documented
    public @interface Capitalized {
        CapitalizeType type() default Capitalize.FIRST;
        String message() default "has incorrect capitalization";
    }

    type参数描述属性应该如何被大写。这是一个完全依赖于注解业务(逻辑)的用户 参数。

    message是用于描述约束违规的默认字符串,它是强制要求的。你可以采取硬编码的方式, 或者通过Java ResourceBundle机制将message的部分/全部内容提取至外部文件。一旦发现message中{parameter}字符串, 就会在{parameter}这个位置注入相应的参数值(在我们的例子里Capitalization is not {type}会生成 Capitalization is not FIRST), 可以将message对应的整个字符串提取至外部文件ValidatorMessages.properties,这也是一种良好实践。 见Error messages

    @ValidatorClass(CapitalizedValidator.class)
    @Target(METHOD) 
    @Retention(RUNTIME)
    @Documented
    public @interface Capitalized {
        CapitalizeType type() default Capitalize.FIRST;
        String message() default "{validator.capitalized}";
    }
    
    ...
    #in ValidatorMessages.properties
    validator.capitalized=Capitalization is not {type}

    如你所见{}符号是递归的。

    为了将一个描述符连接到它的验证器实现,我们使用@ValidatorClass 元注解。验证器类参数必须指定一个实现了Validator<ConstraintAnnotation> 的类。

    我们现在要实现验证器(也就是实现规则检查)。一个验证器实现能检查一个属性的值 (实现PropertyConstraint),并且/或者可以修改hibernate映射元数据 (实现PersistentClassConstraint),籍此表示数据库级的约束。

    public class CapitalizedValidator 
            implements Validator<Capitalized>, PropertyConstraint {
        private CapitalizeType type;
    
        //part of the Validator<Annotation> contract, 
        //allows to get and use the annotation values
        public void initialize(Capitalized parameters) {
            type = parameters.type();
        }
    
        //part of the property constraint contract
        public boolean isValid(Object value) {
            if (value==null) return true;
            if ( !(value instanceof String) ) return false;
            String string = (String) value;
            if (type == CapitalizeType.ALL) {
                return string.equals( string.toUpperCase() );
            }
            else {
                String first = string.substring(0,1);
                return first.equals( first.toUpperCase();
            }
        }
    }

    如果违反约束,isValid()方法将返回false。更多例子请参考内建验证器实现。

    至此我们只看到属性级的验证,你还可以写一个Bean级别的验证注解。Bean自身会被传递给验证器, 而不是bean的属性实例。只要对bean自身进行注解即可激活验证检查。在单元测试套件中还可以找到一个小例子。

    4.1.5. 注解你的领域模型

     

    既然你现在已经熟悉注解了,那么对语法也应该很清楚了。

    public class Address {
        private String line1;
        private String line2;
        private String zip;
        private String state;
        private String country;
        private long id;
        
        // a not null string of 20 characters maximum
        @Length(max=20) 
        @NotNull
        public String getCountry() {
            return country;
        }
        
        // a non null string
        @NotNull
        public String getLine1() {
            return line1;
        }
    
        //no constraint    
        public String getLine2() {
            return line2;
        }
        
        // a not null string of 3 characters maximum
        @Length(max=3) @NotNull
        public String getState() {
            return state;
        }
    
        // a not null numeric string of 5 characters maximum
        // if the string is longer, the message will 
        //be searched in the resource bundle at key 'long'
        @Length(max=5, message="{long}")
        @Pattern(regex="[0-9]+")
        @NotNull
        public String getZip() {
            return zip;
        }
        
        // should always be true
        @AssertTrue
        public boolean isValid() {
            return true;
        }
    
        // a numeric between 1 and 2000
        @Id @Min(1)
        @Range(max=2000)
        public long getId() {
            return id;
        }
    }

    上面的例子只展示了公共属性验证,你还可以对任何可见度的字段(field)进行注解。

    @MyBeanConstraint(max=45)
    public class Dog {
        @AssertTrue private boolean isMale;
        @NotNull protected String getName() { ... };
        ...
    }

    你可以对接口进行注解。Hibernate验证器会检查给定bean所扩展或实现的所有父类和接口, 籍以读取相应的验证器注解(信息)。

    public interface Named {
        @NotNull String getName();
        ...
    }
    
    public class Dog implements Named {
    
        @AssertTrue private boolean isMale;
    
        public String getName() { ... };
    
    }

    在验证Dog bean时会检查name属性的有效性(不为null)。

    4.2. 使用验证器框架

     

    Hibernate验证器旨在实现多层数据验证,我们在一处表示约束(带注解的域模型),然后将其运用于 应用程序的不同层。

    4.2.1. 数据库schema层次验证

     

    无须额外手续,Hibernate Annotations会自动将你为实体定义的约束翻译为映射元数据。例如,如果你的实体 的一个属性注解为@NotNull,在Hibernate生成的DDL schema中这列会被定义为 not null

    4.2.2. Hibernate基于事件的验证

     

    Hibernate验证器有两个内建Hibernate事件监听器。当一个PreInsertEvent 或PreUpdateEvent发生时,监听器会验证该实体实例的所有约束,如有违反会抛出一个异常。 基本上,在Hibernate执行任何插入和更新前对象会被检查。这是激活验证过程的最便捷最简单的方法。当遇到约束 违规时,事件会引发一个运行时InvalidStateException,该异常包含一个描述每个错误的 InvalidValue数组。

    <hibernate-configuration>
        ...
        <event type="pre-update">
            <listener 
              class="org.hibernate.validator.event.ValidatePreUpdateEventListener"/>
        </event>
        <event type="pre-insert">
            <listener 
              class="org.hibernate.validator.event.ValidatePreInsertEventListener"/>
        </event>
    </hibernate-configuration>

    注意

    在使用Hibernate Entity Manager时,Validation框架会被自动激活。如果bean不带验证注解, 就不会有性能损失。

    4.2.3. 程序级验证

     

    Hibernate验证器能应用于你应用程序代码中的任何地方。

    ClassValidator personValidator = new ClassValidator( Person.class );
    ClassValidator addressValidator = new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) );
    
    InvalidValue[] validationMessages = addressValidator.getInvalidValues(address);

    头两行为执行类检查而准备Hibernate验证器。第一行依赖于嵌入在Hibernate验证器内的错误 消息(见Error messages),第二行为这些消息准备资源包。这些代码只执行一次, 并将验证器进行缓存处理,这种方式是一种良好实践。

    第三行真正验证了Address实例并返回一个InvalidValue数组。 你的应用程序逻辑随后可以对错误做出响应。

    除了针对整个bean你还可以

    https://docs.jboss.org/hibernate/annotations/3.4/reference/zh_cn/html/validator.html

  • 相关阅读:
    laravel 1对多,主键不是整型的坑
    laravel 修改默认Eloquent 映射 表名加s复数的方式
    linux开启与关闭防火墙
    laravel 队列使用(发邮件、短信等)
    linux下隐藏nginx版本及php版本信息
    laravel API开发,使用dingo/api
    80端口被占用
    中国大陆地区数据库-(省市县)总表
    未定义“RunCommand”属性
    按键精灵手机端代码通
  • 原文地址:https://www.cnblogs.com/softidea/p/6291135.html
Copyright © 2011-2022 走看看