Spring事务

学习资料:Spring 事务详解 | JavaGuide

Spring支持的事务本质上是数据库支持的事务,所以前提也是数据库支持事务

Spring 不直接实现事务 ,而是通过 事务管理器(PlatformTransactionManager) 来对接不同底层技术:

底层技术 对应的事务管理器
JDBC / MyBatis DataSourceTransactionManager
JPA JpaTransactionManager
Hibernate HibernateTransactionManager

所以 Spring 的事务是 抽象 + 适配 的设计典范。

Spring支持两种方式的事务管理:编程式事务管理(使用硬编码的方式)和声明式事务管理(使用注解@Transactional的方式)

编程式事务:

复制代码
@Autowired
private PlatformTransactionManager transactionManager;

public void save() {
    //1.开启事务
    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
        // 业务代码
        personDao.save(person);
        detailDao.save(detail);
        //2.提交
        transactionManager.commit(status);
    } catch (Exception e) {
        //3.回滚
         transactionManager.rollback(status);
    }
}
//这个是最原始的编程式事务:手动控制事务边界
//其实和直接用 JDBC 的 connection.setAutoCommit(false) + commit()/rollback() 是类似的,只是 Spring 把它抽象成了统一接口。



//TransactionTemplate 对 PlatformTransactionManager 的模板方法封装
@Autowired
private TransactionTemplate transactionTemplate;

public void save() {
    transactionTemplate.execute(status -> {
        // 业务代码
        personDao.save(person);
        detailDao.save(detail);
    });
}

声明式事务:

复制代码
@Service
public class AccountService {

    @Transactional
    public void transfer(int from, int to, int amount) {
        accountDao.reduce(from, amount);
        // int i = 1/0; // 如果这里出异常,整个方法会回滚!
        accountDao.add(to, amount);
    }
}

这种通过注解的方式底层原理实际是通过AOP 动态代理, 在方法执行前后插入事务管理逻辑,底层调用 PlatformTransactionManager 实现编程式事务控制。

AOP通过给这个对象创建一个代理的方式拦截这个加了注解的方法,实际走的是代理的invoke方法。这个代理对象内部仍然持有真实对象的引用。

PlatformTransactionManager的底层原理

复制代码
@Transactional
public void transfer(...) {
    accountDao.reduce(...); // 第1次 DB 操作
    accountDao.add(...);    // 第2次 DB 操作
}

要保证原子性,同一个事务中的多次数据库操作必须使用同一个数据库连接,并且该连接处于手动提交模式

而默认情况下springboot中的datasource会自动配置一个最大连接为10的连接池

spring通过把connection绑定当当前线程(ThreadLocal)来实现使用同一个连接来保证事务操作:

在开启事务的时候,使用一个叫TransactionSynchronizationManager的工具类,内部通过ThreadLocal存储事务相关资源

复制代码
@Override
protected Object doGetTransaction() {
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    
    // 尝试从 ThreadLocal 中获取已存在的 Connection
    ConnectionHolder conHolder = 
        (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);

    if (conHolder != null) {
        // 已有事务 → 复用 Connection(用于传播行为 REQUIRED)
        txObject.setConnectionHolder(conHolder, false);
    } else {
        // 新事务 → 从 DataSource 获取新连接
        Connection con = dataSource.getConnection();
        con.setAutoCommit(false); // 关键!关闭自动提交
        
        // 绑定到当前线程的 ThreadLocal
        ConnectionHolder newConHolder = new ConnectionHolder(con);
        TransactionSynchronizationManager.bindResource(dataSource, newConHolder);
        txObject.setConnectionHolder(newConHolder, true);
    }
    return txObject;
}

但是需要注意的是,ThreadLocal绑定的资源不会自动传递到子线程,

所以在异步任务、线程池等中,事务会失效,除非手动传递连接。

(这也是一次面试中面试官问到的,如果事务中开了一个子线程的话,事务还能生效吗)

相关推荐
代码游侠1 小时前
应用——智能配电箱监控系统
linux·服务器·数据库·笔记·算法·sqlite
阿里巴巴P8资深技术专家1 小时前
基于 Spring AI 和 Redis 向量库的智能对话系统实践
人工智能·redis·spring
ps酷教程1 小时前
HttpPostRequestDecoder源码浅析
java·http·netty
闲人编程1 小时前
消息通知系统实现:构建高可用、可扩展的企业级通知服务
java·服务器·网络·python·消息队列·异步处理·分发器
!chen1 小时前
EF Core自定义映射PostgreSQL原生函数
数据库·postgresql
霖霖总总2 小时前
[小技巧14]MySQL 8.0 系统变量设置全解析:SET GLOBAL、SET PERSIST 与 SET PERSIST_ONLY 的区别与应用
数据库·mysql
马克学长2 小时前
SSM校园食堂订餐系统531p9(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·ssm 校园食堂订餐系统
alonewolf_992 小时前
深入剖析MySQL索引底层:B+树、联合索引与跳跃扫描原理全解
数据库·b树·mysql
栈与堆2 小时前
LeetCode-1-两数之和
java·数据结构·后端·python·算法·leetcode·rust
oMcLin2 小时前
如何在 AlmaLinux 9 上配置并优化 Redis 集群,支持高并发的实时数据缓存与快速查询?
数据库·redis·缓存