Spring中事务管理

事务介绍

事务(Transaction),在计算机术语中指的可不是我们所熟知的含义,而是指访问并可能更新数据库中各项数据的一个程序执行单元。

学习Spring中的事务,免不了举一个众所周知得到例子:银行取钱,假设你去ATM机上去取1000块钱,那么应该有如下的步骤:

  1. 插卡/输入密码和取款金额,银行卡扣掉1000元
  2. 从ATM机中吐出1000RMB

但这里值得注意的是,这两个步骤一定是关联的,即第一步执行必然执行第二步,倘若执行了第一步而不执行第二步,那么导致的便是银行卡已经扣掉了1000元,但是你并没有得到从ATM机吐出的1000元,最终你将损失1000元

那么该如何保证以上两个步骤不会出现一步发生异常而另一步执行成功呢?事务!!事务就是用来解决这样的问题的,事务是一系列的动作,他们综合在一起才是一个完整的工作单元,这些动作是必须全部完成的,如果有一个失败的话,那么事务就会回滚到最开始的状态了,在Spring的开发中,事务管理是不可缺少的技术,我们应当重视!

事务的四个特性(ACID)

  • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么全都不起作用
  • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败,在显示中的数据不应该被破坏
  • 隔离性(Isolation):可能有许多事务会同事处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据被损坏
  • 持久性(Durability):一旦事务完成,无论发生什么系统错误,他的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来,通常情况下,事务的结果被写入持久化存储器中

Spring事务管理的核心接口

核心Jar包和接口

核心的Jar包

spring-tx-4.3.18.RELEASE.jar

其中的PlatformTransactionManager.class/TransactionDefinition.class/TransactionStatus.class是事务管理的三个顶级接口

说明

PlatformTransactionManager事务管理器

Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,如上图所示,Spring并不直接管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,也就是将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。

package org.springframework.transaction;

public interface PlatformTransactionManager {
    //事务管理器 通过TransactionDefinition,获得"事务状态"从而管理事务
    TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
    //提交事务
    void commit(TransactionStatus var1) throws TransactionException;
    //事务过程执行异常,回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}

getTransaction(TransactionDefinition var1)

这个方法返回的是 TransactionStatus对象,然后程序根据返回的对象来获取事务状态,然后进行相应的操作。

同时,不同的平台Spring也提供不同的实现类

TransactionStatus事务状态

package org.springframework.transaction;

import java.io.Flushable;

public interface TransactionStatus extends SavepointManager, Flushable {
    //是否是新的事务
    boolean isNewTransaction();
    //是否有保存点
    boolean hasSavepoint();
    //设置回滚
    void setRollbackOnly();
    //是否回滚
    boolean isRollbackOnly();
    //刷新
    void flush();
    //是否完成
    boolean isCompleted();
}

这个接口描述的是一些处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态。

TransactionDefinition基本事务属性的定义

在上面讲到的getTransaction(TransactionDefinition var)中来得到事务,其中的参数对象便是TransactionDefinition,那么这个对象代表着什么呢?

TransactionDefinition类中定义了一些基本事务属性

  1. 传播行为
  2. 隔离规则
  3. 回滚规则
  4. 事务超时
  5. 是否只读

以下是接口代码

package org.springframework.transaction;

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;
    //默认超时时间,默认值:-1,使用数据库底层的超时时间
    int TIMEOUT_DEFAULT = -1;
    //取得传播行为
    int getPropagationBehavior();
    //取得隔离级别
    int getIsolationLevel();
    //取得超时时间
    int getTimeout();
    //是否只读
    boolean isReadOnly();
    //配置事务的详情名称
    String getName();
}

传播行为

定义:当事务方法被另一个事务方法调用的时候,必须制定事务应当如何传播

    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;

下面我们来讲讲这些参数的含义
PROPAGATION_REQUIRED:required,必须 :这个是默认值,代表A如果有事务,B将使用该事务;如果A没有事务那么B将创建一个新的事务
PROPAGATION_SUPPORTS:支持 :A如果有事务,B将使用该事务,如果A没有事务,B将以非事务执行
PROPAGATION_MANDATORY:强制 :A如果有事务,B将使用该事务;如果A没有事务,B将抛异常
PROPAGATION_REQUIRES_NEW:必须为新: 如果A有事务,将A挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务
PROPAGATION_NOT_SUPPORTED:不支持 :如果A有事务,B将以非事务执行;如果A没有事务,B将以非事务执行
PROPAGATION_NEVER:从不 :如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行
PROPAGATION_NESTED:嵌套 :A和B底层采用保存点机制,形成嵌套事务

隔离级别

定义:一个事务可能受到其他并发事务影响的程度

在这里我们应当考虑到,如果有多个事务同时的并发执行且操作的数据项相同时,那么我们应该如何保证我们数据和业务的完整性,安全性和健壮性呢?
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务,并发是必然的,那么久可能会导致以下问题

  1. 脏读(Dirty reads):脏读发生在一个事务读取了另一个事物改写但尚未提交的数据时,如果改写在稍后被回滚(roll back)了,那么第一个事务获取的数据就是无效的

  2. 不可重复读(Nonrepeatable rad):不可重复读发生在一个事务执行相同的查询两次或者两次以上,但每次都得到不同的数据时,通常是因为另一个并发事务在两次查询期间进行了更新

  3. 幻读(Phantom read):幻读与不可重复读类似,他发生在一个事务(A)读取了几行数据,接着另外一个并发事务(B)插入了一些数据时,在随后的查询中,第一个事务(A)就会发现多了一些原本不存在的记录

注意:不可重复读重点是修改,而幻读重点是新增或删除

在Spring事务管理中,我们通过定义隔离级别来预防上述问题

  1. ISOLATION_DEFAULT:使用后端数据库默认的隔离级别

  2. ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读

  3. ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

  4. ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生

  5. ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别

只读

这是事务的第三个特征,是否为只读事务,如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。

事务超时

为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。

回滚规则

默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚。但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

Spring编程式事务和声明式事务的区别

编程式事务处理:所谓编程式事务指的是通过编码方式实现事务,允许用户在代码中精确定义事务的边界。即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

声明式事务处理(推荐):管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。


本作品采用知识共享署名 4.0 国际许可协议进行许可。

如果可以的话,请给我钱请给我点赞赏,小小心意即可!

Last modification:April 12, 2019
If you think my article is useful to you, please feel free to appreciate