Hibernate逻辑删除后插入同主键实体报重复键错误如何解决
问题根源
你遇到的报错并不是你新增的constraint_country联合约束导致的,而是违反了表中country_pkey主键约束:你当前的表结构将code设为主键,主键本身要求该字段值全局唯一,和你是否逻辑删除没有关系——逻辑删除只是给date_deleted赋值,旧记录的code值仍然存在于表中,新增同code的记录自然会触发主键冲突。
解决方案
1. 优先方案:调整表结构+配套Hibernate注解
要实现「逻辑删除后可新增同code记录,同时保证未删除的code唯一」,首先要调整表的主键设计,再配合对应索引规则:
表结构修改
- 新增无业务含义的字段作为主键,比如自增
id字段 - 删除原
code的主键约束 - 新建部分唯一索引(PostgreSQL支持该特性),只限制
date_deleted IS NULL的记录code唯一,比你之前加的联合唯一约束更符合需求:
CREATE TABLE public.country( id int8 NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- 新增主键 code bpchar(2) NOT NULL, description bpchar(50) NULL, date_deleted timestamp NULL ); -- 新增部分唯一索引,保证未删除的code唯一 CREATE UNIQUE INDEX idx_country_unique_code ON country (code) WHERE date_deleted IS NULL;
Hibernate实体类修改
对应调整实体类的注解配置即可,原有逻辑删除的@SQLDelete、@Where注解可以保留:
@Entity @Table(name = "Country") @SQLDelete(sql = "UPDATE Country SET date_deleted=NOW() WHERE id = ?") // 注意这里条件改为新主键id @Where(clause = "date_deleted is NULL") // 如果要让Hibernate自动生成上述部分唯一索引,可以用Hibernate专有注解: @org.hibernate.annotations.Table(appliesTo = "Country", indexes = { @Index(name = "idx_country_unique_code", columnNames = {"code"}, unique = true) }) public class Country { // 新增主键字段 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "code", nullable = false) private String code; @Column(name = "description") private String description; @Column(name = "date_deleted") private Date date_deleted; // 其余逻辑不变 }
2. 备选方案:逻辑删除时修改旧记录code
如果无法调整表结构,可以修改@SQLDelete的逻辑,删除时给旧记录的code加上删除标记,避免和新记录冲突,该方案无需修改表结构但会污染业务字段,仅作为临时方案:
// 逻辑删除时拼接删除标记到旧code上,释放code的占用 @SQLDelete(sql = "UPDATE Country SET date_deleted=NOW(), code = CONCAT(code, '_del_', EXTRACT(EPOCH FROM NOW())) WHERE code = ?")
内容的提问来源于stack exchange,提问作者Mathias
相关产品推荐
相关产品推荐

