欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > 【Spring事务实用版】事务实现 事务失效 事务传播机制

【Spring事务实用版】事务实现 事务失效 事务传播机制

2026/3/23 23:46:21 来源:https://blog.csdn.net/m0_70871140/article/details/144201344  浏览:    关键词:【Spring事务实用版】事务实现 事务失效 事务传播机制

文章目录

    • 一、事务是什么
      • 1.1 事务特性
      • 1.2 事务隔离等级
    • 二、事务的实现
      • 2.1 事务的操作
      • 2.2 Spirng 编程式事务
      • 2.3 Spring声明式编程
        • 2.3.1 使用 `@Transactional` 注解
          • 1. 开启事务支持
          • 2. 在方法或类上添加 `@Transactional`
          • 3. 事务传播行为(Propagation)
          • 4. 事务隔离级别(Isolation)
        • 2.3.2 配置文件中的事务管理
    • 三、事务失效的场景
    • 四、失效的解决方案:
    • 参考:

一、事务是什么

将一组操作封装成⼀个执行(封装到一起),要么全部成功,要么全部失败。

比如转账分为两个操作:

(1):A账户+100元;

(2):B账户-100元;
如果没有事务,AB两个账户是分离的,当B账户给A转账成功,B账户少了100,但是A账户却没有反应;如果使用事务,那么AB两个账户的钱数是联动的,B账户给A转账少100元与A账户增加100元这个操作,是一起成功或者失败的。

1.1 事务特性

事务有4 ⼤特性(ACID):原⼦性(Atomicity,或称不可分割性)、持久性(Consistency)、

⼀致性(Durability)和隔离性(Isolation,⼜称独⽴性)。

原⼦性:⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成。异常回滚。

⼀致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完
全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯
作。

持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

隔离性:数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务
并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。


1.2 事务隔离等级

事务隔离分为不同级别,包括读未提交(Readuncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

在这里插入图片描述

​ a、脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。(读取未提交的数据)

​ b、不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。(边读边写)

​ c、幻读指两个事务同时发生,两个事务修改数据,读到的数据不是自己开始修改的数据。幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体。(同时写,同时读)

Ps: Spring事务隔离级别多了默认(Isolation.DEFAULT)。MYSQL中默认的是Repeatable read级别

二、事务的实现

2.1 事务的操作

-- 开启事务
start transaction;-- 业务执⾏
-- 提交事务
commit;-- 回滚事务
rollback;

2.2 Spirng 编程式事务

SpringBoot 内置了两个对象:DataSourceTransactionManager ⽤来开启事务、提交或回滚事务,TransactionDefinition 是事务的属性,在开启事务的时候需要将TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus。

示例:

@RestController
public class UserController {@Resourceprivate UserService userService;// JDBC 事务管理器@Resourceprivate DataSourceTransactionManager dataSourceTransactionManager;// 定义事务属性@Resourceprivate TransactionDefinition transactionDefinition;@RequestMapping("/sava")public Object save(User user) {// 开启事务TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// 插⼊数据库int result = userService.save(user);// 提交事务dataSourceTransactionManager.commit(transactionStatus);// // 回滚事务dataSourceTransactionManager.rollback(transactionStatus);return result;}
}

2.3 Spring声明式编程

Spring声明式事务管理是通过配置和注解的方式,无需手动编写事务控制代码,实现对事务的管理。它是基于**AOP(面向切面编程)**实现的,Spring 在方法执行前后动态添加事务控制逻辑。

2.3.1 使用 @Transactional 注解

@Transactional 是 Spring 提供的用于声明事务的方法级或类级注解,它可以轻松实现事务管理。以下是使用步骤:


1. 开启事务支持

在 Spring Boot 项目中,只需在配置类上添加 @EnableTransactionManagement 注解即可开启事务支持:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {// 配置类
}

Spring Boot 通常会自动配置事务管理器,手动配置只在特殊需求下需要。


2. 在方法或类上添加 @Transactional

@Transactional 注解添加到需要事务管理的方法或类上:

  • 方法级别事务:
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void saveUser(User user) {userRepository.save(user);// 模拟异常回滚if (user.getName().equals("error")) {throw new RuntimeException("事务回滚示例");}}
}
  • 类级别事务:

@Transactional 应用于类上,表示该类中所有方法都支持事务管理:

@Service
@Transactional
public class OrderService {public void createOrder() {// 事务逻辑}
}

类上的事务属性可以被方法上的属性覆盖。


3. 事务传播行为(Propagation)

@Transactional 提供了多种传播行为,定义了当前事务方法如何与调用方事务进行交互。常见传播行为如下:

传播类型说明
REQUIRED默认值。如果当前方法存在事务,则加入;否则新建事务。
REQUIRES_NEW无论是否有事务,都会创建新事务。当前事务会被挂起,直到新事务完成。
NESTED如果存在事务,则嵌套事务运行;否则行为类似 REQUIRED。
SUPPORTS如果有事务,方法将支持事务;如果没有事务,则以非事务方式执行。
NOT_SUPPORTED当前方法不支持事务。如果存在事务,方法将在非事务环境中运行,当前事务被挂起。
MANDATORY要求必须有事务存在,否则抛出异常。
NEVER不支持事务。如果存在事务,抛出异常。

示例:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void newTransactionMethod() {// 总是新建事务
}

4. 事务隔离级别(Isolation)

通过 @Transactionalisolation 属性设置事务隔离级别:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void processData() {// 使用 READ_COMMITTED 隔离级别
}

常见隔离级别参考前文的介绍。


2.3.2 配置文件中的事务管理

如果需要手动配置事务管理器,可以通过以下方式:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}

声明式事务的优点:

  1. 代码简洁:无需手动管理事务逻辑,减少冗余代码。
  2. 灵活性高:支持通过注解设置事务传播行为、隔离级别等。
  3. 集中管理:事务管理和业务逻辑分离,便于维护。

声明式事务是 Spring 中使用最广泛的事务管理方式,可以满足绝大多数场景的需求。

三、事务失效的场景

本人踩坑!!!

@Transactional self-invocation 问题是一个常见的情况,即当一个类内部的方法调用另一个带有 @Transactional 注解的方法时,事务可能无法按预期执行。这是由于 Spring 的事务代理机制引起的。

问题的原因:

Spring 使用 AOP(Aspect-Oriented Programming)机制来管理事务。@Transactional 实际上是通过代理来实现的。代理会拦截对目标对象方法的调用,并在调用前后管理事务。但是,当一个对象内部调用自己的其他方法时,调用并没有经过代理对象,而是直接调用的本体方法,因此事务处理不会生效。这就是 self-invocation(自调用) 不触发事务的原因。

举个例子:

@Service
public class MyService {@Transactionalpublic void methodA() {// 事务生效methodB(); // 自调用}@Transactionalpublic void methodB() {// 事务不会生效,因为是自调用,没有通过代理}
}

在上面的例子中,methodA 是事务性的,因为它是外部调用的。但是 methodB 虽然带有 @Transactional 注解,由于它是 methodA 的内部调用,因此不会触发事务代理,事务不会生效。

四、失效的解决方案:

  1. @Transactional 方法移到另一个类中

将需要事务管理的方法提取到另一个类中,通过外部调用方法,确保调用经过代理,这样事务就会生效。

@Service
public class MyService {@Autowiredprivate OtherService otherService;public void methodA() {// 事务生效otherService.methodB(); // 通过代理对象调用 methodB,事务生效}
}@Service
public class OtherService {@Transactionalpublic void methodB() {// 事务生效}
}
  1. 使用 ApplicationContext 获取当前类的代理对象

可以通过 ApplicationContext 获取当前类的代理对象,然后通过代理对象来调用 @Transactional 的方法,而不是直接调用本地方法。

@Service
public class MyService {@Autowiredprivate ApplicationContext applicationContext;public void methodA() {// 通过代理对象调用 methodB,事务生效MyService proxy = applicationContext.getBean(MyService.class);proxy.methodB();}@Transactionalpublic void methodB() {// 事务生效}
}
  1. AOP 配置

还可以通过修改 Spring 的 AOP 配置来解决这个问题。例如,确保事务代理使用 CGLIB,而不是 JDK 动态代理,这样即使是自调用也会通过代理对象。默认情况下,Spring 会使用 JDK 动态代理,除非目标类没有实现接口或明确配置使用 CGLIB。

<!-- 强制使用 CGLIB 代理 -->
<tx:annotation-driven proxy-target-class="true" />
  1. 使用 @EnableAspectJAutoProxy 注解

在 Spring Boot 项目中,你可以在配置类上使用 @EnableAspectJAutoProxy(proxyTargetClass = true) 来强制使用 CGLIB 代理,以支持自调用的事务。

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {// 配置类
}

总结:

  • Self-invocation 问题导致 @Transactional 事务在类内部方法调用时不会生效。
  • 解决方法有:将事务方法移到另一个类、通过代理对象调用事务方法、或使用 CGLIB 代理等。

参考:

事务定义相关:

Spring中事务的隔离级别和传播机制_spring的事务隔离级别-CSDN博客

【Spring事务学习】事务分类 && 隔离级别 && 事务传播机制_spring事务隔离级别-CSDN博客

事务底层:

Spring之事务详解-CSDN博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词