@Transactional

本文深入探讨Spring框架中@Transactional注解的使用细节,包括其在类和方法级的应用,事务传播行为,隔离级别,以及在多线程环境下的事务管理策略。文章还解析了Spring事务的实现原理,强调了正确配置事务的重要性。

@Transactional


概述

  • @Transactional 是声明式事务管理 编程中使用的注解

  • 添加位置

    • 接口实现类或接口实现方法上,而不是接口类中
  • 访问权限:public 的方法才起作用

    • @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解
  • 系统设计:将标签放置在需要进行事务管理的方法上,而不是不假思索的放置在接口实现类上( 接口中所有方法都需要进行事务管理,但其实并不需要,如只读的接口就不需要事务管理,但是 由于配置了@Transactional就需要AOP拦截及事务的处理,影响系统性能)

  • 方法上注解属性会覆盖类注解上的相同属性,当接口与接口中方法上同时带有@Transactional注解时

  • 错误使用:

    • 接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效
    • 接口中异常(运行时异常)被捕获而没有被抛出
      • 默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚
      • 可通过 @Transactional rollbackFor进行配置
  • 多线程下事务管理

    • 因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean
    • 在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制
      • 一个使用了@Transactional 的方法,如果方法内包含多线程的使用,方法内部出现异常,不会回滚线程中调用方法的事务

声明式事务管理实现方式:

  • 基于tx和aop名字空间的xml配置文件
// 基本配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:task="http://www.springframework.org/schema/task" xmlns:jms="http://www.springframework.org/schema/jms"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
						  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
						  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
						  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
						  http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
						  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
						  http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.1.xsd">

<bean name=“transactionManager”
class=“org.springframework.jdbc.datasource.DataSourceTransactionManager”>
<property name=“dataSource” ref=“shardingDataSource”></property>
</bean>

<tx:annotation-driven transaction-manager=“transactionManager” proxy-target-class=“true” />

// MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用
// <annotation-driven>标签的声明,则是在Spring内部启用@Transactional来进行事务管理,使用 @Transactional 前需要配置

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 基于@Transactional注解
    @Transactional实质是使用了JDBC的事务来进行事务控制的
    @Transactional基于Spring的动态代理的机制
  • @Transactional实现原理
  • 1)事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令[不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚](物理连接connection逻辑上新建一个会话session;DataSource与TransactionManager配置相同的数据源)
  • 2)事务结束时,回滚在第1步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)

声明式事务的管理实现本质:

  • 事务的两种开启方式
    • 显示开启start transaction | begin,通过 commit | rollback 结束事务
    • 关闭数据库中自动提交 autocommit set autocommit = 0;MySQL 默认开启自动提交;通过手动提交或执行回滚操作来结束事务
  • Spring 关闭数据库中自动提交:在方法执行前关闭自动提交,方法执行完毕后再开启自动提交
 // org.springframework.jdbc.datasource.DataSourceTransactionManager.java 源码实现
 // switch to manual commit if necessary. this is very expensive in some jdbc drivers,
 // so we don't want to do it unnecessarily (for example if we've explicitly
 // configured the connection pool to set it already).
 if (con.getautocommit()) {
     txobject.setmustrestoreautocommit(true);
     if (logger.isdebugenabled()) {
         logger.debug("switching jdbc connection [" + con + "] to manual commit");
     }
     con.setautocommit(false);
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 问题:

  • 关闭自动提交后,若事务一直未完成,即未手动执行 commit 或 rollback 时如何处理已经执行过的SQL操作?

    • C3P0默认的策略是回滚任何未提交的事务
    • C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等
    • JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互

spring事务特性

  • spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口
public interface PlatformTransactionManager {
  TransactionStatus getTransaction(TransactionDefinition definition)
  throws TransactionException;
  void commit(TransactionStatus status) throws TransactionException;
  void rollback(TransactionStatus status) throws TransactionException;
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 事务的隔离级别:是指若干个并发
    的事务之间的隔离程度

  • @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用

  • @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)

  • @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)

  • @Transactional(isolation = Isolation.SERIALIZABLE):串行化

  • 事务传播行为:如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为

    • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
    • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
    • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
    • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
    • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
    • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

@Transactional 属性配置

属性类型描述
valueString可选的限定描述符,指定使用的事务管理器
propagationenum: Propagation可选的事务传播行为设置
isolationenum: Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeoutint (in seconds granularity)事务超时时间设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异常类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异常类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异常类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异常类名字数组
说明:
  • value :主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。比如在Spring中,声明了两种事务管理器txManager1, txManager2.然后,用户可以根据这个参数来根据需要指定特定的txManager.
  • value 适用场景:在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器的
  • REQUIRED_NEW和NESTED两种不同的传播机制的区别
  • REQUIRED_NEW:内部的事务独立运行,在各自的作用域中,可以独立的回滚或者提交;而外部的事务将不受内部事务的回滚状态影响
  • ESTED的事务,基于单一的事务来管理,提供了多个保存点。这种多个保存点的机制允许内部事务的变更触发外部事务的回滚。而外部事务在混滚之后,仍能继续进行事务处理,即使部分操作已经被混滚。 由于这个设置基于JDBC的保存点,所以只能工作在JDBC的机制
  • rollbackFor : 让受检查异常回滚;即让本来不应该回滚的进行回滚操作
  • noRollbackFor :忽略非检查异常;即让本来应该回滚的不进行回滚操作

嵌套事务

  • 带有事务的方法调用其他事务的方法,此时执行的情况取决配置的事务的传播属性

    • PROPAGATION_REQUIRES_NEW :
      • 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
    • PROPAGATION_NESTED :
      • 如果外部事务 commit, 嵌套事务也会被 commit;如果外部事务 roll back, 嵌套事务也会被 roll back 。
      • 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交
  • 关于Spring的事务Transactional,锁同步,并发线程


spring事务回滚规则

  • 指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常
    spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务
  • 默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。
  • 用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

注意事项:

  • @Transactional 使用位置 类上方、方法上方
    Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效
    当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

  • 方法的访问权限为 public
    @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常

  • 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰
    例如一:同一个类中方法,A方法未使用此标签,B使用了,C未使用,A 调用 B , B 调用 C ;则外部调用A之后,B的事务是不会起作用的
    例如二:若是有上层(按照 Controller层、Service层、DAO层的顺序)由Action 调用 Service 直接调用,发生异常会发生回滚;若间接调用,Action 调用 Service 中 的A 方法,A无@Transactional 注解,B有,A调用B,B的注解无效


其他

  • 事务方法的嵌套调用会产生事务传播
  • spring 的事务管理是线程安全的
  • 父类的声明的@Transactional会对子类的所有方法进行事务增强;子类覆盖重写父类方式可覆盖其@Transactional中的声明配置
  • 类名上方使用@Transactional,类中方法可通过属性配置覆盖类上的@Transactional配置;比如:类上配置全局是可读写,可在某个方法上改为只读

源码阅读


参考资料


多线程事务管理

  • 描述

    • 因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean
    • 在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制
  • 解决

    • 如果方法中调用多线程
      • 方法主题的事务不会传递到线程中
      • 线程中可以单独调用Service接口,接口的实现方法使用@Transactional,保证线程内部的事务
    • 多线程实现的方法
      • 使用异步注解@Async的方法上再加上注解@Transactional,保证新线程调用的方法是有事务管理的
  • 原理

    • Spring中事务信息存储在ThreadLocal变量中,变量是某个线程上进行的事务所特有的(这些变量对于其他线程中发生的事务来讲是不可见的,无关的)
    • 单线程的情况下,一个事务会在层级式调用的Spring组件之间传播
    • 在@Transactional注解的服务方法会产生一个新的线程的情况下,事务是不会从调用者线程传播到新建线程的
  • 参考资料

        </div>
<think> 嗯,用户想了解Spring框架中@Transactional注解的使用、原理和应用。根据之前的对话历史,用户已经看到了一些参考资料,包括事务的位置选择、代理机制实现和源码层面的实现位置。不过用户希望获得更系统的解释,可能是个正在学习Spring事务管理的开发者。 用户提到“可能包括使用方法、原理、在框架中的应用等”,说明需要分层解答。先从最直观的使用方法入手比较合理,这样用户能快速上手实践。原理部分需要解释清楚AOP代理机制,这点引用[2]提到了代理对象创建,引用[3]提到TransactionAspectSupport的源码位置,可以结合起来说明。应用场景方面用户可能关心实际开发中的注意事项。 注意到引用[1]特别强调不要在接口上使用@Transactional的细节,这个坑点必须重点提示。另外用户提供的引用中有多处细节互相补充:比如方法级注解会覆盖类级注解(引用[1])、事务传播行为参数(引用[2]补充)、实际事务创建位置(引用[3]的createTransactionIfNecessary方法),这些都需要有机整合到回答中。 安全方面要提醒事务超时和回滚规则设定,特别是服务层事务的耗时控制。性能方面注意读操作不需要事务,避免资源浪费。用户可能遇到的常见坑点比如自调用失效问题,需要给出具体解决方案——用AopContext.currentProxy()或者注入自身Bean的方法。 最后考虑设计几个衍生问题:事务传播行为的具体案例(比如嵌套事务场景)、分布式事务的整合方案、隔离级别的选择依据等。这些既延续当前话题深度,又覆盖用户可能的后续疑问。 </think> 好的,我们来系统梳理一下 Spring 框架中 `@Transactional` 注解的使用方法、实现原理以及在框架中的应用。 ## 一、@Transactional 注解的使用方法 `@Transactional` 注解主要用于声明式事务管理,其核心目的是简化事务代码,让开发者专注于业务逻辑。主要使用方式和配置项如下: 1. **注解位置**: * **类级别**:放置在类声明上方。作用是该类中所有的 `public` 方法都将具有该注解定义的事务属性。这是最常见的用法。 * **方法级别**:放置在具体的 `public` 方法上方(对 `private`, `protected`, `package-visible` 方法无效)。作用范围仅限于该方法。**方法级别的事务属性定义会覆盖类级别的定义**。 * **接口级别(不推荐)**:Spring 官方**强烈建议**不要将 `@Transactional` 标注在接口或接口方法上。因为它仅在 Spring 使用基于接口的代理(如 JDK 动态代理)时才生效。如果使用了基于类的代理(如 CGLIB)或者采用基于 AspectJ 的织入方式,接口上的注解会被忽略,导致事务失效[^1]。 2. **关键配置属性**: * `propagation`: 定义事务的**传播行为**(默认 `Propagation.REQUIRED`)。决定当前方法在调用时,如何处理现有事务(新开事务、入现有事务、非事务执行等)。常见值: * `REQUIRED`:如果当前存在事务,则入该事务;如果不存在,则创建一个新事务。 * `REQUIRES_NEW`:总是新建一个事务,如果当前存在事务,则挂起它。 * `SUPPORTS`:如果当前存在事务,则入;否则以非事务方式执行。 * `NOT_SUPPORTED`:以非事务方式执行,如果当前存在事务,则挂起它。 * `MANDATORY`:必须在事务中运行,否则抛出异常。 * `NEVER`:必须在非事务中运行,否则抛出异常。 * `NESTED`:如果当前存在事务,则在嵌套事务内执行(使用保存点)。 * `isolation`: 定义事务的**隔离级别**(默认 `Isolation.DEFAULT`,通常对应数据库默认级别)。解决并发事务可能引发的脏读、不可重复读、幻读问题。常见值: * `DEFAULT`: 使用底层数据库默认隔离级别。 * `READ_UNCOMMITTED` * `READ_COMMITTED` * `REPEATABLE_READ` * `SERIALIZABLE` * `timeout`: 定义事务的**超时时间**(以秒为单位,默认 -1,表示使用底层系统默认超时)。超过该时间未完成,事务将自动回滚。 * `readOnly`: 指示事务是否为**只读事务**(默认 `false`)。设为 `true` 可以优化某些操作(如 Hibernate 的 flush 模式)。 * `rollbackFor` / `rollbackForClassName`: 指定哪些**异常类型**(或其子类)会导致事务回滚(默认只在抛出 `RuntimeException` 和 `Error` 时回滚)。你可以指定具体的异常类或其类名。 * `noRollbackFor` / `noRollbackForClassName`: 指定哪些异常类型(或其子类)**即使抛出也不导致**事务回滚。 3. **基本使用示例**: ```java @Service public class OrderService { @Autowired private OrderRepository orderRepository; @Autowired private PaymentService paymentService; // 类级别的注解作为默认,方法级别的覆盖特定行为 @Transactional(readOnly = true, timeout = 30) public Order findOrderById(Long id) { return orderRepository.findById(id).orElse(null); } // 使用 REQUIRES_NEW 传播行为,确保支付操作独立提交 @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = PaymentException.class) public void processPayment(Order order, PaymentInfo paymentInfo) throws PaymentException { // ... 支付处理逻辑,可能抛出 PaymentException } // 默认 REQUIRED 传播,创建订单和支付在同一个事务中 @Transactional // 使用类默认的传播行为(REQUIRED)和隔离级别等 public Order createOrder(OrderDetails details, PaymentInfo paymentInfo) throws PaymentException { Order newOrder = orderRepository.save(createNewOrder(details)); processPayment(newOrder, paymentInfo); // 调用 REQUIRES_NEW 方法会挂起当前事务,新开事务 // ... 其他操作 return newOrder; // 如果 processPayment 成功,但此处出错,createOrder 事务会回滚,但 processPayment 事务已提交 } } ``` ## 二、@Transactional 注解的实现原理 `@Transactional` 的核心原理是 Spring 的 **AOP(面向切面编程)** 机制,具体实现依赖于 **代理模式(Proxy Pattern)**: 1. **代理对象的创建**: * 当 Spring 容器启动时,会扫描所有被 `@Component`、`@Service`、`@Repository` 等注解标记的 Bean。 * 对于包含 `@Transactional` 注解的类(或其方法)的 Bean,Spring 事务管理基础设施会识别它们。 * Spring **动态地为这些 Bean 创建代理对象**(Proxy Object)。这个代理对象包裹了原始的目标对象(Target Object)[^2]。代理的创建方式有两种: * **JDK 动态代理**:如果目标类实现了至少一个接口,则生成一个实现了相同接口的代理类。 * **CGLIB 字节码增强**:如果目标类没有实现任何接口,则直接生成目标类的子类作为代理类。 2. **拦截器链与事务拦截器**: * 代理对象内部维护了一个**拦截器链(Interceptor Chain)**。当一个带有 `@Transactional` 的方法被外部调用时,实际上调用的是代理对象上的方法。 * 代理对象会检查被调用的方法是否需要事务管理(即是否被 `@Transactional` 注解或其类被注解且方法是 `public`)。 * 如果需要事务管理,代理对象会将方法调用**委托给一个特殊的拦截器**:`TransactionInterceptor`。 3. **事务控制的核心 - TransactionInterceptor**: * `TransactionInterceptor` 是事务处理的核心逻辑所在。它的 `invoke()` 方法负责整个事务的生命周期管理。 * 在目标方法被调用**之前**: * 拦截器通过 `PlatformTransactionManager`(事务管理器,如 `DataSourceTransactionManager`, `HibernateTransactionManager`, `JtaTransactionManager`)**根据`@Transactional`属性创建一个新的事务或入一个现有事务**。 * 这通常涉及到从 `TransactionManager` 获取一个 `TransactionStatus` 对象来表示当前事务的状态。 * 对应源码的关键入口点如 `TransactionAspectSupport.invokeWithinTransaction()` -> `createTransactionIfNecessary()`[^3]. * 在目标方法被调用**期间**: * 执行目标方法的业务逻辑。 * 在目标方法**执行完成后**(正常返回或抛出异常): * **正常返回**:拦截器根据事务定义决定是否提交事务(对于 `REQUIRED`,通常在方法最外层提交)。提交操作调用 `commit()`。 * **抛出异常**:拦截器捕获异常,检查该异常类型是否满足 `@Transactional(rollbackFor=...)` 或 `@Transactional(noRollbackFor=...)` 的规则。如果异常类型指示需要回滚(默认 `RuntimeException` / `Error`),则调用 `rollback()` 回滚事务。否则,也可能提交事务(但很少见)。 4. **PlatformTransactionManager 的作用**: * 这是 Spring 事务抽象的核心接口 (`DataSourceTransactionManager`, `HibernateTransactionManager` 等是其实现)。它屏蔽了底层具体事务资源(JDBC, JPA, JTA)的差异。 * `TransactionInterceptor` 通过调用 `PlatformTransactionManager` 的 `getTransaction()`, `commit()`, `rollback()` 等方法来具体操作事务。 **流程图简化表示**: ``` [外部调用者] --> [代理对象] --> TransactionInterceptor.invoke() | V createTransactionIfNecessary() [^3] --> 获取/创建事务 (PlatformTransactionManager) | V try { [反射调用目标方法] --> 执行业务逻辑 commitIfNecessary() } catch(Throwable ex) { completeTransactionAfterThrowing() --> 检查异常是否需要回滚 if (需要回滚) rollback() else commit() rethrow ex } ``` ## 三、@Transactional 在 Spring 框架中的应用 1. **声明式事务管理**: * 这是 `@Transactional` 最主要、最核心的应用场景。 * 开发者只需通过简单的注解配置事务属性(传播、隔离、超时、只读、回滚规则),无需手动编写繁琐的 `try-catch-finally` 块来管理 `Connection` 的获取、提交、回滚和释放。Spring 框架自动处理这些事务边界代码。 * **显著优点**:大大减少了样板代码,提高了开发效率和代码可读性;事务管理与业务逻辑解耦,更符合单一职责原则;配置灵活,易于维护和更改。 2. **管理多种持久化技术的事务**: * 通过配置不同的 `PlatformTransactionManager` 实现,`@Transactional` 可以统一管理不同持久层技术的事务: * **JDBC / Spring JdbcTemplate**:使用 `DataSourceTransactionManager`。 * **JPA (Hibernate)**:使用 `JpaTransactionManager`。 * **Hibernate (原生)**:使用 `HibernateTransactionManager`。 * **JTA (分布式事务)**:使用 `JtaTransactionManager`。 * 应用代码中的 `@Transactional` 用法保持一致,只需切换底层的事务管理器即可。 3. **集成测试**: * 在 Spring TestContext Framework 中,`@Transactional` 注解常用于测试方法上。 * 默认情况下,在测试方法结束时,Spring 会自动回滚测试期间对数据库所做的修改,确保测试之间数据的隔离性,避免测试污染。这对于编写可重复执行的 DAO/Repository 层或 Service 层集成测试非常有用。 * 可以通过 `@Rollback(false)` 禁用自动回滚。 ## 四、重要注意事项与常见陷阱 1. **事务方法的可见性**:`@Transactional` 只对 `public` 方法有效。`private`, `protected`, `package-visible` 方法上的注解会被忽略(因为代理对象无法覆盖调用这些方法)。 2. **自调用失效**:在同一个 Bean 内部,一个非事务方法 `A()` 调用同一个 Bean 中的事务方法 `B()`,会导致 `B()` 的事务失效。因为 `A()` 调用 `B()` 时,是通过 `this.B()` 进行的(即目标对象本身,而不是代理对象),绕过了代理和拦截器。解决方法: * 将 `A()` 和 `B()` 拆分到不同的 Bean 中。 * 使用 `AopContext.currentProxy()` 获取当前代理对象,然后调用 `((YourServiceClass) AopContext.currentProxy()).B()`(需要暴露代理 `@EnableAspectJAutoProxy(exposeProxy = true)`)。 * 通过注入自身 Bean 来完成内部调用(不推荐,易循环依赖)。 3. **异常回滚规则**: * 默认只回滚 `RuntimeException` 和 `Error`。**受检异常(Checked Exception,如 `SQLException`, `IOException`)默认不回滚**!务必使用 `rollbackFor` 或 `rollbackForClassName` 明确指定需要回滚的受检异常类型。 * 使用 `noRollbackFor` 指定即使抛出某些 `RuntimeException` 也不回滚。 4. **事务传播行为理解**:务必清晰理解不同传播行为(尤其是 `REQUIRED`, `REQUIRES_NEW`, `NESTED`)的含义和适用场景,否则可能导致事务边界混乱或死锁。 5. **只读事务优化**:对于只进行查询操作的方法,明确设置 `readOnly = true`。这能给底层持久化框架(如 Hibernate)提供优化提示(例如关闭脏检查、flush)。 6. **正确选择事务管理器**:确保配置的事务管理器与使用的持久化技术(JDBC, JPA, Hibernate)匹配。 ## 总结 Spring 的 `@Transactional` 注解是实现声明式事务管理的核心工具。它通过 AOP 和代理机制,在运行时动态地为标注的方法或类添事务管理逻辑。开发者只需通过简单的注解配置事务属性(传播、隔离、超时、只读、回滚规则),即可享受到事务的 ACID 特性,而无需编写繁琐的事务管理代码。理解其工作原理(代理、拦截器、事务管理器)对于正确使用和排查事务相关问题至关重要,同时要特别注意可见性、自调用、异常处理和传播行为等常见陷阱。它广泛应用于简化业务层的事务管理,支持多种持久化技术,并在集成测试中发挥重要作用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值