spring事务传播行为之使用REQUIRES_NEW不回滚

最近写spring事务时用到REQUIRES_NEW遇到一些不回滚的问题,所以就记录一下。场景1:在一个服务层里面方法1和方法2都加上事务,其中方法二设置上propagation=Propagation.REQUIRES_NEW,方法1调用方法2并且在执行完方法2后抛出一个异常,如下代码1@Service2publicclassBookServiceImplimplementsBookServic...

spring事务传播行为之使用REQUIRES_NEW不回滚

最近写spring事务时用到REQUIRES_NEW遇到一些不回滚的问题,所以就记录一下。

场景1:在一个服务层里面方法1和方法2都加上事务,其中方法二设置上propagation=Propagation.REQUIRES_NEW,方法1调用方法2并且在执行完方法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 stub10//售卖  扣除库存数量11String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)";12//入账的sql  将赚到的钱添加到account表中的balance13String addRmbSql = "UPDATE account SET balance = balance? * (SELECT price FROM book WHERE NAME = ?)";14Object []params = {1,"Spring"};15  16   jdbcTemplate.update(sellSql, params);1718   testUpdate();1920   jdbcTemplate.update(addRmbSql, params);2122throw new RuntimeException("故意的一个异常");23  }24  @Transactional(propagation=Propagation.REQUIRES_NEW)25  public void testUpdate() {26//这个业务没什么意义,只是用来测试REQUIRES_NEW的 当执行后SpringMVC这本书库存-127String sql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)"; 28Object []params = {1,"SpringMVC"};29   jdbcTemplate.update(sql, params);3031  }

三张表分别是对应account表,book表,book_stock表

1 private static  ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring/*.xml");23  @Test4  public void testREQUIRES_NEW() {56BookService bean = ac.getBean(BookService.class);78   bean.update();9  }

结果是无论是方法1还是方法2都回滚了,那么REQUIRES_NEW就不起作用了,为了探索原因我修改了一下代码

在第5行的地方打印出对象的类型是什么

1 @Test2  public void testREQUIRES_NEW() {34BookService bean = ac.getBean(BookService.class);5System.out.println("update的调用者:" bean.getClass());6   bean.update();7  }

在第11行的地方打印对象类型

 1 @Transactional(timeout=4) 2  public void update() { 3// TODO Auto-generated method stub 4//售卖 5String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)"; 6//入账的sql 7String addRmbSql = "UPDATE account SET balance = balance? * (SELECT price FROM book WHERE NAME = ?)"; 8Object []params = {1,"Spring"}; 9  10   jdbcTemplate.update(sellSql, params);11System.out.println("testUpdate的调用者:" this.getClass());12   testUpdate();1314   jdbcTemplate.update(addRmbSql, params);1516throw new RuntimeException("故意的一个异常");17  }

运行结果是

显然调用update的对象是一个代理对象,调用testUpdate的对象不是一个代理对象,这就是为什么添加REQUIRES_NEW不起作用,想要让注解生效就要用代理对象的方法,不能用原生对象的.

解决方法:在配置文件中添加标签<aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>将代理暴露出来,使AopContext.currentProxy()获取当前代理

将代码修改为

  <!-- 开启事务注解 --><tx:annotation-driven transaction-manager="transactionManager"/><!-- 将代理暴露出来 --><aop:aspectj-autoproxyexpose-proxy="true"></aop:aspectj-autoproxy>

  11 12行将this替换为((BookService)AopContext.currentProxy())

 1  @Transactional(timeout=4) 2  public void update() { 3// TODO Auto-generated method stub 4//售卖 5String sellSql = "UPDATE book_stock SET stock = stock - ? WHERE isbn = (SELECT isbn FROM book WHERE NAME = ?)"; 6//入账的sql 7String addRmbSql = "UPDATE account SET balance = balance? * (SELECT price FROM book WHERE NAME = ?)"; 8Object []params = {1,"Spring"}; 9  10   jdbcTemplate.update(sellSql, params);11System.out.println("testUpdate的调用者:" ((BookService)AopContext.currentProxy()).getClass());12   ((BookService)AopContext.currentProxy()).testUpdate();1314   jdbcTemplate.update(addRmbSql, params);1516throw new RuntimeException("故意的一个异常");17  }

运行结果

调用的对象变成代理对象了 那么结果可想而知第一个事务被挂起,第二个事务执行完提交了 然后异常触发,事务一回滚SpringMVC这本书库存-1,其他的不变

我还看到过另一种解决方法

在第7行加一个BookService类型的属性并且给个set方法,目的就是将代理对象传递过来... 看26 27行显然就是用代理对象去调用的方法 所以就解决问题了 不过还是用第一个方案好

https://www.guoxiongfei.cn/cntech/14851.html