当前位置: 首页 > article >正文

java——@Transactional 在哪些情况下会失效?

@Transactional注解失效原因分类

@Transactional注解在Spring框架中用于声明式事务管理,确保数据的一致性和可靠性。然而,在某些情况下,@Transactional注解可能会失效。以下是一些常见的情况及原因:

  1. 方法访问权限设置不当
    • @Transactional注解通常只能应用于public方法上。如果将其应用于protected、private或包级私有方法上,由于Spring的代理机制无法拦截这些方法的调用,因此事务注解将失效。
  2. 方法内部调用
    • 当一个事务方法在同一个类中调用另一个事务方法时,如果调用是通过this关键字进行的,那么被调用的方法将不会通过Spring的代理对象,因此@Transactional注解将不会生效。
  3. 事务管理器配置错误
    • 如果Spring容器中配置了多个事务管理器,但在使用@Transactional注解时没有明确指定事务管理器,可能会导致Spring使用默认的事务管理器,而这个默认的事务管理器可能不适用于当前的操作,从而导致事务注解失效。
  4. 方法内部捕捉异常
    • 在使用@Transactional注解的方法中,如果内部捕获了可能导致事务回滚的异常,并且没有重新抛出一个Spring框架能够识别的运行时异常或声明式异常,那么事务管理器将无法感知到异常,从而可能导致事务不会回滚。
  5. final修饰的方法
    • 如果使用final关键字修饰了方法,那么由于该方法不能被重写,Spring的代理机制将无法对其应用@Transactional注解,因此事务将失效。
  6. 静态方法
    • 静态方法同样无法通过动态代理来应用@Transactional注解,因为静态方法不属于类的实例方法,而是属于类本身。
  7. 未被Spring管理的类
    • 如果一个类没有被Spring管理(即没有使用@Controller、@Service、@Component、@Repository等注解进行标注),那么该类中的方法即使使用了@Transactional注解也不会生效。
  8. 传播特性配置错误
    • 在使用@Transactional注解时,可以指定propagation参数来定义事务的传播行为。如果传播特性配置错误(例如设置为Propagation.NEVER,而当前存在事务),则事务将不会生效。
  9. 表不支持事务
    • 如果使用的数据库表不支持事务(例如,某些类型的存储引擎或数据库系统不支持事务),那么即使使用了@Transactional注解,事务也不会生效。
  10. 多线程调用
    • 在多线程环境下,每个线程都有自己的数据库连接和事务上下文。因此,如果一个事务方法被另一个线程调用,那么它们将不会共享同一个事务上下文,从而导致事务失效。

避免@Transactional注解失效策略

  • 方法是public的。
  • 避免在同一个类中直接调用其他事务方法(可以通过注入自身的方式来解决)。
  • 正确配置事务管理器。
  • 不要在事务方法内部捕获并处理可能导致事务回滚的异常(或者重新抛出一个Spring框架能够识别的异常)。
  • 避免使用final和static修饰事务方法。
  • 确保类被Spring管理。
  • 正确配置事务的传播特性。
  • 使用支持事务的数据库表和存储引擎。
  • 在多线程环境下,确保事务方法在同一个线程中执行。

通过遵循这些原则,可以最大程度地确保@Transactional注解在Spring框架中的正确性和有效性。

底层原码

@Transactional 注解是 Spring 框架中用于声明式事务管理的关键注解。它通过 AOP(面向切面编程)机制在方法调用前后插入事务管理逻辑。要理解 @Transactional 的底层实现,我们需要深入到 Spring 的源代码中。

1. @Transactional 注解

@Transactional 是一个元注解,定义在 org.springframework.transaction.annotation 包下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    // 定义了各种属性,如传播行为、隔离级别等
    Propagation propagation() default Propagation.REQUIRED;
    Isolation isolation() default Isolation.DEFAULT;
    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
    boolean readOnly() default false;
    Class<? extends Throwable>[] rollbackFor() default {};
    String[] rollbackForClassName() default {};
    Class<? extends Throwable>[] noRollbackFor() default {};
    String[] noRollbackForClassName() default {};
}

2. TransactionInterceptor 类

@Transactional 注解的实现依赖于 TransactionInterceptor 类,这是一个实现了 MethodInterceptor 接口的拦截器。TransactionInterceptor 负责在方法调用前开启事务,在方法调用后提交或回滚事务。

TransactionInterceptor 的关键方法
  • invoke 方法:这是 MethodInterceptor 接口的核心方法,负责拦截方法调用并处理事务逻辑。
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
    // 获取事务属性
    TransactionAttributeSource tas = getTransactionAttributeSource();
    TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(invocation.getMethod(), invocation.getClass()) : null);
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);

    // 如果没有事务属性,则直接调用目标方法
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, invocation);
        Object retVal;
        try {
            // 执行目标方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // 处理异常
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            cleanupTransactionInfo(txInfo);
        }
        // 提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    // 使用回调方式处理事务
    else {
        // It's a CallbackPreferringPlatformTransactionManager: pass a callback in order to
        // avoid unnecessary intermediate transactions and allow for eager triggering of actual
        // suspension at the head of the chain.
        return ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
                new TransactionCallback<Object>() {
                    @Override
                    public Object doInTransaction(TransactionStatus status) {
                        TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, invocation, status);
                        try {
                            return invocation.proceedWithInvocation();
                        }
                        catch (Throwable ex) {
                            completeTransactionAfterThrowing(txInfo, ex);
                            throw ex;
                        }
                        finally {
                            cleanupTransactionInfo(txInfo);
                        }
                    }
                });
    }
}

3. TransactionAttributeSource 接口

TransactionAttributeSource 接口用于获取方法上的事务属性。Spring 通常使用 AnnotationTransactionAttributeSource 来解析 @Transactional 注解。

4. PlatformTransactionManager 接口

PlatformTransactionManager 是 Spring 中用于管理事务的核心接口。不同的数据访问技术有不同的实现,例如 DataSourceTransactionManagerJpaTransactionManager 等。

5. 事务管理流程

  1. 创建代理对象

    • Spring 使用 JDK 动态代理或 CGLIB 代理来创建目标对象的代理。
    • 代理对象在方法调用前后插入事务管理逻辑。
  2. 拦截方法调用

    • TransactionInterceptor 在方法调用前检查是否有 @Transactional 注解,并根据注解中的配置决定是否需要开启新的事务。
    • 如果需要开启新事务,则调用 PlatformTransactionManager 的 getTransaction 方法开始一个新的事务。
    • 执行目标方法。
  3. 处理结果和异常

    • 如果目标方法执行成功且没有抛出异常,则调用 PlatformTransactionManager 的 commit 方法提交事务。
    • 如果目标方法抛出异常,则调用 PlatformTransactionManager 的 rollback 方法回滚事务。

http://www.kler.cn/a/412533.html

相关文章:

  • Spring中实现动态数据源切换,基于AbstractRoutingDataSource
  • Dart 中 initializer lists
  • windows安全中心,永久卸载工具分享
  • 【纪念365天】我的创作纪念日
  • 刷题日常(数据流中的中位数,逆波兰表达式求值,最长连续序列,字母异位词分组)
  • 基于FPGA的2FSK调制-串口收发-带tb仿真文件-实际上板验证成功
  • 基于Java的Nacos云原生动态服务发现、配置和服务管理平台设计源码
  • deepin社区与此芯科技完成产品兼容性认证
  • 南京移动携手南大打造江苏首个直通高校智算项目
  • docker compose启动springcloud微服务案例
  • 数据结构 (13)串的应用举例
  • Day50 | 动态规划 :线性DP 最大子数组和不同的子序列
  • 如何启用本机GPU硬件加速猿大师播放器网页同时播放多路RTSP H.265 1080P高清摄像头RTSP视频流?
  • 阿里发布 EchoMimicV2 :从数字脸扩展到数字人 可以通过图片+音频生成半身动画视频
  • Android.mk里如何指定编译模块的输出路径
  • Day47 | 动态规划 :线性DP 最长公共子序列最长公共子数组
  • 解决 IDEA 突然出现 unresolved class reference 问题
  • 网络模型(四层)--应用层(http), 传输层(TCP,UDP),网络层(ip),数据的流转
  • 人工智能之数学基础:向量的范数
  • pnpm的menorepo项目配置eslint和prettier
  • setter方法注入(Java EE 学习笔记07)
  • 大工C语言作业答案
  • 【软考速通笔记】系统架构设计师⑥——数据库设计基础知识
  • go-学习
  • ThingsBoard规则链节点:GCP Pub/Sub 节点详解
  • 关闭AWS账号后,服务是否仍会继续运行?