Update是資料同步過程中一個不可缺少的操作,這裡所讨論的更新并非寫一個update語句更新了一批資料,如果是這樣,就沒必要寫此文章了。
這裡所讨論的更新是根據查詢對比,決定是否更新、删除等,甚至還要處理一些相關業務。對于這樣的操作,JDBC比任何方式的效率都好。這裡所謂的批量,是說有一大批這樣資料要通過查詢檢查,然後去做更新、删除操作。
為了進行測試,将問題抽象簡化,根據查詢更新、删除。
環境:
MySQL 5.1
RedHat Linux AS 5
JavaSE 1.5
DbConnectionBroker 微型資料庫連接配接池
優化的政策:
1、使用連接配接池
2、盡可能減少資料庫的連結和檢索次數
做到這兩個方面,就達到優化的的目标了。
SQL腳本
DROP TABLE IF EXISTS tuser;
CREATE TABLE tuser (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(12) DEFAULT NULL,
remark varchar(24) DEFAULT NULL,
createtime datetime DEFAULT NULL,
updatetime datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
下面是優化實作代碼:
import java.io.IOException;
import java.sql.*;
/**
* JDBC批量Update深度優化
*
* @author leizhimin 2009-7-30 8:38:33
*/
public class TestQuery {
public static DbConnectionBroker myBroker = null;
static {
try {
myBroker = new DbConnectionBroker("com.mysql.jdbc.Driver",
"jdbc:mysql://192.168.104.163:3306/testdb",
"vcom", "vcom", 2, 4,
"c:\\testdb.log", 0.01);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 初始化測試環境
*
* @throws SQLException 異常時抛出
*/
public static void init() throws SQLException {
Connection conn = myBroker.getConnection();
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
stmt.addBatch("DROP TABLE IF EXISTS tuser");
stmt.addBatch("CREATE TABLE tuser (\n" +
" id bigint(20) NOT NULL AUTO_INCREMENT,\n" +
" name varchar(12) DEFAULT NULL,\n" +
" remark varchar(24) DEFAULT NULL,\n" +
" createtime datetime DEFAULT NULL,\n" +
" updatetime datetime DEFAULT NULL,\n" +
" PRIMARY KEY (id)\n" +
") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8");
stmt.executeBatch();
conn.commit();
System.out.println("--------資料庫所支援的ResultSet的類型---------");
System.out.println("ResultSet.TYPE_FORWARD_ONLY\t\t\t:"+conn.getMetaData().supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY));
System.out.println("ResultSet.TYPE_SCROLL_INSENSITIVE\t:"+conn.getMetaData().supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE));
System.out.println("ResultSet.TYPE_SCROLL_SENSITIVE\t\t:"+conn.getMetaData().supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE));
myBroker.freeConnection(conn);
* n條預定義SQL插入
* @throws Exception 異常時抛出
public static void initData(int n) throws Exception {
init(); //初始化環境
Long start = System.currentTimeMillis();
String sql = "" +
"insert into testdb.tuser\n" +
" (name, remark, createtime, updatetime)\n" +
"values\n" +
" (?, ?, ?, ?)";
for (int i = 0; i < n; i++) {
Connection conn = myBroker.getConnection();
conn.setAutoCommit(false);
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, RandomToolkit.generateString(12));
pstmt.setString(2, RandomToolkit.generateString(24));
pstmt.setDate(3, new Date(System.currentTimeMillis()));
pstmt.setDate(4, new Date(System.currentTimeMillis()));
pstmt.executeUpdate();
conn.commit();
pstmt.close();
myBroker.freeConnection(conn);
Long end = System.currentTimeMillis();
System.out.println("單條執行" + n + "條Insert操作,共耗時:" + (end - start) / 1000f + "秒!");
* 查詢一條資料,并更新該條資料
* @throws SQLException
public static void testQueryOne4Update() throws SQLException {
String query_sql = "select id, name, remark, createtime, updatetime\n" +
" from testdb.tuser where id = 1";
//注意結果集的參數配置
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
System.out.println("連接配接是否為隻讀模式:" + conn.isReadOnly());
System.out.println("查詢使用的SQL所對應的本地SQL腳本:" + conn.nativeSQL(query_sql));
ResultSet rs = stmt.executeQuery(query_sql);
while (rs.next()) { //一行資料,本while可以省略
//更新資料的name列
rs.updateString("name", "new name");
//儲存更新行
rs.updateRow();
* 查詢多條記錄并做更新操作
public static void testQueryMany4Update() throws SQLException {
" from testdb.tuser where id >2 and id<5";
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
//循環逐條更新查詢結果集資料
while (rs.next()) {
rs.updateString("name", "lavasoft25");
rs.updateDate("updatetime", new Date(System.currentTimeMillis()));
System.out.println(conn.isReadOnly());
System.out.println(conn.nativeSQL(query_sql));
* 查詢一條記錄并做插入操作
public static void testQueryOne4Insert() throws SQLException {
" from testdb.tuser where id = 1 ";
//将結果集指針移動到可插入的行(這行是在記憶體中的一個虛拟行)
rs.moveToInsertRow();
//設定行各個字段的資料
rs.updateString(2, "熔岩");
rs.updateString(3, "ttt");
rs.updateDate(4, new Date(System.currentTimeMillis()));
rs.updateDate(5, new Date(System.currentTimeMillis()));
//插入行資料到該表中
rs.insertRow();
//指針複位:将指針移動到執行moveToInsertRow()之前的位置
rs.moveToCurrentRow();
* 查詢一批資料,并做插入操作
public static void testQueryMany4Insert() throws SQLException {
" from testdb.tuser where id >4 and id<8";
//将結果集指針移動到可插入的行(這行是在記憶體中的一個虛拟行)
rs.moveToInsertRow();
//設定行各個字段的資料
rs.updateString(2, "lavasoft3");
rs.updateString(3, "ttt");
rs.updateDate(4, new Date(System.currentTimeMillis()));
rs.updateDate(5, new Date(System.currentTimeMillis()));
//插入行資料到該表中
rs.insertRow();
//指針複位:将指針移動到執行moveToInsertRow()之前的位置
rs.moveToCurrentRow();
//将指針從目前位置下移一行
rs.next();
* 查詢一條資料,并做插入操作
public static void testQueryOne4Delete() throws SQLException {
" from testdb.tuser where id=8";
//将指針移動到要删除的行上
rs.next();
//從此 ResultSet 對象和底層資料庫中删除目前行。指針不位于插入行上時不能調用此方法。
rs.deleteRow();
public static void testQueryMany4Delete() throws SQLException {
" from testdb.tuser where id>1";
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
//從此 ResultSet 對象和底層資料庫中删除目前行。指針不位于插入行上時不能調用此方法。
System.out.println(rs.getRow());
rs.deleteRow();
rs.beforeFirst();
public static void main(String[] args) throws SQLException {
init();
// testQueryMany4Delete();
}
總結:
1、優化思想就是盡量減少資料庫連接配接和請求。根據查詢結果,直接更改結果,然後儲存實作了資料的更新,一個連接配接做多件事情,性能相比先查詢結果集合,然後建構更新SQL再執行要好很多。删除操作也類似。
2、優化的關鍵就是在查詢結果集上做文章,并不是所有的結果集都支援這樣的更新和删除操作,在建構Statement對象的時候,幾個ResultSet參數需要特别注意:
TYPE_FORWARD_ONLY
該常量訓示指針隻能向前移動的 ResultSet 對象的類型,預設類型。隻允許向前一條一條的通路,并且不會受到其他使用者對該資料庫所作更改的影響。
。
TYPE_SCROLL_INSENSITIVE
該常量訓示可滾動但通常不受其他的更改影響的 ResultSet 對象的類型,允許在清單中向前或向後移動,甚至可以進行特定定位,不會受到其他使用者對該資料庫所作更改的影響。
TYPE_SCROLL_SENSITIVE
該常量訓示可滾動并且通常受其他的更改影響的 ResultSet 對象的類型,像TYPE_SCROLL_INSENSITIVE一樣,允許在記錄中定位。這種類型受到其他使用者所作更改的影響。如果使用者在執行完查詢之後删除一個記錄,那個記錄将從ResultSet中消失。類似的,對資料值的更改也将反映在ResultSet中。
CONCUR_READ_ONLY
該常量訓示不可以更新的 ResultSet 對象的并發模式,适合隻查詢,不對結果集修改的操作。
CONCUR_UPDATABLE
該常量訓示可以更新的 ResultSet 對象的并發模式,适合對查詢結果集做更新、删除的操作。
CLOSE_CURSORS_AT_COMMIT
該常量訓示調用 Connection.commit 方法時應該關閉 ResultSet 對象,一般情況下都應該如此。
HOLD_CURSORS_OVER_COMMIT
該常量訓示調用 Connection.commit 方法時不應關閉 ResultSet 對象,很少用到。
3、更新查詢結果集資料能否真正持久化,與JDBC驅動程式的完善程度有關,不是所有的JDBC驅動程式都支援結果集的進階特性。可以通過DatabaseMetaData來查詢驅動程式的支援情況。
本文轉自 leizhimin 51CTO部落格,原文連結:http://blog.51cto.com/lavasoft/185354,如需轉載請自行聯系原作者