Spring--事务--传播

传播行为是方法之间调用事务采取的 策略问题,比如执行多个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;
    }
}

在这里插入图片描述
由日志可以看出,调用子方法是,挂起当前事务,创建新的事务,执行完后提交,然后唤醒之前的事务。

猜你喜欢

转载自blog.csdn.net/BtWangZhi/article/details/86659077