传播行为是方法之间调用事务采取的 策略问题,比如执行多个insert操作,传统的是出现异常,全部回滚,但是现在不想全部回滚,正常的数据继续执行insert操作。
public enum Propagation {
/**
*需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务 ,
*去否则新建一个事务运行子方法
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
/**
* 无论当前事务是否存在,都会创建新事务运行方法,
* 这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
/**
* 在当前方法调用子方法时,如果子方法发生异常,
* 只因滚子方法执行过的 SQL ,而不回滚当前方法的事务
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
}
REQUIRED和REQUIRED_NEW的最主要的区别是,REQUIRED_NEW不管当前事务是否存在,都新创建一个事务,而REQUIRED当前事务存在,则沿用当前的事务。
2 传播失效的问题
场景:批量添加用户信息,在一个service中创建两个方法,一个是批量添加batchAddUser(),一个是单个添加addUser(),批量添加定义传播属性值为REQUIRED,即不存在则创建事务,而存在则沿用之前的事务,单个添加用户传播属性为REQUIRES_NEW,即永远创建新的事务,在批量添加用户中通过for循环调用单个添加客户方法。
打开日志:
#日志配置
logging.level.root=DEBUG
logging.level.org.springframework=DEBUG
logging.level.org.org.mybatis=DEBUG
核心方法如下:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void batchAddUser() {
User user=null;
for(int i=0;i<10;i++){
user=new User();
user.setName("传播"+i);
user.setPassword("传播"+i);
addUser(user);
}
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUser(User user){
userDao.insertSelective(user);
}
如图中,没有创建新的事务。
原因: 在自调用的过程中 , 是类自身的调用 ,而不是代理对象去调用, 那么就不会产生 AOP , 这样 Spring
就不能把你的代码织入到约定的流程中 , 于是就产生了现在看到的失败场景。
解决方案:从Spring IOC容器中获取代理对象,去启动AOP,
@Service
public class UserBatchServiceImpl implements UserBatchService,ApplicationContextAware {
@Autowired
private UserDao userDao;
private ApplicationContext applicationContext;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUser(User user){
userDao.insertSelective(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void batchAddUser() {
User user=null;
for(int i=0;i<10;i++){
user=new User();
user.setName("传播"+i);
user.setPassword("传播"+i);
//addUser(user);======================================================
UserBatchService userBatchService = applicationContext.getBean(UserBatchService.class);
userBatchService.addUser(user);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
由日志可以看出,调用子方法是,挂起当前事务,创建新的事务,执行完后提交,然后唤醒之前的事务。