同一个对象内事务方法互调无效问题

441 阅读4分钟

本地事务

回顾一下基础知识:

本地事务特性:

原子性(Atomicity)、一致性(Consistency)、隔离性或独立性(lsoklation)和持久性(Durabilily)

简称ACID

事务隔离级别: ---》isolation

1.READ UNCOMMITTED(读未提交)

该隔离级别的事务会读到其他未提交事务的数据,此现象又称为脏读

2.READ COMMITTED(读提交)

一个事物可以读取另一个事物已提交的事务,多次读取会造成不一样的结果,此现象称为不可重复读问题,Oracle和SQL server的默认隔离级别

3.REPEATABLE(可重复读)

该隔离级别是mysql默认的隔离级别,在同一个事物里,select的结果是事物开始时时间点的状态,因此,同样的select操作读到的结果是一致的,但是会有幻读现象

4.SERIALIZABLE(序列化)

在该隔离级别下事务都是串行顺序执行的,mysql数据库的InnoDB引擎会给读操作模式加一把读共享锁,从而避免了脏读,不可重复读和幻读

以上4种隔离级别的并发能力逐渐变弱

在Spring中注解实现@Transactional(isolation = Isolation.READ_UNCOMMITTED )

事务的传播行为---》propagation

1.PROPAGATION_REQUIRES: 如果当前没有事务,就创建一个新事物,如果当前存在事务,就加入事务,该设置是最常用的设置

2.PROPAGATION_SUPPORTS: 支持当前事务。如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行

3.PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果不存在事务,就抛出异常

4.PROPAGATION_REQUIRES_NEW: 创建新事务,无论当前存不存在事务,都创建新事务

5.RPOPAGATION_NOT_SUPPORTED: 以非事务方式执行操作,如果当前存在事务,就把事务挂起

6.PROPAGATION_NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常

7.PROPAGATION_NESTED: 如果当前存在事务,则在嵌套事物内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似操作

在Spring中注解实现@Transactional(propagation = propagation.xxxxxx )

Springboot踩坑:

同一个对象内事务方法互调默认无效

@Service
public class AffairServiceImpl {

    @Transactional()
    public void a(){
        b();  //a事务
        c();   //新事务
        int i=10/0;
    }
    @Transactional(propagation = Propagation.REQUIRED)
    public void b(){

    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void c(){

    }
}

如果这3个事务在一个service里面,那么b,c做的任何设置都会失效,都和a共用一个事物

事务是使用代理对象来控制的,原因是b,c绕过了代理对象

解决方案一(萌新):

要想使他们有用,必须让它们不在同一个service里面

@Service
public class AffairServiceImpl {

@Transactional()
public void a(){
   bService.b();  //a事务
   cService.c();   //新事务
    int i=10/0;
}

如果我必须在本类方法中互调呢?

解决方案二:

1.引入spring-boot-starter-aop 引入了aspectj

2.@EnableAspectJAutoProxy:开启aspectj动态代理功能,以后所有的动态代理都是aspectj创建的(即使没有接口也可以创建动态代理),因为默认的是基于jdk的动态代理,jdk的动态代理必须要有接口

面试题:JDK动态代理和CGLIB动态代理的区别

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

  • JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
  • 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

3.本类用代理对象互调

@Service
public class AffairServiceImpl {

@Transactional()
public void a(){
  //AopContext.currentProxy(); 获取当前上下文的代理对象
  AffairServiceImpl affairService = (AffairServiceImpl) AopContext.currentProxy();
  affairService.b(); //a事务
  affairService.c(); //新事务
  int i=10/0;
}
@Transactional(propagation = Propagation.REQUIRED)
public void b(){

}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void c(){

}

}