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当前事务存在,则沿用当前的事务。
REQUIRE_NEW和NEST的区别是NEST会沿用当前事务的隔离级别,而REQUIRE_NEW则相当于是一个新的事务空间。

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;
    }
}

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

记录出现的一个异常

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)

在父方法中调用子方法,子方法出现异常,在添加注解和不添加注解以及添加propagation为Propagation.REQUIRES_NEW的情况下是否出现如上异常吗,测试代码如下。

@Transactional
    @Override
    public void testException() {
        try {
            userService.innorExcep(1);
        } catch (Exception e) {
            logger.error("执行异常", e);
        }
        User user = new User();
        user.setUserName("u1");
        user.setPassword("p1");
        userMapper.insertSelective(user);
    }

    @Override
    //@Transactional(propagation = Propagation.REQUIRES_NEW)
    //不会出现

    @Transactional
    //会出现

    //不加注解
    //不会出现

    //@Transactional(propagation = Propagation.NESTED)
    //不会出现
    public void innorExcep(int u) {
        User user = new User();
        user.setUserName("u2");
        user.setPassword("p2");
        userMapper.insertSelective(user);
        if (u == 1) {
            throw new IllegalArgumentException("参数错误");
        }
    }

添加@Transactional注解后出现异常,发生异常后,事务中设置了回滚标志位,虽然都被外层给破获了,但是事务在提交时发现了标志位,会回滚。
@Transactional(propagation = Propagation.NESTED)和@Transactional(propagation = Propagation.REQUIRES_NEW)是因为是一个单独的事务,不会干涉外层的事务。
innorExcep方法不加注解,那么就不会产生事务,那么就不会设置回滚标志位。
如果testException方法不加注解,不会开启事务,testException中就不会有提交事务的检查 ,try catch后执行sql直接提交。

如下代码,设置了回滚标志位,finally中的更新代码不会生效。

    @Override
    @Transactional
    public void generateQualityTasks() {
        User user=new User();
        user.setId(2);
        try {
            userService.error();
            user.setPassword("没有问题");
        }catch (Exception e){
            logger.error("出现异常", e);
            user.setPassword("出现异常");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }finally {
            //--------------修改数据失效--------------
            userMapper.updateByPrimaryKeySelective(user);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void error(){
        System.out.println(1/0);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值