关闭 autoCommit 是批处理生效的前提,因为 Oracle 的 executeBatch() 依赖事务边界控制网络行为;默认 autoCommit 开启时,每次 executeBatch() 都触发完整事务提交,导致高耗时与频繁网络往返。
为什么关闭 autoCommit 是批处理生效的前提
oracle 的 executebatch() 不是“只要调用了就批量”,它依赖事务边界控制网络行为。默认开启 autocommit 时,每条 addbatch() 后的 executebatch() 实际仍触发一次完整事务提交——意味着一次网络往返 + 一次日志刷盘。2000 条数据耗时从 11.7 秒降到 3.5 秒,差在三倍以上,根源就是这一步没关。
必须在创建 PreparedStatement 之前调用:conn.setAutoCommit(false)
不能只靠 addBatch() 就以为“自动进批”,更不能把 setAutoCommit(false) 放在 executeBatch() 之后——那时早已退化为单条执行。
常见错误现象:
• executeBatch() 耗时随行数线性增长
• 网络监控显示请求次数 ≈ 插入行数
• 连接池中连接长期处于“ACTIVE”状态,后续查询被锁住
batchSize 设多少才真正减少往返
Oracle 对批大小极其敏感,和 MySQL/PostgreSQL 完全不同:官方建议 5–30 行为最优区间,实测超过 100 行反而慢 12%。原因在于驱动层会拆包重路由,且易触发 PGA 内存溢出(如 ORA-04030)。
推荐策略:
• 通用起步值设 20
• 字段超 50 个(如含 CLOB/BLOB)→ 降到 10
• 纯 ID + 状态字段 → 可试 30
• 单批不要超 5000 行(ojdbc8/11 驱动下),否则风险陡增
立即学习“Java免费学习笔记(深入)”;
别迷信“越大越好”。很多代码把 batchSize 设成 10000,结果 Oracle 在服务端拆成多个子批执行,往返没减,内存先爆。
ojdbc 驱动版本错,batch 就是摆设
用 ojdbc6.jar 连 Oracle 19c/21c/23c,executeBatch() 本质是客户端模拟——每条 addBatch() 仍发一次独立网络包,根本没减少往返。确认方式:System.out.println(oracle.jdbc.driver.OracleDriver.getMajorVersion())
输出必须是 8 或 11,不是 6。
常见陷阱:
• Maven 里引入标着 “universal” 的旧包,实际是重命名的 ojdbc6
• 项目里混用多个 ojdbc 版本,类加载器优先加载了低版本
• 用 Spring Boot 默认 starter,没显式指定 ojdbc8 或 ojdbc11
正确做法:
• Oracle 12c+ 必须用 ojdbc8.jar(JDK 8+)或 ojdbc11.jar(JDK 11+)
• 在 pom.xml 中排除传递依赖,强制指定版本
• 启动时打印驱动版本,写进日志做上线检查项
别漏掉 setRowPrefetch 和 defineColumnType
这两项不改 batch 也能跑,但会多几次网络往返——尤其当你插入后立刻查刚插的数据做校验时。
setRowPrefetch(50)(注意是 OracleStatement 方法,非标准 setFetchSize)让驱动一次从服务器拉 50 行,对 SELECT COUNT(*) 类操作提速明显;defineColumnType(1, Types.VARCHAR) 告诉驱动第 1 列类型,避免每次反查元数据。
关键约束:
• 必须在 executeBatch() 前设置,之后设置无效
• setRowPrefetch 对纯 insert 场景作用有限,但 insert + select 场景收益显著
• defineColumnType 只对明确知道列类型的场景有效,动态列慎用
最容易被忽略的是:这些优化全得在同一个事务生命周期内完成。一旦 commit() 或连接关闭,所有预设失效——不是配置一次就永久生效。


















