事务从头说起


title: 事务从头说到尾
date: 2021-01-16
tags: [ mysql,java,spring,mybatis ]

categories: data

MySql事务

官方文档位置: https://dev.mysql.com/doc/refman/5.7/en/glossary.html

事务特性ACID

原子性

  • 事务中的操作要么全部提交成功,要么全部失败回滚,不可能只提交成功一部分操作
  • 在任何数据被操作之前,首先会把数据备份在Undo log 之中,然后进行数据的操作,如果出现异常或者回滚可以将数据恢复

隔离型

  • 在并发情况下,每个事务都有自己的数据空间,多个事务之间不会相互影响

持久性

  • 事务一旦提交完成后,该事务对数据库所做的操作会被持久到数据库中,不会回滚。
  • 在事务提交之前,会把数据的操作持久化到redo log,一旦系统出现问题,就可以通过redo log来进行数据恢复

一致性

  • 对于业务来说,事务在执行前后的业务一致性没有被破坏
  • 事务追求的最终目标,一致性的实现既需要数据库层面的保障,也需要应用层面的保障

事务隔离级别

读未提交

  • A事务可以读取到B事务尚未提交的数据
  • 会造成 脏读 幻读 不可重复读

读提交

  • A事务只能读取到B事务提交的数据,解决了脏读
  • 但是在一次事务中多次查询,结果是不同的
  • 会造成 幻读 不可重复读
  • 每一次的读操作都会生成一个快照

可重复读

  • 在一次事务中多次查询,结果是相同的,解决了不可重复读
  • 可重复读 与 读提交的不同之处在于 读视图 生成的方式不同
  • 事务在开始之后的第一次读操作之后,会生成一个快照 ,这也就是可重复读的解决方式
  • 会造成 幻读 ,为了解决幻读,引入了间隙锁
    • 间隙锁
    • mvcc

串行化

  • 所有事务顺序执行, 解决了幻读

MVCC

  • MVCC (multiversion concurrency control),多版本并发控制
  • MVCC只对RR和RC隔离级别有影响
  • InnoDB并不简单使用行锁,而是配合MVCC一起使用。
  • InnoDB的MVCC,在每一行数据包含3个隐藏的字段,
    • 6字节的 DB_TRX_ID,标示插入或者更新的事务id(在mysql内部,删除操作也被看作更新,会设置行中的特殊删除位)
    • 7字节的 DB_ROLL_PTR ,指向上一个版本对应的的 undo 日志,
    • 6字节 DB_ROW_ID ,标示新行插入的时候的数据id,innerDB自动产生聚集索引时,聚集索引会包括这个行ID的值,否则这个行ID不会出现在任何索引中。
    • 还有一个特殊删除标志位
  • 查询时将当前事务ID与所查找行的事务ID进行比较来判断是否可以读取当前行的数据。

版本链

  • 每次对某条记录进行更新的时候,都会把老版本的数据写入undo 日志,这样,通过 回滚指针,在undo日志 就会形成一个 版本链
字段1 字段2 try-id Roll_pointer Undo log 内存地址
row 1 col 1 row 1 col 2 1 a b
row 2 col 1 row 2 col 2 2 b c
row 1 col 1 row 2 col 2 3 c d

会形成 a->b->c->d的一个版本链

读视图(ReadView)

  • 读视图包含 我们的数据行和版本链

  • 只能看到小于等于当前事务id的并已完成事务的记录

  • 在读取的时候,会查询undo 日志和数据行 生成一个 读视图

  • 在RR下面,只会生成一次视图,在RC下,每次的查询,都会生成读视图,这也就是不可重复读的解决方式

  • Read View中的的变量则按如下方式初始化:
    read_view->creator_trx_id = current-trx; 当前的事务id
    read_view->up_limit_id = trx1; 当前活跃事务的最小id
    read_view->low_limit_id = trx4; 当前活跃事务的最大id
    read_view->trx_ids = [trx1, trx2, trx3, trx4]; 当前活跃的事务的id列表
    read_view->m_trx_ids = 4; 当前活跃的事务id列表长度

  • 当前读取行的事务id >= 当前活跃的最大事务id :说明事务均没有提交, 说明当前行数据不可见

  • 当前读取行的事务id < 当前活跃的最小事务 id: 说明当前行的数据已经提交,可见

  • Min id < 当前读取行的事务id <= Max ID : 如果在列表当中,就说明没有提交,不可见,如果不在的话,就说明已经提交 ,当前数据可见。

mysql锁机制

  • 锁的生命周期是在一个事务中,从第一次加锁开始,一直到事务结束(commit)的时候释放
  • 使用行锁的情况下,假如where条件中没有命中索引,那么锁的范围就变成了对每一条扫描的行进行加锁,也就是效果等同于表锁了,但明显比表锁还要重得多。
  • 应避免不同事物对共同依赖的资源加锁顺序不同,从而导致死锁

锁类型

  • 表级锁: 开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
  • 页级锁: 开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
  • 行级锁: 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 间隙锁: 在RR隔离级别下,为了防止幻读,于是有gap锁和next-key锁存在,除了对唯一索引的唯一搜索外都会获取gap锁或next-key锁。即锁住其扫描的范围。
  • 意向锁: 解决行锁和表锁同时存在 开销的问题
    • 在行锁的前,会先添加意向锁,这样在表锁添加的时候,就没有必要在整个表去扫描,而先检查该表上是否存在意向锁,存在的意向锁是否与自己准备加的锁冲突

共享锁和排他锁

  • 共享锁: 其他事务可以读,但不能写
    • select * from table_name where id =10 lock in share mode;
  • 排他锁: 其他事务不能读取,也不能写
    • select * from table_name where id=10 for update

死锁

  • 多个事务对共同依赖的多个资源的加锁顺序不一样,并相互等待对方释放某个资源的锁,从而形成了循环依赖。

GAP锁 间隙锁

  • 当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。

  • 防止幻读,以满足相关隔离级别的要求;

  • 满足恢复和复制的需要:

  • MySQL 通过 BINLOG 录入执行成功的 INSERT、UPDATE、DELETE 等更新数据的 SQL 语句,并由此实现 MySQL 数据库的恢复和主从复制。MySQL 的恢复机制(复制其实就是在 Slave Mysql 不断做基于 BINLOG 的恢复)有以下特点:

    一是 MySQL 的恢复是 SQL 语句级的,也就是重新执行 BINLOG 中的 SQL 语句。

    二是 MySQL 的 Binlog 是按照事务提交的先后顺序记录的, 恢复也是按这个顺序进行的。

    由此可见,MySQL 的恢复机制要求:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读。

Spring事务

官方文档位置:https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html

Spring事务 基础知识


TransactionDefinition

  • 事务的默认配置接口
  • 定义
    • 隔离级别 ISOLATION 开头的属性
    • 传播属性 PROPAGATION 开头的属性
    • 超时时间 事务超时时间
    • 只读状态 事务是否只读

TransactionDefinition类定义

public interface TransactionDefinition {
    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;
    int PROPAGATION_NESTED = 6;
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;

    default int getPropagationBehavior() {
        return 0;
    }

    default int getIsolationLevel() {
        return -1;
    }

    default int getTimeout() {
        return -1;
    }

    default boolean isReadOnly() {
        return false;
    }

    @Nullable
    default String getName() {
        return null;
    }

    static TransactionDefinition withDefaults() {
        return StaticTransactionDefinition.INSTANCE;
    }
}

PlatformTransactionManager

  • 事务管理器,用于执行具体的事务操作。
  • 我们虽然可以在代码中之间使用PlatformTransactionManager来直接使用事务,但是它并不是一个给我们基础使用的,他说一个给各个数据库服务商开放的SPI
  • 在此类的具体实现类中,有很多 基于 TransactionSynchronizationManager的 增强方法,在事务的执行前,提交前等等,都可以有自定义方法。
Public interface PlatformTransactionManager{
   //创建一个新的事物
   TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
  //提交事物
   void commit(TransactionStatus status)throws TransactionException;
  //回滚事物
   void rollback(TransactionStatus status)throws TransactionException;
}

TransactionStatus

  • 提供查询 事务的状态 以及 控制控制事务执行的简单方法
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {

    
    @Override
    boolean isNewTransaction();
        //嵌套事务使用
    boolean hasSavepoint();
        
    //设置事务状态为回滚
    @Override
    void setRollbackOnly();

    @Override
    boolean isRollbackOnly();

    void flush();

    @Override
    boolean isCompleted();
}

Spring事务传播属性

类型
PROPAGATION_REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
PROPAGATION_REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
PROPAGATION_SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
PROPAGATION_NEVER 以非事务方式运行,如果当前存在事务,则抛出异常。
PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
PROPAGATION_NESTED 存在事务,则是嵌套事务,如果没有事务,则创建一个新的事务

事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

事务只读属性

事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。

事务的回滚规则

通常情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。但是,我们可以根据需要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。

TransactionSynchronizationManager(事务同步管理器)

我们在事务执行前后可能需要做一些额外的操作这个时候我们就需要用到TransactionSynchronizationManager去注入一个TransactionSynchronization事务同步器,然后重写TransactionSynchronization或者其子类的beforeCommit()或者afterCommit()方法,写入我们需要执行的业务。

Spring事务的实现方式

Spring编程事务

基于事务管理器Api的事务(最基础的实现)

...
    //通过上下文获取 事务管理器
    PlatformTransactionManager transactionManager = 
        applicationContext.getBean(PlatformTransactionManager.class);

    //创建一个新的事务
    TransactionStatus transaction = 
        transactionManager.getTransaction(new DefaultTransactionDefinition());

    //执行数据库操作
    OrderDO orderDO = bean.queryById(2L);
    orderDO.setOrderNo("104");
    int update = bean.update(orderDO);
            
    //提交事务
    transactionManager.commit(transaction);
or 
    transactionManager.rollback(transaction)
...

基于TransactionTemplate的编程式事务

基于模板回调模式实现的事务,将事务的创建和提交在execute方法中完成,业务层只需要实现TransactionCallback或者,可以在方法的任何位置调用该参数的 setRollbackOnly() 方法将事务标识为回滚的,以执行事务回滚。否则结束后,如果没有异常就会提交事务。如果不需要返回值,可以使用 TransactionCallbackWithoutResult 来实现

        TransactionTemplate template = applicationContext.getBean(TransactionTemplate.class);
        Boolean execute = template.execute(new TransactionCallback() {
            @Override
            public Boolean doInTransaction(TransactionStatus transactionStatus) {
                OrderDO orderDO = bean.queryById(2L);
                orderDO.setOrderNo("99");
                int update = bean.update(orderDO);
                System.out.println(update);
                //transactionStatus.setRollbackOnly();
                return true;
            }
        });

execute方法

public  T execute(TransactionCallback action) throws TransactionException {
        Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
        //CallbackPreferringPlatformTransactionManager需要回调函数来实现事务流程
            if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
        } else {
          ///======>>>>>>>>
            
            //与我们使用api创建一样,新创建1个事务
            TransactionStatus status = this.transactionManager.getTransaction(this);

            Object result;
            try {
                //执行action的方法
                result = action.doInTransaction(status);
            } catch (Error | RuntimeException var5) {
                //错误 或者 运行异常 回滚
                this.rollbackOnException(status, var5);
                throw var5;
            } catch (Throwable var6) {
                // 回滚
                this.rollbackOnException(status, var6);
                throw new UndeclaredThrowableException(var6, "TransactionCallback threw undeclared checked exception");
            }
                        //提交事物,
            this.transactionManager.commit(status);
            return result;
        }
    }

commit方法实现

    public final void commit(TransactionStatus status) throws TransactionException {
        if (status.isCompleted()) {
            throw new IllegalTransactionStateException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
        }

        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    //这里会判断是方法否执行了setRollbackOnly,如果执行了,就会执行回滚
        if (defStatus.isLocalRollbackOnly()) {
            if (defStatus.isDebug()) {
                logger.debug("Transactional code has requested rollback");
            }
            processRollback(defStatus, false);
            return;
        }
    //实现事务全局回滚
    //shouldCommitOnGlobalRollbackOnly:默认实现是 false,意思是如果发现事务被标记全局回滚并且该标记不需要提交事务的话,那么则进行回滚。
     //defStatus.isGlobalRollbackOnly():判断是否是读取 DefaultTransactionStatus 中 transaction 对象的 ConnectionHolder 的 rollbackOnly 标志位。
        if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
            if (defStatus.isDebug()) {
                logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
            }
            processRollback(defStatus, true);
            return;
        }
        //提交事务
        processCommit(defStatus);
    }

spring声明事务

Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

优点

声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过等价的基于标注的方式),便可以将事务规则应用到业务逻辑中。

缺点

其事务最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。并且,其方法的调用必须经过Spring容器代理,直接内部调用方法,事务无法起作用

基于 TransactionInterceptor 的声明事务

基于

java版本

        @Bean(name = "transactionInterceptor")
    public TransactionInterceptor transactionInterceptor(PlatformTransactionManager transactionManager){
        TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
        transactionInterceptor.setTransactionManager(transactionManager);
        Properties transactionAttributes = new Properties();
        transactionAttributes.setProperty("update*","PROPAGATION_REQUIRED");
        transactionInterceptor.setTransactionAttributes(transactionAttributes);
        return transactionInterceptor;
    }

xml版本

    
        
        
            
                PROPAGATION_REQUIRED
            
        
    

参数:

首先,我们配置了一个 TransactionInterceptor 来定义相关的事务规则,他有两个主要的属性:

一个是 transactionManager,用来指定一个事务管理器,并将具体事务相关的操作委托给它;

另一个是 Properties 类型的 transactionAttributes 属性,它主要用来定义事务规则,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符,而值就表示相应方法的所应用的事务属性。

指定事务属性的取值有较复杂的规则,这在 Spring 中算得上是一件让人头疼的事。具体的书写规则如下:

传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]
  • 传播行为是唯一必须设置的属性,其他都可以忽略,Spring为我们提供了合理的默认值。
  • 传播行为的取值必须以”PROPAGATION_”开头,具体包括:PROPAGATION_MANDATORY、PROPAGATION_NESTED、PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED、PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_SUPPORTS,共七种取值。
  • 隔离级别的取值必须以”ISOLATION_”开头,具体包括:ISOLATION_DEFAULT、ISOLATION_READ_COMMITTED、ISOLATION_READ_UNCOMMITTED、ISOLATION_REPEATABLE_READ、ISOLATION_SERIALIZABLE,共五种取值。
  • 如果事务是只读的,那么我们可以指定只读属性,使用”readOnly”指定。否则我们不需要设置该属性。
  • 超时属性的取值必须以”TIMEOUT_”开头,后面跟一个int类型的值,表示超时时间,单位是秒。
  • 不影响提交的异常是指,即使事务中抛出了这些类型的异常,事务任然正常提交。必须在每一个异常的名字前面加上”+”。异常的名字可以是类名的一部分。比如”+RuntimeException”、”+tion”等等。
  • 导致回滚的异常是指,当事务中抛出这些类型的异常时,事务将回滚。必须在每一个异常的名字前面加上”-”。异常的名字可以是类名的全部或者部分,比如”-RuntimeException”、”-tion”等等。

另外还需要配置1个 ProxyFactoryBean 来作为作为apo代理类

@Bean
public ProxyFactoryBean orderProxy(InterceptionTxService interceptionTxService){
    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    proxyFactoryBean.setTarget(interceptionTxService);
    proxyFactoryBean.setInterceptorNames("transactionInterceptor");
    return proxyFactoryBean;
}

具体使用

//从Spring上下文获取业务的代理
ProxyFactoryBean factoryBean = applicationContext.getBean(ProxyFactoryBean.class);
//然后从获取代理对象,执行业务操作,即可在方法在事务中执行
InterceptionTxService object = (InterceptionTxService)factoryBean.getObject();
object.update(2,"ce456");

基于TransactionProxyFactoryBean 的声明事务

在上面的基础上,Spring将 TransactionInterceptor 和 ProxyFactoryBean 的配置合二为一。 其他和使用拦截器差不多。

    @Bean
    public TransactionProxyFactoryBean transactionProxyFactoryBean(PlatformTransactionManager transactionManager){
        TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
        transactionProxyFactoryBean.setTarget(transactionManager);
        transactionProxyFactoryBean.setTransactionManager(transactionManager);
        Properties transactionAttributes = new Properties();
        transactionAttributes.setProperty("update*","PROPAGATION_REQUIRED");
        transactionProxyFactoryBean.setTransactionAttributes(transactionAttributes);
        return transactionProxyFactoryBean;
    }

基于aop的声明式事务管理

首先定义 切点和切面,然后实际执行的时候

    @Bean
    public DefaultBeanFactoryPointcutAdvisor advisor(){
        DefaultBeanFactoryPointcutAdvisor advisor = new DefaultBeanFactoryPointcutAdvisor();
        AspectJExpressionPointcut pointcut =new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* com.note.spring.service.OrderService.*(..))");
        advisor.setPointcut(pointcut);
        advisor.setAdviceBeanName("transactionInterceptor");
        return advisor;
    

        InterceptionTxService interceptionTxService = applicationContext.getBean(InterceptionTxService.class);
        DefaultBeanFactoryPointcutAdvisor advisor = applicationContext.getBean(DefaultBeanFactoryPointcutAdvisor.class);
        TransactionInterceptor advice = applicationContext.getBean(TransactionInterceptor.class);

        ProxyFactory factory = new ProxyFactory(interceptionTxService);
        factory.addAdvisor(advisor);
        factory.addAdvice(advice);

        InterceptionTxService proxy = (InterceptionTxService)factory.getProxy();
        proxy.update(2,"ce3333");

  • TransactionInterceptor 代理事务的原理
在  TransactionInterceptor 的 invoke 方法中,是代理执行事务的方法
具体事务由 invokeWithinTransaction 完成

protected Object invokeWithinTransaction(Method method, @Nullable Class targetClass,     TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
  
            //获取事务属性源
        TransactionAttributeSource tas = this.getTransactionAttributeSource();
        //获取事务属性
            TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
        //获取事务管理器
        TransactionManager tm = this.determineTransactionManager(txAttr);
        if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
            。。。 //响应式编程的事务管理,暂时不关注
        } else {
            PlatformTransactionManager ptm = this.asPlatformTransactionManager(tm);
            String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
            Object retVal;
            if (txAttr != null && ptm instanceof CallbackPreferringPlatformTransactionManager) {
                //编程式事务处理
                TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder();

                try {
                    retVal = ((CallbackPreferringPlatformTransactionManager)ptm).execute(txAttr, (statusx) -> {
                        TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(ptm, txAttr, joinpointIdentification, statusx);

                        Object var9;
                        try {
                            Object retVal = invocation.proceedWithInvocation();
                            if (vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) {
                                retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, statusx);
                            }

                            var9 = retVal;
                            return var9;
                        } catch (Throwable var13) {
                            if (txAttr.rollbackOn(var13)) {
                                if (var13 instanceof RuntimeException) {
                                    throw (RuntimeException)var13;
                                }

                                throw new TransactionAspectSupport.ThrowableHolderException(var13);
                            }

                            throwableHolder.throwable = var13;
                            var9 = null;
                        } finally {
                            this.cleanupTransactionInfo(txInfo);
                        }

                        return var9;
                    });
                    if (throwableHolder.throwable != null) {
                        throw throwableHolder.throwable;
                    } else {
                        return retVal;
                    }
                } catch (TransactionAspectSupport.ThrowableHolderException var20) {
                    throw var20.getCause();
                } catch (TransactionSystemException var21) {
                    if (throwableHolder.throwable != null) {
                        this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                        var21.initApplicationException(throwableHolder.throwable);
                    }

                    throw var21;
                } catch (Throwable var22) {
                    if (throwableHolder.throwable != null) {
                        this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                    }

                    throw var22;
                }
            } else {
                //声明式事务处理。
                //是否有必要创建一个事务,根据`事务传播行为`,做出相应的判断
                TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

                try {
                    //回调方法执行,执行目标方法(原有的业务逻辑)
                    retVal = invocation.proceedWithInvocation();
                } catch (Throwable var18) {
                    //出现异常了,进行回滚(注意:并不是所有异常都会rollback的)
                    this.completeTransactionAfterThrowing(txInfo, var18);
                    throw var18;
                } finally {
                    this.cleanupTransactionInfo(txInfo);
                }

                if (vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) {
                    TransactionStatus status = txInfo.getTransactionStatus();
                    if (status != null && txAttr != null) {
                        retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                    }
                }
                                //提交事务
                this.commitTransactionAfterReturning(txInfo);
                return retVal;
            }
        }
    }
  
  

基于 @Transactional 的声明式事务

使用@EnableTransactionManagement注解即可启动@Transactional注解

或者使用


@EnableTransactionManagement 注解会在系统导入两个配置

  1. AutoProxyRegistrar :

    自动代理注册,利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用

核心方法:

// 
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

       //
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      ...
         //核心方法
         
         AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
      ...
    }

}

    private static BeanDefinition registerOrEscalateApcAsRequired(
            Class cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    
    ...
    //最终在此方法里面,将 InfrastructureAdvisorAutoProxyCreator 注册到spring,其本质是以恶搞后置处理器
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        beanDefinition.setSource(source);
        beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
  }

  1. proxyTransactionManagementConfiguration:
@Configuration(
    proxyBeanMethods = false  //设置改类不使用代理,每次每次调用@Bean标注的方法获取到的对象和IOC容器中的都不一样,是一个新的对象,所以我们可以将此属性设置为false来提高性能
)
@Role(2)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    public ProxyTransactionManagementConfiguration() {
    }

    //定义了事务的aop
    @Bean(
        name = {"org.springframework.transaction.config.internalTransactionAdvisor"}
    )
    @Role(2)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource);
        advisor.setAdvice(transactionInterceptor);
        if (this.enableTx != null) {
            advisor.setOrder((Integer)this.enableTx.getNumber("order"));
        }

        return advisor;
    }
        //该接口其中返回的方法继承了TransactionDefinition ,推测应该是配置之类的
    @Bean
    @Role(2)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    //根据上面的配置定义事务拦截器,这里使用了 this.txManager, 应该就是我们使用这个注解,需要把 事务管理器注入的原因,在 TransactionInterceptor 的 invoke 方法中,是真正的事务执行
    @Bean
    @Role(2)
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }

        return interceptor;
    }
}


参考:

关于mysql事务&MVCC以及锁机制的总结:

https://blog.csdn.net/vipshop_fin_dev/article/details/85227296

全面分析 Spring 的编程式事务管理及声明式事务管理:

https://developer.ibm.com/zh/articles/os-cn-spring-trans/

你可能感兴趣的:(事务从头说起)