Spring事务传播机制Propagation.REQUIRES_NEW详解及测试

REQUIRES_NEW官方文档解释:
Create a new transaction, and suspend the current transaction if one exists.
意思是,创建一个新事务,如果当前存在事务,将这个事务挂起。也就是说如果当前存在事务,那么将当前的事务挂起,并开启一个新事务去执行REQUIRES_NEW标志的方法。
先来总结一下结果:
1.标志REQUIRES_NEW会新开启事务,外层事务不会影响内部事务的提交/回滚
2.标志REQUIRES_NEW的内部事务的异常,会影响外部事务的回滚

标志REQUIRES_NEW会新开启事务,外层事务不会影响内部事务的提交/回滚

如下代码,PersonServiceImpl被标志使用事务,事务传播机制为REQUIRED,insert方法中调用了PersonOtherServiceImpl 类的insert2方法,而此insert2方法被标注REQUIRES_NEW。

@Service
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public class PersonServiceImpl implements PersonService {
    private static final Logger log = LoggerFactory.getLogger(PersonServiceImpl.class);
    @Autowired
    private PersonMapper personMapper ;
    @Autowired
    private PersonOtherService personOtherService ;
    @Override
    public void insert() {
        log.info("start insert @@@@@@@@@@@@@@@@@");
        Person p = new Person() ;
        p.setId(1);
        p.setName("one");
        personOtherService.insert2();
        int i = 1/0 ;
        personMapper.insert(p) ;
        log.info("end insert @@@@@@@@@@@@@@@@@");
    }
}

@Service
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public class PersonOtherServiceImpl implements PersonOtherService {
    private static final Logger log = LoggerFactory.getLogger(PersonOtherServiceImpl.class);
    @Autowired
    private PersonMapper personMapper ;
    @Override
    public void insert2() {
        log.info("start update !!!!!!!!!!!!!!");
        Person person = new Person() ;
        person.setId(2);
        person.setName("two");
        personMapper.insert(person) ;
        log.info("end update !!!!!!!!!!!!!!!");
    }
}

我们表写单元测试进行测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class PropagationApplicationTests {
    @Autowired
    private PersonService personService ;
    @Test
    public void contextLoads() {
        personService.insert();
    }
}

控制台输出如下:

2018-10-30 15:05:33.364 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.liu.transation.service.impl.PersonServiceImpl.insert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
2018-10-30 15:05:33.671  INFO 10984 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2018-10-30 15:05:33.675 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [com.mysql.jdbc.JDBC4Connection@30922f8d] for JDBC transaction
2018-10-30 15:05:33.677 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@30922f8d] to manual commit
2018-10-30 15:05:33.677  INFO 10984 --- [           main] c.l.t.service.impl.PersonServiceImpl     : start insert @@@@@@@@@@@@@@@@@
2018-10-30 15:05:33.677 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Suspending current transaction, creating new transaction with name [com.liu.transation.service.impl.PersonOtherServiceImpl.insert]
2018-10-30 15:05:33.677 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [com.mysql.jdbc.JDBC4Connection@7fef0b40] for JDBC transaction
2018-10-30 15:05:33.677 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@7fef0b40] to manual commit
2018-10-30 15:05:33.677  INFO 10984 --- [           main] c.l.t.s.impl.PersonOtherServiceImpl      : start update !!!!!!!!!!!!!!
2018-10-30 15:05:33.724 DEBUG 10984 --- [           main] c.l.t.m.PersonMapper.insertSelective     : ==>  Preparing: insert into person ( id, name ) values ( ?, ? ) 
2018-10-30 15:05:33.755 DEBUG 10984 --- [           main] c.l.t.m.PersonMapper.insertSelective     : ==> Parameters: 2(Integer), two(String)
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] c.l.t.m.PersonMapper.insertSelective     : <==    Updates: 1
2018-10-30 15:05:33.771  INFO 10984 --- [           main] c.l.t.s.impl.PersonOtherServiceImpl      : end update !!!!!!!!!!!!!!!
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@7fef0b40]
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7fef0b40] after transaction
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Resuming suspended transaction after completion of inner transaction
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@30922f8d]
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@30922f8d] after transaction
2018-10-30 15:05:33.771 DEBUG 10984 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource

java.lang.ArithmeticException: / by zero
//异常信息省略

分析一下输出结果,进入insert方法前开启一个事务,然后初始化数据源并取得一个Connection对象,在设置Connection手动提交事务,准备工作完成。进入insert方法,遇到insert2时,将当前事务挂起并创建一个新的事务,取得Connection,设置Connection手动提交事务,开始执行insert2的方法体,执行之后,提交insert2的事务并释放Connection放回数据库连接池,此方法执行结束。接下来回到insert方法中,唤醒insert的事务,接着执行insert的方法体,发现1/0抛出异常,事务进行回滚,但不会回滚insert2的事务。

标志REQUIRES_NEW的内部事务的异常,会影响外部事务的回滚

如上代码,将i=1/0这行代码剪切到insert2方法中,则insert2会抛出异常,之后insert方法中的事务也会跟着rollback,控制台输入如下:

2018-10-30 15:21:05.964 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.liu.transation.service.impl.PersonServiceImpl.insert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
2018-10-30 15:21:06.261  INFO 10040 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2018-10-30 15:21:06.261 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [com.mysql.jdbc.JDBC4Connection@60bf494c] for JDBC transaction
2018-10-30 15:21:06.261 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@60bf494c] to manual commit
2018-10-30 15:21:06.277  INFO 10040 --- [           main] c.l.t.service.impl.PersonServiceImpl     : start insert @@@@@@@@@@@@@@@@@
2018-10-30 15:21:06.324 DEBUG 10040 --- [           main] c.l.t.m.PersonMapper.insertSelective     : ==>  Preparing: insert into person ( id, name ) values ( ?, ? ) 
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] c.l.t.m.PersonMapper.insertSelective     : ==> Parameters: 1(Integer), one(String)
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] c.l.t.m.PersonMapper.insertSelective     : <==    Updates: 1
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Suspending current transaction, creating new transaction with name [com.liu.transation.service.impl.PersonOtherServiceImpl.insert]
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [com.mysql.jdbc.JDBC4Connection@3c81cd82] for JDBC transaction
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@3c81cd82] to manual commit
2018-10-30 15:21:06.355  INFO 10040 --- [           main] c.l.t.s.impl.PersonOtherServiceImpl      : start update !!!!!!!!!!!!!!
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] c.l.t.m.PersonMapper.insertSelective     : ==>  Preparing: insert into person ( id, name ) values ( ?, ? ) 
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] c.l.t.m.PersonMapper.insertSelective     : ==> Parameters: 2(Integer), two(String)
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] c.l.t.m.PersonMapper.insertSelective     : <==    Updates: 1
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
2018-10-30 15:21:06.355 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@3c81cd82]
2018-10-30 15:21:06.386 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3c81cd82] after transaction
2018-10-30 15:21:06.386 DEBUG 10040 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource
2018-10-30 15:21:06.386 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Resuming suspended transaction after completion of inner transaction
2018-10-30 15:21:06.386 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
2018-10-30 15:21:06.386 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@60bf494c]
2018-10-30 15:21:06.386 DEBUG 10040 --- [           main] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@60bf494c] after transaction
2018-10-30 15:21:06.386 DEBUG 10040 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource

java.lang.ArithmeticException: / by zero
  • 38
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值