@Transaction注解失效的几种场景(附有示例代码)

2024-02-06 23:28

本文主要是介绍@Transaction注解失效的几种场景(附有示例代码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

0 说明

一、抛出检查异常导致事务不能正确回滚

二、业务方法内自己 try-catch 异常导致事务不能正确回滚

三、AOP 切面顺序导致导致事务不能正确回滚

四、非 public 方法导致的事务失效

五、父子容器导致的事务失效

六、调用本类方法导致传播行为失效

七、@Transactional注解没有保证原子行为

八、@Transactional 方法导致的 synchronized 失效


0 说明

当使用 @Transactional 注解时,可能会出现失效的情况。这种失效可能由于各种因素造成,比如异常处理、AOP切面的顺序、方法的可见性等。失效可能会导致事务无法正确地回滚或提交,从而影响数据的一致性和完整性。以下是八种常见的@Transaction注解失效的场景。

一、抛出检查异常导致事务不能正确回滚

当一个受 @Transactional 注解管理的方法中抛出了检查异常(即继承自 Exception,而非 RuntimeException),Spring 会默认将其视为受检查异常,并不会触发事务的回滚。因此,若想确保事务正确回滚,应当将异常转换为运行时异常。

@Service
public class ExampleService {@Autowiredprivate ExampleRepository repository;@Transactionalpublic void updateData() {try {repository.update(); // 抛出检查异常} catch (SQLException ex) {throw new RuntimeException(ex); // 转换为运行时异常}}
}

二、业务方法内自己 try-catch 异常导致事务不能正确回滚

如果在事务方法内部捕获了异常并进行了处理,而没有将其重新抛出或者抛出非 RuntimeException,则事务不会被回滚。

@Service
public class ExampleService {@Autowiredprivate ExampleRepository repository;@Transactionalpublic void processData() {try {// 业务逻辑} catch (Exception ex) {// 异常处理,但未重新抛出}}
}

三、AOP 切面顺序导致导致事务不能正确回滚

如果在事务方法中使用了 AOP 切面,并且切面的顺序不正确,可能会导致事务失效。通常情况下,应确保事务切面的顺序在其他切面之前。

@Aspect
@Component
public class ExampleAspect {@Around("execution(* com.example.service.ExampleService.*(..))")public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {// 前置逻辑Object result = joinPoint.proceed(); // 调用目标方法// 后置逻辑return result;}
}

四、非 public 方法导致的事务失效

Spring 的事务代理是通过代理对象来实现的,因此 @Transactional 注解只对公有方法有效。非公有方法如果被其他方法内部调用,则事务不会被切面代理管理,导致失效。

@Service
public class ExampleService {@Transactionalpublic void publicMethod() {// 业务逻辑}@Transactionalprivate void privateMethod() {// 业务逻辑}public void callerMethod() {privateMethod(); // 被调用的非公有方法内的事务失效}
}

五、父子容器导致的事务失效

在 Spring 中,如果父子容器的配置不正确,可能会导致事务失效。一种常见情况是父容器和子容器分别管理了事务,但子容器中的事务不会受父容器的影响,导致事务操作失效。

@Configuration
@EnableTransactionManagement
public class ParentConfig {@Beanpublic DataSource dataSource() {// 配置数据源}@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}
}@Configuration
@EnableTransactionManagement
public class ChildConfig {@Beanpublic DataSource dataSource() {// 配置不同的数据源}@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}
}

六、调用本类方法导致传播行为失效

如果在同一个类中调用带有 @Transactional 注解的方法,Spring 的事务传播行为可能会失效。因为 Spring 通过 AOP 代理来管理事务,同一个类内的方法调用并不会触发代理,事务传播行为不会被应用。

@Service
public class ExampleService {@Transactionalpublic void methodA() {methodB(); // 调用本类方法}@Transactional(propagation = Propagation.REQUIRES_NEW)public void methodB() {// 事务传播行为 REQUIRES_NEW 失效}
}

七、@Transactional注解没有保证原子行为

虽然 @Transactional 注解可以确保一组操作要么全部成功,要么全部失败,但它并不能保证原子性操作。如果在一个事务方法中出现多个数据库操作,并且某些操作成功,而其他操作失败,那么只有失败的那部分会回滚,而成功的部分会保留,这可能导致数据不一致。

@Service
public class ExampleService {@Autowiredprivate ExampleRepository repository;@Transactionalpublic void updateData() {repository.updateData1(); // 更新操作1repository.updateData2(); // 更新操作2// 操作2失败,但操作1已提交,数据不一致}
}

八、@Transactional 方法导致的 synchronized 失效

如果一个方法同时使用了 @Transactional 和 synchronized 注解,那么 synchronized 的锁将被事务代理所绕过,导致锁的失效,多个线程可能同时访问该方法,破坏了同步性。

@Service
public class ExampleService {@Transactionalpublic synchronized void process() {// 事务方法}
}

在上述代码中,尽管 process 方法被声明为 synchronized,但由于被 @Transactional 注解修饰,Spring 的事务代理会绕过 synchronized 锁,导致同步失效。

这篇关于@Transaction注解失效的几种场景(附有示例代码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/685868

相关文章

检查 Nginx 是否启动的几种方法

《检查Nginx是否启动的几种方法》本文主要介绍了检查Nginx是否启动的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1. 使用 systemctl 命令(推荐)2. 使用 service 命令3. 检查进程是否存在4

MySQL中between and的基本用法、范围查询示例详解

《MySQL中betweenand的基本用法、范围查询示例详解》BETWEENAND操作符在MySQL中用于选择在两个值之间的数据,包括边界值,它支持数值和日期类型,示例展示了如何使用BETWEEN... 目录一、between and语法二、使用示例2.1、betwphpeen and数值查询2.2、be

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

Go异常处理、泛型和文件操作实例代码

《Go异常处理、泛型和文件操作实例代码》Go语言的异常处理机制与传统的面向对象语言(如Java、C#)所使用的try-catch结构有所不同,它采用了自己独特的设计理念和方法,:本文主要介绍Go异... 目录一:异常处理常见的异常处理向上抛中断程序恢复程序二:泛型泛型函数泛型结构体泛型切片泛型 map三:文

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

MyBatis中的两种参数传递类型详解(示例代码)

《MyBatis中的两种参数传递类型详解(示例代码)》文章介绍了MyBatis中传递多个参数的两种方式,使用Map和使用@Param注解或封装POJO,Map方式适用于动态、不固定的参数,但可读性和安... 目录✅ android方式一:使用Map<String, Object>✅ 方式二:使用@Param

SpringBoot返回文件让前端下载的几种方式

《SpringBoot返回文件让前端下载的几种方式》文章介绍了开发中文件下载的两种常见解决方案,并详细描述了通过后端进行下载的原理和步骤,包括一次性读取到内存和分块写入响应输出流两种方法,此外,还提供... 目录01 背景02 一次性读取到内存,通过响应输出流输出到前端02 将文件流通过循环写入到响应输出流

SpringBoot实现图形验证码的示例代码

《SpringBoot实现图形验证码的示例代码》验证码的实现方式有很多,可以由前端实现,也可以由后端进行实现,也有很多的插件和工具包可以使用,在这里,我们使用Hutool提供的小工具实现,本文介绍Sp... 目录项目创建前端代码实现约定前后端交互接口需求分析接口定义Hutool工具实现服务器端代码引入依赖获