You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 22:12:50