结论先行
@Transactional
注解的Bean创建代理对象。事务逻辑(开启、提交、回滚)被封装在切面(核心是TransactionInterceptor
)中,与业务代码解耦。@Transactional
注解声明事务属性(传播行为、隔离级别等)。Spring容器在启动时解析此注解并创建代理。TransactionInterceptor
。它根据注解属性、当前事务上下文和业务方法执行结果(成功/异常),通过 PlatformTransactionManager
执行具体的事务操作(begin/commit/rollback)。RuntimeException
, Error
)回滚。检查异常(Exception
)需通过rollbackFor
显式配置回滚。@Transactional
失效。文章持续更新,可以微信搜一搜「 半个脑袋儿 」第一时间阅读
图解说明:
TransactionInterceptor
(TI) 是事务控制的核心枢纽:
TransactionSynchronizationManager
)。PlatformTransactionManager
™ 是与具体数据源交互的执行引擎。DataSourceTransactionManager
)@Configuration
@EnableTransactionManagement // 启用注解驱动的事务管理
public class AppConfig {
@Bean
public DataSource dataSource() {
// 配置数据源 (例如 HikariCP)
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("user");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
// 核心:创建基于JDBC的事务管理器
return new DataSourceTransactionManager(dataSource);
}
@Bean
public MyService myService() {
return new MyServiceImpl(); // 被代理的Service
}
}
@Transactional
注解使用示例@Service
public class MyServiceImpl implements MyService {
@Autowired
private UserRepository userRepository;
@Autowired
private OrderRepository orderRepository;
// 示例1:基本用法 (使用默认传播行为 REQUIRED)
@Transactional
@Override
public void createUserAndOrder(User user, Order order) {
userRepository.save(user); // 插入用户
orderRepository.save(order); // 插入订单,两者在同一个事务中
}
// 示例2:指定传播行为和回滚规则
@Transactional(
propagation = Propagation.REQUIRES_NEW, // 总是开启新事务
isolation = Isolation.READ_COMMITTED, // 读已提交隔离级别
timeout = 30, // 30秒超时
rollbackFor = {BusinessException.class, SQLException.class}, // 自定义回滚异常
noRollbackFor = {ValidationException.class} // 特定异常不回滚
)
@Override
public void updateCriticalData(Data data) throws BusinessException {
// 业务逻辑... 如果抛出BusinessException或SQLException则回滚
// 如果抛出ValidationException则提交事务
}
// 示例3:只读事务 (优化提示)
@Transactional(readOnly = true)
@Override
public User getUserWithOrders(Long userId) {
return userRepository.findUserWithOrders(userId); // 复杂查询
}
}
问题代码 (事务失效):
@Service
public class ProblematicService {
public void outerMethod() {
// ... 一些逻辑
innerMethod(); // 自调用:直接调用本类方法,innerMethod的事务注解失效!
// ... 更多逻辑
}
@Transactional // 此注解在outerMethod内部调用时无效!
public void innerMethod() {
// 需要事务管理的操作 (e.g., 保存数据)
}
}
原因: outerMethod
调用innerMethod
发生在原始目标对象内部,绕过了代理对象,因此TransactionInterceptor
无法拦截innerMethod
。
解决方案1 (推荐):将内部方法抽取到另一个Bean
@Service
public class OuterService {
@Autowired
private InnerService innerService; // 注入包含内部方法的Bean
public void outerMethod() {
// ... 逻辑
innerService.innerMethod(); // 通过代理对象调用,事务生效
// ... 逻辑
}
}
@Service
public class InnerService {
@Transactional // 事务注解生效
public void innerMethod() {
// 事务操作
}
}
解决方案2 (使用AopContext获取当前代理 - 需谨慎):
@Service
@EnableAspectJAutoProxy(exposeProxy = true) // 1. 启动类或配置类上需要启用exposeProxy
public class ProblematicService {
public void outerMethod() {
// ... 逻辑
// 2. 获取当前代理对象并调用其方法
((ProblematicService) AopContext.currentProxy()).innerMethod();
// ... 逻辑
}
@Transactional
public void innerMethod() {
// 事务操作
}
}
// 注意:此方法依赖AOP上下文暴露,可能影响性能或设计,方案1更清晰解耦。
PlatformTransactionManager
(事务管理器)
DataSourceTransactionManager
:用于JDBC和MyBatis等基于DataSource
的持久化。JpaTransactionManager
:用于JPA (Hibernate, EclipseLink)。HibernateTransactionManager
:用于原生Hibernate。JtaTransactionManager
:用于分布式事务(JTA)。@Transactional
关键属性
属性 | 含义 | 默认值 | 常用选项 |
---|---|---|---|
propagation |
传播行为:定义方法间调用时事务如何传播 | Propagation.REQUIRED |
REQUIRED , REQUIRES_NEW , SUPPORTS , NOT_SUPPORTED , MANDATORY , NEVER , NESTED |
isolation |
隔离级别:控制事务并发时的数据可见性 | Isolation.DEFAULT |
DEFAULT , READ_UNCOMMITTED , READ_COMMITTED , REPEATABLE_READ , SERIALIZABLE |
timeout |
超时时间(秒):事务执行超时则自动回滚 | -1 (使用底层默认或无超时) | 正整数 (e.g., 30) |
readOnly |
只读提示:优化只读操作 | false |
true (用于查询), false |
rollbackFor |
触发回滚的异常类:指定哪些异常必须导致回滚 | {} (默认回滚RuntimeException /Error ) |
Exception.class , BusinessException.class |
rollbackForClassName |
同上,用异常类全名指定 | {} |
"com.example.BusinessException" |
noRollbackFor |
不触发回滚的异常类:指定哪些异常不导致回滚(即使它们是RuntimeException ) |
{} |
ValidationException.class |
noRollbackForClassName |
同上,用异常类全名指定 | {} |
"org.springframework.dao.DuplicateKeyException" |
@Transactional
标注在Service层的方法上,而不是Controller或Repository。事务应围绕业务逻辑单元。SQLException
, IOException
, 自定义业务异常BusinessException
),必须使用rollbackFor
或rollbackForClassName
显式声明。RuntimeException
或使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
手动标记回滚。readOnly = true
。这能给数据库驱动和连接池提供优化提示(如使用只读副本、避免flush操作)。REQUIRED
(默认): 大多数场景适用。保证操作在同一个事务中。REQUIRES_NEW
: 需要独立事务的场景(如记录操作日志,即使主业务失败日志仍需保存)。NOT_SUPPORTED
/ NEVER
: 明确要求非事务执行。NESTED
: 需要部分回滚能力的复杂嵌套逻辑(依赖数据库Savepoint支持)。bean.getClass().getName()
通常包含$$EnhancerBySpringCGLIB$$
或 $Proxy
)。TransactionInterceptor
或自定义MethodInterceptor
中设置断点。TransactionSynchronizationManager
绑定的资源 (如TransactionSynchronizationManager.getResource(dataSource)
)。常见问题诊断表:
现象 | 可能原因 | 解决方案 |
---|---|---|
@Transactional 完全不生效 |
1. 未启用@EnableTransactionManagement 2. 方法非 public 3. 异常被捕获未抛出 |
1. 检查配置类注解 2. 确保方法是 public 3. 检查异常处理逻辑 |
自调用导致内部事务失效 | 同一类内非代理方法调用 | 1. 将内部方法抽取到另一个Bean 2. (谨慎)使用 AopContext.currentProxy() |
检查异常不回滚 | 默认仅回滚未检查异常(RuntimeException /Error ) |
在@Transactional 中明确配置rollbackFor = Exception.class 或具体异常 |
期望回滚却提交了 | 1. 抛出的异常类型不在rollbackFor 范围内2. 异常在方法内部被捕获未抛出 |
1. 检查rollbackFor 配置和异常类型2. 确保异常传播到切面 |
Transaction rolled back because it has been marked as rollback-only |
内层事务已标记回滚,外层尝试提交 | 检查传播行为(REQUIRED vs REQUIRES_NEW ),确保正确的事务边界 |
连接泄露 | 未正确释放数据库连接 | Spring事务管理通常自动处理。检查是否混用了低级API或手动获取连接未关闭。 |
Spring通过AOP和动态代理,将复杂的事务管理抽象为声明式的@Transactional
注解,其核心在于TransactionInterceptor
的拦截处理流程。深入理解代理机制、传播行为、隔离级别、异常回滚规则以及自调用陷阱,是构建健壮、数据一致的应用的基础。结合清晰的图示、代码示例和实践要点,开发者能够高效、精准地驾驭Spring事务管理。