spring事务不⽣效的15种场景

可分以下5大类

1、 spring框架、配置问题

1.1、你的service类没有被Spring管理

//@Service (注释了@Service)
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private ProductSubMapper productSubMapper;
@Transactional
public void addProduct(Product product) {
//保存Product
productMapper.save(product);
//保存ProductSub
productSubMapper.saveProductSub(buildProductSub(product));
}
}

事务不⽣效的原因:

上⾯例⼦中, @Service 注解注释之后, spring 事务( @Transactional )没有⽣ 效。 具体逻辑: Spring 事务是由 AOP 机制实现的,也就是说从 Spring IOC 容器获取 bean 时, Spring 会为⽬标类创建代理,来⽀持事务的。但是 @Service 被注释后, service 类不是 spring 管理的,就⽆法创建代理类来⽀持事务。

 解决⽅案:加上 @Service 注解

1.2、没有启⽤事务管理器

@Configuration
public class AppConfig {
// 没有配置事务管理器
}
@Service
public class MyService {
@Transactional
public void doSomething() {
// ...
}
}

事务不⽣效的原因:

没有在 AppConfig 中配置事务管理器,因此 Spring ⽆法创建事务代理对 象,导致事务不⽣效。即使在 MyService 中添加了 @Transactional 注解,该⽅法也不会 被 Spring 管理的事务代理拦截。  解决⽅案:

为了解决这个问题,应该在 AppConfig 中配置⼀个事务管器。例如:

@Configuration
public class AppConfig {
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
@Service
public class MyService {
@Transactional
public void doSomething() {
// ...
}
}

    springboot ⾥⾯在启动类⾥⾯添加 @EnableTransactionManagement 就可以(默认创建 的都是开启的,所以⼀般不会有问题)

2、spring AOP代理问题

2.1、事务⽅法被final、static关键字修饰

@Service (注释了@Service)
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private ProductSubMapper productSubMapper;
@Transactional
public final void addProduct(Product product) {
//保存Product
productMapper.save(product);
//保存ProductSub
productSubMapper.saveProductSub(buildProductSub(product));
}
}

事务不⽣效的原因:

如果⼀个⽅法被声明为 final 或者 static ,则该⽅法不能被⼦类重写, 也就是说⽆法在该⽅法上进⾏动态代理,这会导致 Spring ⽆法⽣成事务代理对象来管理事务。

解决⽅案: addProduct 事务⽅法不要⽤ final 修饰或者 static 修饰。

2.2、同⼀个类中,⽅法内部调⽤

@Service
public class XServiceImpl implements XService {
@Autowired
private XMapper xMapper;
@Autowired
private XFlowMapper xFlowMapper;
public void addX(X x){
// 调⽤内部的事务⽅法
this.executeAddX(x);
}
@Transactional
public void executeAddX(X x) {
xMapper.save(x);
xFlowMapper.saveFlow(buildFlowByX(x));
}
}

事务不⽣效的原因:

事务是通过 Spring AOP 代理来实现的,⽽在同⼀个类中,⼀个⽅法调⽤另 ⼀个⽅法时, 调⽤⽅法直接调⽤⽬标⽅法的代码,⽽不是通过代理类进⾏调⽤。即以上代码,调⽤ ⽬标 executeAddX ⽅法不是通过代理类进⾏的,因此事务不⽣效。

解决⽅案:

当然,有时候你也可以在该 Service 类中注⼊⾃⼰

AopContext.currentProxy() 获取代理对象

可以新建多⼀个类,让这两个⽅法分开,分别在不同的类中。如下:

@Service
public class XExecuteServiceImpl implements XExecuteService {
@Autowired
private XMapper xMapper;
@Autowired
private XFlowMapper xFlowMapper;
@Transactional
public void executeAddX(X x) {
xMapper.save(x);
xFlowMapper.saveFlow(buildFlowByX(x));
}
}
@Service
public class XAddServiceImpl implements XAddService {
@Autowired
private XExecuteService xExecuteService;
public void addX(User user){
xExecuteService.executeAddXXX(user);
}
}

2.3、⽅法的访问权限不是public

@Service
public class XServiceImpl implements XService {
@Autowired
private XMapper xLuoMapper;
@Autowired
private XFlowMapper xFlowMapper;
@Transactional
private void addX(X x) {
xMapper.save(x);
xFlowMapper.saveFlow(buildFlowByX(x));
}
}

事务不⽣效的原因:

spring 事务⽅法 addX 的访问权限不是 public ,所以事务就 不⽣效啦,因为 Spring 事务是由 AOP 机制实现的, AOP 机制的本质就是动态代理,⽽代理 的事务⽅法不是 public 的话, computeTransactionAttribute() 就会返回null,也就 是这时事务属性不存在了。

可查看AbstractFallbackTransactionAttributeSource下的这个方法computeTransactionAttribute

解决⽅案: 事务⽅法的访问权限修改为 public

3、底层数据库不⽀持

3.1、数据库的存储引擎不⽀持事务

Spring事务的底层,还是依赖于数据库本⾝的事务⽀持。在 MySQL 中, MyISAM 存储引擎是不⽀持 事务的, InnoDB 引擎才⽀持事务。因此开发阶段设计表的时候,确认你的选择的存储引擎是⽀持事 务的

4、@Transactional配置问题

4.1、配置错误的 @Transactional注解

@Transactional(readOnly = true)
public void updateUser(User user) {
userDao.updateUser(user);
}

事务不⽣效的原因:

虽然使⽤了 @Transactional 注解,但是注解中的 readOnly=true 属 性指⽰这是⼀个只读事务,因此在更新 User 实体时会抛出异常。

解决⽅案:

将 readOnly 属性设置为 false ,或者移除了 @Transactional 注解中的 readOnly 属性。

4.2、事务超时时间设置过短

@Transactional(timeout = 1) public void doSomething() { //... }

事务不⽣效的原因:

在上⾯的例⼦中, timeout 属性被设置为 1 秒,这意味着如果事务在 1 秒内⽆法完成,则报事务超时了。

4.3、使⽤了错误的事务传播机制

@Service
public class XServiceImpl {
@Autowired
private XMapper xMapper;
@Autowired
private XFlowMapper xFlowMapper;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void doInsertX(X x) throws Exception {
xMapper.save(x);
xFlowMapper.saveFlow(buildFlowByX(x));
}
}

事务不⽣效的原因: Propagation.NOT_SUPPORTED 传播特性不⽀持事务。

解决⽅案:选择正确的事务传播机制。

Spring 提供了七种事务传播机制。它们分别是:

REQUIRED (默认):如果当前存在⼀个事务,则加⼊该事务;否则,创建⼀个新事务。该传播 级别表⽰⽅法必须在事务中执⾏。

SUPPORTS :如果当前存在⼀个事务,则加⼊该事务;否则,以⾮事务的⽅式继续执⾏

MANDATORY :如果当前存在⼀个事务,则加⼊该事务;否则,抛出异常。

REQUIRES_NEW :创建⼀个新的事务,并且如果存在⼀个事务,则将该事务挂起。

NOT_SUPPORTED :以⾮事务⽅式执⾏操作,如果当前存在⼀个事务,则将该事务挂起。

NEVER :以⾮事务⽅式执⾏操作,如果当前存在⼀个事务,则抛出异常。

NESTED :如果当前存在⼀个事务,则在嵌套事务内执⾏。如果没有事务,则按 REQUIRED 传 播级别执⾏。嵌套事务是外部事务的⼀部分,可以在外部事务提交或回滚时部分提交或回滚。

              a  主事务和嵌套事务属于同⼀个事务

               b  嵌套事务出错回滚不会影响到主事务

               c  主事务回滚会将嵌套事务⼀起回滚了

4.4、rollbackFor属性配置错误

@Service
public class XServiceImpl implements XService {
@Autowired
private XMapper xMapper;
@Autowired
private XFlowMapper xFlowMapper;
@Transactional(rollbackFor = Error.class)
public void addX(X x) {
xMapper.save(x);
xFlowMapper.saveFlow(x);
//模拟异常抛出
throw new Exception();
}
}

事务不⽣效的原因: 其实 rollbackFor 属性指定的异常必须是 Throwable 或者其⼦类。默认 情况下, RuntimeException 和 Error 两种异常都是会⾃动回滚的。但是因为以上的代码例 ⼦,指定了 rollbackFor = Error.class ,但是抛出的异常⼜是 Exception ,⽽ Exception和Error 没有任何什么继承关系,因此事务就不⽣效。

解决⽅案: rollbackFor 属性指定的异常与抛出的异常匹配

5、开发使⽤不当

5.1、事务注解被覆盖导致事务失效

public interface MyRepository {
@Transactional
void save(String data);
}
public class MyRepositoryImpl implements MyRepository {
@Override
public void save(String data) {
// 数据库操作
}
}
public class MyService {
@Autowired
private MyRepository myRepository;
@Transactional
public void doSomething(String data) {
myRepository.save(data);
}
}
public class MyXService extends MyService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomething(String data) {
super.doSomething(data);
}
}

事务失效的原因: MyXervice 是 MyService 的⼦类,并且覆盖了 doSomething() ⽅法。在该 ⽅法中,使⽤了不同的传播⾏为 (REQUIRES_NEW) 来覆盖⽗类的 @Transactional 注 解。  在这种情况下,当调⽤ MyXService 的 doSomething() ⽅法时,由于⼦类⽅法 中的注解覆盖了⽗类的注解, Spring 框架 将不会在⽗类的⽅法中启动事务。 因此,当 MyRepository 的 save() ⽅法被调⽤时,事务将不会被启动,也不会回滚。 ◦即: MyXService 的 doSomething() 开启了⼀个事务A,然后调⽤⽗类的⽅法 MyService 的 doSomething() 没有开启事务,然后 MyService 的 doSomething() 调⽤的 myRepository 的 save() 开启了⼀个新的事务B。 这将导致数据不⼀致的问题, MyXService 的 doSomething() 发⽣了异常, myRepository 的 save() 的事务可能不会回滚(如果 myRepository.save() 执⾏发 ⽣的异常都会回滚)。

解决⽅案: MyXService 的 doSomething() 也使⽤@Transactional

5.2、嵌套事务的坑

@Service
public class XServiceInOutService {
@Autowired
private XFlowService xFlowService;
@Autowired
private XMapper xMapper;
@Transactional
public void addX(X x) throws Exception {
xMapper.save(x);
xFlowService.saveFlow(x);
}
}
@Service
public class XFlowService {
@Autowired
private XFlowMapper xFlowMapper;
@Transactional(propagation = Propagation.NESTED)
public void saveFlow(X x) {
xFlowMapper.save(x);
throw new RuntimeException();
}
}

以上代码使⽤了嵌套事务,如果 saveFlow 出现运⾏时异常,会继续往上抛,到外层 addX 的⽅法,导致 xMapper.save 也会回滚。

如果不想因为被内部嵌套的事务影响,可以⽤ try-catch 包住,如下:

@Transactional
public void addX(X x) throws Exception {
xMapper.save(x);
try {
xFlowService.saveFlow(x);
} catch (Exception e) {
log.error("save flow fail,message:{}",e.getMessage());
}
}

5.3、事务多线程调⽤

@Service
public class XService {
@Autowired
private XMapper xMapper;
@Autowired
private XFlowService xFlowService;
@Transactional
public void addX(X x) {
xMapper.save(x);
new Thread(() -> {
xFlowService.saveFlow(x);
}).start();

}
}
@Service
public class XFlowService {
@Autowired
private XFlowMapper xFlowMapper;
@Transactional
public void save(X x) {
xFlowMapper.saveFlow(x);
}
}

事务不⽣效原因:这是因为 Spring 事务是基于线程绑定的, 每个线程都有⾃⼰的事务上下⽂, ⽽多线程环境下可能会存在多个线程共享同⼀个事务上下⽂的情况,导致事务不⽣效。 Spring 事务管理器通过使⽤线程本地变量( ThreadLocal )来实现线程安全。⼤家有兴趣的话,可以 去看下源码

在Spring事务管理器中,通过 TransactionSynchronizationManager 类来管理事务上下 ⽂。 TransactionSynchronizationManager 内部维护了⼀个 ThreadLocal 对象,⽤来 存储当前线程的事务上下⽂。在事务开始时, TransactionSynchronizationManager 会将 事务上下⽂绑定到当前线程的 ThreadLocal 对象中,当事务结束时, TransactionSynchronizationManager 会将事务上下⽂从 ThreadLocal 对象中移除

使⽤编程式事务:

         使⽤ TransactionTemplate 对象实现编程式事务;

         使⽤更加底层的 TransactionManager 对象实现编程式事务。

5.4、异常被捕获并处理了,没有重新抛出

@Service
public class XServiceImpl implements XService {
@Autowired
private XMapper xMapper;
@Autowired
private XFlowMapper xFlowMapper;
@Transactional
public void addX(X x) {
try {
xMapper.save(x);
xFlowMapper.saveFlow(x);
} catch (Exception e) {
log.error("add error,id:{},e", x.getId(),e
}
}
}

事务不⽣效的原因: 事务中的异常已经被业务代码捕获并处理,⽽没有被正确地传播回事务管理 器,事务将⽆法回滚。我们可以从 spring 源码( TransactionAspectSupport 这个类)中找 到答案:

public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
//这⽅法会省略部分代码,只留关键代码哈
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targ
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionMa
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIObject retVal;
try {
//Spring AOP中MethodInterceptor接⼝的⼀个⽅法,它允许拦截器在执⾏被代理⽅法之前
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
//⽤于在发⽣异常时完成事务(如果Spring catch不到对应的异常的话,就不会进⼊回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//⽤于在⽅法正常返回后提交事务。
commitTransactionAfterReturning(txInfo);
return retVal;
}
}

在 invokeWithinTransaction ⽅法中,当 Spring catch到 Throwable 异常的时候,就会 调⽤ completeTransactionAfterThrowing() ⽅法进⾏事务回滚的逻辑。但是,在 XServiceImpl 类的 spring 事务⽅法 addX中,直接把异常 catch 住了, 并没有重新 throw 出来,因此 Spring ⾃然就 catch 不到异常啦,因此事务回滚的逻辑就不会 执⾏,事务就失效了。

解决⽅案:在 spring 事务⽅法中,当我们使⽤了 try-catch ,如果catch住异常,记录完异 常⽇志什么的,⼀定要重新把异常抛出来.

5.5、⼿动抛了别的异常

@Service
public class XServiceImpl implements XService {
@Autowired
private XMapper xMapper;
@Autowired
private XFlowMapper xFlowMapper;
@Transactional
public void addX(X x) throws Exception {

xMapper.save(x);

xFlowMapper.saveFlow(x);
throw new Exception();
}
}

失效的原因:上⾯的代码例⼦中,⼿动抛了 Exception 异常,但是是不会回滚的,因为Spring 默认只处理 RuntimeException和Error ,对于普通的 Exception 不会回滚,除⾮,⽤ rollbackFor 属性指定配置。

解决⽅案:添加属性配置 @Transactional(rollbackFor = Exception.class) 。

注:事务的回滚仅仅对于unchecked的异常有效。对于checked异常⽆效。也 就是说事务回滚仅仅发⽣在,出现RuntimeException或Error的时候。通俗⼀点就是:代码中出现的 空指针等异常,会被回滚。⽽⽂件读写、⽹络超时问题等,spring就没法回滚了

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
引用\[1\]:Spring事务的原理是通过@Transactional注解来配置事务的传播级别和异常处理方式。其中,@Transactional(propagation = Propagation.NOT_SUPPORTED)表示不支持事务,若存在事务则挂起;@Transactional(propagation = Propagation.NEVER)表示不使用事务,若存在事务则抛出异常。解决方案是使用Spring默认的传播级别(PROPAGATION_REQUIRED),或其他支持事务的传播级别。\[1\] 引用\[2\]:Spring事务默认支持RuntimeException异常,抛出的异常为RuntimeException异常及其子类异常事务均可生。但若手动抛出Exception异常,而Exception是RuntimeException的父类,会导致事务不生。解决方案有两:一是指定Spring事务异常捕获类型@Transactional(rollbackFor = Exception.class),二是抛出Spring事务支持的异常类型throw new RuntimeException("手动抛出运行时异常")。\[2\] 引用\[3\]:Spring事务在后端开发中是非常常见的,它是保证数据一致性的基本保证。在Spring中,我们可以通过对方法进行事务的配置来实现事务操作,而不需要手动编写事务代码,这大大降低了开发的难度。然而,有时候我们会遇到事务不生的问题。这篇文章针对这个问题进行了具体的分析和举例,总结了15场景Spring事务不生的原因和解决方案。\[3\] 问题: spring事务不生15场景有哪些? 回答: Spring事务不生15场景包括但不限于: 1. 使用了@Transactional注解但未开启事务; 2. 使用了try-catch块捕获了异常; 3. 方法没有被Spring容器管理; 4. 事务方法被同一个类中的其他非事务方法调用; 5. 事务方法被同一个类中的其他事务方法调用,但没有使用代理对象调用; 6. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务传播级别; 7. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务隔离级别; 8. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务超时时间; 9. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务只读属性; 10. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务回滚规则; 11. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务传播行为; 12. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务传播行为和事务隔离级别; 13. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务传播行为和事务超时时间; 14. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务传播行为和事务只读属性; 15. 事务方法被同一个类中的其他事务方法调用,但使用了不同的事务传播行为和事务回滚规则。\[3\] #### 引用[.reference_title] - *1* *2* [Spring事务不生原因及解决方案](https://blog.csdn.net/secretdaixin/article/details/127805326)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Spring事务不生的原因](https://blog.csdn.net/t194978/article/details/123942493)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值