oracle中currval在mybatis 中报ora-08002或返回null,根本原因是currval必须在同一会话中先调用nextval才能访问;而order="after"无法保证与insert共享物理会话,尤其在连接池、事务或驱动版本不兼容时极易失效;推荐统一使用order="before"配合nextval主动赋值,安全可靠。
为什么 currval 在 mybatis <selectkey></selectkey> 中总是返回 null 或报错?
根本原因是:oracle 的 currval 必须在**同一会话中先调用过 nextval** 才能访问,否则直接查 currval 会报 ora-08002: sequence xxx.currval is not yet defined in this session。mybatis 的 <selectkey order="after"></selectkey> 并不保证与 insert 语句共享同一个 jdbc statement 或物理会话上下文——尤其在开启连接池、事务管理或使用某些驱动版本时,insert 和 selectkey 很可能被拆成两个独立的执行单元。
常见错误现象包括:
-
java.sql.SQLException: ORA-08002(最典型) - 返回值为
null或0(resultType是基本类型时自动补零) - 插入成功但对象主键字段未赋值,后续
entity.getId()仍为null或默认值
order="BEFORE" + NEXTVAL 是更稳妥的选择
把序列取值逻辑提前到 insert 之前,由 MyBatis 主动控制赋值流程,彻底规避 CURRVAL 的会话依赖问题。这是 Oracle 场景下最主流、最可靠的做法。
实操要点:
- 在
<selectkey></selectkey>中写SELECT seq_name.NEXTVAL FROM DUAL,order必须设为"BEFORE" -
keyProperty值要和 Java 对象的主键字段名完全一致(区分大小写,注意驼峰映射) - insert SQL 中必须显式引用该字段,如
VALUES (#{id}, #{name}),不能漏掉或写成VALUES (seq_name.NEXTVAL, ...) - 不要同时配置
useGeneratedKeys="true"—— Oracle 不支持,会触发getInt not implemented for class oracle.jdbc.driver.T4CRowidAccessor
示例片段:
<insert id="insertUser" parametertype="User"><selectkey keyproperty="id" resulttype="java.lang.Long" order="BEFORE">
SELECT user_seq.NEXTVAL FROM DUAL
</selectkey>
INSERT INTO users (id, name) VALUES (#{id}, #{name})
</insert>
如果坚持要用 CURRVAL,必须满足三个硬性条件
不是不能用,而是约束极强。只有当以下全部成立时,order="AFTER" + CURRVAL 才可能成功:
- 数据库连接未被连接池复用(即每次都是全新物理连接),或连接池配置了
connection.isolation=TRANSACTION_SERIALIZABLE等强一致性策略 - JDBC 驱动版本 ≥ 12.2,且
oracle.jdbc.autoCommitSpecCompliant=false(旧版驱动默认开启兼容模式,会隐式提交导致会话中断) - 整个 insert + selectKey 必须包裹在同一
@Transactional方法内,且传播行为为REQUIRED(确保复用同一 DataSource 连接)
即便如此,高并发下仍可能因序列缓存(CACHE 20)导致 CURRVAL 滞后于实际插入值 —— 比如你刚插了一条,另一线程抢走了下一个 NEXTVAL,你的 CURRVAL 就“跳号”了。
Oracle 12c+ 可考虑 IDENTITY 列,但仍有陷阱
如果你用的是 Oracle 12c 或更高版本,可以绕过序列,直接定义表字段为 GENERATED ALWAYS AS IDENTITY。此时 MyBatis 能用 useGeneratedKeys="true" 吗?答案是:部分支持,但需注意:
- 必须用 Oracle JDBC Driver 12.2+,且
oracle.jdbc.returnInsertWithGeneratedKeys=true显式启用(驱动默认关闭) - MyBatis 的
<insert></insert>中仍不能写VALUES子句里含主键字段;必须省略该列,让 DB 自动生成 - 即使配置正确,某些连接池(如 HikariCP)需额外设置
allowMultiQueries=true,否则仍走不到生成键逻辑
简单说:IDENTITY 更接近 MySQL 风格,但 Oracle 的实现细节和驱动适配仍是黑盒,出问题时排查成本反而比手管序列更高。
真正容易被忽略的一点:无论用 NEXTVAL 还是 IDENTITY,只要涉及批量插入(<foreach></foreach>),每个元素都得单独走一次 NEXTVAL 查询 —— 序列本身不支持“一次取 N 个”,别指望靠缓存提升批量性能。这时候不如在业务层预生成 ID 列表再传入。











