TransactionManager
- [TransactionManager 是什么](#TransactionManager 是什么)
- 核心接口与标准
-
- [JTA 标准的 TransactionManager 接口](#JTA 标准的 TransactionManager 接口)
- [Spring 的 PlatformTransactionManager](#Spring 的 PlatformTransactionManager)
- 事务隔离级别
- 事务传播行为
- [TransactionManager 常见问题](#TransactionManager 常见问题)
- [@EnableTransactionManagement、@Transactional 与 TransactionManager的区别与联系](#@EnableTransactionManagement、@Transactional 与 TransactionManager的区别与联系)
TransactionManager 是什么
TransactionManager 直译是事务管理器,它是 Java 中负责统一管理事务全生命周期的核心组件,它封装了所有和事务相关的操作,以及事务规则的执行,都由它来统一调度和控制。解决了传统手动管理事务(如 JDBC 中 conn.setAutoCommit(false))的繁琐、易出错问题,尤其在多数据源、分布式场景下提供了统一的事务管理抽象。
根据管理的事务范围(资源协调边界),Java 中的 TransactionManager 主要分为两类:
-
本地事务管理器(Local TransactionManager)
用于协调单一独立资源节点内的事务,仅保障该资源节点内的事务 ACID 特性,依赖资源自身的原生事务能力实现。
典型实现:
- DataSourceTransactionManager:适配基于 JDBC 的关系型数据源(MySQL/Oracle 等),Spring 生态最常用的本地事务管理器;
- HibernateTransactionManager:适配 Hibernate ORM 框架,与 Hibernate Session 生命周期绑定;
- JpaTransactionManager:适配 JPA 规范实现(如 Hibernate JPA、EclipseLink);
- RedisTransactionManager:适配 Redis 单实例/单机集群的事务操作;
- JtaTransactionManager(本地模式):仅适配支持 JTA 规范的单一本地数据源(非分布式场景);
-
分布式事务管理器(Distributed TransactionManager)
针对跨多个独立资源节点(多数据源、跨微服务节点等)的事务管理,核心目标是保证分布式场景下多个资源的事务一致性。
典型实现(按一致性保障策略细分):
- 强一致性分布式事务管理器(基于 JTA/XA 标准):Atomikos、Bitronix、JBossTS(Narayana);
- 柔性一致性分布式事务管理器(新一代):
Seata(阿里开源):支持 AT/TCC/SAGA/XA 四种模式,适配微服务场景;
RocketMQ 事务消息:基于消息最终一致性的分布式事务方案;
ShardingSphere-JDBC:分库分表场景下的分布式事务管理器(支持 XA/SAGA/TCC);
核心接口与标准
JTA 标准的 TransactionManager 接口
JTA 是 Java EE 定义的事务规范,javax.transaction.TransactionManager 是其核心接口,定义了事务管理的核心方法:
java
import javax.transaction.TransactionManager;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.RollbackException;
public class JtaTransactionDemo {
public static void main(String[] args) {
// 获取JTA事务管理器实例(不同实现方式获取方式不同)
TransactionManager tm = com.atomikos.icatch.jta.UserTransactionManager();
try {
// 1. 开启事务
tm.begin();
// 2. 执行业务逻辑(操作多个数据源)
doBusinessLogic();
// 3. 提交事务
tm.commit();
} catch (NotSupportedException | SystemException e) {
// 事务开启失败
e.printStackTrace();
} catch (RollbackException e) {
// 事务提交失败(已自动回滚)
e.printStackTrace();
} catch (Exception e) {
// 业务异常,手动回滚
try {
tm.rollback();
} catch (SystemException se) {
se.printStackTrace();
}
}
}
private static void doBusinessLogic() {
// 操作多个数据源的业务逻辑
}
}
核心方法说明:
| 方法 | 作用 |
|---|---|
| begin() | 开启一个新事务 |
| commit() | 提交当前事务(仅当事务状态正常时) |
| rollback() | 回滚当前事务 |
| setRollbackOnly() | 标记事务为只能回滚(后续无法提交) |
| suspend() | 挂起当前事务,返回挂起的事务对象 |
| resume(Transaction tx) | 恢复被挂起的事务 |
| getStatus() | 获取事务当前状态(如活动、已提交、已回滚) |
Spring 的 PlatformTransactionManager
Spring 对事务管理器做了抽象,提供 org.springframework.transaction.PlatformTransactionManager 接口,屏蔽了底层不同事务管理器的差异,是 Spring 事务管理的核心,接口源码如下:
java
public interface PlatformTransactionManager {
// 1. 获取/开启事务
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
// 2. 提交事务
void commit(TransactionStatus status) throws TransactionException;
// 3. 回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
getTransaction():获取/开启事务
java
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
入参 TransactionDefinition 定义了事务的所有属性(传播行为、隔离级别、超时时间等),接口核心方法如下(同时定义了属性的默认常量,如 PROPAGATION_REQUIRED(默认传播行为)、ISOLATION_DEFAULT(默认隔离级别)):
java
public interface TransactionDefinition {
// 传播行为
int getPropagationBehavior();
// 隔离级别
int getIsolationLevel();
// 超时时间(秒)
int getTimeout();
// 是否只读
boolean isReadOnly();
// 事务名称
@Nullable
String getName();
}
返回值 TransactionStatus:表示当前事务的实时状态,核心方法如下:
java
public interface TransactionStatus extends TransactionExecution, SavepointManager {
// 是否是新事务
boolean isNewTransaction();
// 是否有保存点
boolean hasSavepoint();
// 设置为仅回滚
void setRollbackOnly();
// 是否标记为仅回滚
boolean isRollbackOnly();
// 刷新状态
void flush();
// 是否已完成
boolean isCompleted();
}
commit():提交事务
java
void commit(TransactionStatus status) throws TransactionException;
PlatformTransactionManager 接口仅定义方法声明,通用实现逻辑在抽象类 AbstractPlatformTransactionManager 中,核心流程如下:
java
// 摘自 AbstractPlatformTransactionManager
public final void commit(TransactionStatus status) throws TransactionException {
// 1. 校验:事务已完成则抛异常(避免重复提交/回滚)
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
// 2. 若标记为"仅回滚",则执行回滚而非提交
if (status.isRollbackOnly()) {
rollback(status);
return;
}
// 3. 执行实际提交逻辑(由具体实现类重写doCommit)
try {
doCommit(status);
} catch (TransactionException ex) {
// 提交失败:尝试回滚,避免数据不一致
try {
doRollback(status);
} catch (RuntimeException | Error rbex) {
logger.error("Commit exception overridden by rollback exception", ex);
throw rbex; // 优先抛出回滚异常,避免覆盖原提交异常
}
throw ex;
} finally {
// 4. 清理事务资源(解绑线程、释放连接等)
cleanupAfterCompletion(status);
}
}
rollback():回滚事务
java
void rollback(TransactionStatus status) throws TransactionException;
同样,通用实现逻辑在 AbstractPlatformTransactionManager 中:
java
// 摘自 AbstractPlatformTransactionManager
public final void rollback(TransactionStatus status) throws TransactionException {
// 1. 校验:事务已完成则抛异常
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
// 2. 执行实际回滚逻辑(由具体实现类重写doRollback)
try {
doRollback(status);
} catch (RuntimeException | Error ex) {
throw ex;
} finally {
// 3. 清理事务资源
cleanupAfterCompletion(status);
}
}
实战示例:Spring Boot 中使用 DataSourceTransactionManager
DataSourceTransactionManager 是 Spring 适配 JDBC 数据源的本地事务管理器,也是 Spring Boot 单数据源场景下的默认事务管理器。
首先需要添加前置依赖,在 pom.xml 引入 Spring Boot JDBC 起步依赖(自动引入事务相关核心包):
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
步骤 1:配置事务管理器
Spring Boot 默认自动配置 DataSourceTransactionManager,通过 application.yml/application.properties 配置数据源
yaml
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
仅在多数据源或自定义事务配置场景需手动声明
方法一:手动声明配置(Java Config)
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
@Configuration
// Spring Boot中可省略(自动开启),非Boot环境/自定义AOP时需加
@EnableTransactionManagement
public class TransactionConfig {
// 配置数据源(完整可运行的HikariCP配置)
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setMaximumPoolSize(10);
return dataSource;
}
// 配置本地事务管理器(单数据源)
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
方法二:XML 配置
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1. 配置数据源(以HikariCP为例) -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test_db"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
</bean>
<!-- 2. 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 3. 配置事务通知(定义事务规则) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="transfer*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="30"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 4. 配置AOP切面(绑定事务规则到业务方法) -->
<aop:config>
<aop:pointcut id="serviceOperation"
expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>
</aop:config>
</beans>
步骤 2:编程式使用事务管理器(非注解方式)
Dao 层:
java
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
private final JdbcTemplate jdbcTemplate;
// 构造器注入JdbcTemplate
public UserDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 扣减余额
public void deductBalance(Long userId, Double amount) {
String sql = "UPDATE user_account SET balance = balance - ? WHERE user_id = ?";
jdbcTemplate.update(sql, amount, userId);
}
// 增加余额
public void increaseBalance(Long userId, Double amount) {
String sql = "UPDATE user_account SET balance = balance + ? WHERE user_id = ?";
jdbcTemplate.update(sql, amount, userId);
}
}
Service 层:
java
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Service
public class UserService {
private final PlatformTransactionManager transactionManager;
private final UserDao userDao;
// 构造器注入
public UserService(PlatformTransactionManager transactionManager, UserDao userDao) {
this.transactionManager = transactionManager;
this.userDao = userDao;
}
public void transferMoney(Long fromUserId, Long toUserId, Double amount) {
// 1. 定义事务属性:隔离级别、传播行为、超时时间等
TransactionDefinition def = new DefaultTransactionDefinition() {{
setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); // 隔离级别
setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 传播行为
setTimeout(30); // 超时时间(秒)
}};
// Spring 提供的 Builder 写法
//TransactionDefinition def = DefaultTransactionDefinition.builder()
// .isolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED)
// .propagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED)
// .timeout(30)
// .build();
// 2. 获取事务状态(开启事务)
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 3. 执行业务逻辑(扣减A账户,增加B账户)
userDao.deductBalance(fromUserId, amount);
userDao.increaseBalance(toUserId, amount);
// 4. 提交事务
transactionManager.commit(status);
} catch (Exception e) {
// 5. 异常回滚
transactionManager.rollback(status);
throw new RuntimeException("转账失败", e);
}
}
}
步骤 3:注解式使用
Spring 的 @Transactional 注解是对 TransactionManager 的封装,无需手动调用 commit/rollback:
java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
private final UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
// 注解式事务(底层由DataSourceTransactionManager执行)
@Transactional(
isolation = Isolation.READ_COMMITTED,
propagation = Propagation.REQUIRED,
timeout = 30
)
public void transferMoney(Long fromUserId, Long toUserId, Double amount) {
userDao.deductBalance(fromUserId, amount);
userDao.increaseBalance(toUserId, amount);
}
}
事务隔离级别
事务隔离级别是关系型数据库实现事务隔离性(ACID 中 "I" 的核心)的标准化规则,是数据库系统为平衡并发事务的一致性与系统执行性能,对多个并发执行的事务之间的数据可见性边界所做的约束性规定。它明确了一个事务在执行期间,能够读取到其他并发事务的哪些数据状态,核心用于解决并发事务引发的脏读、不可重复读、幻读等数据一致性问题。
| 隔离级别枚举值 | 对应数据库隔离级别 | 解决的问题 | 未解决的问题 |
|---|---|---|---|
| DEFAULT | 数据库默认级别,随数据库而定 | - | - |
| READ_UNCOMMITTED | 读未提交 | 无(最低隔离级别) | 解决脏读、不可重复读、幻读 |
| READ_COMMITTED | 读已提交(MySQL默认) | 脏读 | 解决不可重复读、幻读 |
| REPEATABLE_READ | 可重复读(Oracle默认) | 解决脏读、不可重复读 | 幻读(MySQL 已通过 MVCC 基本解决) |
| SERIALIZABLE | 串行化 | 解决脏读、不可重复读、幻读 | 无(最高隔离级别) |
事务传播行为
事务传播行为是Spring 事务管理框架特有的规则,描述了被 @Transactional 注解修饰的方法在被另一个方法调用时,该方法应如何参与调用方的事务上下文,核心用于解决多方法嵌套调用场景下的事务协同问题。
| 传播行为枚举值 | 核心规则 | 适用场景 | 关键特点 |
|---|---|---|---|
| REQUIRED(默认值) | 若当前存在事务则加入,无事务则新建 | 绝大多数增删改操作 | 同事务,最常用 |
| SUPPORTS | 若当前存在事务则加入,无事务则以非事务方式执行 | 纯查询操作(get/find/list) | 不主动创建事务,复用外层事务避免脏读 |
| REQUIRES_NEW | 无论当前是否有事务,都新建独立事务,原有事务会被挂起 | 独立提交的操作(日志记录、消息发送、积分记录) | 事务独立,主事务回滚不影响子事务执行结果 |
| NESTED | 若当前有事务则在嵌套事务执行,无事务则新建;子事务回滚不影响主事务 | 批量操作(批量下单、分批处理数据) | 基于数据库保存点,主事务回滚子事务必回滚 |
| MANDATORY | 必须在已存在的事务中执行,无事务则抛出异常 | 强制要求事务的核心资金操作 | 无事务直接报错,极少用 |
| NOT_SUPPORTED | 以非事务方式执行,若当前有事务则挂起当前事务 | 长耗时非核心查询(避免占用事务资源超时) | 完全不参与事务,有事务也会暂停 |
| NEVER | 以非事务方式执行,若当前有事务则抛出异常 | 强制禁止事务的操作(纯缓存读写) | 有事务直接报错,极少用 |
TransactionManager 常见问题
问题1:长事务锁表/死锁
场景:
java
@Transactional
public void processLargeOrder(Order order) {
// 1. 查询并锁定库存记录(行锁/表锁)
Inventory inventory = inventoryDao.selectForUpdate(order.getProductId());
// 2. 复杂计算逻辑(耗时5秒)
BigDecimal amount = complexCalculation(order);
// 3. 调用外部系统(网络IO,耗时10秒)
paymentService.charge(order.getUserId(), amount);
// 4. 更新库存
inventory.setQuantity(inventory.getQuantity() - order.getQuantity());
inventoryDao.update(inventory);
}
问题:事务持有锁25秒,其他线程被阻塞,导致死锁或超时
解决方案:
java
@Service
public class OrderServiceV2 {
@Autowired
private TransactionTemplate transactionTemplate;
// 方案1:拆分事务,尽早释放锁
public void processLargeOrder(Order order) {
// 第一步:快速锁定并扣减库存(短事务)
boolean inventorySuccess = transactionTemplate.execute(status -> {
Inventory inventory = inventoryDao.selectForUpdate(order.getProductId());
if (inventory.getQuantity() < order.getQuantity()) {
status.setRollbackOnly();
return false;
}
inventoryDao.deductQuantity(order.getProductId(), order.getQuantity());
return true;
});
if (!inventorySuccess) {
throw new InventoryException("库存不足");
}
// 第二步:复杂计算(无事务)
BigDecimal amount = complexCalculation(order);
// 第三步:支付(独立事务)
try {
paymentService.charge(order.getUserId(), amount);
} catch (Exception e) {
// 支付失败,恢复库存(需要幂等)
restoreInventory(order.getProductId(), order.getQuantity());
throw e;
}
// 第四步:记录日志等非核心操作
asyncLogService.logOrder(order, amount);
}
// 方案2:使用乐观锁
@Transactional
public void processOrderWithOptimisticLock(Order order) {
// 使用版本号控制
int rows = inventoryDao.deductWithVersion(
order.getProductId(),
order.getQuantity(),
currentVersion);
if (rows == 0) {
// 版本冲突,重试或抛异常
throw new OptimisticLockException("库存已被修改,请重试");
}
// 后续操作...
}
}
问题2:连接池耗尽
场景:
java
@Transactional
public void batchProcess(List<Data> dataList) {
for (Data data : dataList) {
// 每个循环都调用其他服务方法(开启新事务传播)
someService.process(data);
}
// 事务持有连接直到循环结束
}
解决方案:
java
@Service
public class BatchProcessor {
@Autowired
private DataService dataService;
// 方案1:非事务批量处理
@Transactional(propagation = Propagation.NOT_SUPPORTED) // 挂起事务
public void batchProcess(List<Data> dataList) {
int batchSize = 100;
for (int i = 0; i < dataList.size(); i += batchSize) {
List<Data> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));
// 每个批次独立事务
processBatch(batch);
// 释放连接
EntityManagerHelper.clear(); // JPA清理上下文
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW, timeout = 30)
private void processBatch(List<Data> batch) {
for (Data data : batch) {
dataService.process(data);
}
}
// 方案2:使用连接泄漏检测
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize(20);
ds.setLeakDetectionThreshold(10000); // 10秒连接泄漏检测
ds.setConnectionTimeout(30000); // 30秒连接超时
return ds;
}
}
}
问题3:分布式事务不一致
场景:
java
@Transactional
public void placeDistributedOrder(Order order) {
// 本地数据库
orderDao.save(order);
// 远程服务调用
inventoryService.deduct(order.getProductId(), order.getQuantity()); // HTTP调用
// 另一个远程服务
couponService.useCoupon(order.getCouponId()); // RPC调用
}
解决方案:
java
@Service
public class DistributedOrderService {
// 方案1:最终一致性 + 本地消息表
@Transactional
public void placeOrderWithLocalMessage(Order order) {
// 1. 保存订单(本地事务)
orderDao.save(order);
// 2. 记录本地消息(同一个事务)
Message message = new Message();
message.setType("INVENTORY_DEDUCT");
message.setContent(JSON.toJSONString(order));
messageDao.save(message);
// 3. 发送消息到MQ(事务提交后)
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronization() {
@Override
public void afterCommit() {
// 异步发送消息
mqProducer.send(message);
}
}
);
}
// 方案2:TCC模式
public void placeOrderWithTCC(Order order) {
// Try阶段
inventoryService.tryDeduct(order.getProductId(), order.getQuantity());
couponService.tryUse(order.getCouponId());
try {
// Confirm阶段
orderDao.save(order);
inventoryService.confirmDeduct(order.getProductId());
couponService.confirmUse(order.getCouponId());
} catch (Exception e) {
// Cancel阶段
inventoryService.cancelDeduct(order.getProductId());
couponService.cancelUse(order.getCouponId());
throw e;
}
}
// 方案3:使用Seata分布式事务
@GlobalTransactional // Seata注解
public void placeOrderWithSeata(Order order) {
orderDao.save(order);
inventoryFeignClient.deduct(order.getProductId(), order.getQuantity());
couponFeignClient.use(order.getCouponId());
}
}
问题4:部分更新问题
场景:
java
@Transactional
public void updateUserProfile(User user) {
userDao.updateBasicInfo(user); // 成功
userDao.updatePreferences(user); // 失败,但第一个更新已提交?
userDao.updateStatistics(user); // 不会执行
}
解决方案:
java
@Service
public class UserService {
// 方案1:使用声明式事务,默认会全部回滚
@Transactional(rollbackFor = Exception.class)
public void updateUserProfile(User user) {
// 所有操作在一个事务中
}
// 方案2:手动控制保存点
@Transactional
public void updateUserWithSavepoint(User user) {
Object savepoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
try {
userDao.updateBasicInfo(user);
} catch (Exception e) {
// 只回滚到保存点
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savepoint);
log.warn("基本信息更新失败,已回滚", e);
}
try {
userDao.updatePreferences(user);
} catch (Exception e) {
log.error("偏好设置更新失败,但基本信息已提交", e);
// 可以继续执行其他操作
}
// 继续执行...
}
// 方案3:使用补偿事务
public void updateUserWithCompensation(User user) {
// 记录原始状态
User original = userDao.findById(user.getId());
try {
userDao.updateBasicInfo(user);
userDao.updatePreferences(user);
userDao.updateStatistics(user);
} catch (Exception e) {
// 执行补偿操作
compensateUserUpdate(original);
throw e;
}
}
}
问题5:事务注解滥用
场景:
java
@Service
public class ProductService {
@Transactional // ❌ 滥用:查询方法不需要事务
public Product getProduct(Long id) {
return productDao.findById(id);
}
@Transactional // ❌ 滥用:简单操作过度设计
public void updateProductName(Long id, String name) {
productDao.updateName(id, name);
}
@Transactional(propagation = Propagation.REQUIRES_NEW) // ❌ 不必要的独立事务
public void logAccess(Long productId) {
accessLogDao.log(productId);
}
}
解决方案:
java
@Service
public class ProductServiceOptimized {
// ✅ 正确:查询方法使用SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public Product getProduct(Long id) {
return productDao.findById(id);
}
// ✅ 正确:简单更新使用默认事务
@Transactional
public void updateProductName(Long id, String name) {
productDao.updateName(id, name);
}
// ✅ 正确:日志记录不需要事务,或使用NOT_SUPPORTED
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void logAccess(Long productId) {
// 如果日志失败,不应影响主业务
try {
accessLogDao.log(productId);
} catch (Exception e) {
log.error("记录访问日志失败", e);
}
}
// ✅ 事务配置规范
@Transactional(
propagation = Propagation.REQUIRED, // 默认
isolation = Isolation.READ_COMMITTED, // 明确隔离级别
timeout = 30, // 设置超时
rollbackFor = {BusinessException.class, RuntimeException.class}, // 明确回滚异常
readOnly = false
)
public void complexBusiness() {
// 复杂业务逻辑
}
}
问题6:循环依赖与自调用
场景:
java
@Service
public class OrderService {
@Autowired
private OrderService orderService; // 循环依赖
@Transactional
public void placeOrder(Order order) {
validateOrder(order); // 事务生效
processPayment(order); // 事务生效
updateInventory(order); // 事务生效
sendNotification(order); // ❌ 事务失效(自调用)
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void sendNotification(Order order) {
// AOP代理失效,事务注解无效
notificationDao.save(new Notification(order));
}
}
解决方案:
java
@Service
public class OrderServiceFixed {
@Autowired
private NotificationService notificationService; // 拆分为独立服务
@Autowired
private OrderService selfProxy; // 自注入解决自调用
@Transactional
public void placeOrder(Order order) {
validateOrder(order);
processPayment(order);
updateInventory(order);
// 方案1:调用其他服务
notificationService.sendOrderNotification(order);
// 方案2:通过代理调用自身方法
selfProxy.sendNotificationInternal(order);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendNotificationInternal(Order order) {
notificationDao.save(new Notification(order));
}
}
@Service
public class NotificationService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendOrderNotification(Order order) {
notificationDao.save(new Notification(order));
}
}
// 方案3:使用编程式事务
@Service
public class OrderServiceProgrammatic {
@Autowired
private TransactionTemplate transactionTemplate;
public void placeOrder(Order order) {
// 主事务
transactionTemplate.execute(status -> {
validateOrder(order);
processPayment(order);
updateInventory(order);
return null;
});
// 独立事务发送通知
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
notificationDao.save(new Notification(order));
}
});
}
}
问题7:事务难以监控和排查
解决方案:
java
@Aspect
@Component
@Slf4j
public class TransactionMonitorAspect {
private ThreadLocal<TransactionContext> context = new ThreadLocal<>();
@Around("@annotation(transactional)")
public Object monitorTransaction(ProceedingJoinPoint joinPoint,
Transactional transactional) throws Throwable {
TransactionContext ctx = new TransactionContext();
ctx.setStartTime(System.currentTimeMillis());
ctx.setMethod(joinPoint.getSignature().toShortString());
ctx.setPropagation(transactional.propagation());
context.set(ctx);
MDC.put("txId", UUID.randomUUID().toString());
log.info("事务开始: {}, 传播行为: {}", ctx.getMethod(), ctx.getPropagation());
try {
Object result = joinPoint.proceed();
ctx.setSuccess(true);
return result;
} catch (Exception e) {
ctx.setSuccess(false);
ctx.setError(e.getMessage());
// 检查是否需要回滚
for (Class<?> rollbackFor : transactional.rollbackFor()) {
if (rollbackFor.isAssignableFrom(e.getClass())) {
log.warn("事务回滚: {}, 原因: {}", ctx.getMethod(), e.getMessage());
throw e;
}
}
log.info("事务提交(异常但无需回滚): {}, 异常: {}", ctx.getMethod(), e.getMessage());
throw e;
} finally {
ctx.setEndTime(System.currentTimeMillis());
long duration = ctx.getEndTime() - ctx.getStartTime();
if (duration > 5000) { // 5秒以上记录警告
log.warn("长事务警告: {}, 耗时: {}ms", ctx.getMethod(), duration);
}
// 发送到监控系统
Metrics.counter("transaction.count",
"method", ctx.getMethod(),
"success", String.valueOf(ctx.isSuccess()),
"duration", String.valueOf(duration)
).increment();
context.remove();
MDC.remove("txId");
}
}
@Data
private static class TransactionContext {
private String method;
private Propagation propagation;
private long startTime;
private long endTime;
private boolean success;
private String error;
}
}
@EnableTransactionManagement、@Transactional 与 TransactionManager的区别与联系
各核心组件的定义与核心职责
TransactionManager(事务管理器)
TransactionManager 是 Spring 事务管理的底层执行引擎,其顶层抽象为 org.springframework.transaction.PlatformTransactionManager 接口(Spring 事务抽象规范的核心),定义了事务生命周期的三大核心操作,构成事务管控的底层能力:
TransactionStatus getTransaction(TransactionDefinition definition):根据事务定义获取或开启新事务;
void commit(TransactionStatus status):提交事务(仅当事务状态为正常时执行);
void rollback(TransactionStatus status):回滚事务(当业务异常或事务标记为回滚时执行)。
为适配不同持久化技术,Spring 提供了专属的 TransactionManager 实现类,不同持久化技术对应专属的 TransactionManager 实现。
@Transactional(事务声明注解)
@Transactional 是 Spring 声明式事务的规则声明载体,其核心价值是 "声明需要事务管控的方法及事务行为规则",而非直接执行事务操作。该注解基于 Spring AOP 动态代理实现,最终通过调用 TransactionManager 完成事务的生命周期管控,核心作用包括:
- 标记范围:标注在 Spring 容器管理的 Bean 类 / 方法上(类级别标注时,所有 public 方法均继承事务规则;方法级别标注优先级更高);
- 定义规则:通过注解属性精细化配置事务行为(符合 ACID 特性),核心属性及作用如下:
propagation:事务传播行为,定义多方法嵌套调用时的事务嵌套规则;
isolation:事务隔离级别,解决并发场景下的脏读、不可重复读、幻读问题;
rollbackFor/noRollbackFor:精准控制事务回滚 / 不回滚的异常类型(默认仅对运行时异常回滚);
timeout:事务超时时间,超出设定时长后自动回滚,避免长事务占用资源。
@EnableTransactionManagement(事务管理启用注解)
@EnableTransactionManagement 是 Spring 声明式事务功能的基础设施开关注解,其核心作用是触发 Spring 容器中事务相关 AOP 组件的注册,使 @Transactional 注解能被识别并关联到 TransactionManager。该注解底层通过 TransactionManagementConfigurationSelector 导入两个核心配置类,完成事务 AOP 机制的初始化:
AutoProxyRegistrar:启用 Spring AOP 的自动代理机制,为标注 @Transactional 的 Bean 生成动态代理对象;
ProxyTransactionManagementConfiguration:注册 TransactionInterceptor(事务拦截器)------ 作为连接 @Transactional 和 TransactionManager 的核心桥梁,包含 TransactionAttributeSource(事务属性解析器,解析 @Transactional 的配置规则)和 TransactionManagerResolver(事务管理器解析器,匹配对应的 TransactionManager 实例)。
三者的协同生效约束
三者的协同工作需满足严格的 "存在性约束" 和 "适配性约束",缺一不可,否则事务管控会失效。
存在性约束:
- 无 @EnableTransactionManagement:Spring 不会注册事务拦截器等 AOP 组件,@Transactional 仅为 "无效注解",无法触发任何事务管控逻辑;
- 无 TransactionManager:事务拦截器能识别 @Transactional,但无底层执行引擎,运行时会抛出 NoTransactionManagerFoundException;
- 无 @Transactional:TransactionManager 无触发条件,无法主动介入方法执行,业务操作无事务保护(即使 @EnableTransactionManagement 已开启)。
适配性约束:
- TransactionManager 类型适配:TransactionManager 必须与持久化技术匹配(如 JDBC 操作使用 JpaTransactionManager 会导致事务失效);
- @Transactional 代理生效条件:标注 @Transactional 的方法需满足 Spring AOP 代理生效规则,否则事务拦截器无法捕获注解:
- 方法权限:必须为 public 修饰(JDK 动态代理仅能代理接口的 public 方法;Spring 规范中 CGLIB 代理也仅解析 public 方法的 @Transactional 注解);
- 调用方式:禁止内部调用(同一类中方法互相调用时,调用方为目标对象本身,而非代理对象,无法触发事务拦截器链);
- Bean 管控:目标对象必须是 Spring 容器管理的 Bean(需通过 @Service/@Component 等注解声明,或 @Bean 配置,且通过 @Autowired/ApplicationContext.getBean() 获取)------ 仅容器内的 Bean 会被生成代理对象,是事务拦截的基础。
代码示例
配置类:启用事务 + 注册事务管理器
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
// 启用声明式事务,proxyTargetClass = true 强制使用CGLIB代理(适配非接口类)
@EnableTransactionManagement(proxyTargetClass = true)
public class TransactionConfig {
// 注册符合Spring抽象规范的事务管理器(适配JDBC/MyBatis)
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
业务类:声明事务并绑定执行引擎
java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
private final OrderMapper orderMapper;
private final InventoryMapper inventoryMapper;
// 构造器注入(Spring 推荐的依赖注入方式,保证严谨性)
public OrderService(OrderMapper orderMapper, InventoryMapper inventoryMapper) {
this.orderMapper = orderMapper;
this.inventoryMapper = inventoryMapper;
}
// 声明式事务:指定传播行为、回滚规则,绑定默认TransactionManager
@Transactional(
propagation = Propagation.REQUIRED, // 默认传播行为,嵌套事务合并
isolation = Isolation.READ_COMMITTED, // 读已提交,避免脏读
rollbackFor = Exception.class, // 所有异常均回滚
timeout = 30 // 事务超时时间30秒
)
public void createOrder(Long productId, Integer count) {
// 扣减库存(事务内操作1)
inventoryMapper.decreaseStock(productId, count);
// 创建订单(事务内操作2)
orderMapper.createOrder(productId, count);
// 任一操作异常,TransactionManager会回滚所有操作
}
}