Java中PreparedStatement批量插入并获取主键的核心是:需避免executeBatch()后直接调用getGeneratedKeys(),因JDBC规范未保证其返回全部主键;推荐逐条executeUpdate()后立即getGeneratedKeys(),或使用MySQL rewriteBatchedStatements=true参数、PostgreSQL RETURNING子句等数据库特有方案。

Java 中 PreparedStatement 批量插入并获取主键,核心是使用 prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) 配合 executeBatch() 和 getGeneratedKeys(),但要注意:标准 JDBC 规范下,executeBatch() 后不能直接调用 getGeneratedKeys() 获取全部主键——多数驱动(如 MySQL、PostgreSQL)只支持在单条 executeUpdate() 后获取对应生成键,批量执行时需特殊处理。
方案一:逐条执行 + 批量提交(推荐,兼容性好)
不依赖 executeBatch 的“原子批量”,而是循环调用 addBatch() + executeUpdate(),并在每次执行后立即获取主键,最后统一 commit。这样既保证主键可取,又避免事务过长:
- 创建 PreparedStatement 时传入
Statement.RETURN_GENERATED_KEYS - 循环中设置参数 → 调用
executeUpdate()(不是 addBatch)→ 立即调用getGeneratedKeys()取当前记录主键 - 所有操作在同一个 Connection 上,手动控制
setAutoCommit(false)+commit()
示例关键代码:
connection.setAutoCommit(false);
String sql = "INSERT INTO user(name, email) VALUES (?, ?)";
try (PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
for (User u : users) {
ps.setString(1, u.getName());
ps.setString(2, u.getEmail());
int affected = ps.executeUpdate(); // 不用 addBatch
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) {
Long id = rs.getLong(1);
u.setId(id); // 写回对象
}
}
}
connection.commit();
}
方案二:MySQL 特定支持(仅限 MySQL 驱动)
MySQL Connector/J 5.1.19+ 在启用 rewriteBatchedStatements=true 参数时,会将批量插入重写为一条 INSERT ... VALUES (...),(...),(...) 语句,并支持 getGeneratedKeys() 返回全部自增 ID(按插入顺序):
立即学习“Java免费学习笔记(深入)”;
- JDBC URL 加参数:
?rewriteBatchedStatements=true - PreparedStatement 创建仍需
Statement.RETURN_GENERATED_KEYS - 调用
addBatch()多次 →executeBatch()→ 再调用一次getGeneratedKeys()
注意:该行为是 MySQL 驱动特有,其他数据库(如 PostgreSQL、Oracle)不支持;且要求主键为自增整型,非 UUID 或序列等场景可能失败。
方案三:用 RETURNING 子句(PostgreSQL / Oracle)
PostgreSQL 支持 INSERT ... RETURNING id,Oracle 12c+ 支持 RETURNING INTO。此时不用 Statement.RETURN_GENERATED_KEYS,而是把主键字段直接写进 SQL:
- PostgreSQL 示例:
INSERT INTO user(name, email) VALUES (?, ?) RETURNING id - 执行
executeQuery()(不是 executeBatch),结果集直接返回主键 - 适合单条或小批量;若真要大批量,可分批执行并收集 ResultSet
优点是语义清晰、标准 SQL 扩展;缺点是数据库强绑定,无法跨库复用。
注意事项与避坑点
以下问题高频发生,务必检查:
- 不要在 executeBatch() 后立刻 getGeneratedKeys():HikariCP、Druid 等连接池默认会清空 keys,且 JDBC 规范未定义此行为,多数驱动返回空结果集
-
主键字段名必须匹配:getGeneratedKeys() 返回的 ResultSet 列名因驱动而异(如 MySQL 是
GENERATED_KEY,PostgreSQL 是id),建议用rs.getLong(1)而非rs.getLong("id") - 批量大小要合理:单次 insert 超过 1000 行易触发 MySQL max_allowed_packet 或内存溢出,建议 100–500 行/批
- 事务一致性:无论哪种方式,批量操作应包裹在事务中,避免部分成功部分失败导致数据不一致


















