Java JDBC批处理优化

在这里,我们将讨论如何使用像Statement和PreparedStatement JDBC API来批量向数据库中插入数据。此外,我们将努力探索一些场景,如何在内存不足时正常运行,以及如何优化批量操作。

首先,使用Java JDBC基本的API批量插入数据到数据库中。
Simple Batch - 简单批处理:
    我把它叫做简单批处理。要求很简单,执行批量插入列表,而不是为每个INSERT语句每次提交数据库,我们将使用JDBC批处理操作和优化性能。

想想一下下面的代码:
Bad Code:
 String [] queries = {
    "insert into employee (name, city, phone) values ('A', 'X', '123')",
    "insert into employee (name, city, phone) values ('B', 'Y', '234')",
    "insert into employee (name, city, phone) values ('C', 'Z', '345')",
 };
Connection connection = new getConnection();
Statement statemenet = connection.createStatement();
for (String query : queries) {
    statemenet.execute(query);
}
statemenet.close();
connection.close();
这是糟糕的代码。它单独执行每个查询,每个INSERT语句的都提交一次数据库。考虑一下,如果你要插入1000条记录呢?这是不是一个好主意。

下面是执行批量插入的基本代码。来看看:

Good Code:

...

Connection connection = new getConnection();
Statement statemenet = connection.createStatement();
for (String query : queries) {
    statemenet.addBatch(query);
}
statemenet.executeBatch();
statemenet.close();
connection.close();

这里不是直接执行查询,而是使用addBatch()方法,加入所有的查询,然后使用statement.executeBatch()

方法一次执行他们。没有什么花哨,只是一个简单的批量插入。



为什么不使用PreparedStatement呢?
import java.sql.Connection;
import java.sql.PreparedStatement;
 //...
String sql = "insert into employee (name, city, phone) values (?, ?, ?)";
Connection connection = new getConnection();
PreparedStatement ps = connection.prepareStatement(sql);
for (Employee employee: employees) {
    ps.setString(1, employee.getName());
    ps.setString(2, employee.getCity());
    ps.setString(3, employee.getPhone());
    ps.addBatch();
}
ps.executeBatch();
ps.close();
connection.close();
看看上面的代码,我们使用的java.sql.PreparedStatement和在批处理中添加INSERT语句。这是你必须实现批量插入逻辑的解决方案,而不是上述Statement那个。


这一解决方案仍然存在一个问题。考虑这样一个场景,在您想要使用批处理向数据库一次性插入上万条记录,可能产生的OutOfMemoryError:

java.lang.OutOfMemoryError: Java heap space
com.mysql.jdbc.ServerPreparedStatement$BatchedBindValues.<init>(ServerPreparedStatement.java:72)
com.mysql.jdbc.ServerPreparedStatement.addBatch(ServerPreparedStatement.java:330)
org.apache.commons.dbcp.DelegatingPreparedStatement.addBatch(DelegatingPreparedStatement.java:171)
这是因为你试图在一个批次添加所有语句,并一次插入。最好的办法是将执行分批次。看看下面的解决方案

Smart Insert: Batch within Batch - 智能插入:将整批分批
这是一个简单的解决方案。考虑批量大小为1000,每1000个查询语句为一批插入提交。
String sql = "insert into employee (name, city, phone) values (?, ?, ?)";
Connection connection = new getConnection();
PreparedStatement ps = connection.prepareStatement(sql);
final int batchSize = 1000;
int count = 0;
for (Employee employee: employees) {
    ps.setString(1, employee.getName());
    ps.setString(2, employee.getCity());
    ps.setString(3, employee.getPhone());
    ps.addBatch();
    if(++count % batchSize == 0) {

        ps.executeBatch();

//conn.commit(); //show VARIABLES like 'autocommit',Can't call commit when autocommit=true

ps.clearBatch();// 清空ps中积攒的sql

    }
}
ps.executeBatch(); // insert remaining records
ps.close();

connection.close();


这才是理想的解决方案,它避免了SQL注入和内存不足的问题。看看我们如何递增计数器计数,一旦BATCHSIZE 达到 1000,我们调用executeBatch()提交。

你可能感兴趣的:(优化,数据库,jdbc,batch,批处理)