真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

spring事務(wù)傳播行為之使用REQUIRES_NEW不回滾

最近寫spring事務(wù)時用到REQUIRES_NEW遇到一些不回滾的問題,所以就記錄一下。

成都創(chuàng)新互聯(lián)專注于資溪企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站建設(shè)。資溪網(wǎng)站建設(shè)公司,為資溪等地區(qū)提供建站服務(wù)。全流程按需定制開發(fā),專業(yè)設(shè)計,全程項目跟蹤,成都創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

場景1:在一個服務(wù)層里面方法1和方法2都加上事務(wù),其中方法二設(shè)置上propagation=Propagation.REQUIRES_NEW,方法1調(diào)用方法2并且在執(zhí)行完方法2后拋出一個異常,如下代碼

?1 @Service

?2 public class BookServiceImpl implements BookService {

?3? ? ?

?4? ? ?@Autowired

?5? ? ?private JdbcTemplate jdbcTemplate;

?6? ? ?

?7? ? ?@Transactional(timeout=4)

?8? ? ?public void update() {

?9? ? ? ? ?// TODO Auto-generated method stub

10? ? ? ? ?//售賣? 扣除庫存數(shù)量

11? ? ? ? ?String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";

12? ? ? ? ?//入賬的sql? 將賺到的錢添加到account表中的balance

13? ? ? ? ?String addRmbSql = "UPDATE account SET balance = balance + ? * (SELECT price FROM book WHERE NAME = ?)";

14? ? ? ? ?Object []params = {1,"Spring"};

15? ? ?

16? ? ? ? ?jdbcTemplate.update(sellSql, params);

17? ? ? ? ?

18? ? ? ? ?testUpdate();

19? ? ? ? ?

20? ? ? ? ?jdbcTemplate.update(addRmbSql, params);

21? ? ? ? ?

22? ? ? ? ?throw new RuntimeException("故意的一個異常");

23? ? ?}

24? ? ?@Transactional(propagation=Propagation.REQUIRES_NEW)

25? ? ?public void testUpdate() {

26? ? ? ? ?//這個業(yè)務(wù)沒什么意義,只是用來測試REQUIRES_NEW的 當(dāng)執(zhí)行后SpringMVC這本書庫存-1

27? ? ? ? ?String sql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";?

28? ? ? ? ?Object []params = {1,"SpringMVC"};

29? ? ? ? ?jdbcTemplate.update(sql, params);

30? ? ? ? ?

31? ? ?}

?

三張表分別是對應(yīng)account表,book表,book_stock表

1 private static? ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring/*.xml");

2? ? ? ? ?

3? ? ?@Test

4? ? ?public void testREQUIRES_NEW() {

5? ? ? ? ?

6? ? ? ? ?BookService bean = ac.getBean(BookService.class);

7? ? ? ? ?

8? ? ? ? ?bean.update();

9? ? ?}

結(jié)果是無論是方法1還是方法2都回滾了,那么REQUIRES_NEW就不起作用了,為了探索原因我修改了一下代碼

在第5行的地方打印出對象的類型是什么

1 @Test

2? ? ?public void testREQUIRES_NEW() {

3? ? ? ? ?

4? ? ? ? ?BookService bean = ac.getBean(BookService.class);

5? ? ? ? ?System.out.println("update的調(diào)用者:"+bean.getClass());

6? ? ? ? ?bean.update();

7? ? ?}

在第11行的地方打印對象類型

?1 @Transactional(timeout=4)

?2? ? ?public void update() {

?3? ? ? ? ?// TODO Auto-generated method stub

?4? ? ? ? ?//售賣

?5? ? ? ? ?String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";

?6? ? ? ? ?//入賬的sql

?7? ? ? ? ?String addRmbSql = "UPDATE account SET balance = balance + ? * (SELECT price FROM book WHERE NAME = ?)";

?8? ? ? ? ?Object []params = {1,"Spring"};

?9? ? ?

10? ? ? ? ?jdbcTemplate.update(sellSql, params);

11? ? ? ? ?System.out.println("testUpdate的調(diào)用者:"+this.getClass());

12? ? ? ? ?testUpdate();

13? ? ? ? ?

14? ? ? ? ?jdbcTemplate.update(addRmbSql, params);

15? ? ? ? ?

16? ? ? ? ?throw new RuntimeException("故意的一個異常");

17? ? ?}

運行結(jié)果是

顯然調(diào)用update的對象是一個代理對象,調(diào)用testUpdate的對象不是一個代理對象,這就是為什么添加REQUIRES_NEW不起作用,想要讓注解生效就要用代理對象的方法,不能用原生對象的.

解決方法:在配置文件中添加標(biāo)簽將代理暴露出來,使AopContext.currentProxy()獲取當(dāng)前代理

將代碼修改為

1? ?

2

3

4

11 12行將this替換為((BookService)AopContext.currentProxy())

?1? ? ?@Transactional(timeout=4)

?2? ? ?public void update() {

?3? ? ? ? ?// TODO Auto-generated method stub

?4? ? ? ? ?//售賣

?5? ? ? ? ?String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";

?6? ? ? ? ?//入賬的sql

?7? ? ? ? ?String addRmbSql = "UPDATE account SET balance = balance + ? * (SELECT price FROM book WHERE NAME = ?)";

?8? ? ? ? ?Object []params = {1,"Spring"};

?9? ? ?

10? ? ? ? ?jdbcTemplate.update(sellSql, params);

11? ? ? ? ?System.out.println("testUpdate的調(diào)用者:"+((BookService)AopContext.currentProxy()).getClass());

12? ? ? ? ?((BookService)AopContext.currentProxy()).testUpdate();

13? ? ? ? ?

14? ? ? ? ?jdbcTemplate.update(addRmbSql, params);

15? ? ? ? ?

16? ? ? ? ?throw new RuntimeException("故意的一個異常");

17? ? ?}

運行結(jié)果

調(diào)用的對象變成代理對象了? 那么結(jié)果可想而知第一個事務(wù)被掛起,第二個事務(wù)執(zhí)行完提交了 然后異常觸發(fā),事務(wù)一回滾? SpringMVC這本書庫存-1,其他的不變

我還看到過另一種解決方法??

在第7行加一個BookService類型的屬性并且給個set方法,目的就是將代理對象傳遞過來...? ? 看26 27行顯然就是用代理對象去調(diào)用的方法? ?所以就解決問題了? ?不過還是用第一個方案好

?1 @Service

?2 public class BookServiceImpl implements BookService {

?3? ? ?

?4? ? ?@Autowired

?5? ? ?private JdbcTemplate jdbcTemplate;

?6? ? ?

?7? ? ?private BookService proxy;

?8? ? ?

?9? ? ?public void setProxy(BookService proxy) {

10? ? ? ? ?this.proxy = proxy;

11? ? ?}

12? ? ?

13? ? ?@Transactional(timeout=4)

14? ? ?public void update() {

15? ? ? ? ?// TODO Auto-generated method stub

16? ? ? ? ?//售賣

17? ? ? ? ?String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";

18? ? ? ? ?//入賬的sql

19? ? ? ? ?String addRmbSql = "UPDATE account SET balance = balance + ? * (SELECT price FROM book WHERE NAME = ?)";

20? ? ? ? ?Object []params = {1,"Spring"};

21? ? ?

22? ? ? ? ?jdbcTemplate.update(sellSql, params);

23? ? ?/*? ? System.out.println("testUpdate的調(diào)用者:"+((BookService)AopContext.currentProxy()).getClass());

24? ? ? ? ?((BookService)AopContext.currentProxy()).testUpdate();*/

25? ? ? ? ?

26? ? ? ? ?System.out.println(proxy.getClass());

27? ? ? ? ?proxy.testUpdate();

28? ? ? ? ?

29? ? ? ? ?jdbcTemplate.update(addRmbSql, params);

30? ? ? ? ?

31? ? ? ? ?throw new RuntimeException("故意的一個異常");

32? ? ?}

OK這個問題解決那就下一個

場景2:在一個服務(wù)層里面方法1和方法2都加上事務(wù),其中方法二設(shè)置上propagation=Propagation.REQUIRES_NEW,方法1調(diào)用方法2并且在執(zhí)行方法2時拋出一個異常? ? ?沒注意看是不是覺得兩個場景是一樣的,因為我是拷貝下來改的...? ?差別就是在哪里拋出異常? 這次是在方法2里面拋出異常, 我將代碼還原至場景1的第一個解決方案,然后在方法2里面拋出異常 代碼如下

?1 @Service

?2 public class BookServiceImpl implements BookService {

?3? ? ?

?4? ? ?@Autowired

?5? ? ?private JdbcTemplate jdbcTemplate;

?6? ? ?

?7? ? ?@Transactional(timeout=4)

?8? ? ?public void update() {

?9? ? ? ? ?// TODO Auto-generated method stub

10? ? ? ? ?//售賣

11? ? ? ? ?String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";

12? ? ? ? ?//入賬的sql

13? ? ? ? ?String addRmbSql = "UPDATE account SET balance = balance + ? * (SELECT price FROM book WHERE NAME = ?)";

14? ? ? ? ?Object []params = {1,"Spring"};

15? ? ?

16? ? ? ? ?jdbcTemplate.update(sellSql, params);

17? ? ? ? ?

18? ? ? ? ?System.out.println("testUpdate的調(diào)用者:"+((BookService)AopContext.currentProxy()).getClass());

19? ? ? ? ?((BookService)AopContext.currentProxy()).testUpdate();

20? ? ? ? ?

21? ? ? ? ?jdbcTemplate.update(addRmbSql, params);

22? ? ? ? ?

23? ? ?}

24? ? ?@Transactional(propagation=Propagation.REQUIRES_NEW)

25? ? ?public void testUpdate() {

26? ? ? ? ?//這個業(yè)務(wù)沒什么意義,只是用來測試REQUIRES_NEW的

27? ? ? ? ?String sql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";?

28? ? ? ? ?Object []params = {1,"SpringMVC"};

29? ? ? ? ?jdbcTemplate.update(sql, params);

30? ? ? ? ?

31? ? ? ? ?throw new RuntimeException("在方法二里面拋出一個異常");

32? ? ?}

預(yù)期結(jié)果是testUpdate這個事務(wù)是要回滾的,update這個方法的事務(wù)正常執(zhí)行,所以數(shù)據(jù)庫的變化是balance字段的錢要+60? Spring這本書的庫存-1,但是結(jié)果是數(shù)據(jù)庫完全沒有變化

分析:在testUpdate方法內(nèi)拋異常被spring aop捕獲,捕獲后異常又被拋出,那么異常拋出后,是不是update方法沒有手動捕獲,而是讓spring aop自動捕獲,所以在update方法內(nèi)也捕獲到了異常,因此都回滾了

這張圖片的代碼是我debug模式下? 在testUpdate方法中執(zhí)行到拋出異常的地方? 再點step over 跳到的地方? ?顯然spring aop捕獲到了異常后,再次拋出,這就是為什么update方法會捕獲到異常

OK問題很簡單? ?解決方案也很簡單? ?只需要手動捕獲該異常,不讓spring aop捕獲就OK了

將update方法改為

?1 @Transactional(timeout=4)

?2? ? ?public void update() {

?3? ? ? ? ?// TODO Auto-generated method stub

?4? ? ? ? ?//售賣

?5? ? ? ? ?String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";

?6? ? ? ? ?//入賬的sql

?7? ? ? ? ?String addRmbSql = "UPDATE account SET balance = balance + ? * (SELECT price FROM book WHERE NAME = ?)";

?8? ? ? ? ?Object []params = {1,"Spring"};

?9? ? ?

10? ? ? ? ?jdbcTemplate.update(sellSql, params);

11? ? ? ? ?

12? ? ? ? ?try {

13? ? ? ? ? ? ?System.out.println("testUpdate的調(diào)用者:"+((BookService)AopContext.currentProxy()).getClass());

14? ? ? ? ? ? ?((BookService)AopContext.currentProxy()).testUpdate();

15? ? ? ? ?} catch (RuntimeException e) {

16? ? ? ? ? ? ?// TODO Auto-generated catch block

17? ? ? ? ? ? ?System.out.println(e.getMessage());

18? ? ? ? ? ? ?e.printStackTrace();

19? ? ? ? ?}

20? ? ? ? ?

21? ? ? ? ?jdbcTemplate.update(addRmbSql, params);

22? ? ? ? ?

23? ? ?}

執(zhí)行結(jié)果? ? update執(zhí)行成功? ?testUpdate回滾

?總結(jié):同一個Service不同事務(wù)的嵌套會出現(xiàn)調(diào)用的對象不是代理對象的問題,如果是多個不同Service的不同事務(wù)嵌套就沒有這個問題。場景2的要記得手動捕獲異常,不然全回滾了.至于為什么調(diào)用testUpdate方法的對象不是代理對象,可能還要看源碼,懂的人可以在評論區(qū)分享一下。


分享名稱:spring事務(wù)傳播行為之使用REQUIRES_NEW不回滾
標(biāo)題網(wǎng)址:http://weahome.cn/article/iphjde.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部