本文主要介绍Spring事务失效的分析和解决方法,通过实例代码详细介绍,对大家的学习或工作有一定的参考价值。有需要的朋友可以参考一下。
本文主要介绍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;
//======其他情况========
/**
*如果当前存在事务,则创建一个事务作为当前事务的嵌套事务运行。
*如果当前没有事务,则相当于{ @ linktransactiondefinition # propagation _ required }
*/
int PROPAGATION _ NESTED=6;
@Transaction
@事务标注由Spring的tx模块提供,使用AOP实现事务控制,即底层是动态代理;
@Transaction可以作用于接口、接口方法、类和类方法;当在类上使用时,该类的所有公共方法都将具有这种类型的transaction属性,或者您可以在方法级别使用此注释来覆盖类级别的定义。
事务失效
1.异常类型错误,默认是运行时异常才会回滚
2.异常被捕捉后没有抛出,需要抛异常才能回滚
3.是否发生自身调用的问题
4.注解所在位置是否为公众的修饰
5.数据源没有配置事务管理器
6.不支持事务的引擎,如米沙姆
下面是一个事务失效的例子
@Slf4j
@服务
公共类OrderServiceImpl实现订单服务{
@自动连线
私有顺序映射器;
@事务性
@覆盖
public void parent() {
尝试{
这个。child();
} catch(异常e) {
log.error(插入异常,e);
}
订单Order=new Order();
秩序。setorderno(“parent”);
秩序。设置状态(" 0 ");
秩序。settitle(“父”);
秩序。设置金额( 1000 );
订单映射器。插入(订单);
}
@事务性
@覆盖
public void child() {
订单Order=new Order();
秩序。setorderno(“child”);
秩序。设置状态(" 0 ");
秩序。settitle( child );
秩序。设置金额( 2000 );
订单映射器。插入(订单);
抛出新的运行时异常();
}
}
当调用父母方法时,会调用儿童方法,但执行完成后插入的记录有两条,一条是父母的,一条是儿童的;
这是因为上面的父母方法调用的儿童方法出现问题,@交易是基于面向切面编程的方式进行事务控制的(CglibAopProxy.java进行方法拦截,TransactionInterceptor.java进行代理对象调用执行方法),需要使用的是代理对象调用方法,上面的代码使用的还是这个,即当前实例化对象,因此执行this.child(),方法不能被拦截增强;
将上面的父母方法修改如下
@自动连线
私有应用程序上下文上下文;
private OrderService OrderService;
@PostConstruct
public void init() {
orderService=上下文。获取bean(orderService。类);
}
@事务性
@覆盖
public void parent() {
尝试{
//获取代理对象,通过代理对象调用子级()
订单服务。child();
//获取代理对象
//OrderService OrderService=(OrderService)AOP上下文。当前代理();
//orderservice。child();
} catch(异常e) {
log.error(插入异常,e);
}
订单Order=new Order();
秩序。setorderno(“parent”);
秩序。设置状态(" 0 ");
秩序。settitle(“父”);
秩序。设置金额( 1000 );
订单映射器。插入(订单);
}
执行父母方法,当出现异常的时候,事务会进行回滚;
如果想实现当调用父母方法时,调用儿童方法发生异常,只回滚儿童方法插入的数据,父方法插入的数据不回滚,修改如下
@事务性(传播=传播。需要_新)
@覆盖
public void child() {
订单Order=new Order();
秩序。setorderno(“child”);
秩序。设置状态(" 0 ");
秩序。settitle( child );
秩序。设置金额( 2000 );
订单映射器。插入(订单);
抛出新的运行时异常();
}
@事务性(传播=传播。需要_新)
如果当前存在事务,则挂起事务并开启一个新事务执行,新事务执行完毕后,唤醒之前的挂起的事务,则继续执行;如果当前不存在事务,则新建一个事务;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。