乐趣区

关于java:一文搞懂什么是事务

一文搞懂什么是事务

[toc]

事务概念

咱们要了解下事务概念:什么是事务呢?事务是并发管制的单位,是用户定义的一个操作序列。有四个个性(ACID):

  • 原子性(Atomicity):事务是数据库的逻辑工作单位,事务中包含的诸操作要么全做,要么全不做。
  • 一致性(Consistency):事务执行的后果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
  • 隔离性(Isolation):一个事务的执行不能被其余事务烦扰。
  • 持续性 / 永久性(Durability):一个事务一旦提交,它对数据库中数据的扭转就应该是永久性的。

以上是书面解释,简略来说就是把你的操作统一化,要么所有操作都胜利,要么就都不胜利,如果执行中有某一项操作失败,其之前所有的操作都回滚到未执行这一系列操作之前的状态。

脏读、不可反复读、幻读

先了解这三种因为并发拜访导致的数据读取问题,再了解事务隔离级别就简略多了。

脏读

A 事务读取 B 事务尚未提交的数据,此时如果 B 事务产生谬误并执行回滚操作,那么 A 事务读取到的数据就是脏数据。就如同本来的数据比拟洁净、纯正,此时因为 B 事务更改了它,这个数据变得不再纯正。这个时候 A 事务立刻读取了这个脏数据,但事务 B 良心发现,又用回滚把数据恢复成原来洁净、纯正的样子,而事务 A 却什么都不晓得,最终后果就是事务 A 读取了此次的脏数据,称为脏读。

这种状况常产生于转账与取款操作中

不可反复读(前后屡次读取,数据内容不统一)

事务 A 在执行读取操作,由整个事务 A 比拟大,前后读取同一条数据须要经验很长的工夫。而在事务 A 第一次读取数据,比方此时读取了小明的年龄为 20 岁,事务 B 执行更改操作,将小明的年龄更改为 30 岁,此时事务 A 第二次读取到小明的年龄时,发现其年龄是 30 岁,和之前的数据不一样了,也就是数据不反复了,零碎不能够读取到反复的数据,成为不可反复读。

幻读(前后屡次读取,数据总量不统一)

事务 A 在执行读取操作,须要两次统计数据的总量,前一次查问数据总量后,此时事务 B 执行了新增数据的操作并提交后,这个时候事务 A 读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

小总结: 不可反复读和幻读到底有什么区别?

(1) 不可反复读是读取了其余事务更改的数据,针对 update 操作

解决:应用行级锁,锁定该行,事务 A 屡次读取操作实现后才开释该锁,这个时候才容许其余事务更改方才的数据。

(2) 幻读是读取了其余事务新增的数据,针对 insert 和 delete 操作

解决:应用表级锁,锁定整张表,事务 A 屡次读取数据总量之后才开释该锁,这个时候才容许其余事务新增数据。

这时候再了解事务隔离级别就简略多了呢。

数据库事务的隔离级别

SQL 规范定义的四种隔离级别被 ANSI(美国国家标准学会)和 ISO/IEC(国际标准)采纳,每种级别对事务的解决能力会有不同水平的影响。事务是一系列的动作,它们综合在一起才是一个残缺的工作单元,这些动作必须全副实现,如果有一个失败的话,那么事务就会回滚到最开始的状态,好像什么都没产生过一样。

数据库事务的隔离级别有 4 个,由低到高顺次为 Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别能够一一解决脏读、不可反复读、幻读 这几类问题。

DEFAULT

默认值,示意应用底层数据库的默认隔离级别。大部分数据库为 READ_COMMITTED(MySql 默认 REPEATABLE_READ)

READ UNCOMMITTED(读未提交)

该隔离级别示意一个事务能够读取另一个事务批改但还没有提交的数据。该级别不能避免脏读和不可反复读,因而很少应用该隔离级别。

READ_COMMITTED (读提交)

该隔离级别示意一个事务只能读取另一个事务曾经提交的数据。该级别能够避免脏读,这也是大多数状况下的推荐值。

REPEATABLE_READ (可反复读)

该隔离级别示意一个事务在整个过程中能够多次重复执行某个查问,并且每次返回的记录都雷同。即便在屡次查问之间有新增的数据满足该查问,这些新增的记录也会被疏忽。该级别能够避免脏读和不可反复读。

SERIALIZABLE (串行化)

所有的事务顺次一一执行,这样事务之间就齐全不可能产生烦扰,也就是说,该级别能够避免脏读、不可反复读以及幻读。然而这将重大影响程序的性能。通常状况下也不会用到该级别。在该隔离级别下事务都是串行程序执行的,MySQL 数据库的 InnoDB 引擎会给读操作隐式加一把读共享锁,从而防止了脏读、不可重读复读和幻读问题。

MVCC(多版本并发管制)

mysql 中,默认的事务隔离级别是可反复读(repeatable-read),为了解决不可反复读,innodb 采纳了 MVCC(多版本并发管制)来解决这一问题。
MVCC 是利用在每条数据前面加了暗藏的两列(创立版本号和删除版本号),每个事务在开始的时候都会有一个递增的版本号, 用来和查问到的每行记录的版本号进行比拟。MYSQL MVCC

Spring 事务流传行为

先来介绍下 Spring 事务流传行为的应用办法:

@Transactional(propagation=Propagation.REQUIRED)
public void test() {//todo something}

注解 @Transactional
通过应用 propagation 属性设置,例如:@Transactional(propagation = Propagation.REQUIRED)

它的 propagation 属性取值有以下几种:

public enum Propagation {REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

    SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

    MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

    REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

    NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

    NEVER(TransactionDefinition.PROPAGATION_NEVER),

    NESTED(TransactionDefinition.PROPAGATION_NESTED);

}

事务流传行为:

  • REQUIRED:如果以后存在事务,则退出该事务;如果以后没有事务,则创立一个新的事务。
  • SUPPORTS:如果以后存在事务,则退出该事务;如果以后没有事务,则以非事务的形式持续运行。
  • MANDATORY:如果以后存在事务,则退出该事务;如果以后没有事务,则抛出异样。
  • REQUIRES_NEW:创立一个新的事务,如果以后存在事务,则把以后事务挂起。
  • NOT_SUPPORTED:以非事务形式运行,如果以后存在事务,则把以后事务挂起。
  • NEVER:以非事务形式运行,如果以后存在事务,则抛出异样。
  • NESTED:如果以后存在事务,则创立一个事务作为以后事务的嵌套事务来运行;如果以后没有事务,则该取值等价于 REQUIRED

Spring 事务的两种实现

Spring 反对“编程式事务 ”治理和“ 申明式事务”治理两种形式:

1编程式事务:编程式事务应用 TransactionTemplate 或者间接应用底层的 PlatformTransactionManager 实现事务。对于编程式事务 Spring 比拟举荐应用 TransactionTemplate 来对事务进行治理。

2申明式事务:申明式事务是建设在 AOP 之上的。其本质是对办法前后进行拦挡,而后在指标办法开始之前创立或者退出一个事务,在执行完指标办法之后依据执行状况“提交”或者“回滚”事务。

两种事务管理间的区别

  • 编程式事务容许用户在代码中准确定义事务的边界。
  • 申明式事务有助于用户将操作与事务规定进行解耦,它是基于 AOP 交由 Spring 容器实现,是开发人员只重点关注业务逻辑实现。
  • 编程式事务侵入到了业务代码外面,然而提供了更加细微的事务管理。而申明式事务基于 AOP,所以既能起到事务作用,又能够不影响业务代码的具体实现。一般而言比拟举荐应用申明式事务,尤其是应用 @Transactional 注解,它能很好地帮忙开发者实现事务的同时,也缩小代码开发量,且使代码看起来更加清新整洁。

Spring 编程式事务

一般来说编程式事务有两种办法能够实现:
模板事务的形式(TransactionTemplate)平台事务管理器形式(PlatformTransactionManager)

  • 模板事务的形式(TransactionTemplate):次要是应用 TransactionTemplate 类实现事务,这也是 Spring 官网比拟举荐的一种编程式应用形式;

例:

  • ① 获取模板对象 TransactionTemplate;
  • ② 抉择事务后果类型;
  • ③ 业务数据操作解决;
  • ④ 业务执行实现事务提交或者产生异样进行回滚;

其中 TransactionTemplate 的 execute 能承受两种类型参数执行事务,别离为:

 TransactionCallback<Object>():执行事务且能够返回一个值。TransactionCallbackWithoutResult():执行事务没有返回值。

上面是应用 TransactionTemplate 的实例:

@Service
public class TransactionExample {
  /** 1、获取 TransactionTemplate 对象 **/
  @Autowired
  private TransactionTemplate transactionTemplate;
  
  public void addUser() {
      // 2、应用 TransactionCallback 或者 TransactionCallbackWithoutResult 执行事务
      transactionTemplate.execute(new TransactionCallbackWithoutResult() {
          @Override
          public void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
              try {
                  // 3、执行业务代码(这里进行模仿,执行多个数据库操作方法)userMapper.delete(1);
                  userMapper.delete(2);
              } catch (Exception e) {
                  // 4、产生异样,进行回滚
                  transactionStatus.setRollbackOnly();}
          }
      });
  }
  
}

  • 平台事务管理器形式(PlatformTransactionManager):这里应用最根本的事务管理局对事务进行治理,借助 Spring 事务的 PlatformTransactionManager 及 TransactionDefinition 和 TransactionStatus 三个外围类对事务进行操作。

应用事务管理器形式实现事务步骤:

  • ① 获取事务管理器 PlatformTransactionManager;
  • ② 获取事务属性定义对象 TransactionDefinition;
  • ③ 获取事务状态对象 TransactionStatus;
  • ④ 业务数据操作解决;
  • ⑤ 进行事务提交 commit 操作或者产生异样进行事务回滚 rollback 操作;
@Service
public class TransactionExample {
    
    /** 1、获取 PlatformTransactionManager 对象 **/
    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    public void addUser() {
        // 2、获取默认事务定义
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 设置事务流传行为
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 3、依据事务定义对象设置的属性,获取事务状态
        TransactionStatus status = platformTransactionManager.getTransaction(def);
        try {
            // 4、执行业务代码(这里进行模仿,执行多个数据库操作方法)userMapper.delete(1);
            userMapper.delete(2);
            // 5、事务进行提交
            platformTransactionManager.commit(status);
        } catch(Exception e){
            // 5、事务进行回滚
            platformTransactionManager.rollback(status);
        }
    }
    
}

Spring 申明式事务

申明式事务(declarative transaction management)顾名思义就是应用申明的形式来处理事务。该形式是基于 Spring AOP 实现的,将具体业务逻辑和事务处了解耦,其本质是在执行办法前后进行拦挡,在办法开始之前创立或者退出一个事务,在执行完指标办法之后依据执行状况提交或者回滚事务。

罕用的申明式事务应用办法

罕用的申明式事务应用办法有

  • 1 XML
  • 2 @Transactional 注解

两种办法,因为近几年 SpringBoot 的风行,提供很不便的自动化配置,以致 XML 形式曾经逐步淘汰,比拟举荐应用注解的形式

@Transactional 的作用范畴

注解 @Transactional 不仅仅能够增加在办法下面,还能够增加到类级别上,当注解放在类级别时,示意所有该类的公共办法都配置雷同的事务属性信息。如果类级别配置了 @transactional,办法级别也配置了 @transactional,应用程序会以办法级别的事务属性信息来治理事务,换言之,办法级别的事务属性信息会笼罩类级别的相干配置。

@Transactional 注解中可配置参数
  • value:事务管理器,此配置项是设置 Spring 容器中的 Bean 名称,这个 Bean 须要实现接口 PlatformTransactionManager。
  • transactionManager:事务管理器,该参数和 value 配置保持一致,是同一个货色。
  • isolation:事务隔离级别,默认为 Isolation.DEFAULT 级别
  • propagation:事务流传行为,默认为 Propagation.REQUIRED
  • timeout:事务超时工夫,单位为秒,默认值为 -1,当事务超时时会抛出异样,进行回滚操作。
  • readOnly:是否开启只读事务,是否开启只读事务,默认 false
  • rollbackForClassName:回滚事务的异样类名定义,同 rollbackFor,只是用类名定义。
  • noRollbackForClassName:指定产生哪些异样名不回滚事务,参数为类数组,同 noRollbackFor,只是应用类的名称定义。
  • rollbackFor:回滚事务异样类定义,当办法中出异样,且异样类和该参数指定的类雷同时,进行回滚操作,否则提交事务。
  • noRollbackFor:指定产生哪些异样不回滚事务,当办法中出异样,且异样类和该参数指定的类雷同时,不回滚而是将持续提交事务。
示例
@Transactional(propagation=Propagation.REQUIRED)
public void test() {//todo something}

留神:
一般而言,不举荐将 @Transaction 配置到类上,因为这样很可能使起初的保护人员必须强制应用事务。

应用事务时须要留神的点

  • 1、遇到异样检测不回滚,起因:默认 RuntimeException 级别才回滚,如果是 Eexception 级别的异样须要手动增加
@Transactional(rollbackFor=Exception.class)
  • 2、捕获异样后事物不失效, 起因:捕获解决了异样导致框架无奈感知异样,天然就无奈回滚了。倡议: 若非理论业务要求,则在业务层对立抛出异样,而后在管制层对立解决

@Transactional(rollbackFor=Exception.class)
public void test() {

 try {// 业务代码} catch (Exception e) {// TODO: handle exception}

// 被动捕获异样导致框架无奈捕捉,从而导致事物生效
}



### 总结
本章次要讲了 事务基本概念 ACID 是什么
, 脏读、不可反复读、幻读 等等术语的介绍及例子
数据库事务的隔离级别(4 种),
Spring 事务流传行为(7 种), 事务的实现实例和应用事务时须要留神的中央  通过这篇文章, 小伙伴们应该曾经对事务有了更深的理解吧 , 最初 欢送关注我的公众号:JAVA 宝典 或者加我的微信 咱们一起探讨和学习.

> 关注公众号:java 宝典
退出移动版