資料庫事務:事務是指一組相互依賴的操作行為,如銀行交易、股票交易或網上購物。事務的成功取決於這些相互依賴的操作行為是否都能執行成功,只要有一個操作行為失敗,就意味著整個事務失敗。關於事務的一個經典例子就是:A到銀行辦理轉賬事務,把100元錢轉到B的帳號上,這個事務包含以下操作行為:
(1)從A的賬戶上減去100元。
(2)往B的賬戶上增加100元。
顯然,以上兩個操作必須作為一個不可分割的工作單元。假如僅僅第一步操作執行成功,使得Tom的賬戶上扣除了100元,但是第二步操作執行失敗,Jack的賬戶上沒有增加100元,那麼整個事務失敗。 資料庫事務是對現實生活中事務的類比,它由一組在商務邏輯上相互依賴的SQL語句組成。
下面我們一起來看一下資料庫事務的生命週期:
這個資料庫事務的生命週期圖反應出資料庫事務的三個邊界:
1.事務的開始邊界。
2.事務的正常結束邊界(COMMIT):提交事務,永久儲存被事務更新後的資料庫狀態。
3.事務的異常結束邊界(ROLLBACK):撤銷事務,使資料庫退回到執行事務前的初始狀態。
其實每個資料庫連接都有個全域變數@@autocommit,表示當前的事務模式,它有兩個可選值: 0:表示手工提交模式。 1:預設值,表示自動認可模式。
在自動認可模式下,每個SQL語句都是一個獨立的事務。也就是說,每執行一條sql語句,資料庫都會自動認可這個事務,當我們用資料庫另一個用戶端去查詢的時候,我們可以看到這個新修改或插入的資料。在手工提交模式下,必須顯式指定事務開始邊界和結束邊界:
–事務的開始邊界:begin
–提交事務:commit
–撤銷事務:rollback
下面我們來看一下通過JDBC API是如何聲明事務邊界的:
Connection提供了以下用於控制事務的方法:
1.setAutoCommit(boolean autoCommit):設定是否自動認可事務
2.commit():提交事務
3.rollback():撤銷事務
下面我們看一下具體的應用樣本:
try { con = java.sql.DriverManager.getConnection(dbUrl,dbUser,dbPwd); //設定手工提交事務模式 con.setAutoCommit(false); stmt = con.createStatement(); //資料庫更新操作1 stmt.executeUpdate("update ACCOUNTS set BALANCE=900 where ID=1 "); //資料庫更新操作2 stmt.executeUpdate("update ACCOUNTS set BALANCE=1000 where ID=2 "); con.commit(); //提交事務 }catch(Exception e) { try{ con.rollback(); //操作不成功則撤銷事務 }catch(Exception ex){ //處理異常 …… } //處理異常 …… }finally{…}
看到上邊的樣本我們可以看出,其實hibernate事務邊界就是模仿者JDBC的事務邊界來的,其實在hibernate底層的交易管理就是利用的JDBC的交易管理。我們來看一下hibernate事務邊界:
1.聲明事務的開始邊界:
Transaction tx=session.beginTransaction();
2.提交事務: tx.commit();
3.撤銷事務: tx.rollback();
我們在學習JDBC資料庫交易管理的時候,重點也是痛點的學習了jdbc多個事務並發問題。既然hibernate底層是用JDBC交易管理實現的,那麼它也一定存在著多個事務並發的問題。下面我們就具體來看一下:hibernate多個事務並發的並發問題:
•第一類丟失更新:撤銷一個事務時,把其他事務已提交的更新資料覆蓋。
•髒讀:一個事務讀到另一事務未提交的更新資料。
•虛讀:一個事務讀到另一事務已提交的新插入的資料。
•不可重複讀取:一個事務讀到另一事務已提交的更新資料。
•第二類丟失更新:這是不可重複讀取中的特例,一個事務覆蓋另一事務已提交的更新資料。
下面我們就髒讀來舉一個樣本:
取款事務在T5時刻把存款餘額改為900元,支票轉賬事務在T6時刻查詢賬戶的存款餘額為900元,取款事務在T7時刻被撤銷,支票轉賬事務在T8時刻把存款餘額改為1000元。 由於支票轉賬事務查詢到了取款事務未提交的更新資料,並且在這個查詢結果的基礎上進行更新操作,如果取款事務最後被撤銷,會導致銀行客戶損失100元。
交易隔離等級
關於交易隔離等級,我們來看一下下面的兩個圖解:
由可以看出:隔離等級越高,越能保證資料的完整性和一致性,但是對並發效能的影響也越大。 對於多數應用程式,可以優先考慮把資料庫系統的隔離等級設為Read Committed,它能夠避免髒讀,而且具有較好的並發效能。儘管它會導致不可重複讀取、虛讀和第二類丟失更新這些並發問題,在可能出現這類問題的個別場合,可以由應用程式採用悲觀鎖或樂觀鎖來控制。
下面我們就具體來看一下hibernate怎麼來配置隔離等級:在Hibernate的設定檔中可以顯式的設定隔離等級。每一種隔離等級都對應一個整數:
1:Read Uncommitted
2:Read Committed
4:Repeatable Read
8:Serializable
例如,以下代碼把hibernate.cfg.xml檔案中的隔離等級設為Read Committed:
hibernate.connection.isolation=2
對於從資料庫連接池中獲得的每個串連,Hibernate都會把它改為使用Read Committed隔離等級。