注:本文档只针对spring boot工程使用的情况有针对性的翻译,完整版本请登录GORM的github官网浏览

1. 约束介绍

1.1. Constraints 用法

Constraints约束通过定义DSL(领域专用语言)来生成校验规则、架构生成和CRUD生成元数据. 例如:

import grails.persistence.Entity;
import org.grails.datastore.gorm.GormEntity
@Entity
class User implements GormEntity<User>{
    ...

    static constraints = {
        login size: 5..15, blank: false, unique: true
        password size: 5..15, blank: false
        email email: true, blank: false
        age min: 18
    }
}

参看 Constraints 了解更多校验规则.

1.2. Global Constraints全局约束

可以在 `src/main/resources/application.groovy`中定义全局约束:

grails.gorm.default.constraints = {
    '*'(nullable: true, size: 1..20)
}

其中的*通配符会应用在所有的属性上.也可以定义全局共享约束:

grails.gorm.default.constraints = {
    myShared(nullable: true, size: 1..20)
}

这样就可以向下面代码一样在domain类中重用:

import grails.persistence.Entity;
import org.grails.datastore.gorm.GormEntity
@Entity
class User implements GormEntity<User>{
    ...

    static constraints = {
        login(shared: "myShared")
    }
}

1.3. Quick Reference 快速参考

Constraint约束 Description描述 Example示例

blank

验证字符串值不是空

login(blank:false)

creditCard

验证字符串值是有效的信用卡号

cardNumber(creditCard: true)

email

验证字符串值是有效的邮件地址

homeEmail(email: true)

inList

验证值是否在约束值的范围或集合中

name(inList: ["Joe", "Fred", "Bob"])

matches

验证字符串值是否与给定的正则表达式匹配

login(matches: "[a-zA-Z]+")

max

验证值不超过给定的最大值

age(max: new Date()) price(max: 999F)

maxSize

验证集合的大小不超过给定的最大值

children(maxSize: 25)

min

验证值不低于给定的最小值

age(min: new Date()) price(min: 0F)

minSize

验证集合的大小不低于给定的最小值

children(minSize: 25)

notEqual

验证属性不等于指定值

login(notEqual: "Bob")

nullable

允许属性设置为“null”-默认为“false”

age(nullable: true)

range

使用Groovy的Range类型以确保属性值在指定范围内

age(range: 18..65)

scale

设置为浮点数字所需的尺度(即小数点右边的位数)

salary(scale: 2)

size

使用GroovyRange类型限制集合或数字的大小或字符串的长度

children(size: 5..15)

unique

在数据库级别将属性限制为唯一

login(unique: true)

url

验证字符串值是有效URL

homePage(url: true)

validator

向字段添加自定义验证

See documentation

1.4. 编程访问

可以在代码中通过静态方法 GbSpringUtils.getDomainConstraintsMap(Class domainclass) . 返回类型为 Map<String, http://docs.grails.org/3.2.8/api/grails/validation/ConstrainedProperty.html[ConstrainedProperty]>.

import grails.persistence.Entity;
import org.grails.datastore.gorm.GormEntity
@Entity
class User implements GormEntity<User>{
    String firstName
    String middleName

    static constraints = {
        firstName blank: false, nullable: false
        middleName blank: true, nullable: true
    }
}

上面的例子中, GbSpringUtils.getDomainConstraintsMap(User).firstName.blank 返回值为 false, 而`GbSpringUtils.getDomainConstraintsMap(User).middleName.blank` 返回值为 true.

1.4.1. Command Objects 命令对象

command object是一种只验证约束关系、不生成数据库表映射的对象

可以通过命令对象的静态属性 constraintsMap`直接访问约束.返回类型为 `Map<String, http://docs.grails.org/3.2.8/api/grails/validation/ConstrainedProperty.html[ConstrainedProperty]>

class User implements Validateable {
    String firstName
    String middleName

    static constraints = {
        firstName blank: false, nullable: false
        middleName blank: true, nullable: true
    }
}

上面的例子中, User.constraintsMap.firstName.blank 返回值为 false,而 User.constraintsMap.middleName.blank 返回值为 true.

1.4.2. blank

2. blank 约束

2.1. 目的

验证字符串值是否为空白字符串值''.

2.2. 示例

login blank: false

2.3. 描述

如果字符串值不能为空字符串值'',则设置为“false”.

错误代码 Code: className.propertyName.blank

注意:如果字符串为“null”,它将不能通过验证“blank: true”。在这种情况下,设置 nullable约束为`true`。

2.3.1. creditCard

3. creditCard约束

3.1. 目的

验证字符串值是有效的信用卡号

3.2. 示例

cardNumber creditCard: true

3.3. 描述

如果字符串必须是信用卡号码,则设置为“true”。内部使用`org.apache.commons.validator.CreditCardValidator`类。

错误代码 Code: className.propertyName.creditCard.invalid

3.3.1. email

4. email约束

4.1. 目的

验证字符串值是有效的电子邮件地址.

4.2. 示例

homeEmail email: true

4.3. 描述

如果字符串值必须是电子邮件地址,则设置为“true”。内部使用`org.apache.commons.validator.EmailValidator`类。

错误代码 Code: className.propertyName.email.invalid

4.3.1. inList

5. inList约束

5.1. 目的

验证值是否在约束值的范围或集合中.

5.2. 示例

name inList: ["Joe", "Fred", "Bob"]

5.3. 描述

约束一个值,使其必须包含在给定的列表中.

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.not.inList

5.3.1. matches

6. matches约束

6.1. 目的

验证字符串值是否与给定的正则表达式匹配.

6.2. 例子

login matches: "[a-zA-Z]+"

6.3. 例子

对字符串值应用正则表达式.

错误代码 Code: className.propertyName.matches.invalid

6.3.1. max

7. max约束

7.1. 目的

确保值不超过最大值.

7.2. 示例

age max: new Date()
price max: 999F

7.3. 描述

可以设置最大值为任意实现接口implements `java.lang.Comparable`的类对象.值的类型必须和属性的类型相同。

注意: 如果值为`java.util.Date`类型,约束的值只在评估执行时创建一次。

class User {
    ...

    static constraints = {
        //此日期对象在评估约束执行时创建,而不是每次验证用户类的实例时创建
        birthDate max: new Date()
    }
}

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.max.exceeded

7.3.1. maxSize

8. maxSize约束

8.1. 目的

确保值的大小不超过最大值.

8.2. 示例

children maxSize: 25

8.3. 描述

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.maxSize.exceeded

8.3.1. min

9. min约束

9.1. 目的

确保值不低于最小值

9.2. 示例

age min: new Date()
price min: 0F

9.3. 描述

可以设置最小值为任意实现接口implements `java.lang.Comparable`的类对象.值的类型必须和属性的类型相同。

注意: 如果值为`java.util.Date`类型,约束的值只在评估执行时创建一次。

class User {
    ...

    static constraints = {
        //此日期对象在评估约束执行时创建,而不是每次验证用户类的实例时创建
        age min: new Date()
    }
}

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.min.notmet

9.3.1. minSize

10. minSize约束

10.1. 目的

确保值的大小不低于最小值.

10.2. 示例

children minSize: 25

10.3. 描述

设置集合或数字属性的最小大小.

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.minSize.notmet

10.3.1. notEqual

11. notEqual约束

11.1. 目的

确保属性不等于指定值

11.2. 示例

username notEqual: "Bob"

11.3. 描述

错误代码 Code: className.propertyName.notEqual

11.3.1. nullable

12. nullable约束

12.1. 目的

允许属性设置为“null”,默认设置为`false`

12.2. 示例

age nullable: true

12.3. 描述

如果属性允许设置为`null`值,设置为`true`.对应数据库会设置为allow null.

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.nullable

注意:web请求表单提交,对于没有输入值的输入框, 属性的值为空白字符串,不是`null`.

12.3.1. range

13. range约束

13.1. 目的

使用Groovy的Range类型以确保属性值在指定范围内

13.2. 示例

age range: 18..65

13.3. 描述

Groovy的Range类型可以包含数字类型`IntRange`或日期类型,或者任意类型(实现了implements Comparable ,并且提供`next` 和 previous 两个方法)

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.range.toosmall 或者 className.propertyName.range.toobig

13.3.1. scale

14. scale约束

14.1. 目的

设置为浮点数字所需的尺度(即小数点右边的位数)

14.2. 示例

salary scale: 2

14.3. 描述

设置为浮点数字所需的尺度(即小数点右边的位数)。这个限制适用于以下类型的属性: java.lang.Float, java.lang.Double, and java.math.BigDecimal (及其子类)。当验证被调用时,此约束确定该数字是否包含比刻度允许更多的非零小数位置。如果是这样,它将数字循环到由刻度允许的最大小数位数。此约束不会生成验证错误消息。

这种约束的影响参见 schema generation.

无错误代码 Code: N/A

14.3.1. size

15. size约束

15.1. 目的

使用Groovy的Range类型限制集合、数字或字符串长度的大小

15.2. 示例

children size: 5..15

15.3. 描述

设置集合的大小、数字属性或字符串长度.

注意: 目前这个约束不能用于 blank 或者 nullable.可以使用自定义验证这些组合.

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.size.toosmallclassName.propertyName.size.toobig

15.3.1. unique

16. unique约束

16.1. 目的

在数据库级别将属性限制为唯一,会在数据库表上建立唯一性约束

16.2. 示例

username unique: true

16.3. 描述

如果属性必须是唯一的,则设置为`true`。这是一个持久的调用,将查询数据库。

警告:有可能会发生(虽然不可能在实践中出现)唯一性验证通过,但随后的保存失败。场景为:如果同时有其他的进程修改数据库在GORM检查的同时,那么相关调用将失败。为了防止这一点的唯一方法就是使用`SERIALIZABLE`隔离级别的事务,但对性能影响很大。 还可以通过定义将字段包含为参数值来定义多列的“唯一”约束。如果有另一个字段,请指定它的名称,但如果有多于一个字段,请使用列表List,例如:

示例:

group unique: 'department'

这个例子中,每个`department`部门内`group`名是唯一的,不同的部门可以存在重名的组。组名本身不是唯一的.

示例2:

username(unique: ['group', 'department'])

这个例子要求在一个部门和组内的 `username`用户名是唯一的。不同的部门或不同的组内可以存在重名的情况

这种约束的影响参见 schema generation.

错误代码 Code: className.propertyName.unique

16.3.1. url

17. url约束

17.1. Purpose

验证字符串值是有效URL.

17.2. 例子

homePage url: true

17.3. 描述

如果一个字符串是 URL设置为 true .

错误代码 Code: className.propertyName.url.invalid

17.3.1. validator

18. validator约束

18.1. 目的

为字段添加自定义约束.

18.2. 例子

even validator: {
    return (it % 2) == 0
}

// is equivalent to
even validator: { val ->
    return (val % 2) == 0
}

// 闭包的两个参数,第一个是属性值,第二个是实例对象
password1 validator: { val, obj ->
    obj.password2 == val
}

//闭包的三个参数,第一个是属性值,第二个是实例对象 ,第三个是errors错误对象
password1 validator: { val, obj, errors ->
    if (!(obj.password2 == val)) errors.rejectValue('password1', 'noMatch')
}

// Examples 传递参数到i18n的消息中 message.properties
// Example 1: 使用隐式的参数 0 (属性名称)

class Person {

    String name

    static constraints = {
        name validator: {
          if (!it) return ['entryMissing']
       }
}

// 上述例子将调用如下消息:
// person.name.entryMissing=Please enter a name in the field {0}

// Example 2: 使用隐式的参数 0 (属性名称) and 参数2 (属性值)

class Person {

    Integer yearOfBirth

    static constraints = {
        yearOfBirth validator: {
          if (yearOfBirth>2013) return ['yearTooBig']
       }
}

// 上述例子将调用如下消息:
// person.yearOfBirth.yearTooBig=The value {2} entered in the field {0} is not valid because it lies in the future.

// Example 3: 复杂

class Astronaut {

    Integer yearOfBirth
    Integer yearOfFirstSpaceTravel

        yearOfFirstSpaceTravel validator: { val, obj ->
                if (val < obj.yearOfBirth) ['datePriorTo', val.toString(), obj.yearOfBirth]
                else if (val < (obj.yearOfBirth+18)) ['maybeABitTooYoung', val-obj.yearOfBirth]
        }
}

// 各自的消息
// 注意,参数3是属性值转换toString方法,以避免不必要的格式,如前所述.
astronaut.yearOfFirstSpaceTravel.datePriorTo=The value {3} entered for the year of the first space travel is prior to the year of birth ({4}). Please correct the value.
astronaut.yearOfFirstSpaceTravel.maybeABitTooYoung={3} years seems a bit young for travelling to space, dude!

18.3. 描述

自定义验证通过闭包来实现,并接受3个参数。 如果闭包接受0个或1个参数,参数值将被验证("it" 是闭包的第0个参数). 如果接受2个参数,第一个是值,第二个是domain类的实例. 如果接受3个参数,第一个是值,第二个是domain类的实例,第3个是Spring Errors 对象。

闭包可以返回:

  • null 或者 true (或者没有返回值) 说明值是有效的。

  • false 说明值是无效的并使用默认的消息代码

  • string 指明错误代码增加到 "classname.propertyName."字符串后,从而获取错误消息.如果无法解决特定字段消息,错误代码本身将使用全局错误消息.

  • 一个包含字符串的list列表,后面是参数. 将使用 i18n/message.properties 中的定义组织消息。 参数的映射如下:参数0到2将自动映射到0:属性名称,1:类名,2:属性值。在参数3开始映射附加参数。

请注意,在最后的错误信息,该属性的标签将使用在message.properties文件中的定义。否则,将使用类中定义的属性名称。

当显式传递的错误代码,通常不需要使用"return"关键词返回的错误代码,因为如果从闭包返回时,它将检查错误是否已连接到错误的对象。

这可以看作特例,闭包中传递了第3个参数,预计错误的对象`Errors`将直接更新。