Spring事務(wù)傳播屬性@Transactional和AOP的坑

Spring聲明式事務(wù)操作簡單,我們平常開發(fā)過程中,只需要在需要事務(wù)控制的方法上面加上@Transactional注解就可以綁定事務(wù)控制济赎。但是其中的參數(shù)配置今天給大家捋一捋,并且有個(gè)AOP的神坑需要大家注意约巷。

傳播屬性 特點(diǎn)
REQUIRED 默認(rèn)的傳播屬性偎痛,表示如果當(dāng)前環(huán)境存在事務(wù)就保持此事務(wù)執(zhí)行,否則新開一個(gè)事務(wù)并且在新事務(wù)中執(zhí)行
REQUIRES_NEW 表示不管當(dāng)前環(huán)境是否存在事務(wù)独郎,都新建一個(gè)事務(wù)并在新事務(wù)中執(zhí)行踩麦,將原有事務(wù)進(jìn)行掛起
SUPPORTS 表示如果當(dāng)前環(huán)境存在事務(wù),就在此事務(wù)中執(zhí)行氓癌,否則不以事務(wù)方式執(zhí)行
NOT_SUPPORTED 表示此方法不進(jìn)行事務(wù)控制谓谦,如果當(dāng)前環(huán)境存在事務(wù),則掛起
MANDATORY 表示此方法必須在一個(gè)事務(wù)中進(jìn)行贪婉,如果當(dāng)前環(huán)境沒有事務(wù)則拋出異常
NEVER 表示此方法運(yùn)行不能有事務(wù)控制反粥,一旦有事務(wù)傳播至此就拋出異常
NESTED 表示如果事務(wù)存在,則運(yùn)行在一個(gè)嵌套的事務(wù)中疲迂,如果沒有事務(wù)才顿,則按REQUIRED屬性執(zhí)行

常用的屬性一般是REQUIRED和REQUIRES_NEW這兩個(gè)。
下面我們用代碼來驗(yàn)證一下這常見的兩個(gè)屬性:

1.開啟事務(wù)支持 @EnableTransactionManagement

/**
 * @author wangzhi
 */
@SpringBootApplication
@EnableTransactionManagement
public class DemoApplication {
    public static void main(String[] args) {
        new SpringApplication(DemoApplication.class).run(args);
    }
}

2.實(shí)體類和數(shù)據(jù)庫

/**
 * @author wangzhi
 */
@Data
@TableName("course")
public class CourseEntity {
    
    @TableId(type = IdType.AUTO)
    private Integer id;
    
    private String courseName;
    
    private BigDecimal price;
}
在這里插入圖片描述

3.service層業(yè)務(wù)代碼

我們先檢驗(yàn)一下REQUIRED

/**
 * @author wangzhi
 */
@Service
public class TransactionService {

    @Autowired
    private CourseMapper courseMapper;

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void save() {
        CourseEntity course = new CourseEntity();
        course.setCourseName("語文");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        //故意制造異常
        System.out.println(1/0);
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void saveInit() {
        CourseEntity course = new CourseEntity();
        course.setCourseName("數(shù)學(xué)");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        //調(diào)用save方法
        try{
            save();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4.測試一下尤蒿,你們猜數(shù)據(jù)庫有幾條記錄郑气?

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = DemoApplication.class)
class DemoApplicationTests {

    @Autowired
    TransactionService transactionService;

    @Test
    public void transactionTest() throws Exception {
        transactionService.saveInit();
    }

}

5.看結(jié)果

在這里插入圖片描述

4.別急,再看看REQUIRES_NEW

/**
 * @author wangzhi
 */
@Service
public class TransactionService {

    @Autowired
    private CourseMapper courseMapper;

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void save() {
        CourseEntity course = new CourseEntity();
        course.setCourseName("語文");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        //故意制造異常
        System.out.println(1/0);
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void saveInit() {
        CourseEntity course = new CourseEntity();
        course.setCourseName("數(shù)學(xué)");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        //調(diào)用save方法
        try {
            save();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
在這里插入圖片描述

5.之所以出現(xiàn)如此情況腰池,并不是事務(wù)傳播有問題尾组,而是動(dòng)態(tài)代理造成的忙芒。在同一個(gè)類里面互相調(diào)用事務(wù)方法,切面切的是發(fā)起事務(wù)的方法讳侨,而內(nèi)部不管調(diào)用的什么事務(wù)方法都會(huì)默認(rèn)為this當(dāng)前對(duì)象去調(diào)動(dòng)普通方法呵萨,這些事務(wù)注解說白了就是不管用。事務(wù)是根據(jù)動(dòng)態(tài)代理生成的動(dòng)態(tài)對(duì)象去執(zhí)行事務(wù)的控制爷耀,所以在同類方法內(nèi)部調(diào)用其他事務(wù)方法必須要獲取其對(duì)應(yīng)的代理對(duì)象去調(diào)用才生效甘桑,否則必須放在不同的類里面。結(jié)局方法:

6.引入AOP

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

7.@EnableAspectJAutoProxy(exposeProxy = true)

/**
 * @author wangzhi
 */
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.example.mapper")
@EnableAspectJAutoProxy(exposeProxy = true)
public class DemoApplication {
    public static void main(String[] args) {
        new SpringApplication(DemoApplication.class).run(args);
    }
}

8.改造一下service

/**
 * @author wangzhi
 */
@Service
public class TransactionService {

    @Autowired
    private CourseMapper courseMapper;

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void save() {
        CourseEntity course = new CourseEntity();
        course.setCourseName("語文");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        //故意制造異常
        System.out.println(1/0);
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void saveInit() {
        CourseEntity course = new CourseEntity();
        course.setCourseName("數(shù)學(xué)");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        //調(diào)用save方法
        try {
            TransactionService proxy = (TransactionService)AopContext.currentProxy();
            proxy.save();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

看看結(jié)果:


在這里插入圖片描述

這樣的結(jié)果就對(duì)了歹叮!所以我們在開發(fā)過程中遇到A方法調(diào)用B方法跑杭,如果AB方法都在同一個(gè)類里面,想要B方法的事務(wù)生效必須用代理方式執(zhí)行咆耿。當(dāng)然AB方法不在同一個(gè)類里面可以生效德谅,因?yàn)閯?dòng)態(tài)代理是運(yùn)行時(shí)才構(gòu)造的代理對(duì)象。而且萨螺,類似的技術(shù)點(diǎn)不僅僅出現(xiàn)在事務(wù)這里窄做,比如@Async注解同樣還是這樣,同一個(gè)類里面調(diào)用依然不是異步執(zhí)行慰技,當(dāng)涉及到動(dòng)態(tài)代理相關(guān)都要注意此點(diǎn)椭盏。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吻商,隨后出現(xiàn)的幾起案子掏颊,更是在濱河造成了極大的恐慌,老刑警劉巖艾帐,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乌叶,死亡現(xiàn)場離奇詭異,居然都是意外死亡柒爸,警方通過查閱死者的電腦和手機(jī)准浴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捎稚,“玉大人乐横,你說我怎么就攤上這事⊙粼澹” “怎么了晰奖?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腥泥。 經(jīng)常有香客問我匾南,道長,這世上最難降的妖魔是什么蛔外? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任蛆楞,我火速辦了婚禮溯乒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豹爹。我一直安慰自己裆悄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布臂聋。 她就那樣靜靜地躺著光稼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孩等。 梳的紋絲不亂的頭發(fā)上艾君,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音肄方,去河邊找鬼冰垄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛权她,可吹牛的內(nèi)容都是我干的虹茶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼隅要,長吁一口氣:“原來是場噩夢啊……” “哼蝴罪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起步清,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤洲炊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后尼啡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡询微,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年崖瞭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撑毛。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡书聚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出藻雌,到底是詐尸還是另有隱情雌续,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布胯杭,位于F島的核電站驯杜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏做个。R本人自食惡果不足惜鸽心,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一滚局、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧顽频,春花似錦藤肢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蟀淮,卻和暖如春最住,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背灭贷。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工温学, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人甚疟。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓仗岖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親览妖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子轧拄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345