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

Hibernate StatelessSession.upsert()基于@NaturalId的合并条件配置问题

问题描述

我有一个实体类,包含代理主键(surrogate PK)和由3个varchar字段组成的复合唯一键,代码定义如下:

public class InvoiceEntity {

    @Id
    @GeneratedValue
    private UUID id;

    @NaturalId
    private InvoiceUniqueKey invoiceId;
}

调用statelessSession.upsert(invoiceEntity)时,Hibernate生成的SQL会基于@Id标注的主键执行merge操作:

merge into invoice as t using (select cast(? as uuid) id ... as s on (t.id=s.id)

我需要强制Hibernate使用@NaturalId标注的唯一键作为merge的匹配条件,但尝试多种Hibernate注解并查阅文档后仍未找到方法,该如何解决?

解决方案

方法1:手动实现基于自然ID的UPSERT逻辑

Hibernate的StatelessSession.upsert默认优先用主键匹配,要基于自然ID实现upsert,可手动拆分查询、插入/更新步骤:

  • 先通过自然ID查询是否存在对应记录:
    InvoiceEntity existing = statelessSession.byNaturalId(InvoiceEntity.class)
        .using("invoiceId", invoiceEntity.getInvoiceId())
        .load();
    
  • 根据查询结果执行对应操作:
    if (existing == null) {
        statelessSession.insert(invoiceEntity);
    } else {
        // 将需要更新的字段值复制到已有实例
        existing.setAmount(invoiceEntity.getAmount());
        existing.setStatus(invoiceEntity.getStatus());
        statelessSession.update(existing);
    }
    

方法2:自定义SQL实现带冲突更新的插入

通过@SQLInsert注解自定义插入SQL,利用数据库的冲突更新语法(不同数据库语法略有差异,以下以PostgreSQL为例):

@Entity
@SQLInsert(sql = "INSERT INTO invoice (id, inv_num, client_code, doc_type, amount, status) " +
    "VALUES (?, ?, ?, ?, ?, ?) " +
    "ON CONFLICT (inv_num, client_code, doc_type) DO UPDATE " +
    "SET amount = EXCLUDED.amount, status = EXCLUDED.status")
public class InvoiceEntity {
    @Id
    @GeneratedValue
    private UUID id;

    @NaturalId
    private InvoiceUniqueKey invoiceId;
    
    // 其他业务字段
    private BigDecimal amount;
    private String status;
}

调用statelessSession.insert(invoiceEntity)时,会触发自定义的SQL逻辑:如果自然ID对应的记录已存在,则执行更新操作,实现upsert效果。

方法3:升级到Hibernate 6+使用@Upsert注解(推荐)

Hibernate 6.0及以上版本提供了更灵活的upsert配置,可直接指定用自然ID作为匹配条件:

@Entity
@Upsert(identifier = @Upsert.UpsertIdentifier(naturalId = true))
public class InvoiceEntity {
    @Id
    @GeneratedValue
    private UUID id;

    @NaturalId
    private InvoiceUniqueKey invoiceId;
}

此时调用statelessSession.upsert(invoiceEntity),Hibernate会自动基于@NaturalId标注的唯一键生成匹配条件的merge SQL。

内容的提问来源于stack exchange,提问作者Roman Oborin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:24:58