Java 中,每个类会继承 java.lang.Object 的 equals 方法(可覆盖),Grails的Domain类默认也继承了该方法,一般情况下,默认的 equals 方法能满足大多数要求,但是延迟加载的情况下,一些Domain实例加载后,被包装成了一个Proxy对象(类型为 x.y.Foobar_$$_xxx),此时默认的 equals 方法就会出现问题(比如调用列表的contains、remove等方法时)。而使用 Groovy 的注解 @EqualsAndHashCode,同样无法解决该问题,反而会造成其他的异常。
bropen.toolkit.utils.grails.BeanUtils 提供了一个 equals 的API来处理这种问题,并且在编译的时候将该方法注入到了每个Domain类的class文件中。默认情况下,该API会自动处理Proxy对象的问题,并比较ID来判断两个对象是否是同一个实例。
此外,还可以手动在Domain类前使用注解 @DomainEquals 实现:
- 比较除 id 外的其他字段,如下例所示,只要两个同类的Domain实例的 name 属性相同(或id相同),便认为两个对象相等:
import bropen.toolkit.transform.DomainEquals
@DomainEquals(includes = "name")
class Foobar {
String name
}
def b1 = new Foobar( name: "abc" )
def b2 = new Foobar( name: "abc" )
assert b1 == b2
以外,includes 还可以设置为逗号分隔的多个字段,如下例:
@DomainEquals(includes = "name, age")
这样,对于联合主键的Domain类,可以使用本注解来生成 equals 方法,如:
@DomainEquals(includes = "user, code")
class Foobar {
String user
String code
static mapping = {
id composite: ["user", "code"]
}
} - 比较 Domain 实例和 Map 对象,如下例所示,如果Domain实例的 name 属性和一个Map对象中的同名键值相同,则认为两个对象相等:
@DomainEquals(compareMap = true, includes = "name")