我有 JPA 实体,其中一些属性用@Transient
.
equals/hashCode/toString
我应该在方法中使用这些属性吗?
我的第一个想法是不,但我不知道为什么。
- 尖端?
- 想法?
- 解释?
的情况toString()
不同,你可以做任何你想做的事情,toString()
所以我只会介绍equals()
(和hashCode()
)。
首先,规则:如果你想将一个对象存储在 a或 a中List
,那么它是一个要求 并且被实现,以便它们遵守文档中指定的标准合同Map
Set
equals
hashCode
。
现在,如何实现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();
}
}
不幸的是,这个解决方案有一个主要问题:当使用生成的标识符时,在实体变得持久之前不会分配值,因此如果在保存之前将瞬态实体添加到 aSet
中,它的哈希码会在它位于时发生变化,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 参考文档总结如下:
"永远不要使用数据库标识符来实现相等;使用业务键,一个唯一的,通常是不可变的,属性的组合。如果一个瞬态对象被持久化,那么数据库标识符将会改变。如果瞬态实例(通常与分离的实例一起)是保存在 a 中
Set
,改变了hashcode
的契约Set
。业务键的属性不必像数据库主键一样稳定,只要对象在同一个 Set 中,你只需要保证稳定性。- 12.1.3。考虑对象身份“建议您实现
equals()
和hashCode()
使用业务密钥相等。业务密钥相等意味着该equals()
方法仅比较形成业务密钥的属性。它是一个可以识别我们在现实世界中的实例的密钥(自然候选密钥)” - 4.3。实现 equals() 和 hashCode()
那么,回到最初的问题:
@Transient
属性很可能不是这样一个键的一部分。List
, Map
, Set
。@Transient
和我知道的两种典型用法transient
是将它们用于无法序列化/持久化的东西(例如远程资源句柄)或可以从其他人重建的计算属性。
对于计算数据,在等式关系 () 中使用它们是没有意义的equals/hashCode
,因为这将是多余的。该值是根据已在等式中使用的其他值计算得出的。然而,将它们打印出来仍然是有意义的toString
(例如,使用基价和比率来计算实际价格)。
对于不可序列化/可持久化的数据,这取决于。我可以想象一个不可序列化的资源的句柄,但您仍然可以比较句柄代表的资源名称。相同toString
,也许打印句柄资源名称很有用。
这是我的 2 美分,但如果你解释一下你的特殊用法@Transient
,也许有人可以给出更好的建议。
例外可能来自让它存在transient
,同时您提供 writeObject()
和readObject()
处理它的位置。