Grails 为什么域类中字段的范围修饰符会阻止验证?

Grails 为什么域类中字段的范围修饰符会阻止验证?,grails,gorm,grails-2.0,grails-domain-class,Grails,Gorm,Grails 2.0,Grails Domain Class,我刚从Grails(来自Rails)开始,我注意到Grails似乎并不喜欢域类中字段的范围修饰符 我知道域类中所有未定义范围的字段在默认情况下都是公共的,但如果您实际将其声明为公共的,Grails将不会验证它 class Person { public String firstName public String middleName public String lastName } 如果添加约束,Grails将在调用validate()时抛出NotReadableProperty

我刚从Grails(来自Rails)开始,我注意到Grails似乎并不喜欢域类中字段的范围修饰符

我知道域类中所有未定义范围的字段在默认情况下都是公共的,但如果您实际将其声明为公共的,Grails将不会验证它

class Person {
  public String firstName
  public String middleName
  public String lastName
}
如果添加约束,Grails将在调用validate()时抛出NotReadablePropertyException异常

然而,如果你拿出公开声明,一切正常

有人能解释一下域类中的作用域在幕后发生了什么吗?很难理解为什么明确声明一些已经是公共的东西会破坏框架。我猜你也不想声明任何“私有”的东西,不过如果有一个不应该直接操作的字段可以对域类的使用者隐藏,那就太好了

如果添加约束,Grails将在调用validate()时抛出NotReadablePropertyException异常

以前从未注意到这一点,听起来像个虫子

如果有一个不应该被直接操作的字段可以对域类的使用者隐藏,那就太好了

如果要阻止直接访问属性,只需添加getter和setter。在下面的(人为的)示例中,我确保名称始终作为大写字符串读/写

class Person {
  public String firstName
  public String middleName
  public String lastName

  public void setFirstName(String name) {
    this.firstName = name.toUpperCase()
  }

  public String getFirstName() {
    return this.firstName.toUpperCase()
  }
}
如果添加约束,Grails将在调用validate()时抛出NotReadablePropertyException异常

以前从未注意到这一点,听起来像个虫子

如果有一个不应该被直接操作的字段可以对域类的使用者隐藏,那就太好了

如果要阻止直接访问属性,只需添加getter和setter。在下面的(人为的)示例中,我确保名称始终作为大写字符串读/写

class Person {
  public String firstName
  public String middleName
  public String lastName

  public void setFirstName(String name) {
    this.firstName = name.toUpperCase()
  }

  public String getFirstName() {
    return this.firstName.toUpperCase()
  }
}

当您在没有范围修饰符的情况下向Groovy类添加字段时,它更多的是被推断为公共的,而不是实际的公共的。编译器将该字段转换为私有字段,并为其生成公共getter和setter,尽管它不会覆盖您编写的getter或setter。这很方便,因为您以后可以编写getter和/或setter来实现业务逻辑,而不会影响调用方

但是一个公共字段(声明为“public”)就是一个公共字段。没有生成getter或setter。我建议使用反编译器来查看这一点——在src/groovy中创建一个简单的POGO,例如

class Thing {
   String realProperty
   public String fieldButNotProperty
}
并使用或其他反编译器打开.class文件

GORM自动假定类型化属性是持久性的,除非您使用
transients
列表排除一些属性。该类型是必需的,因此它知道如何持久化数据,而
def name
之类的属性将被忽略。从这个意义上讲,属性类似于JavaBean属性——匹配的getter/setter对

Hibernate不支持Groovy,也不知道幕后发生了什么——它只调用getter和setter在持久化期间设置和访问字段数据。因此,Groovy编译器将这些添加到中,使得Grails中的POGO很容易被Hibernate持久化。您也可以自己做这件事-添加一个具有正确名称和数据类型的getter和setter(例如,
String getName()
void setName(String name)
),它将被视为一个持久属性,即使您对值不做任何操作


之所以出现“NotReadablePropertyException”,是因为没有getter来调用您的“property”。即使您的字段是完全可访问的,但您已经有效地将它们隐藏在GORM和Hibernate之外。

当您在没有范围修饰符的情况下向Groovy类添加字段时,它更多的是被推断为公共的,而不是被隐藏的实际上是公共的。编译器将该字段转换为私有字段,并为其生成公共getter和setter,尽管它不会覆盖您编写的getter或setter。这很方便,因为您以后可以编写getter和/或setter来实现业务逻辑,而不会影响调用方

但是一个公共字段(声明为“public”)只是一个公共字段。没有生成的getter或setter。我建议使用反编译器来查看它的实际情况——在src/groovy中创建一个简单的POGO,例如

class Thing {
   String realProperty
   public String fieldButNotProperty
}
并使用或其他反编译器打开.class文件

GORM自动假定类型化属性是持久的,除非您使用
transients
列表排除某些属性。该类型是必需的,因此它知道如何持久化数据,而
def name
之类的属性将被忽略。从这个意义上讲,属性类似于JavaBean属性-匹配的getter/setter对

Hibernate不支持Groovy,也不知道在后台发生了什么-它只调用getter和setter来设置和访问持久化期间的字段数据。因此,Groovy编译器添加了这些数据,使得Hibernate很容易持久化Grails中的POGO。您可以自己做这件事-使用corr添加getter和setterect名称和数据类型(例如,
String getName()
void setName(String name)
),即使您不处理这些值,它也将被视为一个持久属性


NotReadablePropertyException
的原因是没有getter来调用您的“属性”。即使您的字段完全可以访问,但您已经有效地将它们隐藏在GORM和Hibernate之外。

我认为这可能也是一个bug,但在我的测试工具(GroovyTestCase)中它是相当一致的如果有时间的话,你能告诉我,如果把这个放在单元测试中,那个人。veryify()会抛出一个异常,如果你有诸如firstName blank:false之类的约束条件。我想是