分析和总结spring事务REQUIRES_NEW,REQUIRED的区别
问题描述
我们在开发中经常用到的事务传播属性有REQUIRES_NEW和REQUIRED,但是具体它们怎么使用,有什么区别,不管官方说明还是其他地方的描述总是不能让我们直观明白。下文将通过源码说明和具体实例来总结一下它们的区别和使用场景。
REQUIRES_NEW的使用特性
1.源码说明的理解
事务传播行为的枚举Propagation定义如下:
即创建一个新事务,如果当前事务存在,则挂起当前事务。改如何理解呢,我们用一个例子显示一下。
insertUserAndUserBank作为外层方法,实现两个业务操作,新增用户并新增用户的银行卡信息。int i=1/0模拟了外层出现了异常。
/**
* 新增用户和用户银行卡信息
* 使用注解式事务
*/
@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方法调用。
/**
*增加一条用户银行卡信息
* @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中。
/**
* 新增用户和用户银行卡信息
* 使用注解式事务:默认发生运行时异常或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修饰
/**
*增加一条用户银行卡信息
* @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中。
/**
*增加一条用户银行卡信息
* @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,这样就能保证其他业务有异常时,它也不会被回滚。