@Transient 属性应该在 equals/hashCode/toString 中使用吗?

发布于 2024-09-03 22:21:13 字数 200 浏览 3 评论 0原文

我有 JPA 实体,其中一些属性用 @Transient 注释。

我应该在 equals/hashCode/toString 方法中使用这些属性吗?

我的第一个想法是,但我不知道为什么。

  • 尖端?
  • 有想法吗?
  • 解释?

I have JPA entities where some properties are annotated with @Transient.

Should I use these properties in equals/hashCode/toString methods?

My first thought is NO but I don't know why.

  • Tips?
  • Ideas?
  • Explanations?

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

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

发布评论

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

评论(3

兮子 2024-09-10 22:21:13

toString() 的情况有所不同,您可以使用 toString() 执行任何您想要的操作,因此我将仅介绍 equals() (并且hashCode())。

首先,规则:如果您想将对象存储在 ListMapSet 中,那么它是要求实现equalshashCode,以便它们遵守文档中指定的标准约定

现在,如何实现equals()hashCode()呢?一个“自然”的想法是使用映射为 Id 的属性作为 equals() 的一部分:

public class User {
    ...
    public boolean equals(Object other) {
        if (this==other) return true;
        if (id==null) return false;
        if ( !(other instanceof User) ) return false;
        final User that = (User) other;
        return this.id.equals( that.getId() );
    }
    public int hashCode() {
        return id==null ? System.identityHashCode(this) : id.hashCode();
  }
}

不幸的是,这个解决方案有一个主要问题:当使用生成的标识符时,直到实体变得持久时才会分配值,因此如果瞬态实体在被添加到Set之前保存后,其哈希码在 Set 中时会发生变化,这会破坏 Set 的约定。

因此,推荐的方法是使用属于业务密钥一部分的属性,即对于具有相同数据库标识的每个实例来说唯一的属性组合。例如,对于 User 类,这可能是用户名:

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

Hibernate 参考文档对此进行了如下总结:

永远不要使用数据库标识符来实现平等;使用业务密钥,这是唯一的、通常不可变的属性的组合。如果将瞬态对象持久化,则数据库标识符将会更改。如果瞬态实例(通常与分离实例一起)保存在 Set 中,更改 hashcode 会破坏 Set 属性 的业务密钥约定。不必像数据库主键一样稳定,只要对象在同一个 Set 中就可以保证稳定性。” - 12.1.3。考虑对象身份

建议您使用业务键相等来实现 equals()hashCode()。业务键相等意味着 < code>equals() 方法仅比较构成业务键的属性,它是在现实世界中标识我们的实例的键(自然候选键)” - 4.3。实现 equals() 和 hashCode()

那么,回到最初的问题:

  • 如果可能的话,使用业务密钥。 @Transient 属性很可能不是此类键的一部分。
  • 如果不可能,请使用标识符属性,但确保在将实体添加到 ListMapSet 之前获取分配的值。

另请参阅

The case of toString() is different, you can do whatever you want with toString() so I will only cover equals() (and hashCode()).

First, the rule: if you want to store an object in a List, Map or a Set then it is a requirement that equals and hashCode are implemented so they obey the standard contract as specified in the documentation.

Now, how to implement equals() and hashCode()? A "natural" idea would be to use the properties mapped as Id as part of the equals():

public class User {
    ...
    public boolean equals(Object other) {
        if (this==other) return true;
        if (id==null) return false;
        if ( !(other instanceof User) ) return false;
        final User that = (User) other;
        return this.id.equals( that.getId() );
    }
    public int hashCode() {
        return id==null ? System.identityHashCode(this) : id.hashCode();
  }
}

Unfortunately, this solution has a major problem: when using generated identifiers, the values are not assigned until an entity becomes persistent so if a transient entity is added to a Set before being saved, its hash code will change while it's in the Set and this breaks the contract of the Set.

The recommended approach is thus to use the attributes that are part of the business key i.e. a combination of attributes that is unique for each instance with the same database identity. For example, for the User class, this could be the username:

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

The Hibernate Reference Documentation summarizes this as follow:

"Never use the database identifier to implement equality; use a business key, a combination of unique, usually immutable, attributes. The database identifier will change if a transient object is made persistent. If the transient instance (usually together with detached instances) is held in a Set, changing the hashcode breaks the contract of the Set. Attributes for business keys don't have to be as stable as database primary keys, you only have to guarantee stability as long as the objects are in the same Set." - 12.1.3. Considering object identity

"It is recommended that you implement equals() and hashCode() using Business key equality. Business key equality means that the equals() method compares only the properties that form the business key. It is a key that would identify our instance in the real world (a natural candidate key)" - 4.3. Implementing equals() and hashCode()

So, back to the initial question:

  • Use a business key if possible. @Transient attributes are very likely not part of such a key.
  • If not possible, use identifier properties but make sure to get the values assigned before to add an entity to a List, Map, Set.

See also

无声无音无过去 2024-09-10 22:21:13

据我所知,@Transient 和 Transient 的两种典型用法是将它们用于无法序列化/持久化的内容(例如远程资源) 句柄)或可以从其他属性重建的计算属性。

对于计算数据,在相等关系(equals/hashCode)中使用它们是没有意义的,因为它是多余的。该值是根据等式中已使用的其他值计算得出的。然而,在 toString 中打印它们仍然有意义(例如,使用基本价格和比率来计算实际价格)。

对于不可序列化/可持久化的数据,这取决于情况。我可以想象一个不可序列化的资源的句柄,但您仍然可以比较该句柄代表的资源名称。对于 toString 也是如此,也许打印句柄资源名称很有用。

这是我的 2 美分,但如果你解释一下 @Transient 的特殊用法,也许有人可以给出更好的建议。

The two typical usages of @Transient and transient that I'm aware of, are to use them either for stuff that can't be serialized/persisted (e.g. a remote resource handle) or computed properties which can be reconstructed from others.

For computed data, it makes no sense to use them in the equality relationship (equals/hashCode), because it would be redundant. The value is computed out of other value which are already used in the equality. It can however still makes sense to print them in toString (e.g. a base price and a ratio are used to compute the actual price).

For not serializable/persitable data, it depends. I can imagine a handle to a resource that is not serializable, but you can still compare the resource name that the handle represent. Same for toString, maybe printing the handle resource name is useful.

This was my 2 cent, but if you explain your particular usage of @Transient, someone can maybe give a better advice.

双马尾 2024-09-10 22:21:13

异常可能来自于让它成为瞬态,同时您在处理它的地方提供了 writeObject() 和 readObject() 。

Exception maybe comes from letting it be transient and at the same time you provide writeObject() and readObject() where you process it.

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