调用 TransactionSynchronization#beforeCompletion 方法,由 Spring 事务托管,不是真的关闭连接,从 ThreadLocal 中删除 DataSource 和 ConnectionHolder 的映射关系;例如在 Mybatis-Spring 中的 SqlSessionSynchronization 中,会从 ThreadLocal 中删除 SqlSessionFactory 和 SqlSessionHolder 的映射关系,且调用其 SqlSession#close() 方法
如果有 Savepoint 保存点,也就是嵌套事务,则回滚到保存点,调用 Connection#rollback(Savepoint) 方法回滚到保存点,再调用Connection#releaseSavepoint(Savepoint) 方法释放该保存点
否则,如果是新的事务,例如传播级别为 REQUIRED_NEW 则一定是一个新的事务,则对当前事务进行回滚,调用 Connection#rollback() 方法,如下:
// DataSourceTransactionManager.java @Override protected void doRollback(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); try { con.rollback(); } catch (SQLException ex) { throw new TransactionSystemException("Could not roll back JDBC transaction", ex); } }
否则,不是新的事务也没有保存点,那就是加入到一个已有的事务这种情况,例如 REQUIRED 传播级别,如果已存在一个事务,则加入其中
如果已经标记为回滚,或当加入事务失败时全局回滚(默认 true),那么设置 ConnectionHolder 的 rollbackOnly 为 true,也就是标记需要全局回滚,对应到前面“提交事务”的时候会判断是否标记为全局回滚,标记了则进行回滚,而不是提交
可以看到,对于默认的REQUIRED事务传播级别,如果已有一个事务(“物理事务”),则加入到当前事务中(相当于创建了一个“逻辑事务”),当这个“逻辑事务”出现异常时,整个事务(包括外面的“物理事务”)都需要回滚
总结本文对 Spring 事务做了比较详细的讲解,我们通过 @EnableTransactionManagement 注解驱动整个 Spring 事务模块,此时会往 IoC 注入一个 PointcutAdvisor 事务切面,关联了一个 TransactionAttributeSourcePointcut(Pointcut)事务切点和一个 TransactionInterceptor(Advice)事务拦截器,关于 Spring AOP 的相关内容对于不熟悉的小伙伴可查看我前面的一系列文章。
这个 TransactionAttributeSourcePointcut(Pointcut)事务切点,它里面关联了一个 AnnotationTransactionAttributeSource 事务属性资源对象,通过它解析这个方法(或者类)上面的 @Transactional 注解;底层需要借助 SpringTransactionAnnotationParser 进行解析,解析出一个 TransactionAttribute 事务属性对象,并缓存;没有解析出对应的 TransactionAttribute 对象也就不会被事务拦截器拦截,否则,需要为这个 Bean 创建一个代理对象。
这个 TransactionInterceptor(Advice)事务拦截器让方法的执行处于一个事务中(如果定义了 @Transactional 注解,且被 public 修饰符修饰);首先会创建一个事务(如果有必要),最核心就是将数据库的 commit 设置为 false,不自动提交,在方法执行完后进行提交(或者回滚);事务的拦截处理过程更多的细节可查看本文全部内容。
拓展Spirng 事务(Transactions)的底层实现总体上是这样的:以 @EnableXxx 模块驱动注解驱动整个模块,同时会注入一个 PointcutAdvisor 切面,关联一个 Pointcut 和一个 Advice 通知;通过 Pointcut 筛选出符合条件的 Bean;然后在 Advice 中进行拦截处理,实现相应的功能。