@Transactional 事务失效的场景还不清楚 ?

本文深入探讨Spring中的事务管理,包括声明式事务与编程式事务的区别,@Transactional注解的使用方法及其属性说明,事务传播机制的特性,并列举了几种可能导致@Transactional失效的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、事务的类型
1、声明式事务

声明式事务属于非侵入性,不会影响业务逻辑的实现,只要在配置文件中做相关的事务规则声明
或者基于@Transactional注解的方式,便可以将事务规则应用到业务逻辑中;

2、编程式事务

编程式事务每次实现都要单独实现,业务大且功能复杂时,使用编程式事务是比较繁琐的。
我们需要在代码中调用 beginTransaction()、commit()、rollback()等事务管理相关的方法;

二、@Transactional 声明式事务的用法
1、启用声明式事务管理,在配置类上添加 @EnableTransactionManagement (高版本项目可以不加)
2、将 @ Transactional 注解添加到合适的业务类或方法上,并设置合适的属性信息。
@Transactional(timeout = 50,
               readOnly = false,
               isolation = Isolation.READ_COMMITTED,
               rollbackFor = Throwable.class,
               propagation = Propagation.REQUIRED)
@Service 
public class UserServiceImpl implements UserService {


	@Transactional(readOnly = true)
	@Override
	public List<UserVo> getUserList() {...}
}
3、@Transactional注解中的属性说明

1、timeout 事务的超时时间,默认值为 -1,表示没有超时显示。如果配置了具体时间,则超过该时间限制但事务还没有完成,则自动回滚事务。
2、read-only 指定是否为只读事务,默认值为false,为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only = true
3、rollback-for 用于指定能够触发事务的异常,如果有多个异常需要指定,则用逗号隔开。
4、no-rollback- for 用于指定排查不触发事务的异常。
4、isolation 事务的隔离级别,默认值采用 DEFAULT。

4、事务传播机制特性

事务传播(Propagetion)指的是不同业务或Service中被@Transactional修饰的方法之间相互调用时,事务的传播方式。

传播机制有7种,常用的就两种。如下表格

事务类型描述
Propagetion.REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。这是最用的选择,也是默认的
Propagetion.SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行
Propagetion.MANDATORY支持当前事务,如果当前没有事务,就抛出异常
Propagetion.REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起
Propagetion.NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
Propagetion.NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
Propagetion.NESTED支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

重点介绍常用的两种:

@Transactional(propagation=Propagation.REQUIRED)

如果没有事务就创建事务,如果有就参与当前事务,Spring 默认的事务传播机制就是这个,是最常用的选择。
举个例:
serviceX#methodX() 都是工作在 spring 的事务环境中。
假如有个业务调用链:service1#method1() – service2#method2() – service3#method3() 那么这3个方法通过事务传播机制都工作同一个事务,3个方法中,有1个db操作失败,那么其它db操作都会回滚。

@Transactional(propagation=Propagation.REQUIRES_NEW)

新建事务,如存在事务则挂起,再创建新事务。
用这个事物传播机制的方法,每一个方法都是新的事务。

三、@Transactional 会造成失效的场景

1、 rollback-for 未指定要处理的异常

2、 no-rollback- for 把抛出异常排除了

3、 propagation 事务传播机制没用对,
举个例子:service1#method1() – service2#method2() – service3#method3() 你想让这3个方法绑定在同一个事务里,你却把事务传播机制设成 REQUIRES_NEW,这导致每个每个方法都有自己单独的事务,所以会导致不生效

4、 将 @Transactional 注解修饰到非public的方法上,之所以会失效是因为在Spring AOP 代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。此方法会检查目标方法的修饰符是否是 public ,不是的话,不会获取 @ Transactional 属性

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
}

5、 你代码里面的可能会抛出异常的代码,被你用 try cacth 裹住了,异常没有被抛出来,所有事务会失效。

6、 同一个类中方法调用,导致 @Transactional 事务失效。同一个类中,有两个方法 A和 B,B有事务修饰,A方法无事务修饰 (不论方法B是用public还是private修饰) ,这个时候,A 方法调用 B方法,事务会失效,这是因为 spring 的代理导致的,因为只有当事务方法被当前类以外的代码调用时,才会由 Spring生成的代理对象来管理。

<think>嗯,用户问的是关于@Transactional注解的详细解释和注意事项。首先,我需要回忆一下这个注解在Spring中的作用。记得它是用来管理事务的,可以加在类或方法上,让方法具有事务性,比如回滚、传播行为什么的。 然后,用户可能是个刚开始用Spring的开发人员,或者对事务管理太熟悉。他们可能在实际开发中遇到了事务生效的问题,所以想知道怎么正确使用这个注解,以及有哪些常见的坑需要避免。 接下来,得解释清楚@Transactional的基本功能,比如声明式事务管理,基于AOP实现的,还有它的属性,比如propagation(传播行为)、isolation(隔离级别)、rollbackFor(指定回滚的异常)、timeout(超时时间)等等。这部分需要详细但易懂,避免用太专业的术语,或者至少解释清楚术语。 然后要注意点部分,可能需要提到自调用问题,因为同一个类内部的方法调用,注解可能会生效,因为AOP代理的问题。解决方案可能需要用AspectJ或者从容器中获取代理对象。另外,异常回滚的机制,默认只对RuntimeException和Error回滚,如果捕获了异常或者抛出了检查型异常,事务可能会回滚,需要正确配置rollbackFor属性。 还有事务的传播行为,同配置下事务如何嵌套或创建新事务,比如REQUIRED和REQUIRES_NEW的区别。数据库引擎的支持也是关键,比如MyISAM支持事务,得用InnoDB。另外,在private方法上使用会失效,因为代理的问题。 其他注意事项可能包括事务的超时设置,避免长时间占用连接;正确选择事务的隔离级别,防止脏读、可重复读等问题;以及避免在事务中执行远程调用或者耗时操作,影响性能。 还要考虑到用户可能遇到的问题场景,比如为什么事务没有回滚,可能是异常被捕获了,或者抛出的是unchecked异常。或者在一个方法里调用了另一个@Transactional方法,但没有正确配置传播行为导致事务按预期执行。 最后,总结一下使用@Transactional时的最佳实践,比如检查数据库引擎,正确设置rollbackFor,避免自调用,选择合适的传播和隔离级别,以及注意方法可见性等。可能还需要提到测试的重要性,确保事务行为符合预期。</think>`@Transactional` 是 Spring 框架中用于**声明式事务管理**的核心注解,它通过 AOP(面向切面编程)实现,简化了事务的编程式控制。以下从功能、使用方式和注意事项三个角度详细说明: --- ### **一、核心功能** 1. **事务自动管理** 标记的方法或类会自动开启事务,方法执行成功则提交事务,抛出异常则回滚。 2. **事务属性配置** 支持定义事务的传播行为、隔离级别、超时时间、只读模式等属性: ```java @Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 30, readOnly = false, rollbackFor = {SQLException.class} ) ``` --- ### **二、关键属性详解** 1. **传播行为(Propagation)** 控制事务的嵌套逻辑: - `REQUIRED`(默认):存在事务则加入,否则新建。 - `REQUIRES_NEW`:始终新建事务,挂起当前事务。 - `NESTED`:嵌套事务(依赖数据库支持)。 - `SUPPORTS`/`NOT_SUPPORTED`/`NEVER`:根据是否存在事务调整行为。 2. **隔离级别(Isolation)** 解决并发问题(如脏读、幻读): - `DEFAULT`:使用数据库默认级别。 - `READ_COMMITTED`(常用):避免脏读。 - `SERIALIZABLE`:完全串行化,性能低。 3. **回滚规则(rollbackFor/noRollbackFor)** - 默认回滚:`RuntimeException` 和 `Error`。 - 需显式指定回滚检查型异常(如 `rollbackFor = IOException.class`)。 --- ### **三、使用注意事项** 1. **自调用失效问题** **问题**:同类中非事务方法调用事务方法,事务生效(绕过代理)。 **解决**:通过注入自身代理对象调用: ```java @Autowired private MyService self; // 通过ApplicationContext获取自身代理 public void methodA() { self.methodB(); } @Transactional public void methodB() { ... } ``` 2. **异常处理陷阱** - **吞掉异常**:若在方法内 `catch` 异常未重新抛出,事务会回滚。 - **检查型异常**:默认回滚,需通过 `rollbackFor` 显式配置。 3. **数据库引擎支持** 确保使用支持事务的引擎(如 MySQL 的 InnoDB),MyISAM 支持事务。 4. **方法可见性** 仅对 `public` 方法生效(Spring AOP 代理限制),`private`/`protected` 方法无效。 5. **事务超时与性能** - 避免长事务占用连接:设置合理 `timeout`。 - 只读查询优化:标记 `readOnly = true`(提示数据库优化)。 6. **传播行为选择** 谨慎使用 `REQUIRES_NEW`:可能产生死锁风险,或导致外层事务失败影响内层提交。 --- ### **四、最佳实践** 1. **明确指定回滚异常** 避免默认行为符合预期: ```java @Transactional(rollbackFor = {Exception.class}) ``` 2. **事务粒度控制** 尽量在方法级别使用,避免类级别注解扩散到所有方法。 3. **避免事务中远程调用** 网络操作可能阻塞事务,导致数据库连接池耗尽。 --- ### **五、调试技巧** - 启用事务日志: `logging.level.org.springframework.transaction.interceptor=TRACE` - 检查代理对象:确保方法被 Spring 代理(如 `this.getClass()` 输出应为 `xxx$$EnhancerBySpringCGLIB`)。 --- 掌握这些细节可显著减少事务相关的 Bug,确保数据一致性的同时提升系统可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赖伟春

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值