java preparedstatement 执行批量数据缓存的核心做法是调用 addbatch() 方法将多条 sql 语句暂存于客户端缓冲区,随后通过 executebatch() 一次性发送给数据库驱动。这样做能显著减少网络往返次数并提升写入性能。需要注意的是,必须配合 setautocommit(false) 使用,否则每条语句都会立即提交,失去批量处理的意义。

如何配置连接以支持批量操作
要实现有效的批量处理,首要步骤是关闭 JDBC 连接的自动提交模式。在创建 Connection 后,调用 setAutoCommit(false) 可以确保后续的操作不会立即生效,而是等待显式的 commit() 或 rollback()。这是批量操作的基础前提,因为如果保持自动提交,addBatch() 中的语句会在每次添加时就被单独执行和提交,导致性能退化到逐条执行的水平。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class BatchExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 关闭自动提交
conn.setAutoCommit(false);
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (int i = 0; i < 1000; i++) {
pstmt.setString(1, "User_" + i);
pstmt.setString(2, "user_" + i + "@example.com");
// 添加到批次
pstmt.addBatch();
}
// 执行批次
int[] results = pstmt.executeBatch();
// 提交事务
conn.commit();
System.out.println("Inserted rows: " + results.length);
} catch (Exception e) {
conn.rollback();
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
上述代码展示了标准的批量插入流程。首先获取连接并禁用自动提交,然后准备 SQL 语句。在循环中,设置参数并调用 addBatch() 将当前参数集加入缓冲区。循环结束后,调用 executeBatch() 触发实际执行,最后提交事务。如果在执行过程中发生异常,捕获异常并回滚事务以保证数据一致性。
立即学习“Java免费学习笔记(深入)”;
如何处理大批量数据的内存溢出问题
当批量数据量极大时,将所有数据缓存在客户端内存中可能导致 OutOfMemoryError。解决此问题的方法是分片处理,即设定一个固定的批次大小(如 500 或 1000),每积累到该数量就执行一次 executeBatch() 并清空缓冲区,然后继续下一批。这种策略既利用了批量操作的性能优势,又控制了单次内存占用。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class ChunkedBatchExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "password";
int batchSize = 500;
try (Connection conn = DriverManager.getConnection(url, user, password)) {
conn.setAutoCommit(false);
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (int i = 0; i < 10000; i++) {
pstmt.setString(1, "User_" + i);
pstmt.setString(2, "user_" + i + "@example.com");
pstmt.addBatch();
// 达到批次大小时执行
if ((i + 1) % batchSize == 0) {
pstmt.executeBatch();
pstmt.clearBatch();
}
}
// 执行剩余的数据
int[] remainingResults = pstmt.executeBatch();
conn.commit();
System.out.println("Total inserted: " + (10000));
} catch (Exception e) {
conn.rollback();
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在分片处理的代码中,引入了 batchSize 变量来控制每批的数量。在循环内部,检查当前索引是否达到批次大小的倍数,如果是,则调用 executeBatch() 发送数据,并紧接着调用 clearBatch() 清空已发送的缓冲区,释放内存。循环结束后,再次执行 executeBatch() 以处理不足一批的剩余数据。这种方式确保了在处理百万级数据时,内存使用保持在一个可控的低水平。
executeBatch() 返回的数组代表什么
executeBatch() 方法返回一个 int[] 数组,其中每个元素对应批次中一条 SQL 语句的执行结果。对于普通的 DML 语句(如 INSERT、UPDATE、DELETE),返回值通常是受影响的行数。如果某条语句成功执行但影响行数为零(例如更新了一个值未变的记录),返回值可能是 0。对于某些特殊操作或存储过程,可能返回 BATCH_UPDATE_SUCCESSFUL 常量(值为 -2)或 SUCCESS_NO_INFO 常量(值为 0,表示成功但无具体信息)。如果某条语句执行失败,通常会抛出 BatchUpdateException,此时可以通过该异常的 getUpdateCounts() 方法获取已成功执行的语句计数,失败的语句对应的计数为 EXECUTE_FAILED(值为 -3)。


















