Su的技术博客

  • 首页
  • 原创
  • 视频
  • Java
  • MySQL
  • DDD
  • 事故复盘
  • 架构方案
  • AI
  • Other
  • 工具
    • AI工具集
    • 工具清单
    • JSON在线格式化
    • JSON在线比较
    • SQL在线格式化
  • 打赏
  • 关于
路很长,又很短
  1. 首页
  2. Java
  3. 正文
                           

【面试题】Spring事务无法生效的11个场景

2023-07-17 1488点热度 1人点赞 0条评论
Spring事务无法生效的11个场景
Photo by Clément Hélardot on Unsplash
照片由Clément Hélardot在Unsplash上拍摄

1. Manually throw other exceptions
手动抛出其他异常

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private DemoFlowMapper demoFlowMapper;

    @Transactional
    public void addDemo(Demo demo) throws Exception {
        demoMapper.save(demo);
        demoFlowMapper.saveFlow(demo);
        throw new Exception();
    }
}
  • The reason: In the above code example, an Exception is manually thrown, but it will not be rolled back, because Spring only handles RuntimeException and Error by default, and will not roll back for ordinary Exceptions unless the configuration is specified with the rollbackFor attribute.
    原因:在上面的代码示例中,手动抛出了一个Exception,但不会回滚,因为Spring默认只处理 RuntimeException 和Error,不会为普通回滚, Exceptions 除非用属性指定了配置 rollbackFor 。
  • Solution: Add attribute configuration @Transactional(rollbackFor = Exception.class).
    解决方案:添加属性配置 @Transactional(rollbackFor = Exception.class) 。

2. The transaction annotation is overwritten, causing the transaction to fail
事务批注被覆盖,导致事务失败

public interface MyRepository {
    @Transactional
    void save(String data);
}

public class MyRepositoryImpl implements MyRepository {
    @Override
    public void save(String data) {
        // mysql
    }
}

public class MyService {

    @Autowired
    private MyRepository myRepository;

    @Transactional
    public void doSomething(String data) {
        myRepository.save(data);
    }
}

public class MyDemoService extends MyService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void doSomething(String data) {
        super.doSomething(data);
    }
}

The reason: MyDemoService is a subclass of MyService and overrides the doSomething() method. This method uses a different propagation behavior (REQUIRES_NEW) to override the @Transactional annotation of the parent class. In this case, when the doSomething() method of MyDemoService is called, because the annotations in the subclass method override the annotations of the parent class, the Spring framework will not start the transaction in the method of the parent class. Therefore, when the save() method of MyRepository is called, the transaction will not be started nor rolled back. This will cause data inconsistency issues because the database operations performed in MyRepository’s save() method will not be rolled back.
原因是: MyDemoService 是方法的子类, MyService 并覆盖了 doSomething() 方法。此方法使用不同的传播行为( REQUIRES_NEW )来覆盖 @Transactional 父类的注释。在这种情况下,当调用 doSomething() 的方法 MyDemoService 时,由于子类方法中的注释覆盖了父类的注释,Spring框架将不会在父类的方法中启动事务。因此,当调用 save() 的方法 MyRepository 时,事务将不会启动也不会回滚。这将导致数据不一致问题,因为在的方法中执行的数据库操作 MyRepository save() 将不回滚。

3. The transaction method is modified by final and static keywords
事务方法由final和static关键字修改

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private DemoFlowMapper demoFlowMapper;

    @Transactional
    public final void addDemo(Demo demo) throws Exception {
        demoMapper.save(demo);
        demoFlowMapper.saveFlow(buildFlowByDemo(demo));
        throw new Exception();
    }
}
  • The reason: If a method is declared final or static, the method cannot be overridden by subclasses, that is to say, dynamic proxy cannot be performed on the method, which will cause Spring to fail to generate transaction proxy objects to manage transactions.
    原因:如果一个方法被声明为final或者static,那么这个方法就不能被子类覆盖,也就是说不能对这个方法进行动态代理,这会导致Spring无法生成事务代理对象来管理事务。
  • Solution: Do not modify the addDemo transaction method with final or static.
    解决方案:不要 addDemo 用final或static修改事务处理方法。

4. In the same class, the method is called internally
在同一个类中,内部调用该方法

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private DemoFlowMapper demoFlowMapper;

    public void addDemo(TianLuo tianluo){
     this.executeAddDemo(demo);
   }

    @Transactional
    public void executeAddDemo(Demo demo) {
        demoMapper.save(demo);
        demoFlowMapper.saveFlow(buildFlowByDemo(demo));
    }
}
  • The reason: The transaction is implemented through the Spring AOP proxy, and in the same class, when one method calls another method, the calling method directly calls the code of the target method instead of calling through the proxy class. That is to say, in the above code, the target executeAddDemo method is not called through the proxy class, so the transaction does not take effect.
    原因:事务是通过代理实现 Spring AOP 的,而在同一个类中,当一个方法调用另一个方法时,调用方法直接调用目标方法的代码,而不是通过代理类调用。也就是说,在上面的代码中,目标 executeAddDemo 方法不是通过代理类调用的,因此事务不会生效。
  • Solution: You can create one more class so that these two methods are separated and in different classes. as follows:
    解决方案:您可以再创建一个类,以便将这两个方法分开并放在不同的类中。具体如下:
@Service
public class DemoExcuteServiceImpl implements DemoExcuteService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private DemoFlowMapper demoFlowMapper;

    public void addDemo(TianLuo tianluo){
     this.executeAddDemo(demo);
   }

    @Transactional
    public void executeAddDemo(Demo demo) {
        demoMapper.save(demo);
        demoFlowMapper.saveFlow(buildFlowByDemo(demo));
    }
}

@Service
public class DemoAddServiceImpl implements DemoAddService {

    @Autowired
    private DemoExcuteService demoExecuteService;
    
    public void addTianLuo(Demo demo){
     demoExecuteService.executeAddDemo(demo);
   }
}

Of course, sometimes you can also inject yourself into the Service class, or get the proxy object through AopContext.currentProxy().
当然,有时候你也可以将自己注入到类中 Service ,或者让代理对象通过 AopContext.currentProxy() 。

5. The access permission of the method is not public
方法的访问权限不是公共的

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private DemoFlowMapper demoFlowMapper;

    @Transactional
    private void executeAddDemo(Demo demo) {
        demoMapper.save(demo);
        demoFlowMapper.saveFlow(buildFlowByDemo(demo));
    }
}

The reason: The access authority of the spring transaction method addDemo is not public, so the transaction does not take effect, because the Spring transaction is implemented by the AOP mechanism, the essence of the AOP mechanism is a dynamic proxy, and if the transaction method of the proxy is not public, computeTransactionAttribute() will return null, that is, the transaction attribute does not exist at this time. You can look at the source code of AbstractFallbackTransactionAttributeSource:
原因:spring transaction方法的访问权限 addDemo 不是public的,所以transaction不会生效,因为 Spring transaction是由机制实现 AOP 的,机制的本质 AOP 是一个动态代理,如果代理的transaction方法不是public的, computeTransactionAttribute() 会返回null,也就是transaction属性此时不存在。你可以看一下源代码 AbstractFallbackTransactionAttributeSource :

Spring事务无法生效的11个场景

6. The storage engine of the database does not support transactions
数据库的存储引擎不支持事务

The bottom layer of Spring transactions still depends on the transaction support of the database itself. In MySQL, the MyISAM storage engine does not support transactions, but the InnoDB engine supports transactions. Therefore, when designing tables during the development phase, make sure that the storage engine you choose supports transactions.
事务的底层 Spring 仍然依赖于数据库本身的事务支持。在 MySQL 中, MyISAM 存储引擎不支持事务,但 InnoDB 引擎支持事务。因此,在开发阶段设计表时,请确保您选择的存储引擎支持事务。

Spring事务无法生效的11个场景

7. Misconfigured @Transactional annotation
 @Transactional注释配置错误

@Transactional(readOnly = true)
public void updateUser(User user) {
    userDao.updateUser(user);
}
  • The reason: Although the @Transactional annotation is used, the readOnly=true attribute in the annotation indicates that this is a read-only transaction, so an exception will be thrown when the User entity is updated.
    原因:虽然使用了 @Transactional 注释,但是 readOnly=true 注释中的属性表明这是一个只读事务,因此当更新User实体时会抛出异常。
  • Solution: Set the readOnly attribute to false, or remove the readOnly attribute in the @Transactional annotation.
    解决方案:将属性设置 readOnly 为false,或删除注释中的 readOnly 属性 @Transactional 。

8. The transaction timeout setting is too short
事务超时设置太短

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

The reason: In the above example, the timeout attribute is set to 1 second, which means that if the transaction cannot be completed within 1 second, it will be reported that the transaction has timed out.
原因:在上面的例子中,timeout属性被设置为1秒,这意味着如果交易不能在1秒内完成,则会报告交易超时。

9. Using the wrong transaction propagation mechanism
使用错误的事务传播机制

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private DemoFlowMapper demoFlowMapper;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void executeAddDemo(Demo demo) {
        demoMapper.save(demo);
        demoFlowMapper.saveFlow(buildFlowByDemo(demo));
    }
}
  • The reason: the Propagation.NOT_SUPPORTED propagation feature does not support transactions.
    原因是: Propagation.NOT_SUPPORTED 传播特性不支持事务。
  • Solution: Choose the correct transaction propagation mechanism.
    解决方案:选择正确的事务传播机制。

Spring provides 7 transaction propagation mechanisms:
Spring提供了7种事务传播机制:

  • REQUIRED (default): If a transaction currently exists, join the transaction; otherwise, create a new transaction. This propagation level indicates that the method must be executed within a transaction.
    必需(默认):如果当前存在事务,则加入该事务;否则,创建新事务。此传播级别指示该方法必须在事务中执行。
  • SUPPORTS: If a transaction currently exists, join the transaction; otherwise, continue execution in a non-transactional manner.
    SUPPORTS:如果当前存在事务,则加入该事务;否则,以非事务方式继续执行。
  • MANDATORY: If a transaction currently exists, join the transaction; otherwise, throw an exception.
    MANDATORY:如果当前存在事务,则加入该事务;否则抛出异常。
  • REQUIRES_NEW: Creates a new transaction, and suspends the transaction if one exists.
    要求_新:创建一个新事务,如果存在事务,则挂起该事务。
  • NOT_SUPPORTED: The operation is performed in a non-transactional manner, and the transaction is suspended if there is a current transaction.
    不支持:操作以非事务方式执行,并且如果存在当前事务,则事务被挂起。
  • NEVER: The operation is performed in a non-transactional manner, and an exception is thrown if a transaction currently exists.
    NEVER:操作以非事务的方式执行,如果当前存在事务,则抛出异常。
  • NESTED: If a transaction currently exists, it is executed within a nested transaction. If there is no transaction, it is executed at the REQUIRED propagation level. A nested transaction is part of an outer transaction and can be partially committed or rolled back when the outer transaction commits or rolls back.
    NESTED:如果当前存在事务,则在嵌套事务中执行该事务。如果没有事务,则在传播级别执行事务 REQUIRED 。嵌套事务是外部事务的一部分,并且可以在外部事务提交或回滚时部分提交或回滚。

10. The rollbackFor property configuration error
rollbackFor属性配置错误

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private DemoFlowMapper demoFlowMapper;

    @Transactional(rollbackFor = Error.class)
    public void addDemo(Demo demo){
        demoMapper.save(demo);
        demoFlowMapper.saveFlow(demo);
        throw new Exception();
    }
}
  • The reason: The exception specified by the rollbackFor attribute must be Throwable or its subclass. By default, both RuntimeException and Error are automatically rolled back. But because of the above code example, rollbackFor = Error.class is specified, but the exception thrown is Exception, and Exception and Error have no inheritance relationship, so the transaction will not take effect.
    原因:属性指定的异常 rollbackFor 必须是Throwable或其子类。默认情况下,和 RuntimeException Error 都将自动回滚。但由于上面的代码示例中, rollbackFor = Error.class 指定的是,但抛出的异常是 Exception ,和 Exception 和 Error 没有继承关系,所以事务不会生效。
Spring事务无法生效的11个场景

Transactional annotation source code:
事务注释源代码:

Spring事务无法生效的11个场景

Solution: The exception specified by the rollbackFor attribute matches the exception that was thrown.
解决方案:属性指定的异常 rollbackFor 与引发的异常匹配。

11. Your service class is not managed by Spring
服务类不是由Spring管理的

public class DemoServiceImpl implements DemoService {

    @Autowired
    private DemoMapper demoMapper;
    
    @Autowired
    private DemoFlowMapper demoFlowMapper;

    @Transactional
    public void addDemo(Demo demo){
        demoMapper.save(demo);
        demoFlowMapper.saveFlow(demo);
    }
}
  • The reason: In the above example, after the @Service annotation, the spring transaction (@Transactional) does not take effect, because the Spring transaction is implemented by the AOP mechanism, that is to say, when the bean is obtained from the Spring IOC container, Spring will be the target Classes create proxies to support transactions. But after @Service is annotated, your service classes are not managed by Spring, so how to create proxy classes to support transactions?
    原因:在上面的例子中,annotation之后 @Service ,spring transaction( @Transactional )并不生效,因为 Spring transaction是由机制实现 AOP 的,也就是说,当bean从容器中获取 Spring IOC 时,Spring会为目标 Classes 创建代理来支持transaction。但是注释之后 @Service ,你的服务类就不是Spring管理的了,那么如何创建代理类来支持transactions呢?

 

  • Solution: Add @Service annotation.
    解决方案:添加 @Service 注释。

 

Author:omgzui A full stack engineer, with some medicinal knowledge, likes to read and share.

FROM:https://medium.com/javarevisited/

更多文章:

  1. 聊聊spring事务失效的12种场景,太坑了
  2. Spring中@Autowired和@Inject注解的区别?
  3. 事务异常:Transaction rolled back because it has been marked as rollback-only
  4. 生产环境JVM崩溃问题排查解决
  5. 殷浩详解DDD 第四讲:领域层设计规范
  6. 大家一直在谈的领域驱动设计(DDD),我们在互联网业务系统是这么实践的
  7. 接口优化的常见方案实战总结
  8. 一次 Redis 事务使用不当引发的生产事故
  9. 殷浩详解DDD系列 第二讲 - 应用架构
  10. 既生@Resource,何生@Autowired?
标签: 面试题 Java 后端 Spring 事务
最后更新:2023-07-17

秋天0261

关注Java领域,后端开发、Netty、Zookeeper、Kafka、ES、分布式、微服务、架构等。分享技术干货,架构设计,实战经验等。

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

广告
最新 热点 推荐
最新 热点 推荐
干货 | 论Elasticsearch数据建模的重要性 马蜂窝消息总线——面向业务的消息服务设计 基于 MySQL Binlog 实现可配置的异构数据同步 视频笔记:Google发布Agent2Agent协议 视频笔记:什么是微服务,为什么是微服务? 视频笔记:什么是AI 智能体? 视频笔记:什么是Flink? 如何秒级实现接口间“幂等”补偿:一款轻量级仿幂等数据校正处理辅助工具
Elasticsearch 使用误区之六——富文本内容写入前不清洗基于 MySQL Binlog 实现可配置的异构数据同步马蜂窝消息总线——面向业务的消息服务设计干货 | 论Elasticsearch数据建模的重要性你可以不用RxJava,但必须得领悟它的思想!如何秒级实现接口间“幂等”补偿:一款轻量级仿幂等数据校正处理辅助工具视频笔记:什么是Flink?视频笔记:什么是AI 智能体?
JVM GC问题定位排查方法综述 Eureka源码剖析之三:服务拉取 视频笔记:什么是微服务,为什么是微服务? 【进阶玩法】策略+责任链+组合实现合同签章 Spring Boot 与 Quarkus 对比解析:谁才是你的最佳Java框架? Codeium:强大且免费的AI智能编程助手 Spring中@Autowired和@Inject注解的区别? FSM-COLA无状态状态机

CRUD (1) Event Sourcing (1) graphql (1) id (1) NoSQL (1) quarkus (1) rest (1) RocketMQ (2) Spring Boot (1) zk (1) zookeeper (1) 上下文 (1) 事务消息 (1) 二级缓存 (1) 值对象 (1) 关系数据库 (1) 分布式缓存 (1) 原子性 (1) 唯一ID (1) 商品 (1) 多对多 (1) 子域 (1) 字符集 (1) 客户端心跳 (1) 幂等 (2) 干货 (1) 并发 (1) 应用场景 (1) 应用架构图 (1) 康威定律 (2) 异步复制 (1) 微服务架构 (2) 总体方案 (1) 技术方案 (2) 技术架构 (2) 技术架构图 (1) 技能 (1) 持续集成 (1) 支撑域 (1) 故障恢复 (1) 数据架构图 (1) 方案选型 (1) 日记 (1) 服务发现 (1) 服务治理 (1) 服务注册 (2) 机房 (1) 核心域 (1) 泄漏 (1) 洋葱架构 (1) 消息队列 (5) 源码剖析 (1) 灰度发布 (1) 熔断 (1) 生态 (1) 画图工具 (1) 研发团队 (1) 线程 (2) 组织架构 (1) 缓存架构 (1) 编码 (1) 视频 (18) 读写分离 (1) 贵州 (1) 软件设计 (1) 迁移 (1) 通用域 (1) 集群化 (1) 雪花算法 (1) 顺序消息 (1)

推荐链接🔗
  • AI工具集
  • 工具箱🛠️

COPYRIGHT © 2014-2025 verysu.com . ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

粤ICP备15033072号-2

x