含复合主键的JPA实体如何正确重写equals与hashCode方法?
问题背景
我有一张包含复合主键的表,主键包含自身字段和另一张表的外键。我决定不使用@IdClass或@EmbeddedId,实体类定义如下:
class SomeEntity { @Id private String someId; @Id @ManyToOne(optional = false) @JoinColumn(name = ...) private Other other; // 其他映射 }
此时Hibernate抛出如下警告:
WARN 69752 ... : HHH000038: Composite-id class does not override equals(): ....Some
WARN 69752 ... : HHH000039: Composite-id class does not override hashCode(): ....Some
我自己编写的实现如下,想确认是否正确:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Some that = (Some) o; return Objects.equals(someId, that.someId) && Objects.equals(other, that.other); } @Override public int hashCode() { return Objects.hash(someId, other); }
解答
你的实现是完全正确的,不过有几个关键细节需要明确:
复合主键的equals/hashCode只需要包含主键字段
实体的equals和hashCode是用来标识实体唯一性的核心依据,复合主键的所有字段就是实体身份的唯一判定标准——所以只需要包含someId和other这两个主键字段,其他非主键字段完全不需要加入到这两个方法中。如果强行加入非主键字段,反而会导致实体在状态变化(比如非主键字段更新)时hashCode发生变化,进而引发Hibernate缓存、集合存储等场景下的异常。关联实体
Other的equals/hashCode必须正确实现
你的代码中用Objects.equals(other, that.other)比较关联实体,这依赖于Other本身已经正确重写了equals和hashCode方法——Other的这两个方法必须基于它自己的主键字段(而非默认的对象引用),否则即使两个Other实例对应数据库中的同一条记录,也会被判定为不同对象,进而导致SomeEntity的equals判断出错。Hibernate警告的原因
Hibernate在管理实体时,依赖equals和hashCode处理一级缓存、实体在集合中的存储、判断实体是否被修改等场景。对于复合主键的实体,如果没有重写这两个方法,Hibernate会使用默认的对象引用比较,这显然不符合数据库中实体的唯一性逻辑,所以会抛出警告提醒你修复。
总结来说,你当前的equals和hashCode实现完全符合要求,只要确保关联实体Other也正确实现了这两个方法,就可以解决Hibernate的警告,同时保证实体的唯一性判断逻辑正确。
内容的提问来源于stack exchange,提问作者Jin Kwon

