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) |
|
|
|
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, null, true, 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, false, false, debugEnabled, null);
status.createAndHoldSavepoint(); // ← 核心:创建 SavePoint
return status;
}
传播行为全对比
|
|
|
|
|
|
|---|---|---|---|---|
REQUIRED
|
|
|
|
|
SUPPORTS |
|
|
|
|
MANDATORY |
|
|
|
|
REQUIRES_NEW |
|
|
是 |
|
NOT_SUPPORTED |
|
|
|
|
NEVER |
|
|
|
|
NESTED |
|
|
部分
|
|
4种隔离级别深度解析
三大并发问题
|
|
|
|
|---|---|---|
| 脏读 |
|
|
| 不可重复读 |
|
|
| 幻读 |
|
|
隔离级别对比
|
|
|
|
|
|
|
|---|---|---|---|---|---|
READ_UNCOMMITTED |
|
|
|
|
|
READ_COMMITTED |
|
|
|
|
|
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)
□ 事务超时时间已设置,避免连接池耗尽
监控告警检查:
□ 事务超时告警已配置
□ 慢事务日志已开启
□ 数据库连接池监控已接入
总结
|
|
|
|---|---|
|
|
|
|
|
RuntimeException 和 Error 回滚 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
夜雨聆风