我有JPA实体,其中一些属性使用@Transient
注释。
我应该在equals/hashCode/toString
方法中使用这些属性吗?
我的第一个想法是否,但我不知道为什么。
答案 0 :(得分:6)
toString()
的情况不同,您可以使用toString()
执行任何操作,因此我只会涵盖equals()
(和hashCode()
)。
首先,规则:如果您想要在List
,Map
或Set
中存储对象,则需要{{1 }和equals
已实施,因此他们遵守文档中指定的标准合同。
现在,如何实施hashCode
和equals()
?一个“自然”的想法是使用映射为hashCode()
的属性作为Id
的一部分:
equals()
不幸的是,此解决方案存在主要问题:使用生成的标识符时,在实体变为持久之前不会分配值,因此如果瞬态 entity在保存之前被添加到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
的约定。
因此,推荐的方法是使用属于业务键的属性,即对于具有相同数据库标识的每个实例唯一的属性组合。例如,对于User类,这可以是用户名:
Set
Hibernate参考文档总结如下:
“永远不要使用数据库标识符来实现相等性;使用业务键,唯一的,通常是不可变的属性的组合。如果瞬态对象是持久的,数据库标识符将会改变。瞬态实例(通常与分离的实例一起)保存在
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(); } }
中,更改Set
会破坏hashcode
的合约。业务键的属性不必像以下那样稳定数据库主键,只要对象在同一个Set中,你就必须保证稳定性。“ - 12.1.3. Considering object identity“建议您使用业务密钥相等实施
Set
和equals()
。业务密钥相等意味着hashCode()
方法仅比较属性形成业务密钥。它是识别我们在现实世界中的实例的关键(自然候选键)“ - 4.3. Implementing equals() and hashCode()
所以,回到最初的问题:
equals()
属性很可能不是此类密钥的一部分。@Transient
,List
,Map
。答案 1 :(得分:0)
我所知道的@Transient
和transient
的两个典型用法是将它们用于无法序列化/持久化的东西(例如远程资源句柄)或可以从其他人重建的计算属性。
对于计算数据,在等式关系(equals/hashCode
)中使用它们是没有意义的,因为它会是多余的。该值是从已在相等中使用的其他值计算出来的。但是,在toString
中打印它们仍然有意义(例如,基本价格和比率用于计算实际价格)。
对于不可序列化/可持续数据,这取决于。我可以想象一个不可序列化的资源的句柄,但你仍然可以比较句柄所代表的资源名称。对于toString
也是如此,可能打印句柄资源名称很有用。
这是我的2美分,但如果你解释一下@Transient
的特定用法,有人可以提供更好的建议。
答案 2 :(得分:0)
例外情况可能来自让transient
,同时您提供处理它的writeObject()
和readObject()
。