概述
文章目录
- 一、本地事务与分布式事务
- 二、本地事务实现方式—@Transactional注解
- 1.基本介绍
- 2.@Transactional注解属性
- 3.失效原因
- 1.底层数据库引擎不支持事务
- 2.在非public修饰的方法使用
- 3.异常被catch掉了
- 4.方法中调用同类方法
- 5.rollbackFor(noRollbackFor)属性设置错误
- 6.Spring事务的传播行为类型配置错误
- 4.失效原因总结
- 四、失效的解决方案
一、本地事务与分布式事务
先说事务,事务是逻辑上的一组sql语句,在一个事务中的sql语句要么一起成功,要么一起失败。
本地事务就是这一组sql语句在一个数据库连接中执行,即我们常说的事务就是一个本地事务,遵循ACID的。
分布式事务就是这一组sql语句在不同的数据库连接执行。比如小明给小红转钱,在业务代码中这样写
@Transactional
public void test(){
//执行sql语句给小明-20
String sql="update account set money=money-20 where name='小明'"
//远程调用方法给小红余额+20
httpclient.addXiaoMoney();
}
此时test方法中"给小明-20"和"给小红+20"就不在一个数据库连接中,此时如果test方法发生异常,也只能让"小明-20"的sql回滚。所以@Transactional是本地事务的实现方式
二、本地事务实现方式—@Transactional注解
1.基本介绍
在Spring中实现本地事务的。一共两种,一个是编程式事务,一个是声明式事务,如下
- 编程式事务:begin,commit手动提交事务
- 声明式事务:加上@Transactional注解
现在基本上都是使用的@Transactionsal注解
2.@Transactional注解属性
transactionManager
- 指定事务管理器,值为bean的名称,这个主要用于多事务管理器情况下指定。比如多数据源配置的情况下
isolation
- 事务的隔离级别,默认是isolation.DEFAULT。这个和数据库事务隔离级别对应,一共四个
propagation(Spring事务的传播行为类型)
事务传播行为是说一个被调用方法事务是否和调用方法事务合为一个大事务。如果合,那么调用方法的所有事务设置传播到被调用方法中
timeout
- 事务的超时时间,单位为秒。
readOnly
- 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。如果一个事务只涉及到只读,可以设置为true。
rollbackFor
- 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。 默认是在RuntimeException和Error上回滚。
noRollbackFor
- 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。
3.失效原因
1.底层数据库引擎不支持事务
如果数据不支持事务,则Spring自然无法支持事务。如我们常用的mysql,如果引擎采用的是MyISAM,是不支持事务操作的。改成InnoDB才可以。
2.在非public修饰的方法使用
@Transacational注解使用的是AOP,在使用动态代理的时候只能针对public方法进行代理,源码依据在AbsractFallbackTransactionAttributeSource类中的computeTransactionAttribute方法中,如下图所示,注释写着不允许不是public方法。
3.异常被catch掉了
在整个事务方法中使用try-catch,导致异常无法抛出,自然会导致事务失效。伪代码如下:
@Transactional
public void test(){
try {
//数据库操作语句
}catch (Exception e){
return;
}
}
4.方法中调用同类方法
简单来说就是一个类中的A方法(未标注声明式事务)在内部调用了B方法(标注了声明式事务),这样会导致B方法中的事务失效
public class Test{
public void A(){
//插入一条数据
//调用B方法
B();
}
@Transactional
public void B(){
//插入数据
}
}
为什么会失效呢?其实原因很简单,Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效。
5.rollbackFor(noRollbackFor)属性设置错误
@Transactional(rollbackFor = ServiceException.class),如果在方法中没有抛出该异常则事务失效。反过来对noRollbackFor也一样
6.Spring事务的传播行为类型配置错误
事务的传播属性在上面已经介绍了,默认的事务传播属性是Propagation.REQUIRED,但是一旦配置了错误的传播属性,也是会导致事务失效,如下三种配置将会导致事务失效:
- Propagation.SUPPORTS
- Propagation.NOT_SUPPORTED
- Propagation.NEVER
4.失效原因总结
将上面的失效原因进行结构化,结构化的角度是从事务的使用到事务回滚过程中可能将发生事务失效。下面分为四个步骤
- 事务使用
- 事务执行
- 抛出异常
- 事务回滚
四、失效的解决方案
我们上面说到在类中直接调用方法是不走代理类事务的逻辑的,那么如何解决呢?如果是自己本类注入本类实例,可能会有循环依赖。我们采取另一个方式
使用代理类调用方法就可以了
@Transactional
public void a() {
//this.b();
TestService service = (TestService)AopContext.currentProxy();
service.b();
}
@Transactional
public void b(){}
引入依赖spring-boot-starter-aop
并在启动类开启代理类注解@EnableAspectJAutoProxy(exposeProxy = true)
,该注解会使用AspectJ方式取代jdk动态代理,exposeProxy属性是对外暴露代理对象
参考资料:https://blog.csdn.net/nbdebuyaobuyao/article/details/107998258
最后
以上就是傻傻水池为你收集整理的分布式事务(一)—本地事务及@Transactional使用、失效原因及解决方案的全部内容,希望文章能够帮你解决分布式事务(一)—本地事务及@Transactional使用、失效原因及解决方案所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复