在此筆記里,我們將看到我們?nèi)绾慰梢允褂孟馭tatement和PreparedStatement JDBC API來(lái)批量在任何數(shù)據(jù)庫(kù)中插入數(shù)據(jù)。此外,我們將努力探索一些場(chǎng)景,如在內(nèi)存不足時(shí)正常運(yùn)行,以及如何優(yōu)化批量操作。
首先,使用Java JDBC基本的API批量插入數(shù)據(jù)到數(shù)據(jù)庫(kù)中。
Simple Batch - 簡(jiǎn)單批處理
我把它叫做簡(jiǎn)單批處理。要求很簡(jiǎn)單,執(zhí)行批量插入列表,而不是為每個(gè)INSERT語(yǔ)句每次提交數(shù)據(jù)庫(kù),我們將使用JDBC批處理操作和優(yōu)化性能。
想想一下下面的代碼:
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();
這是糟糕的代碼。它單獨(dú)執(zhí)行每個(gè)查詢,每個(gè)INSERT語(yǔ)句的都提交一次數(shù)據(jù)庫(kù)??紤]一下,如果你要插入1000條記錄呢?這是不是一個(gè)好主意。
下面是執(zhí)行批量插入的基本代碼。來(lái)看看:
Good Code
Connection connection = new getConnection();Statement statemenet = connection.createStatement(); for (String query : queries) { statemenet.addBatch(query);}statemenet.executeBatch();statemenet.close();connection.close();
請(qǐng)注意我們?nèi)绾问褂胊ddBatch()方法,而不是直接執(zhí)行查詢。然后,加入所有的查詢,我們使用statement.executeBatch()方法一次執(zhí)行他們。沒(méi)有什么花哨,只是一個(gè)簡(jiǎn)單的批量插入。
請(qǐng)注意,我們已經(jīng)從一個(gè)String數(shù)組構(gòu)建了查詢。現(xiàn)在,你可能會(huì)想,使其動(dòng)態(tài)化。例如:
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();
請(qǐng)注意我們是如何從Employee對(duì)象中的數(shù)據(jù)動(dòng)態(tài)創(chuàng)建查詢并在批處理中添加,插入一氣呵成。完美!是不是?
等等......你必須思考什么關(guān)于SQL注入?這樣動(dòng)態(tài)創(chuàng)建的查詢SQL注入是很容易的。并且每個(gè)插入查詢每次都被編譯。
為什么不使用PreparedStatement而不是簡(jiǎn)單的聲明。是的,這是個(gè)解決方案。下面是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查詢。這是你必須實(shí)現(xiàn)批量插入邏輯的解決方案,而不是上述Statement那個(gè)。
這一解決方案仍然存在一個(gè)問(wèn)題??紤]這樣一個(gè)場(chǎng)景,在您想要插入到數(shù)據(jù)庫(kù)使用批處理上萬(wàn)條記錄。嗯,可能產(chǎn)生的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)
這是因?yàn)槟阍噲D在一個(gè)批次添加所有語(yǔ)句,并一次插入。最好的辦法是將執(zhí)行分批次??纯聪旅娴慕鉀Q方案
Smart Insert: Batch within Batch - 智能插入:將整批分批
這是一個(gè)簡(jiǎn)單的解決方案??紤]批量大小為1000,每1000個(gè)查詢語(yǔ)句為一批插入提交。
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 recordsps.close();connection.close();
這才是理想的解決方案,它避免了SQL注入和內(nèi)存不足的問(wèn)題。看看我們?nèi)绾芜f增計(jì)數(shù)器計(jì)數(shù),一旦BATCHSIZE 達(dá)到 1000,我們調(diào)用executeBatch()提交。
來(lái)源: http://itindex.blog.51cto.com/3619105/801447
聯(lián)系客服