我们在实际业务场景中,经常会遇到数据频繁修改读取的问题。在同一时刻,不同的业务逻辑对同一个表数据进行修改,这种冲突很可能造成数据不可挽回的错乱,所以我们需要用事务来对数据进行管理。

1. 到底什么是事务?

  我认为是在同一时刻,我们把几件不同的事情当作一件事情来做,要么全部成功要么全部失败这就是事务;

2. 事务的特性(ACID)

原子性: 事务的最小执行单位,不允许分割。事务的原子性确保指令要么全部执行完成,要么全部失败;

一致性: 执行事务前后,数据保持一致;事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定;

隔离性: 并发访问数据库时,一个用户的事物不被其他事物所干扰,各并发事务之间数据库是独立的;

持久性 一个事务被提交之后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响;

3. 并发事务带来的问题

在我们应用程序中,通常会出现多个事务并发运行,同时操作相同的数据来解决业务问题,这个时候就可能引发以下的一些问题:

  脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。

    因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。

  丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。

    这样第一个事务内的修改结果就被丢失,因此称为丢失修改。

  不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,

    由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

  幻读(Phantom read):幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,

    第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

  不可重复度和幻读区别:

    不可重复读的重点是修改,幻读的重点在于新增或者删除。

4. Spring事务管理器接口PlatformTransactionManager

  所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。

  Spring事务管理器的接口是: org.springframework.transaction.PlatformTransactionManager ,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface PlatformTransactionManager {
//根据指定的传播行为,返回当前活动的事务或创建一个新事务
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
//使用事务目前的状态提交事务
void commit(TransactionStatus status) throws TransactionException;
//对执行的事务进行回滚
void rollback(TransactionStatus status) throws TransactionException;
}

  Spring中PlatformTransactionManager根据不同持久层框架所对应的接口实现类,几个比较常见的如下图所示

5. 事务属性接口TransactionDefinition

  Spring事务属性的接口是: org.springframework.transaction.TransactionDefinition ,事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。

package org.springframework.transaction;

import java.sql.Connection;

import org.springframework.lang.Nullable;

public interface TransactionDefinition {
//传播行为
int getPropagationBehavior();
//隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
int getIsolationLevel();
//事务超时
int getTimeout();
//是否只读
boolean isReadOnly();
//返回事务的名字
@Nullable
String getName();
}

  事务属性包含五个方面:传播行为、隔离级别、事务超时、回归规则,是否只读。

 (1)事务隔离级别(定义了一个事务可能受其他并发事务影响的程度):

    TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

 (2)事务传播行为(为了解决业务层方法之间互相调用的事务问题):

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

支持当前事务的情况:

    • TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

    • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

    • TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

不支持当前事务的情况:

    • TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。

    • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。

    • TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

    • TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED 是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。

 (3) 事务超时属性(一个事务允许执行的最长时间)

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

(4) 事务只读属性(对事物资源是否执行只读操作)

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

(5) 回滚规则(定义事务回滚规则)

这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的)。 但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

6. 事务运行状态接口TransactionStatus

Spring事务运行状态的接口是: org.springframework.transaction.TransactionStatus,该接口定义了一组方法,用来获取或判断事务的相应状态信息.

PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。

package org.springframework.transaction;

import java.io.Flushable;

public interface TransactionStatus extends SavepointManager, Flushable {
// 是否是新的事物
boolean isNewTransaction();
// 是否有恢复点
boolean hasSavepoint();
// 设置为只回滚
void setRollbackOnly();
// 是否为只回滚
boolean isRollbackOnly();
//清除
@Override
void flush();
//是否已完成
boolean isCompleted();
}

  

Spring事务控制的更多相关文章

  1. SSM框架之Spring(5)JdbcTemplate及spring事务控制

    Spring(5)JdbcTemplate及spring事务控制 ##1.JdbcTmeplate 它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装.spring ...

  2. 【Spring】Spring 事务控制

    Spring 事务控制 Spring 事务控制介绍 JavaEE 体系进行分层开发,事务控制位于业务层,Spring 提供了分层设计业务层的事务处理解决方案. Spring 的事务控制都是基于 AOP ...

  3. spring 事务控制 设置手动回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

    //假设这是一个service类的片段 try{ //出现异常 } catch (Exception e) { e.printStackTrace(); //设置手动回滚 TransactionAsp ...

  4. Spring事务控制和回滚

    1在一个项目中ssh结构,spring2.5,事务控制采用的是tx拦截器的方式. 自己写了个 int a=1/0;异常抛出了,但是事务还是提交了,怎么搞都不行. 现将看到的一些事务控制总结下来: 事务 ...

  5. Spring事务控制和传递性理解

    1.在同一类方法间相互调用,如果调用方无事务控制,被调用方有事务控制,则被调用方也无事务 原因:外部经过spring容器调用service的方法事务才生效,service类内部方法间相互调用事务不生效 ...

  6. 阶段3 2.Spring_10.Spring中事务控制_5 spring事务控制的代码准备

    创建一个工程,只搭建环境不做配置.等配置的时候把这个项目相关的代码再复制到新项目里面 jar包的打包方式 导入包 事务控制也是基于AOP的.所以这里导入aspectjweaver 复制jdbcTemp ...

  7. 04 Spring:01.Spring框架简介&&02.程序间耦合&&03.Spring的 IOC 和 DI&&08.面向切面编程 AOP&&10.Spring中事务控制

    spring共四天 第一天:spring框架的概述以及spring中基于XML的IOC配置 第二天:spring中基于注解的IOC和ioc的案例 第三天:spring中的aop和基于XML以及注解的A ...

  8. Spring的事务控制-基于注解的方式

    模拟转账操作,即Jone减少500,tom增加500 如果有疑问请访问spring事务控制-基于xml方式 1.创建数据表 2.创建Account实体类 public class Account { ...

  9. Spring事务解析1-使用介绍

    spring的事务控制让我们从复杂的事务处理中得到解脱,是我们再也不需要去处理获得连接,关闭连接,事务提交和回滚等操作,再也不需要在事务相关的方法中处理大量的try..catch...finally代 ...

随机推荐

  1. visual studio 2017安装教程以及各类问题解决方案

    文章的关键词和所含教程: VS2017安装/visual studio 2017安装/Xamarin/Android for visual studio 2017/VS2017找不到网站/VS2017 ...

  2. 51nod 1102 面积最大的矩形

    题目地址在这儿 求取:以某矩形g[i]为最小值的区间的左右端点,得到一个临时解.所有临时解中的最大值即为解. 求取区间的方法可以用单调栈,也可以用下面这种十分简洁的类似于递归的方法.下面这种解法求出来 ...

  3. 深入理解php中的ini配置(2)

    继续接着上一篇写. 运行时改变配置 在前一篇中曾经谈到,ini_set函数可以在php执行的过程中,动态修改php的部分配置.注意,仅仅是部分,并非所有的配置都可以动态修改.关于ini配置的可修改性, ...

  4. Linux中如何配置sudo用户

    Linux中的sudo文件在/etc/sudoers,但不建议直接修改此文件: 可以在/etc/sudoers.d文件夹中新建文件,文件名随意,在文件中添加内容如下: 用户名 ALL=(ALL) AL ...

  5. 联想Thinkpad 遇到双系统 uefi Ubuntu无法进入的引导问题解决方案

    最近因为许多课程设计的需要,安装了Ubuntu双系统,但是一开始遇到了安装好了以后无法进入的问题,后来弄好后手残又把引导项给删了又要弄回去,反反复复很多次,网上的很多经验都十分过时,要么对最新的uef ...

  6. Functional Reactive Programming

    Functional Reactive Programming (FRP) integrates time flow and compositional events into functional ...

  7. BZOJ4870:[SHOI2017]组合数问题(组合数学,矩阵乘法)

    Description Input 第一行有四个整数 n, p, k, r,所有整数含义见问题描述. 1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1 ...

  8. 6、Dubbo-配置(1)

    覆盖关系 下图展示了配置覆盖关系的优先级,从上到下优先级依次降低: 参考官网:http://dubbo.apache.org/zh-cn/docs/user/configuration/configu ...

  9. python3对数据库的基本操作

    其实Python同Java一样,都有对JDBC操作的API. 注意:我的Python版本为3.6.5 Python2.7是应用比较广的,百度博客上很多相关的例子,所以本次不再列出. 只要是用过Java ...

  10. rinted端口转发工具

    下载包: [root@localhost opt]# wget https://boutell.com/rinetd/http/rinetd.tar.gz 解压编译安装: [root@localhos ...