Spring Boot 3.x升级后@EmbeddedId更新报重复行异常问题
问题原因及解决方案
问题根源
Spring Boot 3.x 配套的 Hibernate 6.x 对嵌入式主键(@EmbeddedId)中关联实体的处理逻辑做了严格调整:
- 判断实体是否为持久态时,Hibernate 6.x 会严格校验关联实体的状态。如果更新时使用的
Author对象是手动构造(仅设置主键ID,而非从EntityManager加载的持久态实例),Hibernate 会认为这是全新的主键组合,执行INSERT而非UPDATE,触发复合主键的唯一约束重复行异常。 @Data自动生成的equals/hashCode依赖Author对象的默认实现,若Author的equals未基于主键正确判断,会导致Hibernate无法识别主键已存在,进而执行插入操作。
解决方案
1. 确保关联实体为持久态实例
更新时,不要手动构造 Author 对象仅赋值ID,而是通过Repository从数据库加载持久态的 Author 实例:
// 错误示例:手动构造非持久态Author BookId bookId = new BookId(); Author fakeAuthor = new Author(); fakeAuthor.setId(1L); bookId.setAuthor(fakeAuthor); bookId.setName("Java实战"); // 正确示例:加载持久态Author Author persistentAuthor = authorRepository.findById(1L).orElseThrow(); BookId bookId = new BookId(); bookId.setAuthor(persistentAuthor); bookId.setName("Java实战");
2. 手动重写BookId的equals/hashCode
避免依赖@Data自动生成的方法,基于Author的主键和name字段实现相等性判断:
@Data @Embeddable public class BookId implements Serializable { @ManyToOne @JoinColumn(name="author", nullable=false) private Author author; @Column(name="name") private String name; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BookId bookId = (BookId) o; return Objects.equals(author.getId(), bookId.author.getId()) && Objects.equals(name, bookId.name); } @Override public int hashCode() { return Objects.hash(author.getId(), name); } }
3. 替换为@IdClass实现复合主键(可选)
如果上述方案仍有问题,可以改用@IdClass,将复合主键字段直接定义在实体类中,避免嵌入式主键的关联实体处理问题:
// BookId类:仅保留主键字段,类型与实体对应 @Data public class BookId implements Serializable { private Long author; // 需与Author的主键类型一致 private String name; } // Book实体类 @Data @Entity(name="T_BOOK") @IdClass(BookId.class) public class Book { @Id @ManyToOne @JoinColumn(name="author", nullable=false) private Author author; @Id @Column(name="name") private String name; @Column(name="genre") private String genre; @Column(name="price") private Integer price; }
内容的提问来源于stack exchange,提问作者hunter man
相关产品推荐
相关产品推荐

