[英]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. 我的第一个想法是不,但我不知道为什么。
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 . 首先,规则: 如果要将对象存储在
List
, Map
或Set
,则需要实现equals
和hashCode
,以便它们遵守文档中指定的标准合同 。
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 thehashcode
breaks the contract of theSet
. 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()
andhashCode()
using Business key equality . Business key equality means that theequals()
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: 那么,回到最初的问题:
@Transient
attributes are very likely not part of such a key. @Transient
属性很可能不属于这样一个键。 List
, Map
, Set
. List
, Map
, Set
之前获取指定的值。 异常可能来自于让它是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. 我所知道的
@Transient
和transient
的两个典型用法是将它们用于无法序列化/持久化的东西(例如远程资源句柄 )或可以从其他人重建的计算属性。
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.