Spring事务实现及失效场景

Spring事务基于数据库事务和AOP实现,通过@Transactional注解进行管理。当方法带有此注解,Spring会创建代理对象,改变数据库连接的autocommit属性。执行方法后,根据异常情况决定提交或回滚事务。Spring事务隔离级别对应数据库,传播机制基于数据库连接。事务失效可能由于方法访问权限、非Spring管理bean、内部方法调用、捕获特定异常、NOT_SUPPORTED传播行为或使用不支持事务的数据库引擎。

Spring中的事务是如何实现的?

1、Spring事务底层是基于数据库事务和AOP机制的

2、首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean,当调用代理对象方法时,会先判断该方法上是否加了@Transactional注解,如果加了,那么利用事务管理器创建一个数据库连接并且修改数据库连接的autocommit属性为false,禁止此连接的自动提交。

3、然后执行当前方法,执行完成后,如果没有出现异常就直接提交事务,如果出现异常,且这个异常是需要回滚的就会回滚事务,否则任然提交事务。

4、Spring事务的隔离级别对应的就是数据库的隔离级别

5、Spring事务的传播机制是Spring事务自己实现的,也是Spring事务中最复杂的,Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上就是先建立一个数据库连接,在此新数据库连接上执行sql.

Spring中什么时候@Transactional会失效?

        因为Spring事务是基于代理来实现的,所以某个加了@Transactional的方法只有被代理对象调用时,那么这个注解才会生效。

        如果某个方法是private修饰的,那么@Transactional会失效,因为底层cglib是基于父子类来实现的,子类不能重载父类的private方法。

        1、注解@Transactional配置的方法非public修饰

        2、注解@Transactional所在类非Spring容器管理的bean

        3、注解@Transactional配置的方法被类内部方法调用

        4、业务代码抛出异常类型非RuntimeException,事务失效

        5、异常被try-catch捕获

        6、注解@Transactional中Propagation属性设置为NOT_SUPPORTED无事务

        7、mysql关系型数据库使用的MyISAM存储引擎,不支持事务

### Spring 框架事务管理失效的常见场景及其解决方案 #### 场景一:未启用事务管理 当应用程序配置中缺少 `@EnableTransactionManagement` 注解或 XML 配置中的 `<tx:annotation-driven/>` 时,即使方法上标注了 `@Transactional`,也不会有任何效果。 解决办法是在启动类或配置文件中加入必要的声明来开启基于注解的事务支持[^2]。 ```java @Configuration @EnableTransactionManagement public class AppConfig { // Configuration code here... } ``` #### 场景二:异常被捕获并吞掉 如果在一个带有 `@Transactional` 的方法内部捕获到了异常却没有抛出,那么这个异常就不会触发回滚操作。这通常发生在开发者为了防止程序崩溃而简单地忽略了错误的情况之下。 应当让受控业务逻辑能够感知到发生的异常以便决定是否继续执行流程;对于不可恢复性的严重问题则应允许其传播给调用者从而实现自动回滚机制[^3]。 #### 场景三:使用异步方式调用服务层接口 由于默认情况下每个线程都有独立的作用域,在多线程环境下可能会遇到无法识别当前存在的事务上下文的问题。因此直接通过代理对象同步访问其他组件的方法可以正常工作,但是利用诸如 `CompletableFuture.runAsync()` 或者 `@Async` 这样的特性就会破坏原有链路内的事务状态传递关系。 建议采用编程式的 TransactionTemplate 来手动控制子任务所属父级事物边界范围,或者调整为同一线程内顺序化处理模式以维持一致的行为表现形式。 #### 场景四:违反只读属性设置原则 某些持久化引擎(比如 Hibernate)会因为开启了 flush() 自动刷新策略而在查询期间意外修改实体字段进而导致脏写入现象发生。此时即便指定了 readOnly=true 参数也无法阻止此类变更动作的发生。 最佳实践是要确保所有参与同一笔交易过程里的 DAO 层交互都遵循相同的存取权限级别定义,并且尽可能减少不必要的更新语句频率以免影响性能指标和并发安全性[^1]。 #### 场景五:跨多个不同数据源的操作 一旦涉及到两个以上相互关联却又隶属于各自独立连接池资源的对象实例之间的协作互动,就很容易引发分布式一致性难题。特别是当其中一个环节失败之后怎样安全地中止整个批次提交成为了一个棘手的技术挑战。 推荐做法是引入 XA 协议兼容型中间件产品如 Atomikos、Bitronix 等来进行全局协调调度,亦或是重构设计思路使之转换成幂等性质更强的服务单元组合而成的整体架构体系结构。 #### 场景六:嵌套式调用同一个 Bean 方法 假设有 A 和 B 是由相同容器实例化的单例 bean ,而且它们之间存在互相依赖关系即 A 调用了 B 中同样加有 @Transactional 标记函数 C 。然而根据 JDK 动态代理原理可知除非经过特殊定制否则普通反射手段难以穿透内部私有成员变量所指向的目标对象的真实身份信息,所以这里很可能造成新的外部包裹层而非继承已有正在进行之中的那个共享环境。 为了避免上述情况出现,应该考虑重新规划模块间的职责划分界限,尽量避免循环引用情形的同时也方便后期维护人员理解整体运作机理。 #### 场景七:Propagation 设置不合理 不同的 Propagation 枚举值决定了新发起请求如何融入现有活动之中去。如果不小心选择了不当选项的话,很可能会引起意想不到的结果,例如重复创建额外级别的隔离副本或者是忽视已经存在的约束条件限制等等。 务必仔细权衡各种可能性后再做出最终抉择,一般而言 REQUIRED 类型最为常用因为它能够在必要时候新建 session 同时又能很好地复用已有的 context 结构体。 #### 场景八:Rollback For 不匹配 只有那些显式指定要响应的具体 Exception 子类别才会促使系统强制终止当前正在运行的任务流并且撤销之前所做的任何更改记录。反之如果没有特别说明的话,默认只会对 unchecked runtime exception 生效而已。 所以在编写 catch block 分支判断逻辑的时候一定要谨慎行事,最好把所有可能遇见的风险因素全部罗列出来加以防范措施部署到位。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值