日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

事務(wù)的傳播行為&&嵌套事務(wù)的使用

 liang1234_ 2019-02-21

一、前言:

事務(wù)的傳播行為(propagation)就是為了解決外層方法調(diào)用內(nèi)層事務(wù)方法的各個情況的。

接下來要說的嵌套事務(wù)的使用是基于Spring聲明式事務(wù)管理中的注解@Transactional 方式的。

二、事務(wù)的傳播行為:

  1. @Transactional(propagation=Propagation.REQUIRED) :如果外層調(diào)用方法本身有事務(wù), 那么就加入到該事務(wù)中, 沒有的話新建一個(這是默認(rèn)的設(shè)置項)
  2. @Transactional(propagation=Propagation.NOT_SUPPORTED) :以非事務(wù)方式運行,如果外層調(diào)用方法存在事務(wù),則把當(dāng)這個事務(wù)掛起。
  3. @Transactional(propagation=Propagation.REQUIRES_NEW) :不管外層調(diào)用方法否存在事務(wù),都創(chuàng)建一個自己的事務(wù),外層調(diào)用方法的事務(wù)掛起,自己的執(zhí)行完畢,再執(zhí)行調(diào)用方事務(wù)
  4. @Transactional(propagation=Propagation.MANDATORY) :如果外層調(diào)用方法存在事務(wù),則加入該事務(wù);如果外層調(diào)用方法沒有事務(wù),則拋出異常
  5. @Transactional(propagation=Propagation.NEVER) :以非事務(wù)方式運行,如果外層調(diào)用方法存在事務(wù),則拋出異常。
  6. @Transactional(propagation=Propagation.SUPPORTS) :如果外層調(diào)用方法存在事務(wù),則加入該事務(wù);如果外層調(diào)用方法沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。
  7. @Transactional(propagation=Propagation.NESTED) :如果外層調(diào)用方法存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行;如果外層調(diào)用方法沒有事務(wù),則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED

 

三、關(guān)于事務(wù)傳播行為:

傳播行為就是一個約定:“別的方法調(diào)用自己的時候會以怎樣的方式開啟事務(wù)”。

當(dāng)你給一個方法指定傳播行為的時候這時這個方法本身肯定是支持事務(wù)的方法,然而調(diào)用你的方法卻不一定。

調(diào)用你的方法可能本身是個事務(wù)方法(service事務(wù)方法a調(diào)用service事務(wù)方法b,也可能不是(controller調(diào)用service事務(wù)方法b  /  service非事務(wù)方法a調(diào)用service事務(wù)方法b)

然后就看傳播行為了。

Spring默認(rèn)的是PROPAGATION_REQUIRED

事務(wù)的傳播行為我們一般都是用來解決嵌套事務(wù)的,所以我們一般使用最多的是上面加黑的三種:

四、嵌套事務(wù):

嵌套事務(wù):就是事務(wù)方法A調(diào)用事務(wù)方法B,外層調(diào)用方法內(nèi)層被調(diào)用方法都是事務(wù)方法的情況。

一般我們不關(guān)心外層調(diào)用方法的事務(wù)傳播行為(用默認(rèn)的(不指定就行))。而只關(guān)心內(nèi)層被調(diào)用方法的傳播行為。

我們一般情況下,會有以下三種需求:

  1. 外層調(diào)用方法和內(nèi)層被調(diào)用方法,有異常一起回滾,沒問題一起提交。(共用一個事務(wù))
  2. 內(nèi)層被調(diào)用方法回滾與否,不會影響外層調(diào)用方法。而外層調(diào)用方法出異?;貪L,也不會回滾內(nèi)層被調(diào)用方法(兩個獨立的事務(wù))
  3. 內(nèi)層被調(diào)用方法回滾與否,不會影響外層調(diào)用方法。而外層調(diào)用方法出異?;貪L,也會回滾內(nèi)層被調(diào)用方法(嵌套事務(wù))

這三種情況正好對應(yīng)三種最常用的傳播行為

 

1----->@Transactional(propagation=Propagation.REQUIRED) :

內(nèi)外層方法共用外層方法的事務(wù)

2----->@Transactional(propagation=Propagation.REQUIRES_NEW) :

當(dāng)執(zhí)行內(nèi)層被調(diào)用方法時,外層方法的事務(wù)會掛起。兩個事務(wù)相互獨立,不會相互影響。

3----->@Transactional(propagation=Propagation.NESTED) :

理解Nested的關(guān)鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區(qū)別是,PROPAGATION_REQUIRES_NEW另起一個事務(wù),將會與他的父事務(wù)相互獨立,
而Nested的事務(wù)和他的父事務(wù)是相依的,他的提交是要等和他的父事務(wù)一塊提交的。也就是說,如果父事務(wù)最后回滾,他也要回滾的。

它看起來像這樣

class ServiceA {   
         
 public void methodA() {  
    // 數(shù)據(jù)庫操作等其他代碼      
    try {
            // savepoint(虛擬的)   
            ServiceB.methodB(); // PROPAGATION_NESTED 級別
         } catch (SomeException) {   
             // 執(zhí)行其他業(yè)務(wù), 如ServiceC.methodC();   
         } 
     // 其他操作代碼  
     }   
  
}

也就是說ServiceB.methodB失敗回滾,那么ServiceA.methodA也會回滾到savepoint點上,ServiceA.methodA可以選擇另外一個分支,比如
ServiceC.methodC,繼續(xù)執(zhí)行,來嘗試完成自己的事務(wù)。
 

五、嵌套事務(wù)的使用:

關(guān)于使用我的代碼放到了我的github上了。

1、propagation=Propagation.REQUIRED的情況

內(nèi)層被調(diào)用事務(wù)方法

	@Transactional(propagation=Propagation.REQUIRED)
	public void testRequired(User inner) {
		testDAO.insertUser(inner);
	}

外層調(diào)用方法

	@Override
	//@Transactional(propagation=Propagation.REQUIRED) // 調(diào)用方法可以是事務(wù)方法也可以是非事務(wù)方法 
	public void testRequired(User outer, User inner) {
		testDAO.insertUser(outer);
		try{
			innerBean.testRequired(inner);
		} catch(RuntimeException e){
          log.error("內(nèi)層方法出現(xiàn)異常回滾",e);
		}
	}

拋異常是通過,插入的User對象的UserName重復(fù)控制的,然后觀察數(shù)據(jù)庫就可以看到相應(yīng)的情況結(jié)果。(你可以把代碼下載下來自己跑一下)

測試Main方法如下

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
        OuterBean outerBean = (OuterBean) ctx.getBean("outerBeanImpl");

        /**你能通過控制插入的數(shù)據(jù)的UserName重復(fù)產(chǎn)生異常*/
        User outer = new User();
        outer.setUsername("009");
        outer.setName("zjl");

        User inner = new User();
        inner.setUsername("010");
        inner.setName("zjl");

        /** 選擇代碼進行注釋,完成你想要的測試*/
        outerBean.testRequired(outer, inner);
        // outerBean.testRequiresNew(outer,inner);
        //outerBean.testNested(outer,inner);

    }

這種傳播行為能實現(xiàn):外層調(diào)用方法和內(nèi)層被調(diào)用方法,有異常一起回滾,沒問題一起提交

2、propagation=Propagation.REQUIRES_NEW

內(nèi)層被調(diào)用事務(wù)方法

	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public void testRequiresNew(User inner) {
		testDAO.insertUser(inner);
	}

外層調(diào)用方法

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void testRequiresNew(User outer, User inner) {
		testDAO.insertUser(outer);
		try{
			innerBean.testRequiresNew(inner);
		} catch(RuntimeException e){
			log.error("內(nèi)層方法出現(xiàn)異常回滾",e);
		}
	}

測試方法相同

這種傳播行為能實現(xiàn):內(nèi)層被調(diào)用方法回滾與否,不會影響外層調(diào)用方法。而外層調(diào)用方法出異?;貪L,也不會回滾內(nèi)層被調(diào)用方法

3、propagation=Propagation.NESTED

內(nèi)層被調(diào)用事務(wù)方法

    @Override
	@Transactional(propagation=Propagation.NESTED)
	public void testNested(User inner) {
		testDAO.insertUser(inner);
	}

外層調(diào)用方法

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void testNested(User outer, User inner) {
		testDAO.insertUser(outer);
		try{
			innerBean.testNested(inner);
		} catch(RuntimeException e){
			log.error("內(nèi)層方法出現(xiàn)異?;貪L",e);
		}
	}

測試方法相同

這種傳播行為能實現(xiàn):內(nèi)層被調(diào)用方法回滾與否,不會影響外層調(diào)用方法。而外層調(diào)用方法出異?;貪L,也會回滾內(nèi)層被調(diào)用方法

 

六、使用中的注意事項:

1、外層調(diào)用內(nèi)層方法是兩個事務(wù)的都要try catch 住調(diào)用內(nèi)層方法的代碼塊。共用一個事務(wù)的不要try catch ?。?/strong>要不就出下面2那個異常)。

因為Spring聲明式事務(wù)處理是基于Aop的,默認(rèn)情況下他會在方法拋出運行時異常時,攔截異?;貪L事務(wù),然后會繼續(xù)向上拋出。 所以你要try catch 住要不外層調(diào)用方法會用相應(yīng)異常,那傳播行為就沒有用了。

	// 就類似這種
    @Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void testNested(User outer, User inner) {
		testDAO.insertUser(outer);
		try{
			innerBean.testNested(inner);
		} catch(RuntimeException e){
			log.error("內(nèi)層方法出現(xiàn)異?;貪L",e);
		}
	}

 

2、“Transaction rolled back because it has been marked as rollback-only ”異常的出現(xiàn)

出現(xiàn)場景:這種異常一般是在,嵌套事務(wù)使用中,內(nèi)層事務(wù)使用默認(rèn)的事務(wù)傳播行為(Propagation.REQUIRED),內(nèi)外共用一個事務(wù)時,外層方法把內(nèi)層方法try catch 住了,就會出現(xiàn)。

原因:內(nèi)層方法出異常了,會向上拋異常,SpringAOP攔截到,就會把事務(wù)標(biāo)志為rollback only,就是準(zhǔn)備要回滾。

由于內(nèi)外方法共用一個事務(wù),這時要是外層方法把這個異常捕獲了,外層方法就繼續(xù)提交。但是事務(wù)標(biāo)記已經(jīng)置了,那就會拋這個異常。

 

3、同一的類的事務(wù)方法是無法直接調(diào)用的,如果 ServiceA.methodA調(diào)用 Service.methodB,會使被調(diào)用方法的事務(wù)失效

因為spring的事務(wù)是基于代理類來實現(xiàn)的。在controller里的service其實是代理對象,所以b方法的事務(wù)有效。,而在同一個類中ServiceA.methodA調(diào)用 Service.methodB,你拿到的不是代理后的methodB,所以事務(wù)會失效
解決方法很簡單,在methodA方法類中獲取當(dāng)前對象的代理對象

ServiceA proxy =(ServiceA)AopContext.currentProxy();
proxy.b();

 

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多