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

Hibernate插入DB2时排除自动生成标识列的报错问题

解决Hibernate+DB2使用@EmbeddedId含自增ID的插入报错问题

最近我在用Hibernate向DB2数据库插入一个带@EmbeddedId的实体时,直接踩了坑,报了一连串错误:

SQL Error: -798, SQLState: 428C9 DB2 SQL Error: SQLCODE=-798, SQLSTATE=428C9, SQLERRMC=ID, DRIVER=4.9.78
SQL Error: -727, SQLState: 56098 DB2 SQL Error: SQLCODE=-727, SQLSTATE=56098, SQLERRMC=2;-798;428C9;ID, DRIVER=4.9.78
On release of batch it still contained JDBC statements
Error Refeshing Firmware Level Sources :org.springframework.dao.InvalidDataAccessResourceUsageException: could not perform addBatch; SQL [insert into Db2Schema.table_entity ( ID, KEY1, KEY2, KEY3, .....) values (?, ?, ?, ?, ......)]; nested exception is org.hibernate.exception.SQLGrammarException: could not perform addBatch

先贴一下我当时的实体代码,方便大家对照:

原实体类

@Entity 
@Table(name="table_entity", schema="Db2Schema") 
@DynamicUpdate 
@DynamicInsert
public class TableEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    private TableEntityKey tableEntityKey;
    // 其余实体字段省略
}

原可嵌入主键类

@Embeddable
public class TableEntityKey implements Serializable{
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ID", insertable = false, updatable = false)
    // 之前试过加@Transient,但也没解决
    private Integer id;
    @Column(name="key1", length=254)
    // 其余主键字段省略
}

问题根源分析

折腾了半天,终于搞懂了:DB2的IDENTITY自增列有个硬性规则——绝对不允许在INSERT语句里显式指定该列的值,哪怕传NULL都不行。

而我的问题出在两个地方:

  1. 虽然我给id字段加了insertable=false,但因为它是@EmbeddedId复合主键的一部分,Hibernate的处理逻辑比较特殊,生成INSERT语句时还是把ID列塞进了字段列表里,直接触发了DB2的-798错误。
  2. @GeneratedValue放在@Embeddable类里本身就不是Hibernate推荐的用法,复合主键的设计初衷是用业务上的唯一组合键,自增列是数据库自动生成的,两者语义不匹配,自然容易出问题。

可行的解决方案

方案一:把自增ID从复合主键中剥离(最推荐)

这是最符合设计规范的做法,把自增ID设为独立主键,原来的复合主键部分改成嵌入的业务键,还能给业务键加唯一约束保证业务唯一性。修改后的代码如下:

修改后的实体类

@Entity 
@Table(name="table_entity", schema="Db2Schema") 
@DynamicUpdate 
@DynamicInsert
public class TableEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ID", insertable = false, updatable = false)
    private Integer id;
    
    // 原来的复合主键去掉id后,做成业务键嵌入
    @Embedded
    private TableEntityBusinessKey businessKey;
    
    // 其余实体字段省略
}

新增业务键类

@Embeddable
public class TableEntityBusinessKey implements Serializable{
    @Column(name="key1", length=254)
    private String key1;
    // 其余原来的主键字段省略
    
    // 记得重写equals和hashCode方法,这是嵌入类的要求
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TableEntityBusinessKey that = (TableEntityBusinessKey) o;
        return Objects.equals(key1, that.key1);
        // 其他字段的equals判断
    }

    @Override
    public int hashCode() {
        return Objects.hash(key1);
        // 其他字段的hashCode计算
    }
}

这样调整后,Hibernate生成INSERT语句时会自动忽略ID列,DB2会自动生成自增值,完美解决报错,同时结构也更清晰。

方案二:强制Hibernate不生成ID列的插入语句(不推荐)

如果实在不想改结构,可以试试以下调整,但兼容性不好,容易踩坑:

  1. 给id字段加上@Transient,但这样Hibernate不会映射这个字段,插入后你得手动查询获取生成的ID,非常麻烦。
  2. 用Hibernate的@Generated注解替代@GeneratedValue,明确指定插入后生成:
@Embeddable
public class TableEntityKey implements Serializable{
    @Generated(GenerationTime.INSERT)
    @Column(name="ID", insertable = false, updatable = false)
    private Integer id;
    @Column(name="key1", length=254)
    private String key1;
    // 其余字段省略
}

不过这种方式在不同版本的Hibernate里可能有不同的表现,而且复合主键的处理逻辑本来就复杂,不建议用。

方案三:改用DB2序列生成ID(需修改数据库)

如果允许修改数据库表结构,可以把IDENTITY列改成用序列生成,然后在Hibernate里配置序列生成器:

@Embeddable
public class TableEntityKey implements Serializable{
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "table_id_seq")
    @SequenceGenerator(name = "table_id_seq", sequenceName = "DB2SCHEMA.TABLE_ID_SEQ", allocationSize = 1)
    @Column(name="ID")
    private Integer id;
    // 其余字段省略
}

这种方式下,Hibernate会先从序列获取ID值,再插入到数据库,DB2不会报错,但同样,复合主键里放序列生成的ID还是不太符合设计逻辑,仅作为备选方案。

内容的提问来源于stack exchange,提问作者Muhammad Salman Farooq

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:54:27