专栏首页cwl_Java快速学习-声明式事务管理

快速学习-声明式事务管理

第8章 声明式事务管理

8.1 事务概述

  1. 在JavaEE企业级开发的应用领域,为了保证数据的完整性和一致性,必须引入数据库事务的概念,所以事务管理是企业级应用程序开发中必不可少的技术。
  2. 事务就是一组由于逻辑上紧密关联而合并成一个整体(工作单元)的多个数据库操作,这些操作要么都执行,要么都不执行。
  3. 事务的四个关键属性(ACID) ① 原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。 ② 一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚。 ③ 隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰。 ④ 持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。

8.2 Spring事务管理

8.2.1编程式事务管理

  1. 使用原生的JDBC API进行事务管理 ① 获取数据库连接Connection对象 ② 取消事务的自动提交 ③ 执行操作 ④ 正常完成操作时手动提交事务 ⑤ 执行失败时回滚事务 ⑥ 关闭相关资源
  2. 评价 使用原生的JDBC API实现事务管理是所有事务管理方式的基石,同时也是最典型 的编程式事务管理。编程式事务管理需要将事务管理代码嵌入到业务方法中来控制事务 的提交和回滚。在使用编程的方式管理事务时,必须在每个事务操作中包含额外的事务 管理代码。相对于核心业务而言,事务管理的代码显然属于非核心业务,如果多个模块都使用同样模式的代码进行事务管理,显然会造成较大程度的代码冗余。

8.2.2 声明式事务管理

大多数情况下声明式事务比编程式事务管理更好:它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。 事务管理代码的固定模式作为一种横切关注点,可以通过AOP方法模块化,进而借助Spring AOP框架实现声明式事务管理。 Spring在不同的事务管理API之上定义了一个抽象层,通过配置的方式使其生效,从而让应用程序开发人员不必了解事务管理API的底层实现细节,就可以使用Spring的事务管理机制。 Spring既支持编程式事务管理,也支持声明式的事务管理。

8.2.3 Spring提供的事务管理器

Spring从不同的事务管理API中抽象出了一整套事务管理机制,让事务管理代码从特定的事务技术中独立出来。开发人员通过配置的方式进行事务管理,而不必了解其底层是如何实现的。 Spring的核心事务管理抽象是PlatformTransactionManager。它为事务管理封装了一组独立于技术的方法。无论使用Spring的哪种事务管理策略(编程式或声明式),事务管理器都是必须的。 事务管理器可以以普通的bean的形式声明在Spring IOC容器中。

8.2.4 事务管理器的主要实现

  1. DataSourceTransactionManager:在应用程序中只需要处理一个数据源,而且通过JDBC存取。
  2. JtaTransactionManager:在JavaEE应用服务器上用JTA(Java Transaction API)进行事务管理
  3. HibernateTransactionManager:用Hibernate框架存取数据库

8.3 测试数据准备

8.3.1 需求

8.3.2 数据库表

CREATE TABLE book (
  isbn VARCHAR (50) PRIMARY KEY,
  book_name VARCHAR (100),
  price INT
) ;

CREATE TABLE book_stock (
  isbn VARCHAR (50) PRIMARY KEY,
  stock INT,
) ;

CREATE TABLE account (
  username VARCHAR (50) PRIMARY KEY,
  balance INT,
) ;

INSERT INTO account (`username`,`balance`) VALUES ('Tom',100000);
INSERT INTO account (`username`,`balance`) VALUES ('Jerry',150000);

INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-001','book01',100);
INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-002','book02',200);
INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-003','book03',300);
INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-004','book04',400);
INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-005','book05',500);

INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-001',1000);
INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-002',2000);
INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-003',3000);
INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-004',4000);
INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-005',5000);

8.4 初步实现

  1. 配置文件
<!-- 配置事务管理器 -->
<bean id="transactionManager" 
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>	  
</bean>

<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
  1. 在需要进行事务控制的方法上加注解 @Transactional

8.5 事务的传播行为

8.5.1 简介

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

事务传播属性可以在@Transactional注解的propagation属性中定义。

8.5.2 测试

1). 说明 ① REQUIRED传播行为 当bookService的purchase()方法被另一个事务方法checkout()调用时,它默认会在现有的事务内运行。这个默认的传播行为就是REQUIRED。因此在checkout()方法的开始和终止边界内只有一个事务。这个事务只在checkout()方法结束的时候被提交,结果用户一本书都买不了。

②. REQUIRES_NEW传播行为 表示该方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就应该先挂起它。

8.5.3 补充

在Spring 2.x事务通知中,可以像下面这样在tx:method元素中设定传播事务属性。

8.6 事务的隔离级别

8.6.1 数据库事务并发问题

假设现在有两个事务:Transaction01和Transaction02并发执行。
  1. 脏读 ①Transaction01将某条记录的AGE值从20修改为30。 ②Transaction02读取了Transaction01更新后的值:30。 ③Transaction01回滚,AGE值恢复到了20。 ④Transaction02读取到的30就是一个无效的值。
  2. 不可重复读 ①Transaction01读取了AGE值为20。 ②Transaction02将AGE值修改为30。 ③Transaction01再次读取AGE值为30,和第一次读取不一致。
  3. 幻读 ①Transaction01读取了STUDENT表中的一部分数据。 ②Transaction02向STUDENT表中插入了新的行。 ③Transaction01读取了STUDENT表时,多出了一些行。

8.6.2 隔离级别

数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。

  1. 读未提交:READ UNCOMMITTED 允许Transaction01读取Transaction02未提交的修改。
  2. 读已提交:READ COMMITTED 要求Transaction01只能读取Transaction02已提交的修改。
  3. 可重复读:REPEATABLE READ 确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
  4. 串行化:SERIALIZABLE 确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
  5. 各个隔离级别解决并发问题的能力见下表
  1. 各种数据库产品对事务隔离级别的支持程度

8.6.3 在Spring中指定事务隔离级别

  1. 注解 用@Transactional注解声明式地管理事务时可以在@Transactional的isolation属性中设置隔离级别
  2. XML 在Spring 2.x事务通知中,可以在tx:method元素中指定隔离级别

8.7 触发事务回滚的异常

8.7.1 默认情况

捕获到RuntimeException或Error时回滚,而捕获到编译时异常不回滚。

8.7.2 设置途经

  1. 注解@Transactional 注解 ① rollbackFor属性:指定遇到时必须进行回滚的异常类型,可以为多个 ② noRollbackFor属性:指定遇到时不回滚的异常类型,可以为多个
  1. XML 在Spring 2.x事务通知中,可以在tx:method元素中指定回滚规则。如果有不止一种异常则用逗号分隔。

8.8 事务的超时和只读属性

8.8.1 简介

由于事务可以在行和表上获得锁,因此长事务会占用资源,并对整体性能产生影响。 如果一个事务只读取数据但不做修改,数据库引擎可以对这个事务进行优化。 超时事务属性:事务在强制回滚之前可以保持多久。这样可以防止长期运行的事务占用资源。 只读事务属性: 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务。

8.8.2 设置

  1. 注解 @Transaction注解
  1. XML 在Spring 2.x事务通知中,超时和只读属性可以在tx:method元素中进行指定

8.9 基于XML文档的声明式事务配置

	<!-- 配置事务切面 -->
	<aop:config>
		<aop:pointcut 
			expression="execution(* com.atguigu.tx.component.service.BookShopServiceImpl.purchase(..))" 
			id="txPointCut"/>
		<!-- 将切入点表达式和事务属性配置关联到一起 -->
		<aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/>
	</aop:config>
	
	<!-- 配置基于XML的声明式事务  -->
	<tx:advice id="myTx" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 设置具体方法的事务属性 -->
			<tx:method name="find*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="purchase" 
				isolation="READ_COMMITTED" 
	no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"
				propagation="REQUIRES_NEW"
				read-only="false"
				timeout="10"/>
		</tx:attributes>
	</tx:advice>

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 快速学习-Seata--分布式事务

    事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作 都成功,要么所有的操作都被撤销。简单地说,事务提供一种“要么什么都不做,...

    cwl_java
  • CMS-订单系统的分布式事务如何处理

    用户支付完成会将支付状态及订单状态保存在订单数据库中,由订单服务去维护订单数据库。而学生选课信息在学 习中心数据库,由学习服务去维护学习中心数据库的信息。下图是...

    cwl_java
  • 快速学习-Spring 中的事务控制

    第一:JavaEE 体系进行分层开发,事务处理位于业务层,Spring 提供了分层设计业务层的事务处理解决方案。

    cwl_java
  • 3分钟了解数据库事务

    事务是由几个读取和修改数据的sql命令组成的,但是知道commit命令被执行之后,修改操作才被认为是正常的完成。显式事务常以Begin tran语句开头,以co...

    Edison.Ma
  • 数据库事务详解

    什么是『事务』? 事务就是一组具有原子性的操作,这一组操作要么全都正确执行,要么全都不执行。 事务能保证数据库从一种一致性状态转换为另一种一致性状态。 事...

    大闲人柴毛毛
  • 逐行阅读Spring5.X源码(十三)spring事务源码分析

    spring并不直接管理事务,而是提供了多种事务管理器,他们将事务的管理职责委托给Hibernate或JTA等持久化机制所提供的相关平台框架的事务来实现。通过这...

    源码之路
  • spring事务传播特性

    通过上面对事物的说明,有编程经验的人应该都很清楚为什么需要失误了吧?那就是为了防止出现业务逻辑上的出错,如算错账转错钱等。

    居士
  • Spring事务的传播行为案例分析

    1、spring给出经常面试的考点Spring事务的4个特性含义---这个很容易理解

    宜信技术学院
  • Spring事物的传播行为案例分析

    1、spring给出经常面试的考点Spring事务的4个特性含义---这个很容易理解

    宜信技术学院
  • Spring技术知识点总结之六——Spring 事务传播等级

    Spring 事务传播有七个等级,假设有 methodA 调用 methodB,根据事务传播等级的设置,methodA 与 methodB 有如下效果:

    剑影啸清寒

扫码关注云+社区

领取腾讯云代金券