spring事务不生效和事务不回滚的原因(源码案例分析)

1.首先需要加事务的方法不能是私有的(如果方法私有,则事务不生效)

spring源码如下

在AbstractFallbackTransactionAttributeSource.computeTransactionAttribute方法的第一行判断,判断事务的方法是否为public

2.其次抛出异常的时候,不要去接受异常,让spring去接受异常处理,否则事务不会回滚

TransactionInterceptor.invoke();

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation){
	// 1. 获取事务属性
	TransactionAttributeSource tas = getTransactionAttributeSource();
	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
	// 2. 获取事务管理器
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
	// 3. 获取需要事务的方法名称:类目.方法名
	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
	// 4. 声明式事务
	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// 5. 获取该方法上事务的信息
		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
		Object retVal = null;
		try {
			// 6. 目标方法执行,它是一个拦截器链
            //如果这里我们的业务代码有catch异常,则这里的外层catch不到异常了
            //这会导致异常接收不到,不进行事务回滚
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			// 7. 事务回滚,该方法中有事务回滚的规则
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			// 8. 清除事务信息
			cleanupTransactionInfo(txInfo);
		}
		// 9. 事务提交
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}
	else {
		// 10. 编程式事务,流程和声明式事务一致
	}
}
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
						"] after exception: " + ex);
			}
			//判断回滚条件RuleBasedTransactionAttribute.rollbackOn(ex)
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					//回滚事务
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					throw ex2;
				}
			}
			else {
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				try {
					//否则提交事务
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					throw ex2;
				}
			}
		}
	}

这里比较重要的是事务回滚判断的条件,RuleBasedTransactionAttribute.rollbackOn(ex)

@Override
	public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}

		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;
		//rollbackRules 回滚的规则
		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				//递归寻找,知道找到父类是Throwable.class或者是规则中的异常类,里面代码大家可以粗略看一下,很简单
				int depth = rule.getDepth(ex);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}

		// User superclass behavior (rollback on unchecked) if no rule matches.
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			return super.rollbackOn(ex);
		}

		return !(winner instanceof NoRollbackRuleAttribute);
	}

其中rollbackRules是一串RollbackRuleAttribute的列表,我们看一下他是怎么创建的

看到这里大家应该明白了,只有我们的异常是RuntimeException或者他的子类时,我们这个事务才会回滚

3.内部调用自己的方法,事务不生效

什么意思呢,就是一个对象的一个方法上加上事务,然后该方法调用该对象的另一个方法,就会发生第二个方法事务不生效问题,我在这里举个例子

@Transactional(rollbackFor = Exception.class)
    public  String testTransactional(Integer id, Integer count) {
        for (int i = 0; i < 2; i++) {
            DemoRedis demoRedis = new DemoRedis();
            demoRedis.setCount(i);
            demoRedisMapper.insert(demoRedis);
        }
        String s = testTransactionalV1(10, 2);
        if(s.equals("下单成功3")){
            System.out.println("V1事务完结");
        }
        return "下单成功3";
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public  String testTransactionalV1(Integer id, Integer count) {
        System.out.println("====================================");
        for (int i = 0; i < 2; i++) {
            DemoRedis demoRedis = new DemoRedis();
            demoRedis.setCount(i);
            demoRedisMapper.insert(demoRedis);
        }
        return "下单成功3";
    }

 然后我们再看一下方法栈

其实也好理解,当我们代理类方法执行完之后,我们执行业务代理,业务代理去调用自己的方法,其实就是调用this.testTransactionalV1()方法,此时的类是未被加强的类,所以也就不存在执行事务的代理,我们将testTransactionalV1执行完,该方法也不会将事务提交,尽管我们这里将当前事务挂起,起一个新的事务,但是却没有执行,失效了;

 

将testTransactional执行完

那该怎么解决呢

第一种,循环依赖

用依赖的类来调用方法,获取代理对象

方法栈信息

V1执行完之后的结果

 

第二种方法

在主应用加上该注解

@EnableAspectJAutoProxy(exposeProxy = true)

然后再业务袋中,去获取当前的代理对象

((DemoRedisServiceImpl) AopContext.currentProxy()).testTransactionalV1(10, 2);

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring 中,当我们使用声明式事务管理时,如果事务回滚生效,可能会出现以下几种情况: 1. 没有正确配置事务管理器 在 Spring 中,事务管理器是用来管理事务的核心组件。如果没有正确配置事务管理器,可能会导致事务回滚生效。通常情况下,我们需要将事务管理器配置到 Spring 的配置文件中,并且为每一个需要事务管理的方法添加 @Transactional 注解。 2. 没有正确使用 @Transactional 注解 在使用 @Transactional 注解时,需要注意一些细节问题,例如:是否正确设置事务传播行为、是否正确设置事务隔离级别、是否正确设置回滚条件等。如果没有正确使用 @Transactional 注解,也可能会导致事务回滚生效。 3. 异常被捕获并处理了 如果我们在代码中捕获了异常,并且在 catch 块中对异常进行了处理,那么事务就无法回滚。这是因为在异常被捕获并处理后,Spring 就认为这个异常已经被处理了,事务也就无法回滚了。 4. 配置文件中开启了事务自动提交 如果在 Spring 的配置文件中开启了事务自动提交,那么事务就无法回滚。在开启事务自动提交的情况下,Spring 会自动提交事务,而不会等待事务结束后再进行提交。因此,如果需要使用事务回滚,需要将事务自动提交关闭。 总之,如果事务回滚生效,我们需要仔细检查代码和配置文件,找出问题所在,并进行修正。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值