Java PreparedStatement.getGeneratedKeys()在DB2不生效但MySQL正常问题
问题背景
我在MySQL与DB2中均创建了名为STUDENT的简单表,字段如下:ID(主键、自增)、FIRST_NAME、LAST_NAME、AGE。两库的表结构做了对齐,语法层面无差异。
编写Java程序执行插入操作时,MySQL环境可通过PreparedStatement.getGeneratedKeys()正常返回生成的主键,DB2环境却无任何返回结果。
DB2与MySQL执行连接提交后,均能成功插入数据,多次插入时ID可正常自增,但仅MySQL环境可进入while(rs.next())遍历到生成的键值,DB2的结果集为空,会直接跳过遍历逻辑。
问题复现代码
String sql = "INSERT INTO STUDENT (FIRST_NAME, LAST_NAME, AGE) VALUES ('Jacob', 'Eldy', 19)" final Connection connection = getConnection(dataSource.get()); int[] insertedRows = null; ResultSet rs = null; PreparedStatement ps = null; try { ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.addBatch(); insertedRows = ps.executeBatch(); rs = ps.getGeneratedKeys(); while(rs.next()) { LOGGER.info(rs.getString(1)); } connection.commit(); } catch (Exception e) { try { connection.rollback(); } catch (SQLException e) { e.printStackTrace(); } } finally { close(ps, connection); }
附:建表DDL
MySQL DDL
CREATE TABLE 'STUDENT' ... `ID` int NOT NULL AUTO_INCREMENT PRIMARY KEY('ID') AUTO_INCREMENT=19073
DB2 DDL
CREATE TABLE STUDENT ( ID INTEGER DEFAULT IDENTITY GENERATED ALWAYS NOT NULL PRIMARY KEY (ID) )
问题原因与解决方案
这个问题不是基础代码写法错误,核心是DB2 JDBC驱动的兼容性限制,加上自增列的非标准写法导致驱动无法识别生成键:
- 版本低于10.5的DB2 JDBC驱动(老版本db2jcc.jar)本身不支持
executeBatch()批量执行场景下返回自增主键,只有单条调用executeUpdate()时,Statement.RETURN_GENERATED_KEYS参数才会生效。 - 你写的DB2自增列语法是非标准写法,标准IDENTITY列定义顺序为
GENERATED ALWAYS AS IDENTITY,你写的DEFAULT IDENTITY GENERATED ALWAYS属于数据库容错兼容的写法,部分驱动版本无法识别该列为自动生成的标识列,自然不会返回对应键值。
最优处理方案按优先级排序:
- 先修正DB2表的自增列定义为标准语法,调整后DDL参考:
CREATE TABLE STUDENT ( ID INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, FIRST_NAME VARCHAR(50), LAST_NAME VARCHAR(50), AGE INTEGER, PRIMARY KEY (ID) )
- 升级DB2 JDBC驱动到10.5及以上版本(使用db2jcc4.jar),该版本开始正式支持批量插入场景下的生成键返回,升级后原有代码无需修改即可正常拿到自增ID。
- 如果受生产环境限制无法升级驱动,分两种场景处理:
- 单条插入场景:直接调用
ps.executeUpdate()替代addBatch() + executeBatch()的批量写法,即可正常通过getGeneratedKeys()获取自增主键 - 必须使用批量插入的场景:每批次执行完成后,在同一个连接内执行
SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1,即可拿到当前会话最后一次生成的自增主键,按插入顺序即可和批次内的数据做ID映射。
- 单条插入场景:直接调用
注意:
IDENTITY_VAL_LOCAL()是会话级函数,不会被其他数据库连接的插入操作影响,只要保证插入和查询在同一个Connection内、中间没有执行其他同表的插入操作,拿到的ID值就是准确的。
内容的提问来源于stack exchange,提问作者stackerstack
相关产品推荐
相关产品推荐

