简体   繁体   English

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

[英]Should @Transient property be used in equals/hashCode/toString?

I have JPA entities where some properties are annotated with @Transient . 我有JPA实体,其中一些属性使用@Transient注释。

Should I use these properties in equals/hashCode/toString methods? 我应该在equals/hashCode/toString方法中使用这些属性吗?

My first thought is NO but I don't know why. 我的第一个想法是不,但我不知道为什么。

  • Tips? 提示?
  • Ideas? 想法?
  • Explanations? 解释吗?

The case of toString() is different, you can do whatever you want with toString() so I will only cover equals() (and hashCode() ). toString()情况不同,你可以用toString()做任何你想要的事情,所以我只会覆盖equals() (和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 . 首先,规则: 如果要将对象存储在ListMapSet ,则需要实现equalshashCode以便它们遵守文档中指定的标准合同

Now, how to implement equals() and hashCode() ? 现在,如何实现equals()hashCode() A "natural" idea would be to use the properties mapped as Id as part of the equals() : 一个“自然”的想法是使用映射为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();
  }
}

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 . 不幸的是,这个解决方案有一个主要问题 :当使用生成的标识符时,在实体变为持久化之前不会分配值,因此如果在保存之前将一个瞬态实体添加到Set ,则其哈希代码将在Set更改打破了Set的合同。

The recommended approach is thus to use the attributes that are part of the business key ie 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: 例如,对于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();
  }
}

The Hibernate Reference Documentation summarizes this as follow: Hibernate参考文档总结如下:

" 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." 永远不要使用数据库标识符来实现相等性;使用业务键,唯一的,通常是不可变的属性的组合 。如果瞬态对象是持久的,数据库标识符将会改变。如果瞬态实例(通常与分离的实例一起)是保存在Set ,更改hashcode会破坏Set的约定。业务键的属性不必像数据库主键一样稳定,只要对象在同一个Set中,就必须保证稳定性。 - 12.1.3. - 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. 建议您使用Business键相等来实现equals()hashCode() 。业务键相等性意味着equals()方法仅比较构成业务键的属性。它是一个用于标识我们的实例的键。现实世界(自然候选键)“ - 4.3。 Implementing equals() and hashCode() 实现equals()和hashCode()

So, back to the initial question: 那么,回到最初的问题:

  • Use a business key if possible. 如果可能,请使用业务密钥。 @Transient attributes are very likely not part of such a key. @Transient属性很可能不属于这样一个键。
  • If not possible, use identifier properties but make sure to get the values assigned before to add an entity to a List , Map , Set . 如果不可能,请使用标识符属性,但请确保在将实体添加到ListMapSet之前获取指定的值。

See also 也可以看看

异常可能来自于让它是transient ,同时你提供writeObject()readObject()处理它。

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 (eg a remote resource handle ) or computed properties which can be reconstructed from others. 我所知道的@Transienttransient的两个典型用法是将它们用于无法序列化/持久化的东西(例如远程资源句柄 )或可以从其他人重建的计算属性。

For computed data , it makes no sense to use them in the equality relationship ( equals/hashCode ), because it would be redundant. 对于计算数据 ,在等式关系( equals/hashCode )中使用它们是没有意义的,因为它将是多余的。 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 (eg a base price and a ratio are used to compute the actual price). 然而,在toString打印它们仍然是有意义的(例如,基本价格和比率用于计算实际价格)。

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. 对于toString ,也许打印句柄资源名称很有用。

This was my 2 cent, but if you explain your particular usage of @Transient , someone can maybe give a better advice. 这是我的2美分,但如果你解释你对@Transient的特殊用法,有人可以提供更好的建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM