乐于分享
好东西不私藏

Spring 事务管理机制深度解析:从源码到实战

Spring 事务管理机制深度解析:从源码到实战

基于 Spring Framework 6.x / Spring Boot 3.x,JDK 17+


真实事故现场

订单表有数据,库存却没扣减——来看这段代码,你能发现问题吗?

// 问题代码,你能看出来吗?
@Service
publicclassOrderService {

@Transactional
publicvoidcreateOrder(OrderDTO dto) {
// 1. 插入订单
        orderMapper.insert(dto.toOrder());
// 2. 调用同类的方法扣减库存
        deductStock(dto.getSkuId(), dto.getQuantity()); // ← 事务失效!
    }

@Transactional(propagation = Propagation.REQUIRES_NEW)
publicvoiddeductStock(Long skuId, Integer qty) {
// 扣减库存,抛异常后理应回滚,但实际没有回滚
        inventoryMapper.deduct(skuId, qty);
if (qty > stock) thrownewStockException("库存不足");
    }
}

根因deductStock 是同类内部调用,Spring AOP 代理未拦截,REQUIRES_NEW 实际上没有生效。

这不是特例——这是 Spring 事务开发者最常踩的坑,也是本文要彻底讲透的核心之一。


事务体系总览

三层架构

┌─────────────────────────────────────────────┐
│             业务代码 (@Transactional)          │
├─────────────────────────────────────────────┤
│         Spring 事务抽象层                      │
│  PlatformTransactionManager                  │
│  TransactionDefinition                       │
│  TransactionStatus                           │
├─────────────────────────────────────────────┤
│         具体实现层                              │
│  DataSourceTransactionManager (JDBC/MyBatis) │
│  JpaTransactionManager (JPA/Hibernate)       │
│  JtaTransactionManager (分布式)               │
└─────────────────────────────────────────────┘

两种使用方式对比

方式
优点
缺点
推荐场景
声明式(@Transactional
代码简洁、非侵入
存在失效场景
95% 的普通业务
编程式(TransactionTemplate
灵活可控、无失效问题
代码冗余
精细控制事务边界

核心接口源码解读

1. PlatformTransactionManager

这是 Spring 事务的核心抽象,只有 3 个方法:

// org.springframework.transaction.PlatformTransactionManager
publicinterfacePlatformTransactionManagerextendsTransactionManager {

/**
     * 根据传播行为决定:开启新事务 or 加入已有事务 or 挂起当前事务
     * 返回的 TransactionStatus 代表当前事务状态
     */

    TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;

/**
     * 提交事务
     * 注意:如果 status.isRollbackOnly() = true,会自动回滚而不是提交
     */

voidcommit(TransactionStatus status)throws TransactionException;

/**
     * 回滚事务
     */

voidrollback(TransactionStatus status)throws TransactionException;
}

关键细节commit 方法并不一定真的提交!来看源码:

// AbstractPlatformTransactionManager.java
@Override
publicfinalvoidcommit(TransactionStatus status)throws TransactionException {
// ...
DefaultTransactionStatusdefStatus= (DefaultTransactionStatus) status;

// 如果已被标记为 rollback-only,直接回滚
if (defStatus.isLocalRollbackOnly()) {
        processRollback(defStatus, false);
return;
    }

// 如果全局 rollback-only(嵌套事务场景),也回滚
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
        processRollback(defStatus, true); // ← 触发 UnexpectedRollbackException
return;
    }

// 真正提交
    processCommit(defStatus);
}

💡 这就是为什么内层事务 setRollbackOnly() 后,外层 commit() 会抛 UnexpectedRollbackException 的根本原因。


2. TransactionDefinition

定义事务的”规格”,包含传播行为、隔离级别、超时、只读等:

// org.springframework.transaction.TransactionDefinition
publicinterfaceTransactionDefinition {

// ========== 7种传播行为 ==========
intPROPAGATION_REQUIRED=0;      // 默认:有则加入,无则新建
intPROPAGATION_SUPPORTS=1;      // 有则加入,无则非事务执行
intPROPAGATION_MANDATORY=2;     // 必须在事务中,无则抛异常
intPROPAGATION_REQUIRES_NEW=3;  // 始终新建,挂起当前
intPROPAGATION_NOT_SUPPORTED=4// 非事务执行,挂起当前
intPROPAGATION_NEVER=5;         // 不允许事务,有则抛异常
intPROPAGATION_NESTED=6;        // 嵌套事务(SavePoint)

// ========== 4种隔离级别 ==========
intISOLATION_DEFAULT= -1;          // 使用数据库默认
intISOLATION_READ_UNCOMMITTED=1;  // 读未提交
intISOLATION_READ_COMMITTED=2;    // 读已提交
intISOLATION_REPEATABLE_READ=4;   // 可重复读(MySQL默认)
intISOLATION_SERIALIZABLE=8;      // 串行化

intTIMEOUT_DEFAULT= -1// 不超时

// 默认传播行为
defaultintgetPropagationBehavior() {
return PROPAGATION_REQUIRED;
    }

// 默认隔离级别(由数据库决定)
defaultintgetIsolationLevel() {
return ISOLATION_DEFAULT;
    }

// 是否只读(只读事务可优化数据库性能)
defaultbooleanisReadOnly() {
returnfalse;
    }
}

3. TransactionStatus

代表一个正在运行的事务的当前状态:

// org.springframework.transaction.TransactionStatus
publicinterfaceTransactionStatusextendsTransactionExecution, SavepointManager, Flushable {

// 是否有 SavePoint(NESTED 传播时使用)
booleanhasSavepoint();

// 刷新底层连接(JPA 场景)
@Override
voidflush();
}

// TransactionExecution 中定义:
publicinterfaceTransactionExecution {
booleanisNewTransaction();   // 是否是新开的事务
voidsetRollbackOnly();       // 标记为必须回滚
booleanisRollbackOnly();     // 是否被标记回滚
booleanisCompleted();        // 事务是否已结束
}

AOP代理原理与源码追踪

@Transactional 是如何生效的?

@Transactional 本质上是 AOP 的一个应用,Spring 在启动时通过 AutoProxyCreator 为带注解的 Bean 创建代理对象。

Bean 创建流程:
BeanFactory.getBean()
    → AbstractAutowireCapableBeanFactory.createBean()
        → AbstractAutoProxyCreator.postProcessAfterInitialization()
            → wrapIfNecessary()    ← 判断是否需要代理
                → createProxy()   ← 创建 JDK 动态代理 or CGLIB 代理

关键源码——判断是否需要代理:

// AbstractAutoProxyCreator.java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// ...
// 获取当前 Bean 所有适用的 Advisors(增强器)
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(
        bean.getClass(), beanName, null);

// 如果有 Advisor(比如事务的 BeanFactoryTransactionAttributeSourceAdvisor)
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理
Objectproxy= createProxy(
            bean.getClass(), beanName, specificInterceptors, newSingletonTargetSource(bean));
return proxy;
    }

this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

CGLIB 代理 vs JDK 动态代理:

// DefaultAopProxyFactory.java
@Override
public AopProxy createAopProxy(AdvisedSupport config)throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() 
            || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
// 如果是接口或已经是 Proxy 类,用 JDK 动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) 
                || ClassUtils.isLambdaClass(targetClass)) {
returnnewJdkDynamicAopProxy(config);
        }
// 否则用 CGLIB
returnnewObjenesisCglibAopProxy(config);
    }
else {
returnnewJdkDynamicAopProxy(config);
    }
}

💡 Spring Boot 默认 proxyTargetClass=true,所以大多数情况下用 CGLIB 代理,即使实现了接口也是。


TransactionInterceptor 执行链路

TransactionInterceptor 是事务 AOP 的核心拦截器,每次调用带 @Transactional 的方法都会经过它:

// TransactionInterceptor.java
publicclassTransactionInterceptorextendsTransactionAspectSupport
implementsMethodInterceptor, Serializable {

@Override
@Nullable
public Object invoke(MethodInvocation invocation)throws Throwable {
// 获取目标类
        Class<?> targetClass = (invocation.getThis() != null
            ? AopUtils.getTargetClass(invocation.getThis()) : null);

// 核心逻辑委托给父类 TransactionAspectSupport
return invokeWithinTransaction(
            invocation.getMethod(), targetClass, 
newCoroutinesInvocationCallback() {
@Override
public Object proceedWithInvocation()throws Throwable {
return invocation.proceed(); // ← 调用实际业务方法
                }
// ...
            }
        );
    }
}

真正的核心在父类 TransactionAspectSupport.invokeWithinTransaction()

// TransactionAspectSupport.java(精简版,突出核心逻辑)
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation)
throws Throwable {

// 1. 获取事务属性(解析 @Transactional 注解的所有配置)
TransactionAttributeSourcetas= getTransactionAttributeSource();
finalTransactionAttributetxAttr= (tas != null
        ? tas.getTransactionAttribute(method, targetClass) : null);

// 2. 确定使用哪个 TransactionManager
finalTransactionManagertm= determineTransactionManager(txAttr);

// ========== 响应式事务(WebFlux 场景)==========
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager rtm) {
// ... 响应式处理逻辑(略)
    }

// ========== 普通事务 ==========
PlatformTransactionManagerptm= asPlatformTransactionManager(tm);
finalStringjoinpointIdentification= methodIdentification(method, targetClass, txAttr);

if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 3. 根据传播行为决定:开启新事务 or 加入已有事务 or 挂起
TransactionInfotxInfo= createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

        Object retVal;
try {
// 4. 执行实际的业务方法
            retVal = invocation.proceedWithInvocation();
        }
catch (Throwable ex) {
// 5. 异常处理:决定是回滚还是提交
            completeTransactionAfterThrowing(txInfo, ex);
throw ex;
        }
finally {
// 6. 清理 ThreadLocal 中的事务信息
            cleanupTransactionInfo(txInfo);
        }

// ...

// 7. 提交事务
        commitTransactionAfterReturning(txInfo);
return retVal;
    }
// ...
}

异常处理逻辑源码

// TransactionAspectSupport.java
protectedvoidcompleteTransactionAfterThrowing(
@Nullable TransactionInfo txInfo, Throwable ex)
 {

if (txInfo != null && txInfo.getTransactionStatus() != null) {
// 判断是否应该回滚(根据 rollbackFor 配置)
if (txInfo.transactionAttribute != null
                && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 回滚
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
catch (TransactionSystemException ex2) {
                ex2.initApplicationException(ex);
throw ex2;
            }
// ...
        }
else {
// ⚠️ 异常不匹配 rollbackFor,依然提交!
// 这就是"异常被吞不回滚"的根本原因
try {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
// ...
        }
    }
}

rollbackOn 的默认规则:

// DefaultTransactionAttribute.java
@Override
publicbooleanrollbackOn(Throwable ex) {
// 默认只对 RuntimeException 和 Error 回滚
// checked exception(如 IOException)不回滚!
return (ex instanceof RuntimeException || ex instanceof Error);
}

💡 这就是为什么抛出 IOException 默认不会回滚,需要显式指定 @Transactional(rollbackFor = Exception.class)


事务信息的 ThreadLocal 存储

// TransactionAspectSupport.java
// 事务信息绑定在 ThreadLocal 上,这也解释了为什么多线程会导致事务失效
privatestaticfinal ThreadLocal<TransactionInfo> transactionInfoHolder =
newNamedThreadLocal<>("Current aspect-driven transaction");

protectedstatic TransactionInfo currentTransactionInfo()throws NoTransactionException {
return transactionInfoHolder.get();
}

7种传播行为源码解读

核心处理入口

// AbstractPlatformTransactionManager.java
@Override
publicfinal TransactionStatus getTransaction(
@Nullable TransactionDefinition definition)
throws TransactionException {

TransactionDefinitiondef= (definition != null
        ? definition : TransactionDefinition.withDefaults());

// 获取当前线程的事务对象(DataSource连接等)
Objecttransaction= doGetTransaction();
booleandebugEnabled= logger.isDebugEnabled();

// ========== 当前已有事务 ==========
if (isExistingTransaction(transaction)) {
// handleExistingTransaction 处理 SUPPORTS/MANDATORY/REQUIRES_NEW/
// NOT_SUPPORTED/NEVER/NESTED 等场景
return handleExistingTransaction(def, transaction, debugEnabled);
    }

// ========== 超时校验 ==========
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
thrownewInvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
    }

// ========== 当前没有事务 ==========
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
// MANDATORY:没有外层事务,直接抛异常
thrownewIllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
    }
elseif (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED 
            || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW
            || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// REQUIRED/REQUIRES_NEW/NESTED:没有事务则新建
SuspendedResourcesHoldersuspendedResources= suspend(null);
try {
return startTransaction(def, transaction, debugEnabled, suspendedResources);
        }
// ...
    }
else {
// SUPPORTS/NOT_SUPPORTED/NEVER:以非事务方式执行
// ...
return prepareTransactionStatus(def, nulltrue, newSynchronization, debugEnabled, null);
    }
}

REQUIRES_NEW:挂起当前事务

// AbstractPlatformTransactionManager.handleExistingTransaction()
// 当传播行为是 REQUIRES_NEW 时:
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// 1. 挂起当前事务(把当前连接从 ThreadLocal 移除,保存到 SuspendedResources)
SuspendedResourcesHoldersuspendedResources= suspend(transaction);
try {
// 2. 开启一个全新的独立事务(新的数据库连接)
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
    }
catch (RuntimeException | Error beginEx) {
        resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
    }
}

NESTED:使用 SavePoint

// 当传播行为是 NESTED 时:
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 检查是否支持 SavePoint
if (!isNestedTransactionAllowed()) {
thrownewNestedTransactionNotSupportedException(...);
    }

// 在当前事务中创建 SavePoint
DefaultTransactionStatusstatus= prepareTransactionStatus(
        definition, transaction, falsefalse, debugEnabled, null);
    status.createAndHoldSavepoint(); // ← 核心:创建 SavePoint
return status;
}

传播行为全对比

传播行为
有外层事务
无外层事务
独立事务?
典型场景
REQUIRED

(默认)
加入外层
新建
普通业务方法
SUPPORTS
加入外层
非事务执行
查询方法
MANDATORY
加入外层
抛异常
必须在事务中调用
REQUIRES_NEW
挂起外层,新建
新建
日志、审计(不受外层影响)
NOT_SUPPORTED
挂起外层,非事务
非事务执行
不需要事务的操作
NEVER
抛异常
非事务执行
禁止事务
NESTED
SavePoint 嵌套
新建
部分

(共用连接)
批量处理,单条失败不影响整体

4种隔离级别深度解析

三大并发问题

问题
描述
示例
脏读
读到未提交的数据
A 修改数据未提交,B 读到了,A 回滚后 B 的数据是”脏”的
不可重复读
同一事务内两次读结果不同
A 读数据,B 修改并提交,A 再读结果不同
幻读
同一查询两次结果行数不同
A 查询某范围,B 插入并提交,A 再查多了行

隔离级别对比

隔离级别
脏读
不可重复读
幻读
性能
MySQL默认
READ_UNCOMMITTED
✅ 可能
✅ 可能
✅ 可能
最高
READ_COMMITTED
❌ 解决
✅ 可能
✅ 可能
Oracle默认
REPEATABLE_READ
❌ 解决
❌ 解决
✅ 可能*
MySQL默认
SERIALIZABLE
❌ 解决
❌ 解决
❌ 解决
最低

*MySQL InnoDB 通过 MVCC + 间隙锁,在 REPEATABLE_READ 下也能解决大部分幻读场景。

Spring 设置隔离级别的源码

// DataSourceTransactionManager.doBegin()
@Override
protectedvoiddoBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObjecttxObject= (DataSourceTransactionObject) transaction;
Connectioncon=null;

try {
// 获取数据库连接
if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
ConnectionnewCon= obtainDataSource().getConnection();
            txObject.setConnectionHolder(newConnectionHolder(newCon), true);
        }

        con = txObject.getConnectionHolder().getConnection();

// ========== 设置隔离级别 ==========
IntegerpreviousIsolationLevel= DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);
        txObject.setReadOnly(definition.isReadOnly());

// ========== 关闭自动提交,开启事务 ==========
if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            con.setAutoCommit(false); // ← 事务开始的真正标志
        }

// ========== 设置只读 ==========
        prepareTransactionalConnection(con, definition);

// ========== 超时设置 ==========
inttimeout= determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
            txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
        }

// 绑定连接到 ThreadLocal
if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
        }
    }
catch (Throwable ex) {
// 失败则释放连接
if (txObject.isNewConnectionHolder()) {
            DataSourceUtils.releaseConnection(con, obtainDataSource());
        }
thrownewCannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
    }
}

8大失效场景与修复方案

失效场景一:同类内部调用(最高频!)

// ❌ 失效:this.deductStock() 调用的是原始对象,绕过了代理
@Transactional
publicvoidcreateOrder(OrderDTO dto) {
    orderMapper.insert(dto.toOrder());
this.deductStock(dto.getSkuId(), dto.getQuantity()); // 代理失效
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
publicvoiddeductStock(Long skuId, Integer qty) { ... }

修复方案一:注入自身代理

@Service
publicclassOrderService {

@Autowired
private OrderService self; // Spring 注入的是代理对象

@Transactional
publicvoidcreateOrder(OrderDTO dto) {
        orderMapper.insert(dto.toOrder());
        self.deductStock(dto.getSkuId(), dto.getQuantity()); // 通过代理调用
    }
}

修复方案二:通过 AopContext 获取代理

// 需要开启:@EnableAspectJAutoProxy(exposeProxy = true)
@Transactional
publicvoidcreateOrder(OrderDTO dto) {
    orderMapper.insert(dto.toOrder());
    ((OrderService) AopContext.currentProxy()).deductStock(...);
}

修复方案三:拆分到不同 Service(推荐)

@Service
publicclassOrderService {
@Autowired
private InventoryService inventoryService; // 不同类,代理正常生效

@Transactional
publicvoidcreateOrder(OrderDTO dto) {
        orderMapper.insert(dto.toOrder());
        inventoryService.deductStock(dto.getSkuId(), dto.getQuantity());
    }
}

失效场景二:方法非 public

// ❌ 失效:Spring 默认不代理 private/protected 方法
@Transactional
privatevoidsaveUser(User user) { ... }

源码依据:

// AbstractFallbackTransactionAttributeSource.java
@Nullable
private TransactionAttribute computeTransactionAttribute(Method method, 
@Nullable Class<?> targetClass)
 {

// 非 public 方法直接返回 null,不处理事务
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
returnnull;
    }
// ...
}

修复:改为 public 方法。


失效场景三:异常被捕获吞掉

// ❌ 失效:异常被捕获,Spring 感知不到,直接提交事务
@Transactional
publicvoidtransfer(Long fromId, Long toId, BigDecimal amount) {
try {
        accountMapper.deduct(fromId, amount);
        accountMapper.add(toId, amount);
    } catch (Exception e) {
        log.error("转账失败", e);
// ← 异常没有重新抛出!Spring 认为一切正常,提交事务
    }
}

修复方案一:重新抛出异常

@Transactional
publicvoidtransfer(Long fromId, Long toId, BigDecimal amount) {
try {
        accountMapper.deduct(fromId, amount);
        accountMapper.add(toId, amount);
    } catch (Exception e) {
        log.error("转账失败", e);
throw e; // 重新抛出
    }
}

修复方案二:手动标记回滚

@Transactional
publicvoidtransfer(Long fromId, Long toId, BigDecimal amount) {
try {
        accountMapper.deduct(fromId, amount);
        accountMapper.add(toId, amount);
    } catch (Exception e) {
        log.error("转账失败", e);
// 手动标记当前事务必须回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

失效场景四:Checked Exception 不触发回滚

// ❌ 只有 RuntimeException 才默认回滚
@Transactional
publicvoidimportData(MultipartFile file)throws IOException {
// 抛出 IOException(checked exception),默认不回滚!
    dataMapper.insertBatch(parseFile(file));
}

修复:显式指定 rollbackFor

@Transactional(rollbackFor = Exception.class)
publicvoidimportData(MultipartFile file)throws IOException {
    dataMapper.insertBatch(parseFile(file));
}

失效场景五:Bean 未被 Spring 管理

// ❌ 失效:new 出来的对象不是 Spring Bean,不走 AOP 代理
publicclassDataImporter {

@Transactional// 没用!
publicvoiddoImport() { ... }

publicstaticvoidmain(String[] args) {
newDataImporter().doImport(); // 直接 new,事务失效
    }
}

修复:确保通过 Spring 容器获取 Bean,加上 @Component/@Service 注解。


失效场景六:多线程环境

// ❌ 失效:子线程有自己的 ThreadLocal,与主线程不共享事务
@Transactional
publicvoidbatchProcess(List<Task> tasks) {
    tasks.parallelStream().forEach(task -> {
        taskMapper.process(task); // 子线程中,事务失效!
    });
}

修复方案:使用编程式事务,为每个线程独立开启事务

publicvoidbatchProcess(List<Task> tasks) {
    tasks.parallelStream().forEach(task -> {
        transactionTemplate.execute(status -> {
            taskMapper.process(task);
returnnull;
        });
    });
}

失效场景七:数据库引擎不支持事务

-- ❌ MyISAM 不支持事务
CREATETABLE `orders` (
  ...
) ENGINE=MyISAM; -- 改为 InnoDB

失效场景八:事务传播行为配置错误

// ❌ PROPAGATION_NOT_SUPPORTED 会挂起当前事务,在非事务中执行
@Transactional(propagation = Propagation.NOT_SUPPORTED)
publicvoidqueryForReport() {
// 本意是只读查询,但会挂起外层调用者的事务,可能导致数据不一致
}

编程式事务 API

TransactionTemplate(推荐)

@Service
publicclassOrderService {

@Autowired
private TransactionTemplate transactionTemplate;

publicvoidcreateOrder(OrderDTO dto) {
// 有返回值的场景
LongorderId= transactionTemplate.execute(status -> {
Longid= orderMapper.insert(dto.toOrder());
            inventoryMapper.deduct(dto.getSkuId(), dto.getQuantity());
return id;
        });

// 无返回值的场景
        transactionTemplate.executeWithoutResult(status -> {
            orderMapper.insert(dto.toOrder());
try {
                inventoryMapper.deduct(dto.getSkuId(), dto.getQuantity());
            } catch (Exception e) {
// 手动标记回滚
                status.setRollbackOnly();
            }
        });
    }
}

自定义 TransactionTemplate

@Configuration
publicclassTransactionConfig {

@Bean
public TransactionTemplate shortTransactionTemplate(
            PlatformTransactionManager transactionManager)
 {
TransactionTemplatetemplate=newTransactionTemplate(transactionManager);
        template.setTimeout(5); // 5秒超时
        template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
return template;
    }
}

底层 API(极精细控制)

@Service
publicclassManualTransactionService {

@Autowired
private PlatformTransactionManager transactionManager;

publicvoidexecuteWithManualTransaction() {
DefaultTransactionDefinitiondef=newDefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
        def.setTimeout(10);

TransactionStatusstatus= transactionManager.getTransaction(def);
try {
// 业务逻辑
            doBusinessLogic();
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
throw e;
        }
    }
}

Spring Boot 事务配置实战

基础配置

# application.yml
spring:
datasource:
url:jdbc:mysql://localhost:3306/db?serverTimezone=UTC
username:root
password:root
hikari:
maximum-pool-size:20
minimum-idle:5
connection-timeout:30000
idle-timeout:600000
max-lifetime:1800000

jpa:
properties:
hibernate:
default_batch_fetch_size:100

只读事务优化

// 只读事务:跳过脏检查,提升查询性能约10-30%
@Transactional(readOnly = true)
public List<Order> queryOrders(OrderQuery query) {
return orderRepository.findAll(query.toSpec());
}

多数据源事务

@Configuration
publicclassMultiDataSourceConfig {

@Primary
@Bean
public PlatformTransactionManager primaryTransactionManager(
@Qualifier("primaryDataSource") DataSource dataSource)
 {
returnnewDataSourceTransactionManager(dataSource);
    }

@Bean
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryDataSource") DataSource dataSource)
 {
returnnewDataSourceTransactionManager(dataSource);
    }
}

// 指定使用哪个 TransactionManager
@Transactional(transactionManager = "secondaryTransactionManager")
publicvoidsaveToSecondaryDB(Data data) { ... }

分布式事务选型指南

5种方案对比

方案
一致性
可用性
性能
实现复杂度
适用场景
2PC
强一致
传统金融系统
TCC
最终一致
资金类业务
Saga
最终一致
长流程业务
本地消息表
最终一致
异步解耦场景
Seata AT
最终一致
通用微服务

Seata AT 模式接入示例

// pom.xml
// <dependency>
//     <groupId>com.alibaba.cloud</groupId>
//     <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
// </dependency>

@Service
publicclassOrderService {

@Autowired
private InventoryFeignClient inventoryClient;

// @GlobalTransactional 替代 @Transactional 实现跨服务分布式事务
@GlobalTransactional(rollbackFor = Exception.class)
publicvoidcreateOrder(OrderDTO dto) {
// 本地事务:插入订单
        orderMapper.insert(dto.toOrder());

// 远程调用:扣减库存(跨服务)
// 如果此处抛异常,Seata 会通过 undo log 自动回滚上面的订单插入
        inventoryClient.deductStock(dto.getSkuId(), dto.getQuantity());
    }
}

生产检查清单

在上线前,逐项过一遍:

事务配置检查:
□ 所有事务方法都是 public
□ 同类调用已改为注入代理或拆分 Service
□ 异常处理中没有"吞异常"的情况
□ Checked exception 已配置 rollbackFor = Exception.class
□ 数据库表引擎为 InnoDB(MySQL)

性能优化检查:
□ 纯查询方法已添加 readOnly = true
□ 大事务已分拆为多个小事务
□ 避免在事务中调用远程接口(HTTP/RPC)
□ 事务超时时间已设置,避免连接池耗尽

监控告警检查:
□ 事务超时告警已配置
□ 慢事务日志已开启
□ 数据库连接池监控已接入

总结

核心知识点
关键结论
事务底层原理
AOP 代理 + ThreadLocal 绑定连接
默认回滚规则
只对 RuntimeException 和 Error 回滚
最高频失效原因
同类内部调用,代理被绕过
REQUIRES_NEW vs NESTED
REQUIRES_NEW 独立连接完全隔离,NESTED 共用连接用 SavePoint
只读事务
跳过脏检查,显著提升查询性能
多线程事务
ThreadLocal 不跨线程,必须用编程式事务
分布式事务首选
Seata AT 模式,低侵入、易上手