Oracle不支持JPA的IDENTITY策略,因其无LAST_INSERT_ID()函数且Hibernate不识别12c+隐式序列,必须显式配置@SequenceGenerator并设allocationSize=1、sequenceName精确匹配,否则报ORA-02289或ID为null。

必须用 SEQUENCE,不能靠 identity,也不能只写 native 就完事——否则运行时会报 ORA-02289: sequence does not exist 或插入后 ID 为 null。
为什么 Oracle 不支持 identity 策略?
Oracle 直到 12c 才引入 IDENTITY 列(如 ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY),但 Hibernate 5.2 之前版本默认不识别它;即使升级到新版,@GeneratedValue(strategy = GenerationType.IDENTITY) 在 Oracle 上仍会 fallback 到无效行为或抛异常。Hibernate 官方文档明确将 identity 归类为“仅适用于 MySQL/SQL Server”,对 Oracle 无效。
-
identity生成器会尝试执行SELECT LAST_INSERT_ID()类语句,Oracle 没这函数,直接失败 - 即便建了
IDENTITY列,Hibernate 也**不会自动读取刚生成的值**,导致关联实体保存失败(比如@OneToManycascade save 报org.hibernate.TransientPropertyValueException) - 所以别碰
identity——它在 Oracle 场景下就是个陷阱
@SequenceGenerator 必须配全三个关键参数
只写 @GeneratedValue(strategy = GenerationType.SEQUENCE) 不够,Oracle 要求显式绑定序列名、分配步长和生成器名。漏掉 allocationSize 是最常见错误,会导致主键跳号(比如每次 +20)。
-
sequenceName:必须与数据库中CREATE SEQUENCE的名称**完全一致**(区分大小写!如果建的是S_EMP,就不能写成s_emp) -
name:是 Java 侧引用该生成器的逻辑名,需和@GeneratedValue(generator = "...")中的字符串匹配 -
allocationSize = 1:强制每次只取一个值;不设或设为默认值(通常是 50)会导致 Hibernate 预取一批 ID 缓存在内存里,服务重启后可能跳过中间号段
正确示例:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq_gen")
@SequenceGenerator(
name = "emp_seq_gen",
sequenceName = "EMP_ID_SEQ",
allocationSize = 1
)
@Column(name = "ID")
private Long id;
native 看似省事,实则埋雷
很多人图方便用 GenerationType.NATIVE,以为 Hibernate 会自动选 sequence。但它在 Oracle 下有隐式依赖:必须存在名为 HIBERNATE_SEQUENCE 的全局序列,且你无法控制其 CACHE 值。
- 如果没提前建
HIBERNATE_SEQUENCE,启动就报ORA-02289 - 即使建了,它的
CACHE默认是 20,意味着每次取 20 个 ID 缓存 —— 数据库宕机或应用重启会导致 ID 断层 - 多个表共用一个序列,违反“每表一序”最佳实践,后期分库分表或迁移时难解耦
- XML 映射里写
<generator class="native"></generator>更危险:Hibernate 可能 fallback 到increment(查 MAX(ID)+1),集群下直接主键冲突
建 Sequence 的 SQL 必须带 NOCACHE 或低缓存值
Oracle 序列默认 CACHE 20,但 Hibernate 的 allocationSize 和 DB 的 CACHE 两套机制叠加,会让 ID 生成不可预测。尤其在测试环境反复启停、或使用 Docker 容器化部署时,跳号问题高频出现。
- 开发/测试库建议直接用
NOCACHE:避免因实例销毁丢失缓存 ID - 生产库若要兼顾性能,可设
CACHE 1或CACHE 5,但必须和 Java 侧allocationSize严格一致 - 别信 “
CACHE提升性能” 的老说法 —— 对单次 insert 来说,NEXTVAL开销微乎其微;ID 连续性比毫秒级性能更重要
安全建序语句:
CREATE SEQUENCE EMP_ID_SEQ START WITH 1 INCREMENT BY 1 NOMAXVALUE NOCYCLE NOCACHE;
真正容易被忽略的点是:Hibernate 获取 NEXTVAL 和执行 INSERT 是两条独立 SQL,中间没有事务包裹。如果业务逻辑里手动调用了 session.flush() 或触发了二级缓存写入,可能在 ID 取出后、INSERT 前发生异常,导致该 ID “泄露” —— 后续再没人用它,但序列已前进。这种缝隙无法靠配置消除,只能靠监控序列当前值与表最大 ID 的差值来及时发现。


















