@Transactional的一个不生效问题

本文探讨了Spring框架中@Transactional注解的应用细节及其限制条件,包括为何仅适用于public方法、自调用问题及解决方案,如使用AspectJ替代Spring AOP。


2、了解一下@Transactional注解


事物的注解方式:@Transactional

@Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解所使用。

@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。


看到这里我发现@Override注解和@Transactional注解一起使用不会导致@Transactional不生效。

3、追击为什么  @Transactional 注解应该只被应用到 public 方法上!!!


如下图1:

Spring 事务实现机制(基于CglibAopProxy


@Transactional 只能应用到 public 方法才有效

只有@Transactional 注解应用到 public 方法,才能进行事务管理。这是因为在使用 Spring AOP 代理时,Spring 在调用在图 1 中的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取表 1. @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。

清单 4. AbstractFallbackTransactionAttributeSource


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


这个方法会检查目标方法的修饰符是不是 public,若不是 public,就不会获取@Transactional 的属性配置信息,最终会造成不会用 TransactionInterceptor 来拦截该目标方法进行事务管理。


本以为问题得到了解决,但是彬哥补充到,这个案例即使把方法的protected改为public也是不起作用的。

为什么呢?

原因是因为@Transactional是基于springAOP代理实现的,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。process方法是updateParticularsCheckResult方法内部的方法,无法获取到该bean,所以还是不会使@Transactional生效。

终上,该问题导致@Transactional失效有俩个原因,

原因1、@Transactional只能应用在public方法才有效

原因2、 @Transactional不能作用在方法的内部的任何方法上


4、补充

class C1 : 方法a1 ,方法b1


classC2:   方法a2


事务方法a1  事务方法b1   俩者事务都会执行

事务方法a1  非事务方法b1   a1事务会执行

非事务方法a1  事务方法b1   b1事务不会执行


事务方法a1  事务方法a1   俩者事务都会执行

事务方法a1  非事务方法b1   a1事务会执行

非事务方法a1  事务方法b1   b1事务会执行


其中值得深究的是 非事务方法a1  事务方法b1   b1事务不会执行  ,


原因是: 

在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成一个自调用问题。

若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务就会被忽略,不会发生回滚。


这里需要对 Spring 生成的代理对象来管理造成一个自调用问题。解释一下:

在应用系统采用了 -> 调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑;分析这句话 意思也就是要想让spring的声明式@Transactional 生效,必须要经过 拦截器 TransactionInterceptor处理,而自调用问题导致的原因也正是TransactionInterceptor没有 拦截到。


解决方法:

根本原因是 由于使用 Spring AOP 代理造成的。所以替代Spring AOP 代理问题就解决了,

 使用 AspectJ 取代 Spring AOP 代理。


 

 AspectJ 的 xml 配置信息

 


1
2
3
4
5
6
7
8
9
10
< tx:annotation-driven mode = "aspectj" />
< bean id = "transactionManager"
class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = "dataSource" ref = "dataSource" />
</ bean >
</ bean
class = "org.springframework.transaction.aspectj.AnnotationTransactionAspect"
factory-method = "aspectOf" >
< property name = "transactionManager" ref = "transactionManager" />
</ bean >


同时在 Maven 的 pom 文件中加入 spring-aspects 和 aspectjrt 的 dependency 以及 aspectj-maven-plugin。

 AspectJ 的 pom 配置信息


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
< dependency >
< groupId >org.springframework</ groupId >
< artifactId >spring-aspects</ artifactId >
< version >4.3.2.RELEASE</ version >
</ dependency >
< dependency >
< groupId >org.aspectj</ groupId >
< artifactId >aspectjrt</ artifactId >
< version >1.8.9</ version >
</ dependency >
< plugin >
< groupId >org.codehaus.mojo</ groupId >
< artifactId >aspectj-maven-plugin</ artifactId >
< version >1.9</ version >
< configuration >
< showWeaveInfo >true</ showWeaveInfo >
< aspectLibraries >
< aspectLibrary >
< groupId >org.springframework</ groupId >
< artifactId >spring-aspects</ artifactId >
</ aspectLibrary >
</ aspectLibraries >
</ configuration >
< executions >
< execution >
< goals >
< goal >compile</ goal >
< goal >test-compile</ goal >
</ goals >
</ execution >
</ executions >
</ plugin >


而且使用AspectJ 取代 Spring AOP 代理还可以解决@Transactional 注解只应用到 public 方法的问题。

在 Spring 框架中,使用 `@Transactional` 注解来管理事务是常见的做法。然而,在某些情况下,尤其是在执行删除(delete)操作时,事务可能会失效,导致预期的回滚或提交行为未能按预期执行。以下是些可能导致 `@Transactional` 在 delete 方法中不生效的原因及其解决方案。 ### 1. 异常类型问题 默认情况下,Spring 的事务管理仅对未检查异常(unchecked exceptions),即继承自 `RuntimeException` 或 `Error` 的异常进行回滚。对于已检查异常(checked exceptions),例如 `IOException`、`SQLException` 等,事务不会自动回滚[^3]。因此,如果 delete 方法抛出的是已检查异常,需要显式指定 `rollbackFor` 属性以确保事务能够正确回滚。 ```java @Transactional(rollbackFor = Exception.class) public void deleteWithRollbackOnCheckedException() throws Exception { // 执行 delete 操作 // 如果发生 checked exception,则会回滚 } ``` ### 2. 自调用问题(Self-invocation) 当在一个类内部调用带有 `@Transactional` 注解的方法时,由于 Spring AOP 代理机制的限制,事务可能不会生效。这是因为 Spring 使用基于代理的 AOP 实现,默认情况下只有外部调用才会经过代理对象,从而触发事务管理逻辑。为了解决这个问题,可以考虑重构代,将事务方法移到另一个 bean 中,或者使用 `AopContext.currentProxy()` 来获取当前代理对象并进行自我调用。 ```java @Service public class MyService { @Autowired private MyService selfProxy; @Transactional public void deleteOperation() { // 删除操作 } public void triggerDelete() { ((MyService) AopContext.currentProxy()).deleteOperation(); // 使用代理调用 } } ``` ### 3. 非 public 方法 Spring 的事务管理功能依赖于代理模式,这意味着只有 public 方法才能被正确地代理增强。如果尝试在非 public 方法上使用 `@Transactional` 注解,事务管理将不会生效。确保所有需要事务支持的方法都是 public 访问级别的。 ### 4. 数据源配置问题 确保数据源配置正确,并且启用了事务管理。在 Spring 配置文件中,应该包含 `<tx:annotation-driven />` 元素以启用注解驱动的事务管理。此外,还需要正确配置 `PlatformTransactionManager` bean。 ```xml <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> ``` ### 5. SQL 语句问题 某些数据库操作可能不会触发事务管理,比如 DDL 语句(CREATE, ALTER, DROP)。这些操作通常具有隐式的提交行为,即使它们位于一个事务块内。确保 delete 操作不是与这类语句混合使用的。 ### 总结 要解决 `@Transactional` 在 delete 操作中不生效问题,首先应检查是否是因为异常类型不当导致的回滚失败,然后确认是否存在自调用问题,接着验证方法访问权限是否为 public,最后检查数据源事务管理器的配置是否正确。通过以上步骤,大多数关于 `@Transactional` 不生效问题都可以得到解决。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值