Spring中导致事务传播失效的情况(自调用、方法访问权限、异常处理不当、传播类型选择错误等。在实际开发中,务必确保事务方法正确配置)

ops/2024/10/23 7:11:55/

文章目录

  • 1. 自调用(内部调用)导致事务失效
    • 示例:
    • 解决办法:
  • 2. 事务方法不是 `public` 修饰
    • 示例:
    • 解决办法:
  • 3. 未被 Spring 管理的对象
    • 示例:
    • 解决办法:
  • 4. 异常类型不匹配导致事务回滚失败
    • 示例:
    • 解决办法:
  • 5. 传播类型使用不当
    • 示例:
    • 解决办法:
  • 6. 数据库本身不支持事务
    • 解决办法:
  • 7. 使用了不支持事务的操作
    • 解决办法:
  • 8. 事务管理器未正确配置
    • 解决办法:
  • 9. 代理模式下的事务不生效
    • 解决办法:
  • 总结

在 Spring 中,事务传播机制可以有效管理事务的传播和行为。但在某些情况下,事务传播可能会失效,导致事务没有按预期进行传播和处理。以下是常见导致事务传播失效的几种情况:

1. 自调用(内部调用)导致事务失效

这是最常见的问题之一。当一个类的非事务方法内部调用自身的事务方法时,Spring 的事务代理无法起作用。这是因为 Spring 的事务管理是基于 AOP 实现的,它通过动态代理在方法执行前后插入事务逻辑。但如果是同一个类的内部调用,Spring 无法通过代理拦截这个调用,事务不会被启动。

示例:

java">public class MyService {@Transactionalpublic void transactionalMethod() {// 事务逻辑}public void nonTransactionalMethod() {// 通过内部调用执行事务方法transactionalMethod();}
}

在上述代码中,如果在 nonTransactionalMethod() 中调用了 transactionalMethod()transactionalMethod() 中的事务不会生效,因为这是一个自调用,不经过 Spring 的代理机制。

解决办法:

将事务方法抽取到另一个类中,或者通过依赖注入的方式调用。

java">@Service
public class MyService {@Autowiredprivate MyService self;public void nonTransactionalMethod() {// 使用 self 调用,事务才能生效self.transactionalMethod();}@Transactionalpublic void transactionalMethod() {// 事务逻辑}
}

2. 事务方法不是 public 修饰

Spring 的事务管理是基于代理的,它只会对 public 方法进行代理。对于 privateprotected、或者 package-private 的方法,Spring 事务不会生效,因为代理类无法拦截对这些方法的调用。

示例:

java">@Transactional
private void transactionalMethod() {// 事务逻辑
}

在这种情况下,事务不会生效,因为 transactionalMethod() 不是 public 方法。

解决办法:

确保事务方法是 public 的。

java">@Transactional
public void transactionalMethod() {// 事务逻辑
}

3. 未被 Spring 管理的对象

只有被 Spring 容器管理的 Bean,事务代理才能起作用。如果方法是在一个非 Spring 管理的类中调用的,即使添加了 @Transactional 注解,事务也不会生效。

示例:

java">public class ExternalClass {@Transactionalpublic void transactionalMethod() {// 事务逻辑}
}

如果 ExternalClass 不是由 Spring 容器托管,transactionalMethod() 上的事务将不起作用。

解决办法:

确保事务相关的 Bean 是通过 Spring 管理的,即确保它们是由 Spring 容器注入的,例如通过 @Service@Component 标注的类。

4. 异常类型不匹配导致事务回滚失败

Spring 的默认事务管理策略是只在遇到 unchecked 异常(RuntimeException)Error 时回滚事务。如果方法抛出了一个 checked 异常(如 IOException),事务不会回滚。

示例:

java">@Transactional
public void transactionalMethod() throws Exception {// 如果抛出 checked 异常,事务不会回滚throw new Exception("Checked Exception");
}

在上述示例中,方法抛出的是一个 Exception(checked 异常),默认情况下,Spring 不会回滚事务。

解决办法:

可以通过 rollbackFor 属性明确指定哪些异常需要触发事务回滚:

java">@Transactional(rollbackFor = Exception.class)
public void transactionalMethod() throws Exception {// 事务会回滚throw new Exception("Checked Exception");
}

5. 传播类型使用不当

事务传播类型决定了当一个事务方法调用另一个事务方法时,事务如何传播。如果传播类型配置不当,可能导致事务行为与预期不一致,甚至事务失效。

示例:

使用 Propagation.NOT_SUPPORTEDPropagation.REQUIRES_NEW 传播类型时,如果子事务方法被配置为不支持或需要新的事务,可能会导致当前事务的逻辑与原始事务隔离,无法实现事务传播效果。

java">@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransactionalMethod() {// 该方法不会加入现有事务,事务逻辑不会生效
}

解决办法:

根据业务需求选择正确的传播类型,例如使用 Propagation.REQUIRED 让子方法加入当前事务。

6. 数据库本身不支持事务

并非所有数据库都支持事务操作。比如,某些 NoSQL 数据库(如 MongoDB、Cassandra)对事务的支持是有限的。如果数据库不支持事务,即使在代码中配置了事务管理,事务也不会生效。

解决办法:

确保所使用的数据库支持事务,并正确配置数据库连接。

7. 使用了不支持事务的操作

某些数据库操作或框架方法不支持事务。比如,批量操作或者直接使用 JdbcTemplate 进行大批量数据插入时,可能不会受到事务控制的影响。

解决办法:

确保使用支持事务管理的数据库操作工具,如 JPA、Hibernate,或者正确配置 JdbcTemplate

8. 事务管理器未正确配置

Spring 事务管理依赖于 PlatformTransactionManager,如果没有正确配置事务管理器,事务也不会生效。常见问题包括未正确配置数据源或没有配置事务管理器。

解决办法:

确保 PlatformTransactionManager 正确配置,并且事务管理器与数据源绑定一致。

java">@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);
}

9. 代理模式下的事务不生效

Spring 使用动态代理(JDK 动态代理或 CGLIB)来实现 AOP。如果使用了 JDK 动态代理,它只对接口的方法进行代理,导致如果在实现类上直接调用方法,事务可能不生效。

解决办法:

如果没有接口,确保 @Transactional 注解的类使用的是 CGLIB 代理,而不是 JDK 代理。

java">@EnableTransactionManagement(proxyTargetClass = true) // 强制使用 CGLIB 代理

总结

事务失效的原因主要包括自调用、方法访问权限、异常处理不当、传播类型选择错误等。在实际开发中,务必确保事务方法正确配置,并且在调用方法时遵循 Spring AOP 的代理机制,避免事务失效。


http://www.ppmy.cn/ops/127764.html

相关文章

比瓴科技入选国家工业信息安全发展研究中心SBOM工作组首批成员单位

近日,由开放原子开源基金会主办,开源风险评估与治理技术实验室承办的2024开放原子开源生态大会软件物料清单(SBOM)分论坛在北京成功举办。 在会议上,国家工业信息安全发展研究中心(简称“中心”&#xff0…

Android OpenGL天空盒

在我们的项目学习过程中,我们从一片漆黑的虚空开始构建。为了给这个世界增添一些色彩,我们加入了三个粒子喷泉,但即便如此,我们的世界依然大部分被黑暗和虚无所笼罩。这些喷泉仿佛悬浮在无尽的黑暗之中,没有边界&#…

【工具变量】A股上市企业大数据应用(2001-2023年)-参考柏淑嫄实践

数据简介:企业数字化转型的浪潮孕育出大数据,大数据技术是在数据处理和应用中释放大数据多元价值的必要手段。大数据作为企业发展的战略资源和生产要素对企业转型发展具有重要意义。对上市企业大数据应用程度进行测算不仅有助于了解大数据相关技术在企业…

前端工具类大全--【成果版】

目录 📚前言 如何判断Dom节点 Object.keys Object.assign Object.create 判断Number类型 判断String类型 判断Function类型 判断Object类型 判断Array类型 判断RegExp类型 遍历forEach 遍历map indexOf 📚前言 前端最苦恼的问题之一…

Python实现基于WebSocket的stomp协议调试助手工具分享

stomp协议很简单,但是搜遍网络竟没找到一款合适的客户端工具。大多数提供的都是客户端库的使用。可能是太简单了吧!可是即便这样,假如有一可视化的工具,将方便的对stomp协议进行抓包调试。网上类似MQTT的客户端工具有很多&#xf…

vscode 编译c++代码问题

vscode 编译代码时 include 报错 vscode 编译代码时 include 报错,目标代码有 include 自己写的头文件,报错找不到对应的头文件 * Executing task: C/C: g.exe build active file Starting build... cmd /c chcp 65001>nul && C:\msys64\uc…

linux替换某个文件的某段内容命令

假设文件是a.sql 里面的库是abc,我想把这个abc给替换掉,改成hahaha cat a.sql |grep abc|sed -i s/abc/hahaha/g a.sql 如果想写个脚本指定整个文件夹中的内容替换 #!/bin/bash # 检查是否提供了文件夹路径 if [ -z "\$1" ]; then echo &…

-1比sizeof(char)大?

下列参数代码的输出: char *usrName"hello world" if(-1<sizeof(usrName)) {printf("NG\n"); } else {printf("OK\n"); }正确答案&#xff1a;OK\n 为什么会造成这样的结果&#xff0c;由于进行比较运算时&#xff0c;需要将先将两个数据转换…