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

 更新时间:2025年05月13日 08:34:45   作者:找不到、了  
这篇文章主要介绍了如何合理使用Spring的事务方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

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

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

1、介绍

1.1、底层构造

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

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

1.1.事务管理器

PlatformTransactionManager该接口提供三个方法:

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

1.2.事务定义信息

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绑定资源到当前线程
   │
   ↓
执行业务方法
   │
   ┌─────────┴──────────┐
   ↓                    ↓
方法成功             方法抛出异常
   │                    │
   ↓                    ↓
提交事务              判断是否需要回滚
   │                    │
   ↓                    ↓
清理线程资源           回滚事务
   │                    │
   ↓                    ↓
返回结果              抛出异常

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、编程式事务

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

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

有兴趣的可以参考:

@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、并发事务的问题

2、隔离级别

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.");
        // 调用另一个方法
        userService.addUser(username);
        System.out.println("User " + username + " createUsed ");
    }

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

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

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

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

总结

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java根据表达式获取对象中的值及设置值的例子

    Java根据表达式获取对象中的值及设置值的例子

    这篇文章主要介绍了Java根据表达式获取对象中的值及设置值的例子,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-03-03
  • mybatis的坑-integer类型为0的数据if test失效问题

    mybatis的坑-integer类型为0的数据if test失效问题

    这篇文章主要介绍了mybatis的坑-integer类型为0的数据if test失效问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • Java中的SpringAOP、代理模式、常用AspectJ注解详解

    Java中的SpringAOP、代理模式、常用AspectJ注解详解

    这篇文章主要介绍了Java中的SpringAOP、代理模式、常用AspectJ注解详解,Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务,例如审计和事务管理进行内聚性的开发,需要的朋友可以参考下
    2023-09-09
  • Java如何判断一个字符串是否包含某个字符串

    Java如何判断一个字符串是否包含某个字符串

    这篇文章主要给大家介绍了关于Java如何判断一个字符串是否包含某个字符串的相关资料,在实际编程中,经常需要判断一个字符串中是否包含某个子串,需要的朋友可以参考下
    2023-07-07
  • Spring Boot和Vue前后端分离项目架构的全过程

    Spring Boot和Vue前后端分离项目架构的全过程

    前后端分离是目前互联网开发中比较广泛使用的开发模式,主要是将前端和后端的项目业务进行分离,下面这篇文章主要给大家介绍了关于Spring Boot和Vue前后端分离项目架构的相关资料,需要的朋友可以参考下
    2022-04-04
  • Java中关键字final finally finalize的区别介绍

    Java中关键字final finally finalize的区别介绍

    这篇文章主要给大家分享的是 Java中final,finally,finalize 到底有什么区别,文章围绕final,finally,finalize的相关资料展开详细内容,具有一定的参考的价值,需要的朋友可以参考一下
    2022-04-04
  • Java实现二分搜索树的示例代码

    Java实现二分搜索树的示例代码

    二分搜索树是一颗二叉树,二分搜索树每个节点的左子树的值都小于该节点的值,每个节点右子树的值都大于该节点的值。本文将利用Java实现二分搜索树,需要的可以参考一下
    2022-03-03
  • Spring Security权限想要细化到按钮实现示例

    Spring Security权限想要细化到按钮实现示例

    这篇文章主要为大家介绍了Spring Security权限想要细化到按钮实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Spring中的EventListenerMethodProcessor组件详解

    Spring中的EventListenerMethodProcessor组件详解

    这篇文章主要介绍了Spring中的EventListenerMethodProcessor组件详解,EventListenerMethodProcessor 是 Spring 事件机制中非常重要的一个组件,它管理了一组EventListenerFactory组件,用来将应用中每个使用@EventListener注解定义的事件监听,需要的朋友可以参考下
    2023-12-12
  • RocketMQ生产者调用start发送消息原理示例

    RocketMQ生产者调用start发送消息原理示例

    这篇文章主要为大家介绍了RocketMQ生产者调用start发送消息原理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11

最新评论