hibernate/webapp上下文中的对象相等

ber*_*ert 13 java wicket hibernate equality

你如何处理由hibernate管理的java对象的对象相等性?在"hibernate in action"一书中,他们说人们应该支持商业密钥而不是代理密钥.
大多数时候,我没有业务密钥.想一想映射到一个人的地址.地址保存在一个Set中,并显示在Wicket RefreshingView(具有ReuseIfEquals策略)中.

我可以使用代理id或使用equals()和hashCode()函数中的所有字段.
问题是那些字段在生命周期中对象变化.要么是因为用户输入了一些数据,要么由于在OSIV(在视图中打开会话)过滤器内调用JPA merge()而导致id发生变化.

我对equals()和hashCode()契约的理解是那些在对象的生命周期内不应该改变的.

到目前为止我尝试了什么:

  • equals()基于使用数据库id的hashCode()(如果id为null,则为super.hashCode()).问题:新地址以空id开头,但在附加到某人时获取id,并且此人在osiv-filter中合并()(重新附加).
  • 首次调用hashCode()时,延迟计算哈希码并使哈希码成为@Transitional.不起作用,因为merge()返回一个新对象,并且不会复制哈希码.

我需要的是在我认为在对象创建期间分配的ID.这里有什么选择?我不想引入一些额外的持久属性.有没有办法明确告诉JPA为对象分配ID?

问候

Pas*_*ent 15

使用id实体不是一个好主意,因为瞬态实体还没有id(并且您仍然希望瞬态实体可能等于持久实体).

使用所有属性(除了数据库标识符)也不是一个好主意,因为所有属性都不是标识的一部分.

因此,实现相等性的首选(和正确)方法是使用业务键,如Java Persistence with Hibernate中所述:

使用业务键实现相等性

要获得我们建议的解决方案,您需要了解业务密钥的概念.业务键是属性或属性的某种组合,对于具有相同数据库标识的每个实例都是唯一的.从本质上讲,如果您没有使用代理主键,那么它将是您使用的自然键.与自然主键不同,业务键永远不会变化的绝对要求 - 只要它很少变化,这就足够了.

我们认为基本上每个实体类都应该有一些业务键,即使它包含了类的所有属性(这适用​​于某些不可变类).业务密钥是用户认为唯一标识特定记录的内容,而代理密钥是应用程序和数据库使用的密钥.

业务键等式意味着equals()方法仅比较形成业务键的属性.这是一个完美的解决方案,可以避免前面描述的所有问题.唯一的缺点是,首先需要额外考虑识别正确的业务密钥.无论如何都需要这种努力; 如果数据库必须通过约束检查确保数据完整性,则识别任何唯一键很重要.

对于User类,username是一个很好的候选业务键.它永远不会为null,它与数据库约束是唯一的,并且它很少变化,如果有的话:

    public class User {
        ...
        public boolean equals(Object other) {
            if (this==other) return true;
            if ( !(other instanceof User) ) return false;
            final User that = (User) other;
            return this.username.equals( that.getUsername() );
        }
        public int hashCode() {
            return username.hashCode();
        }
}
Run Code Online (Sandbox Code Playgroud)

也许我错过了一些东西,但是对于一个地址,商业密钥通常由街道号码,街道,城市,邮政编码,国家组成.我没有看到任何问题.

以防万一,Equals和HashCode是另一个有趣的读物.