Oracle 12c+ Identity列默认不支持getGeneratedKeys()返回值,必须显式指定列名数组(如new String[]{"ID"})且大小写严格匹配,否则返回空ResultSet;推荐优先使用RETURNING子句。
Oracle 12c+ Identity列不支持getGeneratedKeys()返回值
直接说结论:oracle 12c 引入的 identity 列(如 id number generated always as identity)在 jdbc 中调用 getgeneratedkeys() 时,**默认不返回任何结果集**,哪怕 sql 是 insert into t (name) values (?) 这种标准写法。这不是驱动 bug,而是 oracle jdbc 驱动对 identity 列的默认行为——它不自动注册为“可生成键”,除非显式告诉驱动你要取哪一列。
必须用Statement.RETURN_GENERATED_KEYS + 列名数组指定
Oracle 驱动要求你明确声明“我要取哪些列的生成值”,不能只靠 Statement.RETURN_GENERATED_KEYS 常量放行就完事。否则 getGeneratedKeys() 返回空 ResultSet,连 next() 都进不去。
实操建议:
- 不要用
conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)—— 这在 Oracle 下对 Identity 列无效 - 改用带列名数组的重载:
conn.prepareStatement(sql, new String[]{"ID"}),其中"ID"必须和表定义中 Identity 列的**实际大小写、拼写完全一致**(Oracle 默认大写,建表时若加双引号则按引号内大小写) - 如果 Identity 列是复合名(比如带 schema),
new String[]{"MY_SCHEMA.MY_TABLE.ID"}不起作用 —— 只接受纯列名 - 确保 JDBC 驱动版本 ≥ 12.2.0.1(旧版如 12.1.x 对 Identity 支持不完整)
getGeneratedKeys() 返回空或报错ORA-32795: cannot insert into a generated always identity column
这个错误不是发生在插入时,而是出现在你试图用 getGeneratedKeys() 且参数没配对的情况下。典型诱因是:你在 INSERT 语句里手动写了 Identity 列(比如 INSERT INTO t (id, name) VALUES (?, ?)),同时又传了列名数组——驱动会混淆“你是想自己设值,还是让我帮你生成”。
正确做法:
- INSERT 语句里**绝对不要出现 Identity 列名**,即只能写
INSERT INTO t (name) VALUES (?) - 列名数组只用于告知驱动“请把刚生成的 ID 值从哪一列取出来”,不参与 SQL 解析
- 如果表有多个 Identity 列(极少但合法),数组要列出全部,顺序无关,但名字必须全对
- 返回的
ResultSet里,列名是大写的ID(即使建表时小写),类型是java.math.BigDecimal,需用getLong(1)或bigDecimal.longValue()转换
替代方案:用RETURNING 子句更可控
当 getGeneratedKeys() 配置复杂或需要同时取多个字段(比如生成 ID + 时间戳),直接用 Oracle 原生 RETURNING 更稳。它不依赖驱动对 Identity 的特殊处理,兼容性更好,也支持批量。
示例写法:
String sql = "INSERT INTO t (name) VALUES (?) RETURNING id INTO ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, "test"); ps.registerOutParameter(2, Types.NUMERIC); ps.execute(); long id = ps.getLong(2);
注意点:
-
RETURNING的列名大小写敏感,必须和表定义一致 - 不能和
getGeneratedKeys()混用,二者选其一 - 批量插入时,
RETURNING需配合executeBatch()和getMoreResults()多次读取,逻辑比单条复杂
getGeneratedKeys() 安静失败。

















