Human0722's blog Human0722's blog
首页
  • Spring

    • Spring Framework
    • Spring Boot
    • Spring Cloud
  • CCNA
  • Vue

    • Vue2
日本语
导航
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Human0722

Gravity always win
首页
  • Spring

    • Spring Framework
    • Spring Boot
    • Spring Cloud
  • CCNA
  • Vue

    • Vue2
日本语
导航
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Spring Framework

    • Spring 快速开始
    • Spring IOC 容器
    • Spring AOP 切面编程
    • Spring JDBC数据库操作
    • Spring Transaction 事务
    • Spring Boot

    • Java 类库

    • 数据库

    • 解决方案

    • Java.Content
    • Spring Framework
    Xueliang
    2021-06-30
    目录

    Spring Transaction 事务

    声明式事务管理

    # 事务简介

    1. 事务是为了保证数据的完整性和一致性而引出的。
    2. 事务是一组逻辑上紧密关联的多个数据库操作,这些操作要么都执行,要么都不执行。
    3. 事务的四个属性(ACID)
    • 原子性 : 要么都执行,要么都不执行。
    • 一致性 : 不论事务多少步操作,保证执行前数据是正确的,执行后是正确的。
    • 隔离性 : 多个事务在并发执行过程中不会互相干扰。
    • 持久性 : 对数据的修改保存下来。

    # Spring 中的事务

    可以和脱离 Spring 编程一样,通过原生的 JDBC API 进行事务管理,获取 Connection 对象后提交或者回滚,这种方式是编程式事务管理。 编程式事务管理中,管理的代码和业务代码混编会造成在吗冗余,可以利用上文的 AOP 编程和事务管理结合起来。将事务管理代码从业务中分离出来,这种方式是声明式事务管理。 Spring 在不同的事务管理 API 上定义了一个抽象层,通过配置的方式来管理事务。 事务管理器以普通的 bean 形式声明在 Spring IOC 容器中。

    Spring 提供的事务管理器:

    • DataSourceTransactionManager : 基于数据源的事务管理,通过 JDBC 存取。
    • JtaTransactionManager : 基于 JTA(java Transaction API) 的事务管理。
    • HibernateTransactionManager : 用 Hibernate 框架存取的事务管理。

    # 初步实现

    1.配置文件

    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 启用事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    1
    2
    3
    4
    5
    6
    7
    8

    2.在需要事务控制的方法上加上注解 @Transactional

    # 事务的传播行为

    当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

    事务的传播行为可以由传播属性指定,Spring 定义了 7 种类传播行为。

    事务的传播属性一个在 @Transactional 注解的 propagation 属性中定义

    传播属性 描述
    REQUIRED(默认) 如果有事务在运行,当前方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行。
    REQUIRES_NEW 当前的方法必须启动新事务,并在它自己的事务内运行。如果有事务正在运行,应该将它挂起
    SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
    NOT_SUPPORTED 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
    MANDATORY 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
    NEVER 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
    NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行
    @Transactional(
    	propagation=Propagation.REQUIRED_NEW
    )
    
    1
    2
    3

    说明:

    1. REQUIRED 传播行为: 如果购物车中有两件商品,其中一件商品是没货的,此时使用 REQUIRED 属性,两件上品一件都无法购买。 required
    2. REQUIRED_NEW 传播行为: 如果购物车中有两件商品,其中一件商品是没货的,此时使用 REQUIRES_NEW 属性,可以成功购买一件。 requiredNew

    # 事务的隔离级别

    # 数据库事务并发的问题

    假设有两个并发的事务 T1 和 T2

    • 脏读
      1. T1 把某条记录的 AGE 属性从 20 改成了 30
      2. T2 读取了更新后的值: 30
      3. T1 回滚,把 AGE 的值改成了 20
      4. T2 读取的 30 就是一个无效值
    • 幻读
      1. T1 读取了表中的一部分数据
      2. T2 向表中插入了新的行
      3. T1 读取表中数据,多出了一些行
    • 不可重复读
      1. T1 读取了 AGE 的值为 20
      2. T2 修改了 AGE 的值为 30
      3. T1 再次读取 AGE 的值为 30,和第一次读取不一致

    # 隔离级别

    数据库系统应具有并发隔离运行各个事务的能力,使他们不会相互影响,避免发生各种问题。 一个事务与其他事务隔离的程度被称为隔离级别. 不同的隔离级别拥有不同的干扰程度, 隔离级别越高,数据一致性就越好,但是并发性越弱。

    • 读未提交 READ UNCOMMITTED 允许 T1 读取 T2 修改但还未提交的数据

    • 读已提交 READ COMMITTED 要求 T1 只能读取 T2 已经提交的数据

    • 可重复读 REPEATABLE READ 确保 T1 可以多次从同一个字段中读取到相同的值,解决不可重复读的问题。即 T1 执行期间禁止其他事务对这个字段进行更新。

    • 串行化 SERIALIZABLE 确保 T1 可以多次从同一个表中读取到相同的行,解决幻读的问题。即 **T1 执行期间禁止其他事务对这个表进行更新操作,可以避免任何并发问题,但是效率低下。

    • 隔离级别解决并发问题对应表

    脏读 不可重复读 幻读
    读未提交 有 有 有
    读已提交 无 有 有
    可重复读 无 无 有
    串行化 无 无 无

    MySQL 四种隔离级别都支持,默认为可重复读。Oracle 不支持读未提交

    @Transactional(
    	isolation=Isolation.READ_COMMITTED
    )
    
    1
    2
    3

    # Spring 中指定隔离级别

    在 Spring 中用 @Transactional 的 isolation 属性中设置隔离级别.

    @Transactional(isolation="READ_COMMITTED")
    public void purchase() {}
    
    1
    2

    # 触发回滚的异常

    事务执行期间默认捕获到 RuntimeException 或者 Error时回滚, 捕获到编译异常时不会滚。 也可以通过属性设置,指定回滚和不回滚的触发异常条件。

    // rollbackFor属性: 指定遇到时必须回滚的异常类型
    // noRollbackFor属性: 指定遇到时不会滚的异常类型
    @Transactional(propagation= Propagation.REQUIRES_NEW,
    	       isolation=Isolation.READ_COMMITTED,
    	       rollbackFor=(IOException.class, SQLException.class),
    	       noRollbackFor=(UserNotFoundException.class)
    	       )
    	       public void purchase(String isbn, String username)
    
    1
    2
    3
    4
    5
    6
    7
    8

    # 事务的超时和只读属性

    • 超时事务属性: 事务在强制回滚之前可以保持多久,可以防止长期运行事务占用资源.
    • 只读事务属性: 表示事务是读取数据但不更新数据, 可以帮助数据库优化引擎优化事务
    @Transactional(
    	readOnly=true,
    	timeout=30
    )
    
    1
    2
    3
    4
    Spring JDBC数据库操作
    HelloWorld

    ← Spring JDBC数据库操作 HelloWorld→

    最近更新
    01
    DefineSprintBootStarter
    03-23
    02
    Spring MVC 启动流程分析
    03-23
    03
    Redis
    03-23
    更多文章>
    Theme by Vdoing | Copyright © 2019-2024 Human0722 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式