事务什么时候生效,你真的了解吗?置信很多小伙伴在面试过程常常会被面试官问到[email protected]什么时候会生效?带着这个问题,我先从以下四个生效的例子,讲述生效的场景,先让咱们初步有个大略的理解,而后再通过源码的解读,剖析@Transactional生效的起因,从而防止当前在工作中踩坑。

@Transactional 生效场景

  1. 公有办法(拜访权限修饰符为private的办法)
  2. 异样类型不匹配
  3. 同类非事务办法调用事务办法
  4. 多线程

示例1:公有办法

应用private润饰@Transactional注解的办法,因为idea会查看实现类办法的拜访权限修饰符,所以此处不进行测试。

示例2:异样类型不匹配

这是因为咱们在写@Transactional后没有设置回滚类型导致,这个在idea中会有黄色正告线标识-办法【exceptionType】须要在Transactional注解指定rollbackFor或者在办法中显式的rollback。提醒咱们指定回滚异样类型,防止产生意料之外的后果。

@Override@Transactionalpublic void exceptionType(boolean isEnableRollbackException) throws IOException {    userMapper.insert(User.builder().name("cpz").age(18).email("[email protected]").build());    if (isEnableRollbackException) {        throw new RuntimeException("RUNTIME Exception");    } else {        throw new IOException("IOE Exception");    }}

代码解析:本例通过向数据库新增一条用户数据,而后抛出不同类型的异样(通过布尔类型参数isEnableRollbackException管制),最初察看该条数据是否在数据库里,即可实现类型异样的测试。

示例3:同类非事务办法调用事务办法

这是因为@Transactional是基于 aop 实现的,而 aop 应用动静代理实现,通过代理间接调用办法,会在办法前后加上事务相干逻辑。而当初间接通过类外部办法调用,则不会在办法前后生成事务相干逻辑,自然而然事务也会生效。

@Overridepublic void internalMethod() {    this.insertUser();    throw new RuntimeException("Runtime Exception");}@Transactionalpublic void insertUser() {          userMapper.insert(User.builder().name("cpz").age(18).email("[email protected]").build());}

代码解析:通过申明事务办法insertUser,而后应用同类办法internalMethod间接调用,从而绕过动静代理对象的加强-开启事务,提交事务等逻辑。最初察看该条数据是否在数据库里,即可实现同类非事务办法调用事务办法的测试。

示例4:多线程

因为父子线程是绝对独立的,因而它们的之间的事务不是同一个的,所以不论是父线程抛出异样还是在子线程中抛出异样,对于另外的线程是不受影响的。

@Overridepublic void multiThread(boolean isThrowFromParentThread) {    if (isThrowFromParentThread) {        new Thread(() -> {            userMapper.insert(User.builder().name("cpz").age(18).email("[email protected]").build());        }).start();        throw new RuntimeException();    } else {        new Thread(() -> {            userMapper.insert(User.builder().name("cpz").age(18).email("[email protected]").build());            throw new RuntimeException();        }).start();    }}

代码解析:通过参数isThrowFromParentThread管制是父子线程抛出异样。最初察看该条数据是否在数据库里,即可实现多线程办法的测试。

源码解读:

1、ReflectiveMethodInvocation#proceed()

首先会通过一个拦截器interceptorOrInterceptionAdvice(TransactionInterceptor),执行invoke(this)办法,this为ReflectiveMethodInvocation对象实例,外面蕴含咱们测试的代理对象,办法、参数等属性。

TransactionInterceptor#invoke(invocation)

2、执行办法之后会进入事务办法invokeWithinTransaction,此处是咱们事务的外围,蕴含:1、获取事务属性后开启事务、2、指标办法的调用、3、事务的回滚、4、事务的提交,对示例的事务生效问题会在这里解惑。

第一步获取事务属性会调用computeTransactionAttribute办法实现属性的获取

外面会去判断是否为public润饰的办法,所以这也是示例1公有办法不行的起因

接下来看看第三步事务的回滚

回滚事务条件

而默认只有运行时异样(RuntimeException)和谬误(ERROR)会回滚

所以示例2不配置rollbackFor = Exception.class会出现异常类型不匹配状况从而导致事务生效。

这里介绍下异样类的档次

「学习交换」

能够扫上面二维码,关注「我的极简博客」公众号。

始终在谋求思路的传递而非代码的COPY

本文由mdnice多平台公布