分析和总结spring事务REQUIRES_NEW,REQUIRED的区别
- 问题描述
- REQUIRES_NEW的使用特性
- REQUIRED的使用特性
- 问题总结
问题描述
我们在开发中经常用到的事务传播属性有REQUIRES_NEW和REQUIRED,但是具体它们怎么使用,有什么区别,不管官方说明还是其他地方的描述总是不能让我们直观明白。下文将通过源码说明和具体实例来总结一下它们的区别和使用场景。
REQUIRES_NEW的使用特性
1.源码说明的理解
事务传播行为的枚举Propagation定义如下:

即创建一个新事务,如果当前事务存在,则挂起当前事务。改如何理解呢,我们用一个例子显示一下。
insertUserAndUserBank作为外层方法,实现两个业务操作,新增用户并新增用户的银行卡信息。int i=1/0模拟了外层出现了异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * 新增用户和用户银行卡信息 * 使用注解式事务 */ @Transactional(propagation=Propagation.REQUIRED,rollbackForClassName="Exception") public void insertUserAndUserBank() throws SQLException { User user=new User(); user.setCode("user01"); user.setName("testName"); //insertUser方法用Transactional修饰,可以看做它是处于insertUserAndUserBank的事务中的 userDao.insertUser(user); UserBank userBank=new UserBank(); userBank.setBank_code("bank01"); userBank.setUser_id(user.getCode()); userBankDao.insertUserBank(userBank); //模拟出现了异常情况 int i=1/0; } |
使用REQUIRES_NEW修饰的insertUserBank方法,实现新增银行卡操作,它被外层insertUserAndUserBank方法调用。
1 2 3 4 5 6 7 8 9 10 11 12 | /** *增加一条用户银行卡信息 * @throws SQLException */ @Transactional(propagation=Propagation.REQUIRES_NEW,rollbackForClassName="Exception") public void insertUserBank(UserBank userBank) throws SQLException { System.out.println("userBankDao的jdbcTemplate:"+jdbcTemplate); System.out.println("userBankDao的jdbcTemplate的connction为:"+jdbcTemplate.getDataSource().getConnection()); String sql = "insert into user_bank_tb (user_id,bank_code) values (?,?)"; jdbcTemplate.update(sql, new Object[]{userBank.getUser_id(),userBank.getBank_code()}); } |
执行结果我们发现,外层事务出现异常,用户表更新操作被回滚,但REQUIRES_NEW修饰的insertUserBank方法执行成功。


总结,REQUIRES_NEW修饰方法的特性:外面的事务对其不影响,不管外层是否提交回滚,它里面的内容会根据自己的执行情况,该提交就提交,该回滚就回滚。
现在我们模拟REQUIRES_NEW修饰方法出现异常情况,看看它是否对外层造成影响。
这里将int i=1/0 从insertUserAndUserBank方法中移到insertUserBank中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /** * 新增用户和用户银行卡信息 * 使用注解式事务:默认发生运行时异常或Erro异常时才会回滚, */ @Transactional(propagation=Propagation.REQUIRED,rollbackForClassName="Exception") public void insertUserAndUserBank() throws SQLException { User user=new User(); user.setCode("user01"); user.setName("testName"); userDao.insertUser(user); UserBank userBank=new UserBank(); userBank.setBank_code("bank01"); userBank.setUser_id(user.getCode()); userBankDao.insertUserBank(userBank); } /** *增加一条用户银行卡信息 * @throws SQLException */ @Transactional(propagation=Propagation.REQUIRES_NEW,rollbackForClassName="Exception") public void insertUserBank(UserBank userBank) throws SQLException { System.out.println("userBankDao的jdbcTemplate:"+jdbcTemplate); System.out.println("userBankDao的jdbcTemplate的connction为:"+jdbcTemplate.getDataSource().getConnection()); String sql = "insert into user_bank_tb (user_id,bank_code) values (?,?)"; jdbcTemplate.update(sql, new Object[]{userBank.getUser_id(),userBank.getBank_code()}); int i=1/0; } |
执行结果我们发现,用户表和银行卡表都未更新,REQUIRES_NEW修饰的insertUserBank方法执行异常,它也引起外层其他业务的回滚。


总结,REQUIRES_NEW修饰方法的特性:它会对外层的事务有影响,如果它里面执行时出现异常不仅自己的内容回滚,也会导致外层事务也回滚。
REQUIRED的使用特性
1.源码说明的理解
事务传播行为的枚举Propagation定义如下:

即支持当前事务,如果不存在,则创建一个新事务。该如何理解呢,我们在用一个类似的例子演示一下。
insertUserAndUserBank作为外层方法,和上述一样。int i=1/0模拟了外层出现了异常。我们把insertUserBank改为REQUIRED修饰
1 2 3 4 5 6 7 8 9 10 11 12 | /** *增加一条用户银行卡信息 * @throws SQLException */ @Transactional(propagation=Propagation.REQUIRED,rollbackForClassName="Exception") public void insertUserBank(UserBank userBank) throws SQLException { System.out.println("userBankDao的jdbcTemplate:"+jdbcTemplate); System.out.println("userBankDao的jdbcTemplate的connction为:"+jdbcTemplate.getDataSource().getConnection()); String sql = "insert into user_bank_tb (user_id,bank_code) values (?,?)"; jdbcTemplate.update(sql, new Object[]{userBank.getUser_id(),userBank.getBank_code()}); } |
执行结果我们发现,用户表和银行卡表都未更新,外层其他业务执行异常,REQUIRED修饰的insertUserBank方法也被回滚。

总结,REQUIRED修饰方法的特性:外面的事务对其有影响,外层事务出现异常,它里面的内容也会回滚。
现在我们模拟REQUIRED修饰方法出现异常情况,看看它是否对外层造成影响。
这里将int i=1/0 从insertUserAndUserBank方法中移到insertUserBank中。
1 2 3 4 5 6 7 8 9 10 11 12 | /** *增加一条用户银行卡信息 * @throws SQLException */ @Transactional(propagation=Propagation.REQUIRED,rollbackForClassName="Exception") public void insertUserBank(UserBank userBank) throws SQLException { System.out.println("userBankDao的jdbcTemplate:"+jdbcTemplate); System.out.println("userBankDao的jdbcTemplate的connction为:"+jdbcTemplate.getDataSource().getConnection()); String sql = "insert into user_bank_tb (user_id,bank_code) values (?,?)"; jdbcTemplate.update(sql, new Object[]{userBank.getUser_id(),userBank.getBank_code()}); int i=1/0 ; } |
执行结构我们发现,用户表和银行卡表都未更新,REQUIRED修饰方法insertUserBank方法执行异常,它也引起外层其他业务的回滚。

问题总结
REQUIRES_NEW的事务,不受外层调用者影响,但会影响外层的事务。
REQUIRED的事务,即受外层调用者影响,也会影响外层的事务。
实际业务如何使用: 在同一个方法中,因为大多数情况是一系列业务要保证要么都成功要么都失败的,所以各个业务方法使用默认的REQUIRED方式即可。
如果中间有一个特殊的业务方法,和其他业务不关联,我们可以给它的方法设置REQUIRES_NEW,这样就能保证其他业务有异常时,它也不会被回滚。