Java向Oracle批量插入慢的主因是ojdbc驱动版本错误、autoCommit未关闭或executeBatch后未检查返回值;需用ojdbc8/11、关闭自动提交、设fetchSize为0、校验返回数组并手动commit。
java 向 oracle 批量插入慢,八成不是代码写得差,而是 ojdbc 驱动版本错、autocommit 没关、或 executebatch 调用后没检查返回值——这些点一漏,批量就退化成“伪批量”。
ojdbc 驱动版本必须匹配 Oracle 数据库大版本
Oracle 12c 及以后(含 19c/21c/23c)必须用 ojdbc8.jar(JDK 8+)或 ojdbc11.jar(JDK 11+)。ojdbc6.jar 会强制走客户端模拟批处理,每条 addBatch 实际仍发一次网络包,executeBatch 形同虚设。
确认方式:运行 System.out.println(oracle.jdbc.driver.OracleDriver.getMajorVersion()),输出应为 8 或 11。别信 Maven 仓库里标 “universal” 的旧包——它大概率是重命名的 ojdbc6。
常见错误现象:executeBatch 耗时随数据量线性增长,监控显示网络请求次数 ≈ 插入行数。
addBatch 前必须关闭 autoCommit 并设 fetchSize
connection.setAutoCommit(false) 是硬性前提,否则每次 executeBatch() 都触发完整事务提交,批量意义归零。
立即学习“Java免费学习笔记(深入)”;
单批行数建议控制在 1000~5000 之间(视单行数据大小调整),超 10000 行易触发 Oracle PGA 内存溢出或 ORA-04030 错误。
显式调用 statement.setFetchSize(0)(仅对 Oracle 有效),避免 JDBC 驱动额外拉取元数据,减少解析开销。
- 别用
ps.setFetchSize(100)这类非零值——Oracle 不需要结果集拉取,设了反而多干活 - batchSize 别死守固定值:字段多、值长(如大文本)时,优先往
1000靠;字段少、值短可试探到5000
executeBatch 后必须手动 commit 且检查返回数组
executeBatch() 返回 int[],不是布尔值,也不是 void。其中每个元素对应一条语句执行影响的行数;若某条失败,对应位置为 Statement.EXECUTE_FAILED(值为 -3),**不会抛异常**。
很多人只调 executeBatch() 就 commit(),结果部分失败却无感知,或事务卡在连接里未提交,造成锁表或连接池耗尽。
正确做法:
int[] results = ps.executeBatch();
for (int i = 0; i < results.length; i++) {
if (results[i] == Statement.EXECUTE_FAILED) {
// 记录第 i 条失败,可跳过、重试或中断
}
}
connection.commit(); // 必须紧跟,不能依赖连接池 close 自动提交
注意:connection.commit() 失败也要捕获,否则事务状态不可知。
字段多时优先用 INSERT ALL 替代重复 VALUES
单条记录字段超过 10 个时,拼 INSERT INTO t(a,b,c,...) VALUES(?,?,?,...) 1000 次,Oracle 硬解析 CPU 开销会反超网络传输节省。
换成 INSERT ALL 单语句塞多行,能显著降低解析压力:
INSERT ALL INTO t(a,b) VALUES(?,?) INTO t(a,b) VALUES(?,?) INTO t(a,b) VALUES(?,?) SELECT * FROM DUAL
限制:Oracle 单条 INSERT ALL 最多支持约 1000 个 INTO 子句,超了必须拆批;且该语法不支持 RETURNING,有返回值需求时不能用。
容易踩的坑:MyBatis 动态 SQL 拼 INSERT ALL 时,<foreach></foreach> 嵌套层级深易生成非法 SQL;建议手写模板 + 分片参数传入,别全靠框架生成。
最常被忽略的是驱动版本和返回值检查——前者让批量失效,后者让失败静默。这两点不解决,调再大的 batchSize 也没用。


















