Spring事务失效问题分析及解决方案
作者:街头卖艺的肖邦 发布时间:2023-07-09 11:08:15
这篇文章主要介绍了Spring事务失效问题分析及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
隔离级别
在 TransactionDefinition.java 接口中,定义了“四种”的隔离级别枚举:
/**
* 【Spring 独有】使用后端数据库默认的隔离级别
*
* MySQL 默认采用的 REPEATABLE_READ隔离级别
* Oracle 默认采用的 READ_COMMITTED隔离级别
*/
int ISOLATION_DEFAULT = -1;
/**
* 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
*/
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
/**
* 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
*/
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
/**
* 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
*/
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
/**
* 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
*
* 但是这将严重影响程序的性能。通常情况下也不会用到该级别。
*/
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
事务的传播级别
事务的传播行为,指的是当前带有事务配置的方法,需要怎么处理事务;例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行;
需要注意,事务的传播级别,并不是数据库事务规范中的名词,而是 Spring 自身所定义的。通过事务的传播级别,Spring 才知道如何处理事务,是创建一个新事务呢,还是继续使用当前的事务;
在 TransactionDefinition.java 接口中,定义了三类七种传播级别:
// ========== 支持当前事务的情况 ==========
/**
* 如果当前存在事务,则使用该事务。
* 如果当前没有事务,则创建一个新的事务。
*/
int PROPAGATION_REQUIRED = 0;
/**
* 如果当前存在事务,则使用该事务。
* 如果当前没有事务,则以非事务的方式继续运行。
*/
int PROPAGATION_SUPPORTS = 1;
/**
* 如果当前存在事务,则使用该事务。
* 如果当前没有事务,则抛出异常。
*/
int PROPAGATION_MANDATORY = 2;
// ========== 不支持当前事务的情况 ==========
/**
* 创建一个新的事务。
* 如果当前存在事务,则把当前事务挂起。
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* 以非事务方式运行。
* 如果当前存在事务,则把当前事务挂起。
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* 以非事务方式运行。
* 如果当前存在事务,则抛出异常。
*/
int PROPAGATION_NEVER = 5;
// ========== 其他情况 ==========
/**
* 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行。
* 如果当前没有事务,则等价于 {@link TransactionDefinition#PROPAGATION_REQUIRED}
*/
int PROPAGATION_NESTED = 6;
@Transaction
@Transaction 注解是Spring的tx模块提供的,使用AOP实现的事务控制,即底层为 * ;
@Transaction 可以作用于接口、接口方法、类以及类方法上;当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,也可以在方法级别使用该标注来覆盖类级别的定义;
事务失效
1.异常类型错误,默认是RuntimException才会回滚
2.异常被catch后没有抛出,需要抛异常才能回滚
3.是否发生自身调用的问题
4.注解所在位置是否为public修饰
5.数据源没有配置事务管理器
6.不支持事务的引擎,如MyIsam
下面是一个事务失效的例子
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
@Override
public void parent() {
try {
this.child();
} catch (Exception e) {
log.error("插入异常", e);
}
Order order = new Order();
order.setOrderNo("parent");
order.setStatus("0");
order.setTitle("parent");
order.setAmount("1000");
orderMapper.insert(order);
}
@Transactional
@Override
public void child() {
Order order = new Order();
order.setOrderNo("child");
order.setStatus("0");
order.setTitle("child");
order.setAmount("2000");
orderMapper.insert(order);
throw new RuntimeException();
}
}
当调用parent方法时,会调用child方法,但执行完成后插入的记录有两条,一条是parent的,一条是child的;
这是因为上面的parent方法调用的child方法出现问题,@Transaction 是基于AOP的方式进行事务控制的(CglibAopProxy.java进行方法拦截,TransactionInterceptor.java进行代理对象调用执行方法),需要使用的是代理对象调用方法,上面的代码使用的还是this,即当前实例化对象,因此执行this.child(),方法不能被拦截增强;
将上面的parent方法修改如下
@Autowired
private ApplicationContext context;
private OrderService orderService;
@PostConstruct
public void init() {
orderService = context.getBean(OrderService.class);
}
@Transactional
@Override
public void parent() {
try {
//获取代理对象,通过代理对象调用child()
orderService.child();
//获取代理对象
//OrderService orderService = (OrderService) AopContext.currentProxy();
//orderService.child();
} catch (Exception e) {
log.error("插入异常", e);
}
Order order = new Order();
order.setOrderNo("parent");
order.setStatus("0");
order.setTitle("parent");
order.setAmount("1000");
orderMapper.insert(order);
}
执行parent方法,当出现异常的时候,事务会进行回滚;
如果想实现当调用parent方法时,调用child方法发生异常,只回滚child方法插入的数据,parent方法插入的数据不回滚,修改如下
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void child() {
Order order = new Order();
order.setOrderNo("child");
order.setStatus("0");
order.setTitle("child");
order.setAmount("2000");
orderMapper.insert(order);
throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
如果当前存在事务,则挂起事务并开启一个新事务执行,新事务执行完毕后,唤醒之前的挂起的事务,则继续执行;如果当前不存在事务,则新建一个事务;
来源:https://www.cnblogs.com/coder-zyc/p/12181688.html
猜你喜欢
- 当目标数据库不能直连的,需要一个服务器作为中间跳板的时候,我们需要通过SSH通道连接数据库。ps:使用ssh连接,相当于本地开了个端口去连接
- 一:SparkSQL1.SparkSQL简介Spark SQL是Spark的一个模块,用于处理结构化的数据,它提供了一个数据抽象DataFr
- web.xml中设置:<servlet> <servlet-name>DisplayChart</servle
- 一. 封装封装是面向对象的三大特性之一;面向对象程序三大特性:封装、继承、多态 。封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和
- 概述Spring boot 中的 @Conditional 注解是一个不太常用到的注解,但确实非常的有用,我们知道 Spring Boot
- spring validation多层对象校验1、第一层对象定义import java.io.Serializable; import ja
- java 请求跨域问题解决方法实例详解新建Util类,在Util中添加下面方法: /* * response请求跨域公共设置
- 一、材料准备1、jdk1.8的安装包2、maven安装包3、idea工具二、配置jdk1.8环境变量1.jdk下载jdk下载网址(点击此链接
- 一、BigInteger介绍如果在操作的时候一个整型数据已经超过了整数的最大类型长度 long 的话,则此数据就无法装入,所以,此时要使用
- 一、概述从3.1版本起,Spring框架就已经支持将缓存添加到现有的Spring应用中,和事务支持一样,缓存抽象允许在对代码影响最小的情况下
- 前言周六在公司写Reactor模型,一女同事问我为啥都2023年了还在学习Reactor模型呀,我问她为啥快30的年纪了,周六还在公司看我写
- 目录前言一、技术介绍1.ReentranReadWriteLock是什么?二、源码分析1.ReadLock2.WriteLock三、单元测试
- 一,功能介绍本点单系统主要是基于SpringBoot框架和小程序开发的,主要是为当代人们的生活提供更便利、更高效的服务,也为营销者提供更好的
- 一、什么是抽象工厂模式为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。抽象工厂模式是所有形态的工厂模式中最为抽象和最具
- Java中可以使用关键字synchronized进行线程同步控制,实现关键资源顺序访问,避免由于多线程并发执行导致的数据不一致性等问题。sy
- 本文实例讲述了Java方法的参数传递机制。分享给大家供大家参考,具体如下:参数传递机制对于程序设计语言来说,一般方法(函数)的参数传递有两种
- Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,它有两种参数形式: public static
- 引言在Broker中,事务消息的初始化是通过BrokerController.initialTransaction()方法执行的。priva
- 定义MD全称Message-Digest,即信息摘要,所以MD家族的算法也叫信息摘要算法MD家族有MD2、MD3、MD4、MD5,一代比一代
- 本篇给大家详细讲解了MTKAndroid平台开发流程,大致分为44个步骤,我们把每个步骤的命令详细讲解了下,一起来学习下。1.拷贝代码仓库从