在hibernate / webapp上下文中的对象相等 [英] Object equality in context of hibernate / webapp

查看:120
本文介绍了在hibernate / webapp上下文中的对象相等的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你如何处理由hibernate管理的java对象的对象相等性?在'hibernate in action'这本书中,他们说应该使用商业密钥而不是代理密钥。

大多数情况下,我没有商业密钥。考虑映射到一个人的地址。地址保存在Set中并显示在Wicket RefreshingView中(带有ReuseIfEquals策略)。

我可以使用代理ID或使用等号中的所有字段( )和hashCode()函数。

问题是这些字段在生命期间会改变ob对象。因为用户输入了一些数据,或者由于在OSIV(打开会话在视图中)过滤器中调用了JPA merge()而导致id发生更改。



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



到目前为止,我已经尝试过:


  • equals()基于hashCode(),它使用数据库id .hashCode()如果id为null)。问题:新地址以空id开始,但在附加到某个人时获得一个id,并且此人在osiv-过滤器中合并()(重新附加)。

  • 当首次调用hashCode()并使该hashcode @Transitional时,懒计算散列码。不行,因为merge()返回一个新的对象,并且hashcode不会被复制过来。



我需要的是在对象创建期间分配的ID我认为。我的选择是什么?我不想介绍一些额外的持久性财产。有没有办法明确告诉JPA为对象分配一个ID?



问候

解决方案使用 id 实体并不是一个好主意,因为暂时实体还没有一个id(并且您仍然希望临时实体可能与持久实体相同)。



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



因此,实现平等的首选(正确)方法是使用业务关键字,如 中的<持久性与Java持久性所述:


实现与商业密钥的相等性



要获得我们推荐的解决方案,您需要了解
a商业钥匙。业务密钥是属性或属性的组合,
对于具有相同数据库标识的每个实例都是唯一的。从本质上讲,如果您不使用代理主键,它将是您使用的自然键。
与自然主键不同,商业
键永不改变的绝对要求 - 只要它很少变化,就足够了。



我们认为,实质上每个实体类都应该有一些商业关键字,如果它包含了类的所有属性(这适用​​于一些
不可变的类),那么即使是
也是如此。业务密钥是用户认为唯一标识特定记录的数据,而代理键是应用程序和
数据库使用的数据。



业务密钥相等意味着equals()方法仅比较构成业务密钥的属性。这是避免前面所述的所有问题的完美解决方案。唯一的缺点是需要额外考虑
首先确定正确的商业密钥。无论如何,这一努力是必需的。
如果数据库必须通过约束检查来确保数据完整性,那么识别任何唯一的键是非常重要的。



对于User类, username 是一个很好的候选业务关键。它不会是空的,
它对于数据库约束是唯一的,并且它很少会改变,如果有的话:

  public class User {
...
public boolean equals(Object other){
if(this == other)return true;
if(!(其他instanceof用户))返回false;
最终用户=(用户)其他;
返回this.username.equals(that.getUsername());
}
public int hashCode(){
return username.hashCode();



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



以防万一, Equals和HashCode 是另一个有趣的阅读。


How do you handle object equality for java objects managed by hibernate? In the 'hibernate in action' book they say that one should favor business keys over surrogate keys.
Most of the time, i do not have a business key. Think of addresses mapped to a person. The addresses are keeped in a Set and displayed in a Wicket RefreshingView (with a ReuseIfEquals strategy).

I could either use the surrogate id or use all fields in the equals() and hashCode() functions.
The problem is that those fields change during the lifetime ob the object. Either because the user entered some data or the id changes due to JPA merge() being called inside the OSIV (Open Session in View) filter.

My understanding of the equals() and hashCode() contract is that those should not change during the lifetime of an object.

What i have tried so far:

  • equals() based on hashCode() which uses the database id (or super.hashCode() if id is null). Problem: new addresses start with an null id but get an id when attached to a person and this person gets merged() (re-attached) in the osiv-filter.
  • lazy compute the hashcode when hashCode() is first called and make that hashcode @Transitional. Does not work, as merge() returns a new object and the hashcode does not get copied over.

What i would need is an ID that gets assigned during object creation I think. What would be my options here? I don't want to introduce some additional persistent property. Is there a way to explicitly tell JPA to assign an ID to an object?

Regards

解决方案

Using the id of an entity is not a good idea because transient entities don't have an id yet (and you still want a transient entity to be potentially equal to a persistent one).

Using all properties (apart from the database identifier) is also not a good idea because all properties are just not part of the identity.

So, the preferred (and correct) way to implement equality is to use a business key, as explained in Java Persistence with Hibernate:

Implementing equality with a business key

To get to the solution that we recommend, you need to understand the notion of a business key. A business key is a property, or some combination of properties, that is unique for each instance with the same database identity. Essentially, it’s the natural key that you would use if you weren’t using a surrogate primary key instead. Unlike a natural primary key, it isn’t an absolute requirement that the business key never changes—as long as it changes rarely, that’s enough.

We argue that essentially every entity class should have some business key, even if it includes all properties of the class (this would be appropriate for some immutable classes). The business key is what the user thinks of as uniquely identifying a particular record, whereas the surrogate key is what the application and database use.

Business key equality means that the equals() method compares only the properties that form the business key. This is a perfect solution that avoids all the problems described earlier. The only downside is that it requires extra thought to identify the correct business key in the first place. This effort is required anyway; it’s important to identify any unique keys if your database must ensure data integrity via constraint checking.

For the User class, username is a great candidate business key. It’s never null, it’s unique with a database constraint, and it changes rarely, if ever:

    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();
        }
}

Maybe I missed something but for an Address, the business key would typically be made of the street number, the street, the city, the postal code, the country. I don't see any problem with that.

Just in case, Equals And HashCode is another interesting reading.

这篇关于在hibernate / webapp上下文中的对象相等的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆