Spring @Transactional註解淺談

引言: 在Spring中@Transactional提供一種控制事務管理的快捷手段,可是不少人都只是@Transactional簡單使用,並未深刻了解,其各個配置項的使用方法,本文將深刻講解各個配置項的使用。java

@Transactional的定義

Spring中的@Transactional基於動態代理的機制,提供了一種透明的事務管理機制,方便快捷解決在開發中碰到的問題。在現實中,實際的問題每每比咱們預期的要複雜不少,這就要求對@Transactional有深刻的瞭解,以來應對複雜問題。spring

首先咱們來看看@Transactional的源代碼定義:數據庫

org.springframework.transaction.annotation.Transactional數組

@Target({ElementType.METHOD, ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Inherited

@Documented

public @interface Transactional {

/** * A qualifier value for the specified transaction. * <p>May be used to determine the target transaction manager, * matching the qualifier value (or the bean name) of a specific * {@link org.springframework.transaction.PlatformTransactionManager} * bean definition. */

String value() default "";

/** * The transaction propagation type. * Defaults to {@link Propagation#REQUIRED}. * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior() */

Propagation propagation() default Propagation.REQUIRED;

/** * The transaction isolation level. * Defaults to {@link Isolation#DEFAULT}. * @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel() */

Isolation isolation() default Isolation.DEFAULT;

/** * The timeout for this transaction. * Defaults to the default timeout of the underlying transaction system. * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout() */

int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

/** * {@code true} if the transaction is read-only. * Defaults to {@code false}. * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction. * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly() */

boolean readOnly() default false;

/** * Defines zero (0) or more exception {@link Class classes}, which must be a * subclass of {@link Throwable}, indicating which exception types must cause * a transaction rollback. * <p>This is the preferred way to construct a rollback rule, matching the * exception class and subclasses. * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)} */

Class<? extends Throwable>[] rollbackFor() default {};

/** * Defines zero (0) or more exception names (for exceptions which must be a * subclass of {@link Throwable}), indicating which exception types must cause * a transaction rollback. * <p>This can be a substring, with no wildcard support at present. * A value of "ServletException" would match * {@link javax.servlet.ServletException} and subclasses, for example. * <p><b>NB: </b>Consider carefully how specific the pattern is, and whether * to include package information (which isn't mandatory). For example, * "Exception" will match nearly anything, and will probably hide other rules. * "java.lang.Exception" would be correct if "Exception" was meant to define * a rule for all checked exceptions. With more unusual {@link Exception} * names such as "BaseBusinessException" there is no need to use a FQN. * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)} */

String[] rollbackForClassName() default {};

/** * Defines zero (0) or more exception {@link Class Classes}, which must be a * subclass of {@link Throwable}, indicating which exception types must <b>not</b> * cause a transaction rollback. * <p>This is the preferred way to construct a rollback rule, matching the * exception class and subclasses. * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)} */

Class<? extends Throwable>[] noRollbackFor() default {};

/** * Defines zero (0) or more exception names (for exceptions which must be a * subclass of {@link Throwable}) indicating which exception types must <b>not</b> * cause a transaction rollback. * <p>See the description of {@link #rollbackForClassName()} for more info on how * the specified names are treated. * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)} */

String[] noRollbackForClassName() default {};

}

複製代碼

基於源代碼,咱們能夠發如今@Transactional中,原來有這麼多的屬性能夠進行配置,從而達到複雜應用控制的目的。具體各個屬性的用法和做用,將在下面逐一進行講解和說明。併發

@Transactional註解中經常使用參數說明

參數名稱 | 功能描述app

-------------|---------------ide

readOnly |該屬性用於設置當前事務是否爲只讀事務,設置爲true表示只讀,false則表示可讀寫,默認值爲false。例如:@Transactional(readOnly=true)測試

rollbackFor|該屬性用於設置須要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。例如:指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})ui

rollbackForClassName|該屬性用於設置須要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。例如:指定單一異常類名稱:@Transactional(rollbackForClassName="RuntimeException")指定多個異常類名稱:@Transactional(rollbackForClassName={"RuntimeException","Exception"})this

noRollbackFor|該屬性用於設置不須要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。例如:指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName|該屬性用於設置不須要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。例如:指定單一異常類名稱:@Transactional(noRollbackForClassName="RuntimeException")指定多個異常類名稱:@Transactional(noRollbackForClassName={"RuntimeException","Exception"})

propagation|該屬性用於設置事務的傳播行爲,具體取值可參考表6-7。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

isolation|該屬性用於設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務併發的狀況,一般使用數據庫的默認隔離級別便可,基本不須要進行設置

timeout|該屬性用於設置事務的超時秒數,默認值爲-1表示永不超時

事物傳播行爲介紹:

@Transactional(propagation=Propagation.REQUIRED) :若是有事務, 那麼加入事務, 沒有的話新建一個(默認狀況下)

    @Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不爲這個方法開啓事務

    @Transactional(propagation=Propagation.REQUIRES_NEW) :不論是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務

    @Transactional(propagation=Propagation.MANDATORY) :必須在一個已有的事務中執行,不然拋出異常

    @Transactional(propagation=Propagation.NEVER) :必須在一個沒有的事務中執行,不然拋出異常(與Propagation.MANDATORY相反)

    @Transactional(propagation=Propagation.SUPPORTS) :若是其餘bean調用這個方法,在其餘bean中聲明事務,那就用事務.若是其餘bean沒有聲明事務,那就不用事務.

    @Transactional(propagation=Propagation.NESTED) : 若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則進行與PROPAGATION_REQUIRED相似的操做。

    前六個策略相似於EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。 它要求事務管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務行爲(如Spring的DataSourceTransactionManager)
複製代碼

事物超時設置:

  @Transactional(timeout=30) //默認是30秒

事務隔離級別:

  @Transactional(isolation = Isolation.READ_UNCOMMITTED):讀取未提交數據(會出現髒讀, 不可重複讀) 基本不使用

  @Transactional(isolation = Isolation.READ_COMMITTED):讀取已提交數據(會出現不可重複讀和幻讀)

  @Transactional(isolation = Isolation.REPEATABLE_READ):可重複讀(會出現幻讀)

  @Transactional(isolation = Isolation.SERIALIZABLE):串行化

MYSQL: 默認爲REPEATABLE_READ級別

SQLSERVER: 默認爲READ_COMMITTED

髒讀 : 一個事務讀取到另外一事務未提交的更新數據

不可重複讀 : 在同一事務中, 屢次讀取同一數據返回的結果有所不一樣, 換句話說,

後續讀取能夠讀到另外一事務已提交的更新數據.

可重複讀:在同一事務中屢次

讀取數據時, 可以保證所讀數據同樣, 也就是後續讀取不能讀到另外一事務已提交的更新數據
複製代碼

幻讀 : 一個事務讀到另外一個事務已提交的insert數據

注意的幾點:

一、@Transactional 只能被應用到public方法上, 對於其它非public的方法,若是標記了@Transactional也不會報錯,但方法沒有事務功能.

二、用 spring 事務管理器,由spring來負責數據庫的打開,提交,回滾.默認遇到運行期例外(throw new RuntimeException("註釋");)會回滾,即遇到不受檢查(unchecked)的例外時回滾;而遇到須要捕獲的例外(throw new Exception("註釋");)不會回滾,即遇到受檢查的例外(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢查例外或說受檢查異常)時,需咱們指定方式來讓事務回滾要想全部異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .若是讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)

以下:

1 @Transactional(rollbackFor=Exception.class) //指定回滾,遇到異常Exception時回滾

2 public void methodName() {

3 &emsp;&emsp;&emsp;throw new Exception("註釋");

4 }

5 @Transactional(noRollbackFor=Exception.class)//指定不回滾,遇到運行期例外(throw new RuntimeException("註釋");)會回滾

6 public ItimDaoImpl getItemDaoImpl() {

7 &emsp;&emsp;&emsp;throw new RuntimeException("註釋");

8 }

複製代碼

三、@Transactional 註解應該只被應用到 public 可見度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 可是這個被註解的方法將不會展現已配置的事務設置。

  四、@Transactional 註解能夠被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 註解的出現不足於開啓事務行爲,它僅僅 是一種元數據,可以被能夠識別 @Transactional 註解和上述的配置適當的具備事務行爲的beans所使用。上面的例子中,其實正是 元素的出現 開啓 了事務行爲。

  五、Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 註解,而不要使用在類所要實現的任何接口上。你固然能夠在接口上使用 @Transactional 註解,可是這將只能當你設置了基於接口的代理時它才生效。由於註解是不能繼承的,這就意味着若是你正在使用基於類的代理時,那麼事務的設置將不能被基於類的代理所識別,並且對象也將不會被事務代理所包裝(將被確認爲嚴重的)。所以,請接受Spring團隊的建議而且在具體的類上使用 @Transactional 註解。

補充

例子講解以上七中事務傳播機制

本身寫的一個測試

@RunWith(SpringRunner.class)

@SpringBootTest(classes = Application.class)

public class UserBaseInfoControllerTest {

    @Autowired

    private UserBaseInfoService userBaseInfoService;

    @Test

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = RuntimeException.class)

    public void testOne() {

        UserBaseInfo userBaseInfo = new UserBaseInfo();

        userBaseInfo.setId(3);

        userBaseInfo.setUserName("xu2");

        userBaseInfo.setPassWord("111111");

        userBaseInfoService.update(userBaseInfo);

        userBaseInfo.setUserName("xu1");

        userBaseInfo.setPassWord("1111111");

        userBaseInfoService.save(userBaseInfo);

    }

}

@Service

public class UserBaseInfoServiceImpl extends AbstractService<UserBaseInfo> implements UserBaseInfoService {

    @Resource

    private UserBaseInfoMapper userBaseInfoMapper;

    @Override

    public void save(UserBaseInfo userBaseInfo) {

        mapper.insertSelective(userBaseInfo);

    }

    @Override

    //@Transactional(propagation = Propagation.REQUIRED,rollbackFor = RuntimeException.class)

    //@Transactional(propagation=Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class)

    //@Transactional(propagation=Propagation.MANDATORY,rollbackFor = RuntimeException.class)

    //@Transactional(propagation=Propagation.NESTED,rollbackFor = RuntimeException.class)

    //@Transactional(propagation=Propagation.NEVER,rollbackFor = RuntimeException.class)

    //@Transactional(propagation=Propagation.NOT_SUPPORTED,rollbackFor = RuntimeException.class)

    //@Transactional(propagation=Propagation.SUPPORTS,rollbackFor = RuntimeException.class)

    public void update(UserBaseInfo userBaseInfo) {

        mapper.updateByPrimaryKeySelective(userBaseInfo);

    }
複製代碼

假設有類A的方法methodB(),有類B的方法methodB().

  1. PROPAGATION_REQUIRED

若是B的方法methodB()的事務傳播特性是propagation_required,那麼以下圖

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

A.methodA()調用B的methodB()方法,那麼若是A的方法包含事務,則B的方法則不重新開啓事務,

一、 若是B的methodB()拋出異常,A的methodB()沒有捕獲,則A和B的事務都會回滾;

二、 若是B的methodB()運行期間異常會致使B的methodB()的回滾,A若是捕獲了異常,並正常提交事務,則會發生Transaction rolled back because it has been marked as rollback-only的異常。

三、 若是A的methodA()運行期間異常,則A和B的Method的事務都會被回滾

  1. PROPAGATION_SUPPORTS

若是B的方法methodB()的事務傳播特性是propagation_supports,麼以下圖

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

A.methodA()調用B的methodB()方法,那麼若是A的方法包含事務,則B運行在此事務環境中,若是A的方法不包含事務,則B運行在非事務環境;

一、若是A沒有事務,則A和B的運行出現異常都不會回滾。

二、若是A有事務,A的method方法執行拋出異常,B.methodB和A.methodA都會回滾。

三、若是A有事務,B.method拋出異常,B.methodB和A.methodA都會回滾,若是A捕獲了B.method拋出的異常,則會出現異常Transactionrolled back because it has been marked as rollback-only。

  1. PROPAGATION_MANDATORY

表示當前方法必須在一個事務中運行,若是沒有事務,將拋出異常,以下圖調用關係:

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

B.methodB()事務傳播特性定義爲:PROPAGATION_MANDATORY

一、若是A的methoda()方法沒有事務運行環境,則B的methodB()執行的時候會報以下異常:No existingtransaction found for transaction marked with propagation 'mandatory'

二、若是A的Methoda()方法有事務而且執行過程當中拋出異常,則A.methoda()和B.methodb()執行的操做被回滾;

三、若是A的methoda()方法有事務,則B.methodB()拋出異常時,A的methoda()和B.methodB()都會被回滾;若是A捕獲了B.method拋出的異常,則會出現異常Transaction rolled back because ithas been marked as rollback-only

  1. PROPAGATION_NESTED

若有一下方法調用關係,如圖:

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

B的methodB()定義的事務爲PROPAGATION_NESTED;

一、 若是A的MethodA()不存在事務,則B的methodB()運行在一個新的事務中,B.method()拋出的異常,B.methodB()回滾,但A.methodA()不回滾;若是A.methoda()拋出異常,則A.methodA()和B.methodB()操做不回。

二、 若是A的methodA()存在事務,則A的methoda()拋出異常,則A的methoda()和B的Methodb()都會被回滾;

三、 若是A的MethodA()存在事務,則B的methodB()拋出異常,B.methodB()回滾,若是A不捕獲異常,則A.methodA()和B.methodB()都會回滾,若是A捕獲異常,則B.methodB()回滾,A不回滾;

5)PROPAGATION_NEVER

表示事務傳播特性定義爲PROPAGATION_NEVER的方法不該該運行在一個事務環境中

有以下調用關係:

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

若是B.methodB()的事務傳播特性被定義爲PROPAGATION_NEVER,則若是A.methodA()方法存在事務,則會出現異常Existingtransaction found for transaction marked with propagation 'never'。

6)PROPAGATION_REQUIRES_NEW

表示事務傳播特性定義爲PROPAGATION_REQUIRES_NEW的方法須要運行在一個新的事務中。

若有如下調用關係:B.methodB()事務傳播特性爲PROPAGATION_REQUIRES_NEW.

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

一、 若是A存在事務,A.methodA()拋出異常,A.methodA()的事務被回滾,但B.methodB()事務不受影響;若是B.methodB()拋出異常,A不捕獲的話,A.methodA()和B.methodB()的事務都會被回滾。若是A捕獲的話,A.methodA()的事務不受影響但B.methodB()的事務回滾。

  1. PROPAGATION_NOT_SUPPORTED

表示該方法不該該在一個事務中運行。若是有一個事務正在運行,他將在運行期被掛起,直到這個事務提交或者回滾才恢復執行。

若有一下調用關係圖:

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

若是B.methodB()方法傳播特性被定義爲:PROPAGATION_NOT_SUPPORTED。

一、 若是A.methodA()存在事務,若是B.methodB()拋出異常,A.methodA()不捕獲的話,A.methodA()的事務被回滾,而B.methodB()出現異常前數據庫操做不受影響。若是A.methodA()捕獲的話,則A.methodA()的事務不受影響,B.methodB()異常拋出前的數據操做不受影響。

相關文章
相關標籤/搜索