Spring @Transactional注解淺談

引言: 在Spring中@Transactional提供一種控制事務管理的快捷手段下隧,但是很多人都只是@Transactional簡單使用容劳,并未深入了解缴饭,其各個配置項的使用方法奥帘,本文將深入講解各個配置項的使用。

@Transactional的定義

Spring中的@Transactional基于動態(tài)代理的機制舅桩,提供了一種透明的事務管理機制酱虎,方便快捷解決在開發(fā)中碰到的問題。在現實中江咳,實際的問題往往比我們預期的要復雜很多逢净,這就要求對@Transactional有深入的了解,以來應對復雜問題歼指。

首先我們來看看@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 {};

}

基于源代碼爹土,我們可以發(fā)現在@Transactional中入愧,原來有這么多的屬性可以進行配置晶乔,從而達到復雜應用控制的目的。具體各個屬性的用法和作用胁后,將在下面逐一進行講解和說明挟阻。

@Transactional注解中常用參數說明

參數名稱 | 功能描述

-------------|---------------

readOnly |該屬性用于設置當前事務是否為只讀事務琼娘,設置為true表示只讀,false則表示可讀寫附鸽,默認值為false脱拼。例如:@Transactional(readOnly=true)

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

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

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|該屬性用于設置底層數據庫的事務隔離級別潮孽,事務隔離級別用于處理多事務并發(fā)的情況揪荣,通常使用數據庫的默認隔離級別即可筷黔,基本不需要進行設置

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

事物傳播行為介紹:

    @Transactional(propagation=Propagation.REQUIRED) :如果有事務, 那么加入事務, 沒有的話新建一個(默認情況下)

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

    @Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事務,都創(chuàng)建一個新的事務,原來的掛起,新的執(zhí)行完畢,繼續(xù)執(zhí)行老的事務

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

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

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

    @Transactional(propagation=Propagation.NESTED) : 如果當前存在事務,則在嵌套事務內執(zhí)行佛舱。如果當前沒有事務椎例,則進行與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

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

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

后續(xù)讀取可以讀到另一事務已提交的更新數據.

可重復讀:在同一事務中多次

讀取數據時, 能夠保證所讀數據一樣, 也就是后續(xù)讀取不能讀到另一事務已提交的更新數據

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

注意的幾點:

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

2肆捕、用 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    throw new Exception("注釋");

4 }

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

6 public ItimDaoImpl getItemDaoImpl() {

7    throw new RuntimeException("注釋");

8 }

3、@Transactional 注解應該只被應用到 public 可見度的方法上席纽。 如果你在 protected捏悬、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯润梯, 但是這個被注解的方法將不會展示已配置的事務設置过牙。

4、@Transactional 注解可以被應用于接口定義和接口方法纺铭、類定義和類的 public 方法上寇钉。然而,請注意僅僅 @Transactional 注解的出現不足于開啟事務行為舶赔,它僅僅 是一種元數據扫倡,能夠被可以識別 @Transactional 注解和上述的配置適當的具有事務行為的beans所使用。上面的例子中顿痪,其實正是 元素的出現 開啟 了事務行為镊辕。

5、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的方法則不從新開啟事務溯泣,

1虐秋、 如果B的methodB()拋出異常,A的methodB()沒有捕獲垃沦,則A和B的事務都會回滾客给;

2、 如果B的methodB()運行期間異常會導致B的methodB()的回滾肢簿,A如果捕獲了異常靶剑,并正常提交事務,則會發(fā)生Transaction rolled back because it has been marked as rollback-only的異常池充。

3桩引、 如果A的methodA()運行期間異常,則A和B的Method的事務都會被回滾

  1. PROPAGATION_SUPPORTS

如果B的方法methodB()的事務傳播特性是propagation_supports,么如下圖

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

A.methodA()調用B的methodB()方法纵菌,那么如果A的方法包含事務阐污,則B運行在此事務環(huán)境中,如果A的方法不包含事務咱圆,則B運行在非事務環(huán)境笛辟;

1、如果A沒有事務序苏,則A和B的運行出現異常都不會回滾手幢。

2、如果A有事務忱详,A的method方法執(zhí)行拋出異常围来,B.methodB和A.methodA都會回滾。

3匈睁、如果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

1、如果A的methoda()方法沒有事務運行環(huán)境再榄,則B的methodB()執(zhí)行的時候會報如下異常:No existingtransaction found for transaction marked with propagation 'mandatory'

2狡刘、如果A的Methoda()方法有事務并且執(zhí)行過程中拋出異常,則A.methoda()和B.methodb()執(zhí)行的操作被回滾困鸥;

3嗅蔬、如果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;

1吴趴、 如果A的MethodA()不存在事務漆诽,則B的methodB()運行在一個新的事務中,B.method()拋出的異常锣枝,B.methodB()回滾,但A.methodA()不回滾厢拭;如果A.methoda()拋出異常,則A.methodA()和B.methodB()操作不回撇叁。

2供鸠、 如果A的methodA()存在事務,則A的methoda()拋出異常陨闹,則A的methoda()和B的Methodb()都會被回滾楞捂;

3、 如果A的MethodA()存在事務趋厉,則B的methodB()拋出異常寨闹,B.methodB()回滾,如果A不捕獲異常君账,則A.methodA()和B.methodB()都會回滾繁堡,如果A捕獲異常,則B.methodB()回滾,A不回滾乡数;

5)PROPAGATION_NEVER

表示事務傳播特性定義為PROPAGATION_NEVER的方法不應該運行在一個事務環(huán)境中

有如下調用關系:

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

1、 如果A存在事務劫侧,A.methodA()拋出異常埋酬,A.methodA()的事務被回滾哨啃,但B.methodB()事務不受影響;如果B.methodB()拋出異常写妥,A不捕獲的話拳球,A.methodA()和B.methodB()的事務都會被回滾。如果A捕獲的話珍特,A.methodA()的事務不受影響但B.methodB()的事務回滾祝峻。

  1. PROPAGATION_NOT_SUPPORTED

表示該方法不應該在一個事務中運行。如果有一個事務正在運行扎筒,他將在運行期被掛起莱找,直到這個事務提交或者回滾才恢復執(zhí)行。

如有一下調用關系圖:

upload_ccf3aea9741cd3ccd53883b11f02eb0f.png

如果B.methodB()方法傳播特性被定義為:PROPAGATION_NOT_SUPPORTED嗜桌。

1奥溺、 如果A.methodA()存在事務,如果B.methodB()拋出異常骨宠,A.methodA()不捕獲的話浮定,A.methodA()的事務被回滾,而B.methodB()出現異常前數據庫操作不受影響层亿。如果A.methodA()捕獲的話桦卒,則A.methodA()的事務不受影響,B.methodB()異常拋出前的數據操作不受影響匿又。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末方灾,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子碌更,更是在濱河造成了極大的恐慌裕偿,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件针贬,死亡現場離奇詭異击费,居然都是意外死亡,警方通過查閱死者的電腦和手機桦他,發(fā)現死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門蔫巩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人快压,你說我怎么就攤上這事圆仔。” “怎么了蔫劣?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵坪郭,是天一觀的道長。 經常有香客問我脉幢,道長歪沃,這世上最難降的妖魔是什么嗦锐? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮沪曙,結果婚禮上奕污,老公的妹妹穿的比我還像新娘。我一直安慰自己液走,他們只是感情好碳默,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缘眶,像睡著了一般嘱根。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巷懈,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天该抒,我揣著相機與錄音,去河邊找鬼砸喻。 笑死柔逼,一個胖子當著我的面吹牛,可吹牛的內容都是我干的割岛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼犯助,長吁一口氣:“原來是場噩夢啊……” “哼癣漆!你這毒婦竟也來了?” 一聲冷哼從身側響起剂买,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惠爽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瞬哼,有當地人在樹林里發(fā)現了一具尸體婚肆,經...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年坐慰,在試婚紗的時候發(fā)現自己被綠了较性。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡结胀,死狀恐怖赞咙,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情糟港,我是刑警寧澤攀操,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站秸抚,受9級特大地震影響速和,放射性物質發(fā)生泄漏歹垫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一颠放、第九天 我趴在偏房一處隱蔽的房頂上張望排惨。 院中可真熱鬧,春花似錦慈迈、人聲如沸若贮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谴麦。三九已至,卻和暖如春伸头,著一層夾襖步出監(jiān)牢的瞬間匾效,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工恤磷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留面哼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓扫步,卻偏偏與公主長得像魔策,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子河胎,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359