跳到主要内容Spring @Transactional 注解详解 | 极客日志Javajava
Spring @Transactional 注解详解
Spring @Transactional 注解是声明式事务管理的核心,通过 AOP 实现业务与事务解耦。其基本用法、主要属性(传播行为、隔离级别、超时等)、工作原理及常见失效原因(如自调用、异常捕获)。涵盖多数据源配置、最佳实践总结,帮助开发者掌握事务控制机制,避免数据不一致问题。
极光4 浏览 在 Spring 框架中,@Transactional 注解是声明式事务管理的核心。它允许开发者通过简单的注解配置,将事务管理逻辑从业务代码中剥离,极大地简化了事务控制。
1. 什么是 @Transactional
@Transactional 是 Spring 提供的声明式事务管理注解,用于标识方法或类需要被事务管理。当标注了该注解的方法被调用时,Spring 会在方法执行前开启事务,方法执行后根据是否抛出异常来决定提交或回滚事务。
- 简化事务代码:无需手动管理
Connection 或 Transaction 对象。
- 声明式事务:通过 AOP 实现,业务代码与事务逻辑解耦。
- 灵活配置:支持事务传播行为、隔离级别、超时、只读等属性。
2. 基本用法
2.1 在方法上使用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void createUser(User user) {
userMapper.insert(user);
}
}
2.2 在类上使用
@Service
@Transactional
public class UserService {
}
- 标注在类上时,该类的所有 public 方法都会被事务管理。
- 方法上的注解会覆盖类上的注解。
2.3 注意事项
- 默认情况下,只有
public 方法才支持 @Transactional。如果标注在 protected、private 或包可见的方法上,Spring 不会应用事务(但不会报错,只是无效)。
- 必须通过 Spring 代理对象调用方法,直接调用(如
this.createUser(...))会导致事务失效。
3. 主要属性详解
@Transactional 提供了丰富的属性,用于精细控制事务行为。
| 属性 | 类型 | 说明 |
|---|
value / transactionManager | String | 指定使用的事务管理器 Bean 名称,用于多数据源场景。 |
propagation | Propagation | 事务传播行为,指定方法如何参与已有事务。 |
isolation | Isolation | 事务隔离级别,定义事务之间的隔离程度。 |
timeout | int | 事务超时时间(秒),默认 -1(使用底层事务系统默认超时)。 |
readOnly | boolean | 是否为只读事务,默认 false。可优化数据库资源。 |
rollbackFor | Class<? extends Throwable>[] | 指定哪些异常触发事务回滚。 |
rollbackForClassName | String[] | 异常类名形式。 |
noRollbackFor | Class<? extends Throwable>[] | 指定哪些异常不触发事务回滚。 |
noRollbackForClassName | String[] | 异常类名形式。 |
3.1 value / transactionManager
当应用中有多个 PlatformTransactionManager 时,通过该属性指定使用哪个事务管理器。
@Transactional("orderTransactionManager")
public void processOrder() {
...
}
3.2 propagation(传播行为)
| 传播行为 | 说明 |
|---|
REQUIRED(默认 required) | 如果当前存在事务,则加入该事务;如果没有,则新建一个事务。 |
SUPPORTS(support) | 如果当前存在事务,则加入;否则以非事务方式执行。 |
MANDATORY(mandatory) | 强制要求当前存在事务,否则抛出异常。 |
REQUIRES_NEW(requires_new) | 新建一个事务,如果当前存在事务,则挂起当前事务。 |
NOT_SUPPORTED(not_supported) | 以非事务方式执行,如果当前存在事务,则挂起它。 |
NEVER(never) | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED(nested) | 如果当前存在事务,则在嵌套事务内执行;否则行为类似 REQUIRED。 |
REQUIRES_NEW 内部事务的提交/回滚不影响外部事务,反之亦然。
NESTED 依赖于底层数据库的保存点(savepoint)支持,允许部分回滚。并非所有数据库都支持。
3.3 isolation(隔离级别)
| 隔离级别 | 说明 |
|---|
DEFAULT | 使用底层数据库的默认隔离级别。 |
READ_UNCOMMITTED | 允许读取未提交的数据(脏读、不可重复读、幻读都可能发生)。 |
READ_COMMITTED | 只能读取已提交的数据(避免脏读,但可能发生不可重复读和幻读)。 |
REPEATABLE_READ | 同一事务内多次读取结果一致(避免脏读和不可重复读,但可能幻读)。 |
SERIALIZABLE | 最高级别,事务串行执行,避免所有并发问题,但性能最差。 |
3.4 timeout
3.5 readOnly
标记事务为只读,有助于数据库优化(如 Oracle 会跳过锁获取),但需确保事务内没有写操作。
3.6 rollbackFor / noRollbackFor
默认情况下,Spring 事务只在遇到运行时异常(RuntimeException 及其子类)和 Error 时回滚,检查型异常(如 IOException)不会触发回滚。通过 rollbackFor 可以指定某些检查异常也应回滚,或通过 noRollbackFor 指定某些运行时异常不回滚。
@Transactional(rollbackFor = Exception.class)
public void update() throws Exception {
}
@Transactional(noRollbackFor = IllegalArgumentException.class)
public void process() {
}
4. 工作原理:Spring 声明式事务的实现机制
Spring 的声明式事务基于 AOP(面向切面编程) 实现,核心组件包括:
- 事务管理器(PlatformTransactionManager):如
DataSourceTransactionManager、JpaTransactionManager。
- 事务拦截器(TransactionInterceptor):AOP 增强,负责在目标方法前后执行事务逻辑。
- 事务属性来源(TransactionAttributeSource):解析
@Transactional 注解,获取事务配置。
- 代理对象:通过 JDK 动态代理或 CGLIB 生成代理对象,包裹目标方法。
4.1 执行流程
- 调用方获取的是 Spring 生成的代理对象(而非原始 Bean)。
- 代理对象中的
TransactionInterceptor 在方法调用前,根据 @Transactional 配置开启事务。
- 执行目标方法(业务逻辑)。
- 方法执行后:
- 如果方法正常返回,则提交事务。
- 如果方法抛出异常,根据配置决定回滚或提交。
- 最终返回结果给调用方。
4.2 源码层面
TransactionInterceptor 实现 MethodInterceptor,在 invoke() 方法中调用 invokeWithinTransaction()。
invokeWithinTransaction() 根据事务管理器、传播行为等处理事务开启、提交、回滚。
5. 事务不生效的常见原因
5.1 自调用问题
@Service
public class UserService {
public void outer() {
inner();
}
@Transactional
public void inner() {
}
}
原因:outer() 中的 inner() 是通过 this 直接调用的,没有经过代理对象,因此事务注解无效。
- 将方法拆分为不同 Bean,通过注入调用。
- 使用
AopContext.currentProxy() 获取当前代理对象:
((UserService) AopContext.currentProxy()).inner();
需要配置 @EnableAspectJAutoProxy(exposeProxy = true)。
5.2 方法不是 public
@Transactional 默认只对 public 方法生效。如果标注在非 public 方法上,Spring 不会应用事务。
5.3 数据库不支持事务
例如 MySQL 的 MyISAM 引擎不支持事务,需要使用 InnoDB。
5.4 事务管理器未配置或配置错误
Spring Boot 默认会配置事务管理器,但多数据源时需要显式指定。
5.5 异常被捕获
@Transactional
public void method() {
try {
throw new RuntimeException();
} catch (Exception e) {
}
}
事务拦截器无法感知异常,因此不会回滚。解决方法:在 catch 块中重新抛出异常,或通过 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 手动标记回滚。
5.6 传播行为配置不当
例如在 REQUIRED 传播下,内部事务抛异常,外部事务也回滚;但在 REQUIRES_NEW 下,内部事务回滚不会影响外部事务。
6. 事务传播行为实践案例
6.1 REQUIRED(默认)
@Service
public class OuterService {
@Autowired
private InnerService innerService;
@Transactional
public void outer() {
innerService.inner();
}
}
@Service
public class InnerService {
@Transactional(propagation = Propagation.REQUIRED)
public void inner() {
}
}
6.2 REQUIRES_NEW
@Service
public class OuterService {
@Autowired
private InnerService innerService;
@Transactional
public void outer() {
innerService.inner();
}
}
@Service
public class InnerService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void inner() {
}
}
6.3 NESTED(需要数据库支持保存点)
@Transactional(propagation = Propagation.NESTED)
public void inner() {
}
7. 隔离级别与并发问题
7.1 并发问题类型
- 脏读:读取到其他事务未提交的数据。
- 不可重复读:同一事务内两次读取同一数据,结果不一致(数据被其他事务修改并提交)。
- 幻读:同一事务内两次查询返回的记录数不同(其他事务插入或删除了记录)。
7.2 隔离级别选择
| 级别 | 脏读 | 不可重复读 | 幻读 |
|---|
| READ_UNCOMMITTED | 可能 | 可能 | 可能 |
| READ_COMMITTED | 避免 | 可能 | 可能 |
| REPEATABLE_READ | 避免 | 避免 | 可能 |
| SERIALIZABLE | 避免 | 避免 | 避免 |
注意:MySQL InnoDB 默认使用 REPEATABLE_READ,并通过 MVCC 机制在一定程度上避免了幻读。
8. Spring Boot 中的 @Transactional
8.1 自动配置
Spring Boot 自动配置了 DataSourceTransactionManager 和 JpaTransactionManager,无需额外配置。只需在启动类上添加 @EnableTransactionManagement(实际上 Spring Boot 会自动启用,但显式加上也无妨)。
8.2 多数据源配置
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public PlatformTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Transactional("secondaryTransactionManager")
public void method() {
...
}
9. 最佳实践总结
- 明确事务边界:事务应尽量短小,避免在事务内执行耗时的网络请求、文件操作等。
- 合理选择传播行为:理解每种传播行为的含义,避免误用导致数据不一致。
- 异常处理:不要随意捕获异常而不抛出,否则事务不会回滚。
- 只读事务优化:对于只读操作,设置
readOnly=true 可提升性能。
- 私有方法无效:确保
@Transactional 标注在 public 方法上。
- 自调用问题:通过注入代理对象或使用
AopContext.currentProxy() 解决。
- 测试事务:在单元测试中,可使用
@Transactional 和 @Rollback 实现测试数据自动回滚。
- 使用默认回滚规则:通常建议所有异常都回滚,除非明确不需要回滚的场景。
10. 总结
@Transactional 是 Spring 声明式事务的核心注解,通过简洁的配置即可实现强大的事务管理。理解其属性、传播行为、隔离级别以及工作原理,对于编写健壮的应用程序至关重要。同时,注意避免常见陷阱(如自调用、异常被吞),才能确保事务按预期生效。
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online