Spring-transaction 事务

1. 事务介绍

1.1 简介

事务,就是一组操作数据库的动作集合。事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态。

2. 事务特点

  • 原子性(Atomicity):整个事务是一个整体,不可分割的最小工作单位。一个事务中的所有操作要么全部执行成功,要么全部都不执行。其中任何一条语句执行失败,都会导致事务回滚

  • 一致性(Consistency):数据库的记录总是从一个一致性状态转变成另一个一致性状态。这里的一致性是语义上的一致性, 并不是语法上的一致性

  • 隔离性(Isolation):一个事物的执行,不受其他事务的干扰,即并发执行的事物之间互不干扰

  • 持久性(Durability):数据一旦提交,结果就是永久性的。并不应为宕机等情况丢失。一般理解就是写入硬盘保存成功

3. mysql 事务实现方式

  • 原子性和持久性利用redo log(重做日志) 实现

  • 一致性利用undo log(回滚日志)实现

  • 隔离性利用锁来实现

4. mysql 事务示例

start TRANSACTION;
insert into b values(2,"李四");
insert into b values(3,"李四");
commit;

//ROLLBACK

2. spring 事务

2.1 事务传播行为

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。

事务传播行为类型说明
PROPAGATION_REQUIRED需要事务(默认)。若当前无事务,新建一个事务;若当前有事务,加入此事务中
PROPAGATION_SUPPORTS支持事务。若当前没有事务以非事务方式执行;若当前有事务,加入此事务中
PROPAGATION_MANDATORY强制使用事务。若当前有事务,就使用当前事务;若当前没有事务,抛出IllegalTransactionStateException异常
PROPAGATION_REQUIRES_NEW新建事务。无论当前是否有事务,都新建事务运行
PROPAGATION_NOT_SUPPORTED不支持事务。若当前存在事务,把当前事务挂起,然后运行方法
PROPAGATION_NEVER不使用事务。若当前方法存在事务,则抛出IllegalTransactionStateException异常,否则继续使用无事务机制运行
PROPAGATION_NESTED嵌套。如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作

2.2 事务隔离级别

隔离级别含义
DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别另外四个与JDBC的隔离级别相对应
READ_UNCOMMITTED最低的隔离级别。事实上我们不应该称其为隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。可能导致脏,幻,不可重复读
READ_COMMITTED大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入或更新的数据。这意味着在事务的不同点上,如果其他事务修改了数据,你就会看到不同的数据。可防止脏读,但幻读和不可重复读仍可以发生
REPEATABLE_READISOLATION_READ_COMMITTED更严格,该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。可防止脏读,不可重复读,但幻读仍可能发生
SERIALIZABLE完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。代价最大、可靠性最高的隔离级别,所有的事务都是按顺序一个接一个地执行。避免所有不安全读取

2.3 事务失效情况排查

如何查看是否使用事务?
这个类 org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 处理事务,打断点看进不进去

  • 1)未启用 spring 事务管理功能
  • 2)方法不是 public 的
  • 3)数据源未配置事务管理器
  • 4)自身调用用提(@Transactional 只有外部调用才有效,也就是必须代理类调用)
  • 5)异常类型错误(RuntimeException和Error的情况下,spring事务才会回滚。也可以通过 rollback 定义回滚的异常类型)
  • 6)异常被吞了,被 try-catch 捕获的时候,spring 获取不到异常
  • 7)业务和 spring 事务代码必须在一个线程中
  • 8)Spring 基于线程上下文的事务设计导致只有指定的事务管理器内部的数据源会加入事务中

2.4 事务回滚机制

  • 指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务
  • 默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。
  • 可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。
  • 还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚-

2.5 编程式事务

2.5.1. Spring-TransactionTemplate

使用时直接注入TransactionTemplate 使用。抛出异常或者手动设置回滚状态 transactionStatus.setRollbackOnly() 时事务回滚。

@Autowired
private TransactionTemplate transactionTemplate;
/**
 * 编程式事务 TransactionTemplate
 * */
public void test2(){
    transactionTemplate.execute(new TransactionCallback<Boolean>() {
        public Boolean doInTransaction(TransactionStatus transactionStatus) {
            try {
                bMapper.insertB("编程式事务,无 id");
                bMapper.insertBs(1, "编程式事务,id 为 1");
                return true;
            }catch (Exception e){
                transactionStatus.setRollbackOnly();
                return false;
            }
        }
    });
}

spring提供了 TransactionCallback 和子类 TransactionCallbackWithoutResult,分别是有返回值和无返回值。

源码简单分析
通过 transactionTemplate.execute() 方法可以知道,默认doInTransaction方法是不需要手动设置回滚状态,只要有异常就会自动回滚,但是手动设置回滚也是可以。

@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
    Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
    if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
        return ((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
    } else {
        TransactionStatus status = this.transactionManager.getTransaction(this);

        Object result;
        try {
            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;
    }
}

AbstractPlatformTransactionManager 会在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");
    } else {
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
        if (defStatus.isLocalRollbackOnly()) {
            if (defStatus.isDebug()) {
                this.logger.debug("Transactional code has requested rollback");
            }

            this.processRollback(defStatus, false);
        } else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
            if (defStatus.isDebug()) {
                this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
            }

            this.processRollback(defStatus, true);
        } else {
            this.processCommit(defStatus);
        }
    }
}

2.5.2. Spring-PlatformTransactionManager

比较原始的事务。

@Autowired
private PlatformTransactionManager transactionManager;
/**
 * 编程式事务 PlatformTransactionManager
 * */
public void test3(){
    //事务基础信息 超时时间、隔离级别、传播属性等
    DefaultTransactionDefinition td = new DefaultTransactionDefinition();
    // 设置传播行为属性, 默认事务级别。 当前无事务则新建事务,已有事务则加入事务。
    td.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
    // 获得事务状态
    TransactionStatus status = transactionManager.getTransaction(td);
    try{
        bMapper.insertB("编程式事务,无 id");
        bMapper.insertBs(1, "编程式事务,id 为 1");
        transactionManager.commit(status);
    }catch (Exception e){
        transactionManager.rollback(status);
    }

}

PlatformTransactionManager 核心事务管理器 ,用来管理事务,包含事务的提交,回滚。
TransactionDefinition 事务基础信息 超时时间、隔离级别、传播属性等
TransactionStatus 事务状态

方法需要实施事务管理,我们首先需要在方法开始执行前启动一个事务,调用 PlatformTransactionManager.getTransaction(…) 方法便可启动一个事务。创建并启动了事务之后,便可以开始编写业务逻辑代码,然后在适当的地方执行事务的提交或者回滚。

2.6 声明式事务

2.6.1 注解

@Transaction 是 Spring 提供用来控制事务回滚/提交的一个注解,让我们从编程式事务转换到声明式事务,内部是使用的 AOP。

@Transaction 可以写在类、接口、方法上

  • 当标注在类上的时候:表示给该类所有的 public 方法添加上 @Transaction 注解
  • 当标注在接口上的时候:Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。像 CGLib 动态代理采用继承的方式将会导致 @Transactional 注解失效
  • 当标注在方法上的时候:事务的作用域就只在该方法上生效,并且如果类及方法上都配置 @Transaction 注解时,方法的注解会覆盖类上的注解

注解源码

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";
 
    @AliasFor("value")
    String transactionManager() default "";
 
    Propagation propagation() default Propagation.REQUIRED;
 
    Isolation isolation() default Isolation.DEFAULT;
 
    int timeout() default -1;
 
    boolean readOnly() default false;
 
    Class<? extends Throwable>[] rollbackFor() default {};
 
    String[] rollbackForClassName() default {};
 
    Class<? extends Throwable>[] noRollbackFor() default {};
 
    String[] noRollbackForClassName() default {};
}

可以看到 @Transaction 相关所有的属性值

字段名类型含义
valueString主要用来指定不同的事务管理器
满足在同一个系统中,存在不同的事务管理器
propagationenum: Propagation可选的事务传播行为设置
isolationenum: Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeoutint (in seconds granularity)事务超时时间设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异常类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异常类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异常类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异常类名字数组

2.6.2 xml 配置

不用,略

2.6.3 声明式事务原理

https://www.cnblogs.com/Xianhuii/p/17102828.html

2.7 spring 事务的核心

  简单说其实就是底层数据库的使用方法,比如通常的sql begin; commit; rollback;... 各数据库各自的都会有自己的一套描述,并不完全一样。但总是因为db层面支持事务,在spring中才会支持事务。即spring只是上层应用。

3. 事务源码解析

todo。。。

posted @ 2024-07-31 10:18  primaryC  阅读(3)  评论(0编辑  收藏  举报