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

发布于 2024-08-30 02:01:24 字数 754 浏览 5 评论 0原文

如何处理 hibernate 管理的 java 对象的对象相等性?在“休眠实践”一书中,他们说人们应该优先使用业务键而不是代理键。
大多数时候,我没有商务钥匙。想想映射到一个人的地址。地址保存在 Set 中并显示在 Wicket RefreshingView 中(使用 ReuseIfEquals 策略)。

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

我对 equals() 和 hashCode() 契约的理解是,它们在对象的生命周期内不应改变。

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

  • 基于 hashCode() 的 equals() ,它使用数据库 id(如果 id 为 null,则使用 super.hashCode() )。问题:新地址以空 id 开头,但在附加到某个人时会获得一个 id,并且此人会在 osiv-filter 中被 merged() (重新附加)。
  • 当第一次调用 hashCode() 时,惰性计算 hashcode 并将该 hashcode 设置为 @Transitional。不起作用,因为 merge() 返回一个新对象并且哈希码不会被复制。

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

问候

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

找回味觉 2024-09-06 02:01:24

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

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

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

使用业务密钥实现平等

要获得我们推荐的解决方案,您需要了解以下概念
一把商务钥匙。业务密钥是一个属性或属性的某种组合,
对于具有相同数据库标识的每个实例来说都是唯一的。本质上,如果您不使用代理主键,则它是您将使用的自然键。
与自然主键不同,这并不是业务的绝对要求
key 永远不会改变——只要它很少改变就足够了。

我们认为本质上每个实体类都应该有一些业务密钥,甚至
如果它包含该类的所有属性(这对于某些
不可变类)。业务键是用户认为唯一标识特定记录的键,而代理键是应用程序和
数据库使用。

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

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

 公共类用户 {
        ...
        公共布尔等于(对象其他){
            if (this==other) 返回 true;
            if ( !(other instanceof User) ) 返回 false;
            最终用户=(用户)其他;
            返回 this.username.equals( that.getUsername() );
        }
        公共 int hashCode() {
            返回用户名.hashCode();
        }
}

也许我错过了一些东西,但对于地址来说,业务键通常由街道号码、街道、城市、邮政编码和国家/地区组成。我认为这没有任何问题。

以防万一,Equals And HashCode 是另一本有趣的读物。

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.

赠我空喜 2024-09-06 02:01:24

也许一个transient属性可以做到这一点?
这样你就不用担心坚持的问题了。像这样:

@Transient
private Integer otherId;

Maybe a transient property would do it?
That way you don't have to worry about the persistence. Like this:

@Transient
private Integer otherId;
枕梦 2024-09-06 02:01:24

我过去常常这样做:equal 和 hashcode 在设置后使用键,否则 equals 使用基本实现(又名 ==)。如果 hashcode() 返回 super.hashcode() 而不是 0,它也应该可以工作。

@Override
public int hashCode() {
    if (code == null) {
        return 0;
    } else {
        return code.hashCode();
    }
}

@Override
public boolean equals(Object obj) {
    if (obj instanceof PersistentObject && Hibernate.getClass(obj).equals(Hibernate.getClass(this))) {
        PersistentObject po = (PersistentObject) obj;

        if (code == null) {
            return po.code == null && this == po;
        } else {
            return code.equals(po.getCode());
        }
    } else {
        return super.equals(obj);
    }
}

I use to do it that way: equal and hashcode use the key when it has been set, otherwise equals uses the base implementation (aka ==). It should work too if hashcode() returns super.hashcode() instead of 0.

@Override
public int hashCode() {
    if (code == null) {
        return 0;
    } else {
        return code.hashCode();
    }
}

@Override
public boolean equals(Object obj) {
    if (obj instanceof PersistentObject && Hibernate.getClass(obj).equals(Hibernate.getClass(this))) {
        PersistentObject po = (PersistentObject) obj;

        if (code == null) {
            return po.code == null && this == po;
        } else {
            return code.equals(po.getCode());
        }
    } else {
        return super.equals(obj);
    }
}
流绪微梦 2024-09-06 02:01:24

问题是您多久可能会出现多个未保存的对象,这些对象可能是需要放入集合或地图中的重复项?对我来说,答案几乎永远不会,所以我对未保存的对象使用代理键和 super.equals/hashcode。

业务密钥在某些情况下是有意义的,但它们可能会导致问题。例如,如果两个人住在同一个地址怎么办 - 如果您希望它成为数据库中的一条记录,那么您必须将其作为多对多进行管理,并且失去级联删除它的能力,因此当最后一个记录时居住在那里的人被删除,您必须做额外的工作才能删除该地址。但是,如果您为每个人存储相同的地址,那么您的业务密钥必须包含人员实体,这可能意味着您的 equals/hashcode 方法中的数据库命中。

The question is how often are you likely to have multiple unsaved objects that might be duplicates that need to go into a set or map? For me, the answer is virtually never so I use surrogate keys and super.equals/hashcode for unsaved objects.

Business keys make sense in some cases, but they can cause problems. For example, what if two people live at the same address - if you want that to be one record in the database, then you have to manage it as a many-to-many and lose the ability to cascade delete it so when the last person living there is deleted, you have to do extra work to get rid of the address. But if you store the same addresss for each person then your business key has to include the person entity, which may mean a database hit inside your equals/hashcode methods.

终难遇 2024-09-06 02:01:24

感谢您的所有意见。我决定使用代理键并在对象创建时提供这些代理键。这样我就可以远离所有那些“很少”改变的东西,并拥有一些坚实的身份基础。第一次测试看起来相当不错。

感谢大家抽出时间。不幸的是,我只能接受一个答案作为解决方案,我会选择帕斯卡,因为他为我提供了很好的阅读;)

享受

Thanks for all your input. I decided to use surrogate keys and provide those right at object creation time. This way i stay clear of all that 'rarely' changing stuff and have something solid to base identity on. First tests look rather good.

thank you all for your time. Unfortunately, i can only accept one answer as solution i will take Pascals, as he provided me with good reading ;)

enjoy

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文