事務的傳播行為
當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啟一個新事務,並在自己的事務中運行。
事務的傳播行為可以由傳播屬性指定。Spring定義了7種類傳播行為。
事務傳播屬性可以在@Transactional注解的propagation屬性中定義。
1) . 說明
①REQUIRED傳播行為
當bookService的purchase()方法被另一個事務方法checkout()調用時,它默認會在現有的事務內運行。這個默認的傳播行為就是REQUIRED。因此在checkout()方法的開始和終止邊界內只有一個事務。這個事務只在checkout()方法結束的時候被提交,結果用戶一本書都買不了。
②. REQUIRES_NEW傳播行為
表示該方法必須啟動一個新事務,並在自己的事務內運行。如果有事務在運行,就應該先掛起它。
事務的隔離級別
1、數據庫事務並發問題
假設現在有兩個事務:Transaction01和Transaction02並發執行。
1) 臟讀(針對一條數據中的一個字段)
①Transaction01將某條記錄的AGE值從20修改為30。
②Transaction02讀取了Transaction01更新后的值:30。
③Transaction01回滾,AGE值恢復到了20。
④Transaction02讀取到的30就是一個無效的值。
2) 不可重復讀(針對一條數據中的一個字段)
①Transaction01讀取了AGE值為20。
②Transaction02將AGE值修改為30。
③Transaction01再次讀取AGE值為30,和第一次讀取不一致。
3) 幻讀(針對表中的一行數據而言)
①Transaction01讀取了STUDENT表中的一部分數據。
②Transaction02向STUDENT表中插入了新的行。
③Transaction01讀取了STUDENT表時,多出了一些行。
2、 隔離級別
數據庫系統必須具有隔離並發運行各個事務的能力,使它們不會相互影響,避免各種並發問題。一個事務與其他事務隔離的程度稱為隔離級別。SQL標准中規定了多種事務隔離級別,不同隔離級別對應不同的干擾程度,隔離級別越高,數據一致性就越好,但並發性越弱。
1) 讀未提交:READ UNCOMMITTED
允許Transaction01讀取Transaction02未提交的修改。
2) 讀已提交:READ COMMITTED
要求Transaction01只能讀取Transaction02已提交的修改。
3) 可重復讀:REPEATABLE READ
確保Transaction01可以多次從一個字段中讀取到相同的值,即Transaction01執行期間禁止其它事務對這個字段進行更新。
4) 串行化:SERIALIZABLE
確保Transaction01可以多次從一個表中讀取到相同的行,在Transaction01執行期間,禁止其它事務對這個表進行添加、更新、刪除操作。可以避免任何並發問題,但性能十分低下。(單線程,在一個請求中,其他線程讀都不可以)
用@Transactional注解聲明式地管理事務時可以在@Transactional的isolation屬性中設置隔離級別。
觸發事務回滾的異常
1、默認情況
捕獲到RuntimeException或Error時回滾,而捕獲到編譯時異常不回滾。
2、設置途經
1) 注解@Transactional 注解
① rollbackFor屬性:指定遇到時必須進行回滾的異常類型,可以為多個
② noRollbackFor屬性:指定遇到時不回滾的異常類型,可以為多個
事務的超時和只讀屬性
由於事務可以在行和表上獲得鎖,因此長事務會占用資源,並對整體性能產生影響。 如果一個事務只讀取數據但不做修改,數據庫引擎可以對這個事務進行優化。
超時事務屬性:事務在強制回滾之前可以保持多久。這樣可以防止長期運行的事務占用資源。
只讀事務屬性: 表示這個事務只讀取數據但不更新數據, 這樣可以幫助數據庫引擎優化事務。
2、注解設置
@Transaction注解

package com.atguigu.book.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.atguigu.book.dao.BookDao; import com.atguigu.book.exception.MyException; import com.atguigu.book.service.BookService; @Service //@Transactional public class BookServiceImpl implements BookService { @Autowired private BookDao dao; /** * @Transactional:對方法中所有的操作作為一個事務進行管理 * 在方法上使用,只對方法有效果 * 在類上使用,對類中所有的方法都有效果 * @Transactional中可以設置的屬性: * * propagation:A方法和B方法都有事務,當A在調用B時,會將A中的事務傳播給B方法,B方法對於事務的處理方式就是事務的傳播行為 * Propagation.REQUIRED:必須使用調用者的事務(默認值) * Propagation.REQUIRES_NEW:將調用者的事務掛起,不使用調用者的事務,使用新的事務進行處理 * * isolation:事務的隔離級別,在並發的情況下,操作數據的一種規定 * 讀未提交:臟讀 1 * 讀已提交:不可重復讀 2 * 可重復讀:幻讀 4 * 串行化:性能低,消耗大 8 * * * timeout:在事務強制回滾前最多可以執行(等待)的時間 * * readOnly:指定當前事務中的一系列的操作是否為只讀 * 若設置為只讀,不管事務中有沒有寫的操作,mysql都會在請求訪問數據的時候,不加鎖,提高性能 * 但是如果有寫操作的情況,建議一定不能設置只讀 * * rollbackFor|rollbackForClassName|noRollbackFor|noRollbackForClassName:設置事務回滾的條件 */ @Transactional(propagation=Propagation.REQUIRES_NEW, timeout=3, noRollbackFor= {NullPointerException.class, MyException.class}) public void buyBook(String bid, String uid) { /*try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ Integer price = dao.selectPrice(bid); dao.updateSt(bid); dao.updateBalance(uid, price); } }