如何合理使用Spring的事务方式

2025-05-19 15:50

本文主要是介绍如何合理使用Spring的事务方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《如何合理使用Spring的事务方式》:本文主要介绍如何合理使用Spring的事务方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教...

Spring Framework 提供了全面的事务管理功能,以确保在应用程序中操作的原子性、一致性、隔离性和持久性(即 ACID 特性)。

事务管理是重要的,尤其是在涉及到数据库操作时,以确保数据的完整性和一致性。

1、介绍

1.1、底层构造

Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的:

  • PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
  • TransactionDefinition:事务定义信息(隔离、传播、超时、只读)—通过配置如何进行事务管理。
  • TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息。

1.1.事务管理器

如何合理使用Spring的事务方式

PlatformTransactionManager该接口提供三个方法:

  • commit:提交事务
  • rollback:回滚事务
  • getTransaction:获取事务状态

1.2.事务定义信息

如何合理使用Spring的事务方式

TransactionDefinition该接口主要提供的方法:

  • getIsolationLevel:隔离级别获取
  • getPropagationBehavior:传播行为获取
  • getTimeout:获取超时时间
  • isReadOnly 是否只读(保存、更新、删除—对数据进行操作-变成可读写的,查询-设置这个属性为true,只能读不能写)

1.3.事务状态

TransactionStatus代表获取事务的状态。

	try {
		操作
	} catch (){
		rollback
	} finally {
		commit 
}

1.4.联系

用户管理事务,先配置事务管理方案TransactionDefinition、管理事务通过TransactionManager完成,TransactionManager根据 TransactionDefinition进行事务管理,

在事务运行过程中,每个时间点都可以通过获取TransactionStatus了解事务运行状态!

1.2、特点

定义

  • 事务是一个操作序列,这些操作要么全部成功,要么全部失败。无法将部分操作视为成功,其他部分则视为失败。因此,事务是一种确保数据一致性和完整性的重要手段。

ACID 特性

  • 原子性(Atomicity): 事务内的所有操作要么全部成功,要么全部失败。
  • 一致性(Consistency): 事务必须使数据库从一个一致的状态转变到另一个一致的状态。
  • 隔离性(Isolation): 一个事务的执行不能被其他事务干扰,确保并发执行下的每个事务都是在其自己的上下文中执行。
  • 持久性(Durability): 事务一旦提交,其结果便永久保存,即使系统故障也不会丢失。

1.3、原理

常见面试题:事务的原理

Spring 事务的底层实现主要使用的技术:AOP(动态代理) + ThreadLocal + try/catch。

1.动态代理AOP:

基本所有要进行逻辑增强的地方都会用到动态代理,AOP 底层也是通过动态代理实现。

2.事务同步管理器(TransactionSynchronizationManager)

Spring使用线程局部变量(ThreadLocal)来管理事务资源:

  • ThreadLocal:主要用于线程间的资源隔离,以此实现不同线程可以使用不同的数据源、隔离级别等等。
public abstract class TransactionSynchronizationManager {
    private static final ThreadLocal<Map<Object, Object>> resources =
        new NamedThreadLocal<>("Transactional resources");
    
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
        new NamedThreadLocal<>("Transaction synchronizations");
    
    private static final ThreadLocal<String> currentTransactionName =
        new NamedThreadLocal<>("Current transaction name");
    
    private static final ThreadLocal<Boolean> currentTransactionReadOnly =
        new NamedThreadLocal<>("Current transaction read-only status");
    
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
        new NamedThreadLocal<>("Current transaction isolation level");
    
    private static final ThreadLocal<Boolean> 编程actualTransactionActive =
        new NamedThreadLocal<>("Actual transaction active");
}

3.try/catch:

最终是执行 commit 还是 rollback,是根据业务逻辑处理是否抛出异常来决定。

Spring 事务的核心逻辑伪代码如下:

public void invokeWithinTransaction() {
    // 1.事务资源准备
    try {
        // 2.业务逻辑处理,也就是调用被代理的方法
    } catch (Exception e) {
        // 3.出现异常,进行回滚并将异常抛出
    } finally {
        // 现场还原:还原旧的事务信息
    }
    // 4.正常执行,进行事务的提交
    // 返回业务逻辑处理结果
}

4.流程

@Transactional方法调用
   │
   ↓
TransactionInterceptor拦截
   │
   ↓
获取事务属性(@Transactional配置)
   │
   ↓
根据传播行为决定是否创建新事务
   │
   ↓
TransactionSynchronizationManager绑定资China编程源到当前线程
   │
   ↓
执行业务方法
   │
   ┌─────────┴──────────┐
   ↓                    ↓
方法成功             方法抛出异常
   │                    │
   ↓                    ↓
提交事务              判断是否需要回滚
   │                    │
   ↓                    ↓
清理线程资源           回滚事务
   │                    │
   ↓                    ↓
返回结果              抛出异常

2. Spring 的事务管理

Spring 提供了强大的事务管理功能,包括编程式和声明式事务管理。

1、声明式事务管理

XML和注解形式)

特点

  • 易于使用:只需在方法上添加注解或在配置文件中定义事务规则即可启用事务管理。
  • 非侵入性:事务管理逻辑与业务逻辑分离,不会干扰业务逻辑代码。
  • 可配置性:可以灵活地配置事务的传播行为、隔离级别、超时和回滚规则等。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Transactional // 默认传播行为为 REQUIRED
    public void createUser(String username) {
        System.out.println("User " + username + " created.");
        // 调用另一个方法
        addUser(username);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW) // 新事务
    public void addUser(String username) {
        System.out.println("User " + username + " added in a new transaction.");
    }
    
    @Transactional(propagation = Propagation.NESTED) // 嵌套事务
    public void updateUser(String username) {
        System.out.println("Updating user: " + username);
    }
}

输出:

在调用 createUser 方法之后,如果内部调用 addUser,因为 addUser 不会使用aop代理,

这也意味着,addUser 的成功或失败不会影响到 createUser 所在的原有事务。

2、编程式事务

通过TransactionTempandroidlate手动管理事务。这里不过多讲解,实际使用较少。

在代码中显式地管理事务的开始、提交和回滚。这种方式更加灵活,但同时也增加了代码的复杂性。

有兴趣的可以参考:

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void createUser(User user) {
        TransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            userRepository.save(user);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

和JDBC相比:

  • 不需要开始或是关闭connection(可能还有try-finally的语法),相对应的,使用的是Transaction callbacks。
  • 也不需要手动的catch SQLExceptions,因为Spring会把这个异常当作runtime exceptions来处理。
  • 当然想要使用TransactionTemplate,需要先设置TransactionManager,并需要传入dataSource。这些都可以配置成Spring的bean,放到context中。

3、事务传播行为

Spring 提供了多种事务传播行为来定义一个事务方法应如何参与现有事务。常用的传播行为包括:

对事务比较积极的三个传播行为:

  • REQUIRED: 使用现有的事务,如果没有,则创建一个新的事务(默认值)。
  • REQUIRES_NEW: 总是创建一个新的事务,并挂起当前事务。
  • MANDATORY: 必须存在一个当前事务,若没有则抛出异常。

观望者:

  • NESTED: 在当前事务中执行一个嵌套事务(支持保存点)。

比较佛系的三个传播行为

  • SUPPORTS: 存在则加入,不存在,则非事务运行。
  • PROPAGATION_NOT_SUPPORTED:不能参与。
  • PROPAGATION_NEVER:不参与,参与事务,则抛出异常。

3、事务的隔离级别

1、并发事务的问题

如何合理使用Spring的事务方式

2、隔离级别

如何合理使用Spring的事务方式

如何合理使用Spring的事务方式

3、如何设置

(1) 声明式事务(通过 @Transactional 注解)

@Transactional(isolation = Isolation.READ_COMMITTED)
public void transferMoney() {
    // 业务逻辑
}

(2) 编程式事务(通过 TransactionTemplate

@Autowired
private TransactionTemplate transactionTemplate;

public void executeInTransaction() {
    transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
    transactionTemplate.execute(status -> {
        // 业务逻辑
        return null;
    });
}

4、选择隔离级别

优先使用默认值(ISOLATION_DEFAULT)

  • 除非有明确需求,否则依赖数据库默认配置,避免框架与数据库行为不一致。

平衡性能与一致性

  • 高并发场景 → READ_COMMITTED(减少锁竞争)。
  • 强一致性场景 → REPEATABLE_READSERIALIZABLE(需测试性能影响)。

5、查看隔离级别

  • 数据库日志:执行 SHOW VARIABLES LIKE 'transaction_isolation';mysql)或等效命令。
  • 代码调试:通过 TransactionSynchronizationManager.getCurrentTransactionIsolationLevel() 获取。

4、事务失效

1. 直接调用同一类中的事务方法

在同一类中直接调用被事务注解的方法会导致事务失效。因为 Spring 的 AOP 代理只在通过 Spring 管理的 Bean 进行调用时才会生效。

代码示例:

@Service
public class UserServiceTxImpl implements UserServiceTx {

   
    @Transactional(rollbackFor = RuntimeException.class) // 默认传播行为为 REQUIRED
    public void createUser(String username) {
        System.out.println("User " + username + " created.");
        // 调用另一个方法
        addUser(username);
        System.out.println("User " + username + " createUsed ");
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class) // 新事务
    public void addUser(String username) {
        System.out.println("User " + username + " added in a new transaction.");
        throw new RuntimeException("Exception in innerMethod");
    }


}

解决方案

1、可以在本bean里面直接使用spring注入的方式:不过会导致循环依赖。

2、 AopContext.currentProxy();

@Service
public class UserServiceTxImpl implements UserServiceTx {

    @Autowired
    private UserServiceTxImpl userService;

    @Transactional(rollbackFor = RuntimeException.class) // 默认传播行为为 REQUIRED
    public void createUser(String username) {
        System.out.println("User " + username + " created.");
        // 调用另一个方法
        userService.addUser(username);
        System.out.println("User " + username + " createUsed ");
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class) // 新事务
    public void addUser(String username) {
        System.out.println("User " + username + " added in a new transaction.");
        throw new RuntimeException("Exception in innerMethod");
    }


}
UserServiceTx o = (UserServiceTx) AopContext.currentProxy();

2. 直接使用 SQL 操作

如果你在事务管理的方法中执行了原生的 SQL 操作(例如通过 JDBC),而没有通过 Spring 的事务管理进行处理,事务可能会失效。

@Service
public class UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional
    public void createUser(String username) {
        System.out.println("In createUser method");

        // 直接使用 JDBC 操作,不通过 Spring 数据库访问
        jdbcTemplate.execute("INSERT INTO users (username) VALUES ('" + username + "')");

        // 模拟异常
        throw new RuntimeException("Intentional exception"); // 模拟抛出异常
    }
}

这些操作不通过 Spring 的数据访问层,将会导致事务管理操作失效。

3. 使用不正常的异常处理

Spring 只对运行时异常(RuntimeException)自动回滚,而对检查性异常(Exception)默认不回滚。

如果需要回滚需要rollbackFor指定回滚异常。

  @Transactional(rollbackFor = RuntimeException.class) // 默认传播行为为 REQUIRED
    public void createUser(String username) {
        System.out.println("User " + username + " created.")js;
        // 调用另一个方法
        userService.addUser(username);
        System.out.println("User " + username + " createUsed ");
    }

4、非public方法,目前事务 只支持pub

如何合理使用Spring的事务方式

5、mysql的存储引擎说mysaim不支持事务。

6、对异常进行try catch捕获,导致无法生效。

7、事务处理的响应超时。

总结

Spring 的事务管理功能为开发者提供了一种高效且灵活的方式来处理复杂的数据库操作,确保数据的一致性和完整性,通过注解或 XML 配置,可以很容易地查看哪些方法是事务性的,逻辑结构清晰。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持China编程(www.chinasem.cn)。

这篇关于如何合理使用Spring的事务方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154674

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置