关于事务的一些理解

事务就是逻辑上的一组操作,要么都执行,要么都不执行

拜个大神

Spring的共同创始人Juergen Hoeller,Spring事务的源码几乎都是他写的,膜拜大神。

事务的ACID

  • 原子性:事务是最小的执行单位,不可分割;一个事务中的所有操作,要么全部完成,要么全部不完成;发生错误就回滚
  • 一致性:事务执行前后,数据保持一致
  • 隔离性:并发访问数据库时,一个用户的事务不被其他干扰;有四个级别,包括未提交读、提交读、可重复读、串行化
  • 持久性:事务被提交后,对数据库中数据的改变是永久的,即使系统炸了也不会丢失

Spring事务管理

  • PlatformTransactionManager: 事务管理器
  • TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)
  • TransactionStatus: 事务运行状态

PlatformTransactionManager可以看作事务上层的管理者,TransactionDefinition和TransactionStatus可以看作事务描述。

PlatformTransactionManager可以根据TransactionDefinition的定义如隔离级别、传播行为等进行事务管理,TransactionStatus则用来获取相应状态如是否新事务、是否回滚等。

PlatformTransactionManager:事务管理接口

PlatformTransactionManager接口定义了三个方法:

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface PlatformTransactionManager {
    //获得事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //提交事务
    void commit(TransactionStatus var1) throws TransactionException;
    //回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}

TransactionDefinition:事务属性(定义)

事务管理器PlatformTransactionManager里获取事务时所用的方法getTransaction,其中参数就是TransactionDefinition,定义了一些基本的事务属性,可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。

事务属性包含了 5 个方面:

  • 隔离级别
  • 传播行为
  • 回滚规则
  • 是否只读
  • 事务超时

接口中定义了 5 个方法和一些属性

package org.springframework.transaction;

import org.springframework.lang.Nullable;

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;
    // 返回事务的传播行为,默认值为 REQUIRED。
    int getPropagationBehavior();
    //返回事务的隔离级别,默认值是 DEFAULT
    int getIsolationLevel();
    // 返回事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
    int getTimeout();
    // 返回是否为只读事务,默认值为 false
    boolean isReadOnly();

    @Nullable
    String getName();
}

一般开发都是@Transactional开启

事务传播行为

传播行为是为了解决事务方法间相互调用的问题。

例如,A事务方法调用B事务方法,此时B是继续在A中运行,还是自己另外开启一个新事务运行,由B的事务传播行为决定。

事务传播行为有 7 种:

  1. PROPAGATION_REQUIRED

如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。

@Transactional默认的传播行为

有两点注意:

  • 外部方法没有开始事务时,PROPAGATION_REQUIRED的内部方法回新开启自己的事务,且互不干扰,相互独立
  • 外部方法有开启事务,且被PROPAGATION_REQUIRED,那么所有被PROPAGATION_REQUIRED修饰的方法,不管内外,都属于同一个事务,有一个方法回滚,整个事务回滚

举个例子,b 回滚,a 也回滚,a b 属于同一事务

@Service
Class A {
    @Autowired
    B b;
    @Transactional(propagation = Propagation.REQUIRED)
    public void aMethod {
        //do something
        b.bMethod();
    }
}
@Service
Class B {
    @Transactional(propagation = Propagation.REQUIRED)
    public void bMethod {
       //do something
    }
}
  1. PROPAGATION_SUPPORTS

    如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。

  2. PROPAGATION_MANDATORY

如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

  1. PROPAGATION_REQUIRES_NEW

重新创建一个新的事务,如果当前存在事务,暂停当前的事务。

即不管外部有没有开启事务,PROPAGATION_REQUIRES_NEW都会新开一个事务,且相互独立,互不干扰。

举个例子,a 回滚,b 不会跟着回滚,因为是两个事务

@Service
Class A {
    @Autowired
    B b;
    @Transactional(propagation = Propagation.REQUIRED)
    public void aMethod {
        //do something
        b.bMethod();
    }
}

@Service
Class B {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void bMethod {
       //do something
    }
}
  1. PROPAGATION_NOT_SUPPORTED

以非事务的方式运行,如果当前存在事务,暂停当前的事务。

  1. PROPAGATION_NEVER

以非事务的方式运行,如果当前存在事务,则抛出异常。

  1. PROPAGATION_NESTED

当前存在事务,就在嵌套事务内执行,即在外部方法开启事务的情况下,在内部开启一个新的事务,作为嵌套事务存在

若没有事务,则单独开启一个事务,和 Propagation.REQUIRED 效果一样。

b 回滚,a 也回滚

@Service
Class A {
    @Autowired
    B b;
    @Transactional(propagation = Propagation.REQUIRED)
    public void aMethod {
        //do something
        b.bMethod();
    }
}

@Service
Class B {
    @Transactional(propagation = Propagation.NESTED)
    public void bMethod {
       //do something
    }
}

事务隔离级别

隔离级别有 4 种:

  • Read Uncommitted(读取未提交):最低级别,可能导致脏读、幻读或不可重复读
  • Read Committed(读取已提交):允许读取并发事务已经提交的数据,可以阻止脏读,但不可重复读,幻读可能发生
  • Repeatable Read(可重读):对同一字段的多次读取结果都是一致的,除非被本身修改,可以阻止脏读、不可重复读,但幻读可能发生
  • Serializable(可串行化):最高级别,不会产生事务干扰,但影响性能

脏读:一个事务中访问到了另外一个事务未提交的数据

幻读:前后多次读取,数据总量不一致,如一个事务读了几行数据,另一个并发事务插入新数据,之后第一个事再次务查询发现多了原本不存在的数据

不可重复读:前后多次读取,数据内容不一致

事务回滚规则

定义哪些异常会导致事务回滚而哪些不会,默认运行时异常(RuntimeException 的子类)回滚,error也回滚。

@Transactional注解原理

基于AOP实现,AOP基于动态代理实现,对象实现接口,默认用JDK动态代理,没有接口使用CGLIB。

自己实现的Spring框架下CGLIB中关于@Transactional的实现

/**
     * 获取事务状态对象
     * @param object
     * @param method
     * @return
     */
    private TransactionStatus geTransactionStatus(Object object,Method method){
        TransactionStatus status = new TransactionStatus();
        //不开启事务
        status.isNeed = false;
        if (transactionManager != null && (object.getClass().isAnnotationPresent(Transactional.class) || method.isAnnotationPresent(Transactional.class))){
            status.isNeed = true;
            //类有事务注解
            if ((object.getClass().isAnnotationPresent(Transactional.class))){
                //获取事务注解
                Transactional transactional = object.getClass().getAnnotation(Transactional.class);
                //事务隔离级别
                status.isolationLevel = transactional.Isolation();
                //事务传播级别
                status.propagationLevel = transactional.Propagation();
                //回滚异常类型
                status.rollbackFor = transactional.rollbackFor();
            }
            //方法有事务注解
            if(method.isAnnotationPresent(Transactional.class)){
                Transactional transactional = method.getAnnotation(Transactional.class);
                status.isolationLevel = transactional.Isolation();
                status.propagationLevel = transactional.Propagation();
                status.rollbackFor = transactional.rollbackFor();

            }
        }
        return status;
    }

TransactionStatus:事务状态

用来记录事务的状态,获取相应信息

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事务
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
}