Spring 的事务抽象

597 阅读5分钟

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

数据库事务

数据库事务是数据库管理系统执行过程中的一个逻辑单位,由一系列的数据库操作构成。当事务被提交给了数据库管理系统,系统需要确保事务中所有的操作成功且结果被永久保存在数据库中,如果操作没有成功完成,则所有操作回滚到事务执行前的状态。

数据库事务存在的目的有两个:

  1. 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
  2. 当多个应用程序在并发访问数据库的时候,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

举个例子,张三通过银行系统给李四汇款100元,系统需要在数据库中至少执行两个操作:

  1. 张三的账户减少100元
  2. 李四的账户增加100元

数据库事务可以保证这两个操作都成功或者都失败,从而不会使100元钱凭空产生或消失。

数据库事务通常具有四个特性,被称为 ACID 特性:

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一直状态转变为另一个一致状态。也就是说,数据库中的数据应该满足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

JDBC 事务

JDBC 是 Java Database Connectivity 的简称,是 Java 语言中规范客户端程序访问关系型数据库的应用程序接口。

使用 JDBC 操作事务的步骤通常如下(代码中只包含关键步骤):

Connection connection
try { 
    // 获取数据库连接 connection
    connection = getConnection();
    // 开启事务 
    connection.setAutoCommit(false);
    // 执行数据库操作
    ...
    // 提交事务
    connection.commit()
} catch (SQLException ex) {
    // 处理异常
    ...
    //回滚事务
    connection.rollback()
} finally {
    // 关闭连接
    connection.close()
}

默认情况下,JDBC 会自动提交每一个操作,如果开启事务,需要关闭自动提交。执行完一系列操作后,最后再手动提交事务。如果出现异常,则手动回滚事务。最后关闭连接。

Spring 的事务抽象

Spring Framework 为事务管理提供了一致的抽象。它的关键是事务策略的概念,事务策略是通过 TransactionManager 定义的,最核心的一个借口是 PlatformTransactionManager

PlatformTransactionManager

PlatformTransactionManager 的接口定义十分简介:

public interface PlatformTransactionManager extends TransactionManager {

	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;

}
  • getTransaction 方法会根据提供的 TransactionDefinition 参数,返回一个 TransactionStatus 对象。
  • commit 方法用于提交事务,需要 TransactionStatus 作为参数。
  • rollback 方法用于回滚事务,需要 TransactionStatus 作为参数。

其中的 TransactionStatus 是一个接口,主要描述事务的状态。TransactionDefinition 则定义了事务的传播行为、隔离性、超时设置和是否只读。

TransactionDefinition

TransactionDefinition 接口的源码中,包含一下内容:

  • 一系列表示传播行为的常量
  • 一系列表示隔离性的常量
  • 表示默认超时时间的常量
  • 获取传播行为、隔离性、超时时间、是否只读的方法

也就是说,它是对事务的传播行为、隔离性、超时时间、是否只读几个特性的定义。

  • 传播行为指的是当事务上下文中已经存在时运行事务的方法,比如在现有的事务中继续运行或者暂停现有事务创建新的事务等等。
  • 隔离性指的是当前事务与其他事务的工作隔离的程度。比如是否可以看到来自其他事务的为提交的写入等。
  • 超时指的是事务在超时和被底层事务基础设施自动回滚之前的运行时间。
  • 只读特性指的就是当前事务是否是一个只读事务,通常在一个事务只需要读取但不修改数据时定义。

事务的传播性

TransactionDefinition中,事务的传播特性包含一下七种:

  • PROPAGATION_REQUIRED:当前有事务就使用当前事务,否则创建新的事务。(默认值)
  • PROPAGATION_SUPPORTS:事务不是必需的。
  • PROPAGATION_MANDATORY:如果当前没有事务则抛出异常。
  • PROPAGATION_REQUIRES_NEW:无论当前是否有事务,都创建新的事务。
  • PROPAGATION_NOT_SUPPORTED:不支持事务,按照非事务方式运行。
  • PROPAGATION_NEVER:不支持事务,如果有事务则抛出异常。
  • PROPAGATION_NESTED:如果当前有事务就在当前事务里再创建一个事务。

事务的隔离性

TransactionDefinition中,事务的隔离性包含以下五种:

默认值是 ISOLATION_DEFAULT,直接使用数据库的隔离级别。其它四种:

隔离性脏读不可重复读幻读
ISOLATION_READ_UNCOMMITTED1
ISOLATION_READ_COMMITTED2×
ISOLATION_REPEATABLE_READ4××
ISOLATION_SERIALIZABLE8×××

Spring 事务抽象的使用

编程式事务管理

编程式事务管理主要通过 TransactionTemplate 来完成。

在需要用到事务的组件中注入 transactionTemplate

@Autowired 
TransactionTemplate transactionTemplate;

@Autowired 
JdbcTemplate jdbcTemplate;

完成事务操作:

transactionTemplate.execute(new TransactionCallbackWithoutResult() { 
    @Override 
    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { 
        // 执行 SQL
        jdbcTemplate.execute(SQL); 
    } 
});

声明式事务管理

声明式事务管理使用 Transactional 注解来完成:

@Autowired 
JdbcTemplate jdbcTemplate; 

// 当出现 RuntimeException 异常的时候会 rollback
@Transactional(rollbackFor = RuntimeException.class) 
public void foo() throws RuntimeException { 
    jdbcTemplate.execute(SQL); 
    throw new RuntimeException(); 
}