简单的事务管理

事务很重要,业务中经常要用,所以有必要好好提一提

先预览一下
这里写图片描述

定义

事务(Transaction)是一个不可分割的整体,一系列操作是完整的。

事务的定义很清楚,具有4种属性,业内称为“事务ACID特性

分别为:原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability)

只有具备了以上四个特性,事务才能够正常的操作,原子性是基础,隔离性是手段,持久性是属性,一致性是最终的目的。

事务并发引发的问题

1.脏读:
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据(还未提交事务的情况下),然后使用了这个数据。

事务A读取了事务B未提交的数据

2.不可重复读:(强调另一事务对本事务中数据的变化)
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(即不能读到相同的数据内容)

事务A读取了事务B已提交的更改数据

3.幻读:(强调另一事务对本事务中数量的变化)
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

事务A读取了事务B已提交的新增数据

事务的隔离级别

隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT(一般的数据库默认,但MySQL默认为读已提交):这是默认值,表示使用底层数据库的默认隔离级别。
    对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。

  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED(读未提交):该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。

  • TransactionDefinition.ISOLATION_READ_COMMITTED(读已提交):该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值(MySQL默认的隔离级别)。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ(可重复读):该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE(序列化):所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

PS:数据库隔离事务的具体操作是:插入数据时就锁定表,更新数据时就锁定行。

Spring的事务传播行为

除了JDBC给我们的事务隔离级别的解决方案外,还有Spring的事务解决方案,其实就是对JDBC的补充和扩展。

事务传播可以理解为:

从方法A传播到方法B,Spring解决的只是方法之间的事务传播

Spring一共提供了7种事务传播行为,

在TransactionDefinition定义中包括了如下7个表示传播行为的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

Spring还带来了几个有用的附加功能:

事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

事务的只读属性

事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。

编程式事务管理

Spring 的编程式事务管理概述

在 Spring 出现以前,编程式事务管理对基于 POJO 的应用来说是唯一选择。用过 Hibernate 的人都知道,我们需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。通过 Spring 提供的事务管理 API,我们可以在代码中灵活控制事务的执行。在底层,Spring 仍然将事务操作委托给底层的持久化框架来执行。
大致分为:
- 基于底层 API 的编程式事务管理
- 基于 TransactionTemplate 的编程式事务管理(一种Spring数据访问层常见的模板回调模式)

声明式事务管理

Spring 的声明式事务管理概述

Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过等价的基于标注的方式),便可以将事务规则应用到业务逻辑中。因为事务管理本身就是一个典型的横切逻辑,正是 AOP 的用武之地。 Spring 开发团队也意识到了这一点,为声明式事务提供了简单而强大的支持。

声明式事务管理曾经是 EJB 引以为傲的一个亮点,如今 Spring 让 POJO 在事务管理方面也拥有了和 EJB 一样的待遇,让开发人员在 EJB 容器之外也用上了强大的声明式事务管理功能,这主要得益于 Spring 依赖注入容器和 Spring AOP 的支持。依赖注入容器为声明式事务管理提供了基础设施,使得 Bean 对于 Spring 框架而言是可管理的;而 Spring AOP 则是声明式事务管理的直接实现者

Spring 为我们提供的声明式事务管理功能有以下几个:

  • 基于 TransactionInter… 的声明式事务管理(需要配置的文件太多了)
  • 基于 TransactionProxy… 的声明式事务管理(经典的Spring声明式事务管理)
  • 基于 < tx> 命名空间的声明式事务管理(基于标签配置)
  • 基于 @Transactional 的声明式事务管理(基于注解)

编程式和声明式事务的比较

和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

小结

再来看一下这个事务管理的思维导图,简直太棒了!
这里写图片描述

感谢《架构探险》
感谢全面分析 Spring 的编程式事务管理及声明式事务管理

猜你喜欢

转载自blog.csdn.net/mikeoperfect/article/details/80628753