Oracle中Liquibase与JPA使用IDENTITY自增主键冲突问题咨询
1 为什么JPA插入时自增ID没有从2开始计数
Oracle的GENERATED BY DEFAULT AS IDENTITY字段本质是内部绑定了一个隐式的自增序列,核心逻辑是:
- 插入数据时如果指定了ID字段的值,就直接使用你指定的值写入
- 插入数据时如果没有指定ID字段的值,就从绑定的隐式序列中取值使用
重点:手动指定ID插入的操作不会同步更新隐式序列的当前值
你建表时没有指定序列起始值,隐式序列默认从1开始计数,你手动插入了ID=1的记录后,序列的当前值仍然是1,JPA插入时不会指定ID,取到的序列值就是1,就和已有的记录冲突,抛出ORA-00001异常。
2 如何同时支持JPA自动生成ID、Liquibase手动指定ID插入
两种场景本身是兼容的,你只需要保证IDENTITY绑定的序列当前值永远大于表中已有的最大ID即可,有三种常用实现方案:
2.1 建表时直接指定序列起始值
如果你的初始化手动插入数据量固定,比如你确定只会插1条ID=1的记录,建表时直接指定序列从2开始即可:
CREATE TABLE identity_demo ( id NUMBER(19,0) GENERATED BY DEFAULT AS IDENTITY (START WITH 2) PRIMARY KEY, description VARCHAR(100) not null );
如果后续新增了手动插入的变更集,只要同步修改START WITH的取值大于手动插入的最大ID即可。
2.2 动态同步序列值到当前表最大ID
如果你不确定后续会插多少条手动数据,可以在所有手动插入数据的Liquibase变更集最后,加一个动态调整序列值的变更集:
--changeset xxx:211202160323-3 DECLARE current_max_id NUMBER; BEGIN -- 读取当前表最大ID,没有数据的话默认取0 SELECT NVL(MAX(id), 0) INTO current_max_id FROM identity_demo; -- 把IDENTITY的序列起始值设为最大ID+1 EXECUTE IMMEDIATE 'ALTER TABLE identity_demo MODIFY id GENERATED BY DEFAULT AS IDENTITY (START WITH ' || (current_max_id + 1) || ')'; END; /
这个脚本可以重复执行,每次都会自动把序列调整到正确的起始位置,不需要手动修改取值。
2.3 改用显式序列作为JPA主键生成策略
如果你不想依赖Oracle的隐式IDENTITY序列,也可以自己创建独立的显式序列,交给JPA使用:
- 新增Liquibase变更集创建序列:
--changeset xxx:211202160323-4 CREATE SEQUENCE identity_demo_seq START WITH 2 INCREMENT BY 1;
- 修改实体类的主键生成策略:
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "identity_demo_gen") @SequenceGenerator(name = "identity_demo_gen", sequenceName = "identity_demo_seq", allocationSize = 1) @Column(name = "id") private Long id;
这种方案序列完全独立可控,调整起来更灵活,也兼容手动插入ID的场景。
内容的提问来源于stack exchange,提问作者Sedef Bostancı
相关产品推荐
相关产品推荐

