JPA生成ID更新序列时报数据库对象不存在错误如何解决
问题描述
使用JPA Repository向DB2数据库保存实体对象时,标注@Entity的实体类中主键字段定义如下:
@Id @GeneratedValue(generator = "sequence-generator") @Column(name = "ADDRESS_ID") private Long addressId;
保存实体时,JPA为序列生成器生成的执行SQL如下:
[EL Fine]: sql: 2022-06-14 09:03:35.836--ClientSession(xxx)--Connection(xxx)--UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ? bind => [50, sequence-generator]
执行上述语句直接抛出异常,即使在DBeaver IDE中手动执行该SQL也会返回-204错误(DB2中该错误码代表目标数据库对象不存在),提示当前Schema下找不到SEQUENCE对象,异常详情如下:
Internal Exception: com.ibm.db2.jcc.am.SqlSyntaxErrorException: DB2 SQL Error: SQLCODE=-204, SQLSTATE=42704, SQLERRMC=SMBGLOBAL.SEQUENCE, DRIVER=4.25.13 Error Code: -204 Call: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ? bind => [50, sequence-generator]
问题原因
从日志前缀[EL Fine]可以判断当前使用的JPA实现是EclipseLink。当@GeneratedValue注解只指定了generator名称、未明确声明主键生成策略时,EclipseLink默认采用Table主键生成策略:该策略不依赖数据库原生序列对象,而是通过一张固定命名为SEQUENCE的表存储所有生成器的当前序列值,每次获取主键前先更新这张表的计数。当前连接的DB2 Schema下不存在这张默认命名的序列表,因此抛出-204对象不存在错误。
解决方案
根据实际业务场景二选一即可:
方案1:使用DB2原生序列(推荐,性能更高)
该方案直接利用数据库原生序列能力,不需要额外维护序列表,并发性能更好。
- 先在DB2对应Schema下创建业务主键使用的原生序列,示例建序列SQL:
CREATE SEQUENCE SEQ_ADDRESS_ID AS BIGINT START WITH 1 INCREMENT BY 50 NO CYCLE CACHE 50;
- 修改实体类主键注解配置,明确指定主键生成策略为序列,同时关联已创建的DB2序列:
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence-generator") @SequenceGenerator( name = "sequence-generator", sequenceName = "SEQ_ADDRESS_ID", allocationSize = 50 ) @Column(name = "ADDRESS_ID") private Long addressId;
配置注意事项:
allocationSize参数值必须和数据库序列定义的INCREMENT值保持一致,否则会出现主键冲突、跳号异常问题。- 如果应用连接的DB2账号没有创建序列的权限,联系DBA分配权限或提前建好对应序列即可。
方案2:沿用默认Table生成策略
如果不想使用数据库原生序列,继续用默认的表存储序列值,只需要在对应Schema下创建JPA期望的SEQUENCE表即可,建表及初始化SQL如下:
CREATE TABLE SEQUENCE ( SEQ_NAME VARCHAR(255) NOT NULL PRIMARY KEY, SEQ_COUNT BIGINT NOT NULL ); -- 初始化当前使用的序列生成器记录 INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) VALUES ('sequence-generator', 0);
建表完成后JPA执行的UPDATE语句就能正常命中目标表,不会再抛出对象不存在的错误。
内容的提问来源于stack exchange,提问作者coosta
相关产品推荐
相关产品推荐

