@Transactional注解的原理

轉(zhuǎn)自 http://www.reibang.com/p/24ed55ad4531
Spring 事務(wù)注解 @Transactional 本來可以保證原子性句喜,如果事務(wù)內(nèi)有報(bào)錯(cuò)的話柒昏,整個(gè)事務(wù)可以保證回滾醇滥,但是加上try catch或者事務(wù)嵌套佩谣,可能會(huì)導(dǎo)致事務(wù)回滾失敗凡怎。測試一波。

準(zhǔn)備
建兩張表粘招,模擬兩個(gè)數(shù)據(jù)操作

CREATE TABLE user (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(20) DEFAULT NULL,
age smallint(3) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE role (
id int(11) NOT NULL AUTO_INCREMENT,
role_name varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
測試
根據(jù)排列組合原理啥寇,我們進(jìn)行四種測試:1、無try catch洒扎、無嵌套辑甜;2、有try catch袍冷、無嵌套磷醋;3、無try catch胡诗、有嵌套邓线;4、都有煌恢。

最簡單測試
如果我們單純@Transactional骇陈,事務(wù)可以正常回滾嗎瑰抵?

@GetMapping("/saveNormal0")
@Transactional
public void saveNormal0() throws Exception {
    int age = random.nextInt(100);
    User user = new User().setAge(age).setName("name:"+age);
    userService.save(user);
    throw new RuntimeException();
}

如果事務(wù)內(nèi)報(bào)了RuntimeException錯(cuò)誤你雌,事務(wù)可以回滾。

@GetMapping("/saveNormal0")
@Transactional
public void saveNormal0() throws Exception {
    int age = random.nextInt(100);
    User user = new User().setAge(age).setName("name:"+age);
    userService.save(user);
    throw new Exception();
}

如果事務(wù)內(nèi)報(bào)了Exception錯(cuò)誤(非RuntimeException錯(cuò)誤)二汛,事務(wù)不可以回滾婿崭。

@GetMapping("/saveNormal0")
@Transactional( rollbackFor = Exception.class)
public void saveNormal0() throws Exception {
    int age = random.nextInt(100);
    User user = new User().setAge(age).setName("name:"+age);
    userService.save(user);
    throw new Exception();
}

如果是Exception錯(cuò)誤(非RuntimeException),加上 rollbackFor = Exception.class 參數(shù)也可以實(shí)現(xiàn)回滾肴颊。

結(jié)論一:對于@Transactional可以保證RuntimeException錯(cuò)誤的回滾氓栈,如果想保證非RuntimeException錯(cuò)誤的回滾,需要加上rollbackFor = Exception.class 參數(shù)婿着。

try catch 影響
經(jīng)過博主多種情況測試授瘦,發(fā)現(xiàn)try catch對回滾這個(gè)事本身沒有什么影響,結(jié)論一照樣成立竟宋。try catch只是對異常是否可以被@Transactional 感知 到有影響奥务。如果錯(cuò)誤拋到切面可以感知到的地步,那就可以起作用袜硫。

@GetMapping("/saveTryCatch")
@Transactional( rollbackFor = Exception.class)
public void saveTryCatch() throws Exception{
    try{
        int age = random.nextInt(100);
        User user = new User().setAge(age).setName("name:"+age);
        userService.save(user);
        throw new Exception();
    }catch (Exception e){
        throw e;
    }
}

比如上面一段代碼就回滾了氯葬。

@GetMapping("/saveTryCatch")
@Transactional( rollbackFor = Exception.class)
public void saveTryCatch() throws Exception{
    try{
        int age = random.nextInt(100);
        User user = new User().setAge(age).setName("name:"+age);
        userService.save(user);
        throw new Exception();
    }catch (Exception e){
    }
}

然而,將catch中的錯(cuò)誤不繼續(xù)網(wǎng)上拋婉陷,切面無法感知到錯(cuò)誤帚称,無法進(jìn)行處理,那么事務(wù)就無法回滾了秽澳。

結(jié)論二:try catch只是對異常是否可以被@Transactional 感知 到有影響闯睹。如果錯(cuò)誤拋到切面可以感知到的地步,那就可以起作用担神。

事務(wù)嵌套 影響
首先經(jīng)過實(shí)驗(yàn)楼吃,結(jié)論一仍然成立,即,當(dāng)不加上rollbackFor = Exception.class 的時(shí)候孩锡,無論內(nèi)外報(bào)RuntimeException酷宵,都會(huì)回滾;無論內(nèi)外報(bào) 非RuntimeException 錯(cuò)誤躬窜,都不會(huì)回滾浇垦。如果加上rollbackFor = Exception.class,無論內(nèi)外怎么報(bào)錯(cuò)荣挨,都會(huì)回滾男韧。這些代碼就不給出了。接下來默垄,試下下面兩種情況:

@GetMapping("/out")
@Transactional( rollbackFor = Exception.class)
public void out() throws Exception{
    innerService.inner();
    int age = random.nextInt(100);
    User user = new User().setAge(age).setName("name:" + age);
    userService.save(user);
    throw new Exception();
}
@Transactional
public void inner() throws Exception{
    Role role = new Role();
    role.setRoleName("roleName:"+new Random().nextInt(100));
    roleService.save(role);

// throw new Exception();
}
情況一此虑,外面事務(wù)加上rollbackFor = Exception.class,里面事務(wù)不加口锭,測試內(nèi)外分別報(bào)錯(cuò)的情況(為了簡化代碼量朦前,只給出了外面報(bào)錯(cuò)的代碼),都可以回滾讹弯。因?yàn)榭黾龋瑹o論如何,錯(cuò)誤都拋給了外面那個(gè)事務(wù)進(jìn)行處理组民,而外面那個(gè)加上了rollbackFor = Exception.class棒仍,具備處理非RuntimeException錯(cuò)誤的能力,所以都可以讓事務(wù)進(jìn)行正吵羰ぃ回滾莫其。

下面看情況二,里面的事務(wù)加上rollbackFor = Exception.class耸三,外面不加乱陡,外面報(bào)錯(cuò)。

@GetMapping("/out")
@Transactional
public void out() throws Exception{
    innerService.inner();
    int age = random.nextInt(100);
    User user = new User().setAge(age).setName("name:" + age);
    userService.save(user);
    throw new Exception();
}

@Transactional( rollbackFor = Exception.class)
public void inner() throws Exception{
    Role role = new Role();
    role.setRoleName("roleName:"+new Random().nextInt(100));
    roleService.save(role);
}

事務(wù)都無法回滾仪壮,這是我們有個(gè)疑問憨颠,里面的事務(wù)明明有很強(qiáng)的處理能力啊,為什么和外面一起回滾失敗呢积锅,別著急爽彤,等等聊這個(gè)。

然后試下里面報(bào)錯(cuò):

@GetMapping("/out")
@Transactional
public void out() throws Exception{
    innerService.inner();
    int age = random.nextInt(100);
    User user = new User().setAge(age).setName("name:" + age);
    userService.save(user);
}
 @Transactional( rollbackFor = Exception.class)
public void inner() throws Exception{
    Role role = new Role();
    role.setRoleName("roleName:"+new Random().nextInt(100));
    roleService.save(role);
    throw new Exception();
}

咦缚陷,這回都進(jìn)行了正常的回滾适篙。我的天,這回外面沒有處理能力箫爷,為什么接受里面拋出來的錯(cuò)誤嚷节,也進(jìn)行了回滾D羧濉!硫痰!看上去衩婚,就好像里外事務(wù)總是同生共死的對不對?原來碍论,@Transactional還有個(gè)參數(shù)谅猾,看下源碼柄慰,這個(gè)注解還有默認(rèn)值:

Propagation propagation() default Propagation.REQUIRED;
REQUIRED的意思是說鳍悠,事務(wù)嵌套的時(shí)候,如果發(fā)現(xiàn)已經(jīng)有事務(wù)存在了坐搔,就加入這個(gè)事務(wù)藏研,而不是新建一個(gè)事務(wù),所以根本就不存在兩個(gè)事務(wù)概行,一直只有一個(gè)蠢挡!至于,此參數(shù)其他值凳忙,本文不進(jìn)行測試业踏。回到上面的問題涧卵,當(dāng)外面報(bào)錯(cuò)的時(shí)候勤家,此時(shí)查看事務(wù),沒有增加rollbackFor = Exception.class參數(shù)柳恐,即沒有處理非RuntimeException能力伐脖,所以代碼走完,貌似“兩個(gè)事務(wù)”乐设,都回滾失敗了讼庇。當(dāng)里面報(bào)錯(cuò)的時(shí)候,事務(wù)已經(jīng)添加上了處理非RuntimeException能力近尚,所以蠕啄,代碼走完就回滾成功了。

結(jié)論三:由于REQUIRED屬性戈锻,“兩個(gè)事務(wù)”其實(shí)是一個(gè)事務(wù)歼跟,處理能力看報(bào)錯(cuò)時(shí)刻,是否添加了處理非RuntimeException的能力舶沛。

try catch和事務(wù)嵌套 共同影響
在結(jié)論一二三成立的條件下嘹承,探索共同影響的問題就簡單多了,由于情況太多如庭,就不進(jìn)行過多的代碼展示了叹卷。

結(jié)論
結(jié)論一:對于@Transactional可以保證RuntimeException錯(cuò)誤的回滾撼港,如果想保證非RuntimeException錯(cuò)誤的回滾,需要加上rollbackFor = Exception.class 參數(shù)骤竹。
結(jié)論二:try catch只是對異常是否可以被@Transactional 感知 到有影響帝牡。如果錯(cuò)誤拋到切面可以感知到的地步,那就可以起作用蒙揣。
結(jié)論三:由于REQUIRED屬性靶溜,“兩個(gè)事務(wù)”其實(shí)是一個(gè)事務(wù),處理能力看報(bào)錯(cuò)時(shí)刻懒震,是否添加了處理非RuntimeException的能力罩息。

作者:啤酒就辣條
鏈接:http://www.reibang.com/p/24ed55ad4531
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)个扰,非商業(yè)轉(zhuǎn)載請注明出處瓷炮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市递宅,隨后出現(xiàn)的幾起案子娘香,更是在濱河造成了極大的恐慌,老刑警劉巖办龄,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烘绽,死亡現(xiàn)場離奇詭異,居然都是意外死亡俐填,警方通過查閱死者的電腦和手機(jī)安接,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玷禽,“玉大人赫段,你說我怎么就攤上這事∈噶蓿” “怎么了糯笙?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長撩银。 經(jīng)常有香客問我给涕,道長,這世上最難降的妖魔是什么额获? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任够庙,我火速辦了婚禮,結(jié)果婚禮上抄邀,老公的妹妹穿的比我還像新娘耘眨。我一直安慰自己,他們只是感情好境肾,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布剔难。 她就那樣靜靜地躺著胆屿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪偶宫。 梳的紋絲不亂的頭發(fā)上非迹,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音纯趋,去河邊找鬼憎兽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吵冒,可吹牛的內(nèi)容都是我干的纯命。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼桦锄,長吁一口氣:“原來是場噩夢啊……” “哼扎附!你這毒婦竟也來了蔫耽?” 一聲冷哼從身側(cè)響起结耀,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匙铡,沒想到半個(gè)月后图甜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鳖眼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年黑毅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钦讳。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡矿瘦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出愿卒,到底是詐尸還是另有隱情缚去,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布琼开,位于F島的核電站易结,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏柜候。R本人自食惡果不足惜搞动,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望渣刷。 院中可真熱鬧鹦肿,春花似錦、人聲如沸辅柴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碾篡,卻和暖如春虱而,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背开泽。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工牡拇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人穆律。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓惠呼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親峦耘。 傳聞我的和親對象是個(gè)殘疾皇子剔蹋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360