Java用JDBC批處理插入

 如何使用JDBC API在Java中執行批量插入。雖然你可能已經知道,但我會盡力解釋基礎到複雜的場景。

 
在此筆記裏,我們將看到我們如何可以使用像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()方法一次執行他們。沒有什麼花哨,只是一個簡單的批量插入。

 

請注意,我們已經從一個String數組構建了查詢。現在,你可能會想,使其動態化。例如:

  

import java.sql.Connection;

import java.sql.Statement;

//...

 

Connection connection = new getConnection();

Statement statemenet = connection.createStatement();

for (Employee employee: employees) {

    String query = "insert into employee (name, city) values('"

            + employee.getName() + "','" + employee.getCity + "')";

    statemenet.addBatch(query);

}

statemenet.executeBatch();

statemenet.close();

connection.close();

  

 請注意我們是如何從Employee對象中的數據動態創建查詢並在批處理中添加,插入一氣呵成。完美!是不是?

 等等......你必須思考什麼關於SQL注入?這樣動態創建的查詢SQL注入是很容易的。並且每個插入查詢每次都被編譯。

 爲什麼不使用PreparedStatement而不是簡單的聲明。是的,這是個解決方案。下面是SQL注入安全批處理。

 

SQL Injection Safe Batch - SQL注入安全批處理

 

思考一下下面代碼:

 

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();

 

    }

 

}

 

ps.executeBatch(); // insert remaining records

 

ps.close();

 

connection.close();

 

 

 

 

 

這纔是理想的解決方案,它避免了SQL注入和內存不足的問題。看看我們如何遞增計數器計數,一旦BATCHSIZE 達到 1000,我們調用executeBatch()提交。希望對你有幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章