1.事務的概念
• 事務指邏輯上的一組操作,組成這組操作的各個單元,要不全部成功,要不全部不成功。
• 例如:A——B轉帳,對應於如下兩條sql語句
update from account set money=money+100 where name=‘b’;
update from account set money=money-100 where name=‘a’;
l 資料庫開啟事務命令
• start transaction 開啟事務
• Rollback 復原事務
• Commit 提交事務
2. 詳解jdbc與資料庫事務的串連
當Jdbc程式向資料庫獲得一個Connection對象時,預設情況下這個Connection對象會自動向資料庫提交在它上面發送的SQL語句。若想關閉這種預設提交方式,讓多條SQL在一個事務中執行,可使用下列語句:
l JDBC控制事務語句
• Connection.setAutoCommit(false);start transaction
• Connection.rollback(); rollback
• Connection.commit(); commit
l 設定交易回復點
• Savepoint sp = conn.setSavepoint();
• Conn.rollback(sp);
• Conn.commit(); //復原後必須要提交
3.多個線程開啟各自事務操作資料庫中資料時,資料庫系統要負責隔離操作,以保證各個線程在擷取資料時的準確性。
l 如果不考慮隔離性,可能會引發如下問題:
l 髒讀: 指一個事務讀取了另外一個事務未提交的資料。
這是非常危險的,假設A向B轉帳100元,對應sql語句如下所示:
1.updateaccount set money=money+100 while name=‘b’;
2.updateaccount set money=money-100 while name=‘a’;
當第1條sql執行完,第2條還沒執行(A未提交時),如果此時B查詢自己的帳戶,就會發現自己多了100元錢。如果A等B走後再復原,B就會損失100元。
l 不可重複讀取: 在一個事務內讀取表中的某一行資料,多次讀取結果不同。
例如銀行想查詢A帳戶餘額,第一次查詢A帳戶為200元,此時A向帳戶存了100元並提交了,銀行接著又進行了一次查詢,此時A帳戶為300元了。銀行兩次查詢不一致,可能就會很困惑,不知道哪次查詢是準的。
• 和髒讀的區別是,髒讀是讀取前一事務未提交的髒資料,不可重複讀取是重新讀取了前一事務已提交的資料。
• 很多人認為這種情況就對了,無須困惑,當然是後面的為準。我們可以考慮這樣一種情況,比如銀行程式需要將查詢結果分別輸出到電腦螢幕和寫到檔案中,結果在一個事務中針對輸出的目的地,進行的兩次查詢不一致,導致檔案和螢幕中的結果不一致,銀行工作人員就不知道以哪個為準了。
• 虛讀(幻讀)
• 是指在一個事務內讀取到了別的事務插入的資料,導致前後讀取不一致。
• 如丙存款100元未提交,這時銀行做報表統計account表中所有使用者的總額為500元,然後丙提交了,這時銀行再統計發現帳戶為600元了,造成虛讀同樣會使銀行不知所措,到底以哪個為準。
4.資料庫共定義了四種隔離等級:
• Serializable:可避免髒讀、不可重複讀取、虛讀情況的發生。(序列化)(序列化)
• Repeatable read:可避免髒讀、不可重複讀取情況的發生。(可重複讀)
• Read committed:可避免髒讀情況發生(讀已提交)。
• Read uncommitted:最低層級,以上情況均無法保證。(讀未提交)
• set transactionisolation level 設定交易隔離等級
• select @@tx_isolation 查詢當前交易隔離等級
5.建立JDBC的事務主要分以下步驟
1).設定事務的提交方式為非自動認可:
conn.setAutoCommit(false);
2).將需要添加事務的代碼放入try,catch塊中。
3).在try塊內添加事務的提交操作,表示操作無異常,提交事務。
conn.commit();
4).在catch塊內添加復原事務,表示操作出現異常,撤銷事務:
conn.rollback();
5).設定事務提交方式為自動認可:
conn.setAutoCommit(true);
6. 批處理
業務情境:當需要向資料庫發送一批SQL語句執行時,應避免向資料庫一條條的發送執行,而應採用JDBC的批處理機制,以提升執行效率。
l 實現批處理有兩種方式,第一種方式:
• Statement.addBatch(sql)
• 優點:可以向資料庫發送多條不同的SQL語句。
• 缺點:(1)SQL語句沒有先行編譯。
(2)當向資料庫發送多條語句相同,但僅參數不同的SQL語句時,需重複寫上很多條SQL語句
l 執行批處理SQL語句
• executeBatch()方法:執行批處理命令
• clearBatch()方法:清除批處理命令
相關代碼:
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
String sql1 ="insert into user(name,password,email,birthday)
values('kkk','123','abc@sina.com','1978-08-08')";
String sql2 ="update user set password='123456' where id=3";
st =conn.createStatement();
st.addBatch(sql1); //把SQL語句加入到批命令中
st.addBatch(sql2); //把SQL語句加入到批命令中
st.executeBatch();
} finally{
JdbcUtil.free(conn, st, rs);
}
l 實現批處理的第二種方式:
• PreparedStatement.addBatch()
• 優點:發送的是先行編譯後的SQL語句,執行效率高。
• 缺點:只能應用在SQL語句相同,但參數不同的批處理中。因此此種形式的批處理經常用於在同一個表中批量插入資料,或批次更新表的資料。
相關代碼:
conn = JdbcUtil.getConnection();
String sql ="insert into user(name,password,email,birthday) values(?,?,?,?)";
st =conn.prepareStatement(sql);
for(inti=0;i<50000;i++){
st.setString(1,"aaa" + i);
st.setString(2,"123" + i);
st.setString(3,"aaa" + i + "@sina.com");
st.setDate(4,newDate(1980, 10, 10));
st.addBatch();
if(i%1000==0){
st.executeBatch();
st.clearBatch();
}
}
st.executeBatch();
7.獲得資料庫自動產生的主鍵
樣本:
Connection conn =JdbcUtil.getConnection();
String sql ="insert into user(name,password,email,birthday)
values('abc','123','abc@sina.com','1978-08-08')";
PreparedStatement st =conn.
prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
st.executeUpdate();
ResultSet rs =st.getGeneratedKeys(); //得到插入行的主鍵
if(rs.next())
System.out.println(rs.getObject(1));
l 註:此參數僅對insert操作有效