驗證mysql的自動提交事務和手動提交事務(java版)
個人理解的一個事務:是一個Connection一系列的操作過程,如果是兩個Connection連線在操作,那就是兩個事務。
事務的前提:資料庫的儲存引擎是innodb。
事務的目的:保證資料的安全性。
事務安全:
1.自動提交事務:每執行一條sql語句,就同步到資料庫中。
2.手動提交事務:執行一系列的sql語句後一起同步到資料庫中。
事務的四大特性:
A(atomic):原子性,事務中的全部操作是一個整體,要麼全部成功,要麼全部失敗;
C(consistency):一致性:事務開啟後,資料表中的資料狀態沒有變化,因為還沒有提交;
I(Isolation):隔離型,多個事務操作是相互隔離,互不影響的;
D(Durability):永續性,事務一旦提交,資料表就發生永久改變.
自動提交事務和手動提交事務的區別:
自動提交事務:
在資料庫工具下的測試:
資料庫的資料:
自動提交事務執行select查詢的結果:
用java程式碼測試自動提交事務時的測試結果:
@Test
//自動提交事務(隱形事務時)
public void testNotAddAffair(){
Connection con = JDBCTemple.getConnection();
String sql = "select count(1) from sal_table";
int count = 0 ;
try {
//查詢操作
count = JDBCTemple.selectData(con,sql);
System.out.println("(try中)插入前的count=" + count);
//插入操作
String insert = "insert into sal_table(sname,sal) values(?,?)";
Object[] objs = {"王五", 2000};
JDBCTemple.updateDate(con,insert, objs);
//再次查詢操作
count = JDBCTemple.selectData(con,sql);
System.out.println("(try中)插入一條資料以後的count=" + count);
//插入操作
//Object[] objs1 = {"王五他大哥", 2000};
JDBCTemple.updateDate(con,insert, null);
count = JDBCTemple.selectData(con,sql);
System.out.println("(try中)第二次插入後的count=" + count);
} catch (Exception e1) {
count = JDBCTemple.selectData(con,sql);
System.out.println("(catch中)出現異常後count=" + count);
e1.printStackTrace();
}finally{
System.out.println("(finally中)最終count=" + count);
}
}
執行結果如下:
結果分析:
1.插入之前,第一次執行select查詢語句時,查詢到的結果是20,與工具中查的結果是一樣的。
2.插入後,第二次執行select查詢語句時,查詢到的結果為21,說明資料可能插入到資料庫中。
3.第二次插入時,因為引數是null,所以出現了空指標異常,沒有成功插入到資料庫中,sql執行出現異常,轉到catch{}程式碼塊中。
4.在catch{}程式碼塊中再次查詢,查詢到的結果為21,說明第一次資料已經插入到資料庫中。
5.在finally{}程式碼塊中再次查詢,查詢的資料還是21,說明最終第一次資料插入到資料庫,但是第二次沒有成功插入到資料庫中。
顯示提交事務
在資料庫工具下的測試:
手動提交事務執行select查詢的結果
用java程式碼測試手動提交事務時的測試結果:
@Test
public void testAddAffair(){
Connection con = JDBCTemple.getConnection();
String sql = "select count(1) from sal_table";
int count = 0;
try {
//事務的提交方式為手動提交
con.setAutoCommit(false);
//查詢操作
count = JDBCTemple.selectData(con,sql);
System.out.println("(try中)插入前的count=" + count);
//插入操作
System.out.println("開始第一次插入資料...");
String insert = "insert into sal_table(sname,sal) values(?,?)";
Object[] objs = {"王五", 2000};
JDBCTemple.updateDate(con,insert, objs);
System.out.println("第一次插入資料成功!");
//再次查詢操作
count = JDBCTemple.selectData(con,sql);
System.out.println("(try中)插入一條資料以後的count=" + count);
//插入操作
System.out.println("開始第二次插入資料....");
//Object[] objs1 = {"王五他大哥", 2000};
JDBCTemple.updateDate(con,insert, null);
System.out.println("第二次插入資料成功!");
count = JDBCTemple.selectData(con,sql);
System.out.println("(try中)插入兩條資料以後的count=" + count);
//提交事務
con.commit();
} catch (Exception e1) {
try {
con.rollback();
count = JDBCTemple.selectData(con,sql);
System.out.println("(catch(try)中)回滾後的count=" + count);
} catch (SQLException e) {
System.out.println("回滾失敗!");
e.printStackTrace();
}
count = JDBCTemple.selectData(con,sql);
System.out.println("(catch中)的count=" + count);
e1.printStackTrace();
}finally{
count = JDBCTemple.selectData(con,sql);
System.out.println("(finally中)最終count=" + count);
}
}
執行結果如下:
結果分析:
很明顯的一點是:顯示提交事務時,如果出現異常,那麼執行結果和未執行sql語句之前的結果是一樣的。
1.插入之前,第一次執行select查詢語句時,查詢到的結果是21,與工具中查的結果是一樣的。
2.插入後,第二次執行select查詢語句時,查詢到的結果為22,說明這條資料可能插入到資料庫中。
3.第二次插入時,因為引數是null,所以出現了空指標異常,沒有成功插入到資料庫中,sql執行出現異常,轉到catch{}程式碼塊中。
4.在catch{}程式碼塊中再次查詢,查詢到的結果為21,又回到了第一條資料沒有插入之前的結果。
5.在finally{}程式碼塊中再次查詢,查詢的資料還是21,說明第一次的插入最終沒有同步到資料庫中
那麼問題出現了,第一次插入的資料最終沒有同步到資料庫中,那麼第一次插入後查詢到的資料條數是22,這是為什麼?屬於幻讀嗎?
對於以上結果做以下分析:
1.原子性:以上所有的操作共用一個客戶端的Connection物件,因為顯示提交事務,所以為了保證資料的原子性,try{}程式碼塊中的所有更新 操作屬於不可分割的一部分,要麼全部成功,要麼全部失敗。
2.是否屬於幻讀的問題:如果單單理解幻讀史兩次讀取資料的行數結果不一樣,那麼這確實是幻讀。但是真正幻讀的概念是兩次讀取資料中間的時間間隔內,有其他的事務(其他的Connection連線)對錶中資料進行了更新的操作,導致兩次資料不一致的問題。在這個小demo中,始終用的是同一個Connection連結,屬於同一個事務,所以不屬於幻讀。
3.理解第一次插入後查詢到的資料條數是22,就要理解事務操作時資料庫執行過程,請看下圖:
從上圖我們知道這個22的出現是因為:第一次插入的資料寫入到了日誌檔案中,當我們在第一次插入後查詢資料時,先從資料庫表格中查到資料21,再經過日誌檔案處理,變成22,此時資料庫表格中並沒有更新。當第二次插入資料失敗時,回滾資料,將日誌中的記錄刪除,最終沒有將第一次的資料同步到資料庫中,所以最終資料庫中的記錄數還是21.