对于数据库的操作,每一次执行executeUpdate(),其实都会向数据库发送一次SQL,每次发送都等同于通过网络进行了一次信息传送。而网络传送信息实际上必须启动I/O、进行路由等动作,这样进行大量更新,当执行的次数过多时,性能会很低,因此批量更新显得尤为重要!
在使用批量更新之前我们进行大量数据更新是使用如下的代码段:
Statement stmt = conn.createStatement();
while( [condition is true] ){
stmt.executeUpdate( [sql] );
}
我们可以通过使用addBatch()方法来收集SQL,并使用executeBatch()方法将所有的SQL传送出去,如下是通过Statement 来进行批次更新:
Statement stmt = conn.createStatement();
while( <condition is true> ){
stmt.addBatch( <sql> );
}
stmt.executeBatch();
其中Statement实例中的addBatch()是使用了ArrayList来收集SQL,Mysql驱动程序的Statement的源码如下所示:
public void addBatch(String sql) throws SQLException {
synchronized(this.checkClosed().getConnectionMutex()) {
if(this.batchedArgs == null) {
this.batchedArgs = new ArrayList();
}
if(sql != null) {
this.batchedArgs.add(sql);
}
}
}
顺带一提,当初找这个源码的时候发现在Jdk中只有一个Statement的接口的声明,并没有具体的源码,由于不同的数据库驱动厂商对接口的实现做了不同的处理,因而Mysql的Statement具体源码的实现在com.mysql.jdbc.StatementImpl。
通过源码可以看出,addBatch()方法会收集所有的SQL,最后串为一句SQL,最终传给数据库,当大量更新产生时,这些SQL语句就会通过一次网络传送给数据库,节省了I/O、网络路由等操作所消耗的时间。
需要注意的是批次更新限制了SQL语句不能是SELECT,否则会抛出异常。
最终在数据库执行时的顺序就是addBatch()的顺序,executeBatch()会返回int[] , 代表每笔SQL造成的数据异动列数。任何的SQL错误都会抛出BatchUpdateException,可以使用该对象的getUpdateCounts()取得int[], 代表先前执行成功的SQL所造成的异动笔数。
上面是一个Statement的例子,如果是使用PreparedStatement进行批次更新,如下是一个范例:
PreparStatement stmt = conn.preparedStatemnt("insert into table(key, key) values(?, ?)");
while( <condition is true> ){
stmt.setString(1, "..");
stmt.setString(1, "..");
stmt.addBatch(); //收集参数
}
stmt.executeBatch(); //执行送出所有参数
Mysql的PreparedStatement实现类的addBatch()源码如下:
public void addBatch() throws SQLException {
synchronized(this.checkClosed().getConnectionMutex()) {
if(this.batchedArgs == null) {
this.batchedArgs = new ArrayList();
}
for(int i = 0; i < this.parameterValues.length; ++i) {
this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);
}
this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
}
}
从源码可以看出addBatch()会收集占位字符真正的数值,内部是使用ArrayList来收集占位字符实际的数值。
驱动程序本身是否支持批次更新也要注意,比如Mysql要支持批次更新,必须在JDBC URL上附加rewriteBatchedStatements=true参数才有作用。