事务失效原因分析

事务失效原因分析

一、自调用(最常见)

情境①:

methodA(无事务注解)调用methodB(有事务注解/无论事务propagation是如何定义的),会导致methodB的事务失效

代码参考:

@Service
public class ServiceA{
    @Autowired
    public XxMapper xxMapper;
    
	@Override
	public void methodA {
    	// dosomething
    	methodB();
	}
	@Override
	@Transaction
	public void methodB {
		xxMapper.insert();
	}
}

原因分析:

事务分为编程式事务和声明式事务,此例中采用的是声明式事务,即通过注解的形式使用事务。

声明式事务基于Spring AOP技术,Spring AOP的实现是靠(jdk实现接口/cglib继承类)动态代理的方式。

所以如果一个类的methodA调用本类中另一个方法methodB,实际上是这个类对自己的调用,并没有通过Spring容器去请求代理类,因此Spring AOP不起作用

换言之,在目前常规的web开发中,我们在Controller层调用Service层,这个调用不是通过在Controller层中new对象的形式去做的,而是通过Spring容器托管,由容器去帮我们调用。而在Serivce层中,如果methodA调本类中的methodB,默认是使用this.methodB(),这是不经过Spring容器的,所以Spring无法干涉。

情境②:

methodA(有事务注解,且propagation = Propagation.SUPPORTS | Propagation.NOT_SUPPORTS )调用methodB(有事务注解/无论事务propagation是如何定义的),会导致methodB的事务失效

代码参考:

@Service
public class ServiceA{
    @Autowired
    public XxMapper xxMapper;
    
	@Override
    @Transaction(propagation =  Propagation.SUPPORTS)
    // @Transaction(propagation =  Propagation.NOT_SUPPORTS)
	public void methodA {
    	// dosomething
    	methodB();
	}
	@Override
	@Transaction
	public void methodB {
		xxMapper.insert();
	}
}

原因分析:

此处分析情境②事务失效前先补充说明事务的传播特性

分类propagation描述
事务一定存在Propagation.REQUIRED_NEW如果当前存在事务,则加入当前事务,如果当前不存在事务,则新建一个事务。
同上Propagation.REQUIRED如果当前不存在事务,则新建一个事务,如果存在事务,则将当前事务挂起,新建一个事务,两个事务独立。
同上Propagation.NESTED如果当前不存在事务,则新建一个事务,如果存在事务,则将当前事务挂起,新建一个事务,两个事务有依存关系。
报异常Propagation.NEVER如果当前(调用方)存在事务,则抛出异常
同上Propagation.MANDATORY如果当前(调用方)不存在事务,则抛出异常
中庸Propagation.SUPPORT如果当前存在事务,则加入当前事务,如果当前不存在事务,则以非事务的方式执行。
同上Propagation.NOT_SUPPORT如果当前存在事务,则将当前事务挂起,被调用方是非事务方式执行

根据表中的结论,不难分析。不论methodB的事务传播特性如何,它的事务是失效的,相当于没有在方法上加事务注解,所以只要methodA的事务传播特性不是REQUIRED_NEW/REQUIRED/NESTED,它的事务也不会生效。

情境③:

methodA(有事务注解,且propagation = Propagation.MANDATORY)调用methodB(有事务注解/无论事务propagation是如何定义的),会报异常

代码参考:

@Service
public class ServiceA{
    @Autowired
    public XxMapper xxMapper;
    
	@Override
    @Transaction(propagation =  Propagation.MANDATORY)
    // @Transaction(propagation =  Propagation.NOT_SUPPORTS)
	public void methodA {
    	// dosomething
    	methodB();
	}
	@Override
	@Transaction
	public void methodB {
		xxMapper.insert();
	}
}

原因分析:

Propagation.MANDATORY,要求当前方法中要有事务,但是却没有,所以会报

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

二、try{}catch{}异常

@Transaction注解中的属性rollback定义了出现什么异常时,事务回滚。
但是如果没有将异常抛出,做了try/catch处理,则Spring AOP无法感知,此时事务会失效

三、rollback的异常类型与真实异常不匹配

@Transaction注解默认的rollback属性为RuntimeException.class,如果业务流程中定义的异常类型不是RuntimeException或者其子类,则事务也不会生效

四、数据库Engine不支持事务

如果使用mysql,而恰巧又使用MyISAM作为数据库Engine。则无论是编程式事务还是声明式事务都不会生效。因此,一定要确保数据库Engine支持事务。

结语

这四种事务失效的场景是我从入门至今,真实体验过的场景。今天,恰巧听到项目组同事在讨论这个东西,就做一个总结回顾。可能上述有误,或者不够准确,因此我会不断回顾自己输出的内容,并在评论区对相应错误进行指出更正!

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring事务失效的场景主要有以下几种: 1. 事务传播属性设置错误:Spring中的事务传播属性指定了事务方法调用其他事务方法时,事务如何传播。如果事务传播属性设置错误,事务可能会失效。例如,设置了Propagation.REQUIRED_NEW属性的方法在调用其他事务方法时,会将当前事务挂起,新开一个事务,如果调用的方法没有使用事务注解,则新开的事务失效。 2. 异常被捕获并处理:如果在事务方法中捕获了异常并进行了处理,事务可能会因为异常被处理而不回滚。例如,在try-catch块中捕获了异常并使用了Logger输出异常信息,而没有将异常再次抛出,这样事务就不会回滚。 3. 事务方法中使用了ThreadLocal:ThreadLocal是线程局部变量,它可以在当前线程中存储数据,但是在事务方法中使用ThreadLocal可能会导致事务失效。因为ThreadLocal存储的数据只在当前线程中可见,如果在事务方法中使用了ThreadLocal存储了一些数据,但在事务提交时这些数据并没有被清空,那么这些数据将会影响到下一次事务的执行,从而导致事务失效。 4. 数据库引擎不支持事务:有些数据库引擎不支持事务,例如MyISAM引擎,如果在使用这些引擎的表上执行事务操作,事务将会失效。 5. 并发情况下使用乐观锁:在并发情况下,如果事务方法中使用了乐观锁,可能会导致事务失效。因为在事务提交时,如果数据已经被其他事务修改过,那么乐观锁会认为数据没有被修改过,从而导致事务提交失败。 需要注意的是,以上场景只是一些常见的情况,实际上事务失效原因可能是多种多样的,需要具体问题具体分析

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值