探究Spring事务:了解失效场景及应对策略

在现代软件开发中,数据的一致性和完整性是至关重要的。为了保证这些特性,Spring框架提供了强大的事务管理机制,让开发者能够更加自信地处理数据库操作。然而,事务并非银弹,存在一些失效的情景,本文将带您深入探究Spring事务及其失效场景,并为您呈现应对策略。

spring事务失效的场景

_20230804232601.png

@Transactional概述

在Spring Boot中,@Transactional是一个用于声明式事务管理的注解。事务是一种用来维护数据库操作的一致性和隔离性的机制,确保在一组操作中,要么所有操作都成功提交,要么所有操作都回滚,以保持数据的完整性。@Transactional注解让你能够在方法级别上定义事务的行为,而无需显式编写事务管理代码。

@Transactional注解的详细解释:

  • 标记事务起点:@Transactional注解放在方法上,表示该方法应该在一个事务内执行。当方法被调用时,Spring会自动开始一个事务。

  • 事务属性: @Transactional注解支持多个属性,用于配置事务的各个方面。一些常用的属性包括:

    • isolation:指定事务的隔离级别,定义了事务之间的可见性。例如,Isolation.READ_COMMITTED表示读已提交的隔离级别。
    • propagation:定义了事务的传播行为,即方法被另一个事务方法调用时如何处理事务。例如,Propagation.REQUIRED表示如果已存在事务,则沿用该事务,否则创建一个新的事务。
    • readOnly:指定事务是否只读。如果只读,可以优化事务处理,因为不需要考虑写操作。
    • timeout:定义事务的超时时间,超过该时间未提交则自动回滚。
    • rollbackFornoRollbackFor:指定在哪些异常情况下回滚事务或不回滚事务。
  • 嵌套事务: 如果在方法中调用了另一个被@Transactional注解标记的方法,那么默认情况下会共享外部方法的事务。如果希望内部方法有自己的事务,可以使用Propagation.REQUIRES_NEW传播行为。

  • 回滚策略: 默认情况下,Spring会将未捕获的运行时异常(RuntimeException及其子类)作为触发事务回滚的标志。你也可以通过rollbackFor属性指定哪些异常触发回滚。

  • 应用范围: @Transactional注解可以用于类级别(作用于所有方法)或方法级别(作用于单个方法),具体取决于你的需求。

  • 生效方式: @Transactional注解依赖于Spring的AOP(面向切面编程)技术,它会在运行时通过代理机制来拦截方法调用,并根据注解的配置来管理事务。

TransactionSynchronizationManager辅助打印事务

在Spring Boot中,你可以使用TransactionSynchronizationManager来获取有关已开启事务的信息。TransactionSynchronizationManager是Spring的一个工具类,用于管理和监视事务的状态。要打印已开启的事务,你可以通过以下步骤进行:

  • 导入必要的类: 首先,确保你的类中导入了TransactionSynchronizationManager类。
arduino 复制代码
import org.springframework.transaction.support.TransactionSynchronizationManager;
  • 打印当前事务名称或者状态
csharp 复制代码
boolean isTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();

if (isTransactionActive) {
    System.out.println("An active transaction is present.");
    System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
} else {
    System.out.println("No active transaction.");
}

失效场景详解

  • 未被Spring管理的对象

未被标记为@Component、@Service、@Repository或其他Spring管理注解的普通Java类不会被Spring自动管理。

typescript 复制代码
import org.springframework.transaction.annotation.Transactional;

public class NoManageService {
/**
import org.springframework.transaction.annotation.Transactional;

public class NoManageService {
    /**
     * 事务不生效场景:未被Spring管理的对象
     */
    @Transactional
    public void doHandle() {
        // 一通处理
    }
}

}
  • 异常未被正确捕获和抛出

异常被捕获但未重新抛出,事务将不会回滚。

csharp 复制代码
    /**
     * 事务不生效场景:异常未被正确捕获和抛出
     */
    @Transactional(rollbackFor = Exception.class)
    public void method1(){
        try {
            //一通处理猛如虎
        }catch (Exception e){
            //异常只吞不吐不回滚
        }
    }
  • 在非公共方法上使用@Transactional
csharp 复制代码
    /**
     * 事务不生效场景:在非公共方法上使用@Transactional
     */
    @Transactional(rollbackFor = Exception.class)
    private void method2(){
        //一通处理猛如虎
    }
  • 事务方法内部调用

当事务方法内部通过普通的方法调用,而不是通过Spring管理的Bean调用其他带有@Transactional注解的方法时,事务可能失效。这是因为Spring的事务机制是基于代理实现的,只有通过代理对象调用的方法才能被AOP拦截并应用事务。

csharp 复制代码
    /**
     * 事务不生效场景:事务方法内部调用,未被AOP拦截
     */

    public void method3(){
        method5();
    }

    @Resource
    private TestService testService;
    /**
     * 事务生效,使用自我引用的方式调用: 如果在同一个类中需要在事务内部调用其他事务方法,
     * 可以通过自我引用的方式来调用,以便事务代理可以生效。
     */
    public void method4(){
        testService.method5();
    }



    @Transactional(rollbackFor = Exception.class)
    public void method5(){
        //一通处理猛如虎
    }
  • 事务传播机制配置错误

如果在方法中调用了另一个被@Transactional注解标记的方法,那么默认情况下会共享外部方法的事务。如果希望内部方法有自己的事务,可以使用Propagation.REQUIRES_NEW传播行为。

java 复制代码
    /**
     * method6中调用method7,会共享method6方法的事务
     */
    @Transactional(rollbackFor = Exception.class)
    public void method6(){
        //获取当前事务名称,若未开启事务,则为null
        String  tranName = TransactionSynchronizationManager.getCurrentTransactionName();
        log.info("method6事务名称=======》{}",tranName);
        //一通处理猛如虎
        testService.method7();
    }
    @Transactional(rollbackFor = Exception.class)
    public void method7(){
        //获取当前事务名称,若未开启事务,则为null
        String  tranName = TransactionSynchronizationManager.getCurrentTransactionName();
        log.info("method7事务名称=======》{}",tranName);
        //一通处理猛如虎
    }

调用method6则会打印

ini 复制代码
23:04:48.861 [http-nio-8180-exec-1] INFO  c.x.t.TestServiceImpl - [method6,69] - method6事务名称=======》cn.xj.transactional.TestServiceImpl.method6
23:04:48.862 [http-nio-8180-exec-1] INFO  c.x.t.TestServiceImpl - [method7,77] - method7事务名称=======》cn.xj.transactional.TestServiceImpl.method6
java 复制代码
    /**
     * method8中调用method9,method9会重新开启个事务
     */
    @Transactional(rollbackFor = Exception.class)
    public void method8(){
        //获取当前事务名称,若未开启事务,则为null
        String  tranName = TransactionSynchronizationManager.getCurrentTransactionName();
        log.info("method8事务名称=======》{}",tranName);
        //一通处理猛如虎
        testService.method9();
    }
    @Transactional(rollbackFor = Exception.class,propagation= Propagation.REQUIRES_NEW)
    public void method9(){
        //获取当前事务名称,若未开启事务,则为null
        String  tranName = TransactionSynchronizationManager.getCurrentTransactionName();
        log.info("method9事务名称=======》{}",tranName);
        //一通处理猛如虎
    }

调用method8则会打印

ini 复制代码
23:12:51.058 [http-nio-8180-exec-1] INFO  c.x.t.TestServiceImpl - [method8,93] - method8事务名称=======》cn.xj.transactional.TestServiceImpl.method8
23:12:51.086 [http-nio-8180-exec-1] INFO  c.x.t.TestServiceImpl - [method9,101] - method9事务名称=======》cn.xj.transactional.TestServiceImpl.method9

总结

Spring事务为我们提供了一个强大的工具来维护数据的一致性和完整性。然而,在使用过程中,了解事务失效的场景以及应对策略同样重要。通过合理配置事务传播行为、异常处理以及使用注解管理事务,我们能够更好地应对各种情况,确保系统的稳定性和可靠性。

相关推荐
Amagi.1 小时前
Spring中Bean的作用域
java·后端·spring
2402_857589361 小时前
Spring Boot新闻推荐系统设计与实现
java·spring boot·后端
J老熊2 小时前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
Benaso2 小时前
Rust 快速入门(一)
开发语言·后端·rust
sco52822 小时前
SpringBoot 集成 Ehcache 实现本地缓存
java·spring boot·后端
原机小子2 小时前
在线教育的未来:SpringBoot技术实现
java·spring boot·后端
吾日三省吾码2 小时前
详解JVM类加载机制
后端
努力的布布2 小时前
SpringMVC源码-AbstractHandlerMethodMapping处理器映射器将@Controller修饰类方法存储到处理器映射器
java·后端·spring
PacosonSWJTU3 小时前
spring揭秘25-springmvc03-其他组件(文件上传+拦截器+处理器适配器+异常统一处理)
java·后端·springmvc
记得开心一点嘛3 小时前
在Java项目中如何使用Scala实现尾递归优化来解决爆栈问题
开发语言·后端·scala