小言_互联网的博客

《精通Spring4.x 企业应用开发实战》第11章 Spring的事务管理

404人阅读  评论(0)

前言

汇总:《精通Spring4.x 企业应用开发实战》

一、基础知识预备

在学习Spring事务之前,我们需要提前对一些相关知识进行回顾。这些知识是Spring事务的内部基础。

1.数据库事务基础知识

事务ACID特性

在这里插入图片描述

事务ACID-‘隔离性’如何保证

和 Java 程序采用对象锁机制进行线程同步类似,数据库管理系统采用数据库锁机制保证事务的隔离性。当多个事务试图对相同的数据进行操作时,只有持有锁的事务才能操作数据,直到前一个事务完成后,后面的事务才有机会对数据进行操作。Oracle 数据库还使用了数据版本的机制,在回滚段为数据的每个变化都保存一个版本,使数据的更改不影响数据的读取。

并发事务问题

数据库中的相同数据可能同时被多个事务并发访问,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性。
这些问题可以归结为5类:
1、3类数据读问题:脏读、不可重复读和幻象读;
2、2 类数据更新问题:第一类丢失更新、第二类丢失更新。

注意:以上5类并发问题,都是基于数据库事务‘无隔离性’的假设前提下造成的。

关于’脏写、脏读、不可重复读和幻象读的详细解释‘,以及’不可重复读和幻读的区别‘,请参考该章节的语雀版-第11章 Spring的事务管理

事务隔离级别

如果用户直接操作数据库的锁,会非常麻烦。因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中的 SQL 语句,然后自动为事务操作的数据资源添加适合的锁。
SQL 92 推荐使用 REPEATABLE READ 以保证数据的读一致性,不过用户可以根据应用的需要选择适合的隔离等级。

2.ThreadLocal基础知识

在第 10 章中我们知道,Spring 通过各种模板类降低了开发者使用各种数据持久化技术的难度。这些模板类都是线程安全的,也就是说,多个 DAO 可以复用同一个模板实例而不会发生冲突
1、使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源。
2、但这些资源(连接、会话)本身是非线程安全的,也就是说它们不能在同一时刻被多个线程共享。为什么Connection是非线程安全的?
3、虽然模板类通过资源池获取数据连接或会话,但资源池本身解决的是数据连接或会话的缓存问题,并非数据连接或会话的线程安全问题。

按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用 synchronized 进行线程同步。但模板类并未采用线程同步机制,因为线程同步会降低并发性,影响系统性能。此外,通过代码同步解决线程安全的挑战性很大,可能会增加几倍的实现难度。那么,模板类究竟仰仗何种“魔法神功”,可以在无须线程同步的情况下就化解线程安全的难题呢?答案就是 ThreadLocal!
ThreadLocal 在 Spring 中发挥着重要的作用,在管理 request 作用域的 Bean、事务管[理、任务调度、AOP 等模块中都出现了它的身影。要想了解 Spring 事务管理的底层技术,ThreadLocal 是必须攻克的“山头堡垒”。

对ThreadLocal的详细讲解请看语雀版-第11章 Spring的事务管理

二、JDBC 对事务的支持

我们先来了解下JDBC对事务是如何支持的,然后再对比一下Spring对事务是如何支持的。

1.代码方式判断数据库是否支持事务

并不是所有的数据库都支持事务,即使支持事务的数据库也并非支持所有的事务隔离级别:
1、用户可以通过 Connection#getMetaData()方法获取 DatabaseMetaData 对象;
2、然后通过该对象的 supportsTransactions()、 supportsTransactionlsolationLevel(int level)方法查看底层数据库的事务支持情况。

2.Connection提交与回滚事务

Connection 默认情况下是自动提交的,即每条执行的 SQL 语句都对应一个事务。为了将多条 SQL 语句当成一个事务执行:
1、必须先通过 Connection#setAutoCommit(false)阻止 Connection 自动提交;
2、然后通过 Connection#setTransactionlsolation()设置事务的隔离级别。
Connection 中定义了对应 SQL 92 标准4 个事务隔离级别的常量。通过 Connection#commit()提交事务,通过 Connection#rollback()回滚事务

下面是典型的 JDBC 事务数据操作的代码,如代码清单 11-1 所示。

3.事务操作(提交/回滚/保存点)

1、在 JDBC 2.0 中,事务最终只能有两个操作:提交回滚。但是,有些应用可能需要对事务进行更多的控制,而不是简单地提交或回滚。
2、JDBC 3.0 (Java 1.4 及以后的版本)引入了一个全新的保存点特性,Savepoint 接口允许用户将事务分割为多个阶段,用户可以指定回滚到事务的特定保存点,而并非像 JDBC 2.0 一样只能回滚到开始事务的点,如图11-1 所示。

下面的代码使用了保存点功能,在发生特定问题时,回滚到指定的保存点,而非回滚整个事务,如代码清单11-2 所示。
并非所有数据库都支持保存点功能,用户可以通过 DatabaseMetaData#supportsSavepoints()方法查看是否支持。

三、Spring 对事务管理的支持

1.事务模板

Spring不仅仅提供了统一异常体系,还提供了统一事务模板功能。

不管是选择 Spring JDBC、 Hibernate、JPA 还是选择 MyBatis, Spring 都可以让用户用统一的编程模型进行事务管理:
1、像 Spring DAO 为不同的持久化实现提供了模板类一样,Spring 事务管理继承了这一风格,也提供了事务模板类 TransactionTemplate
2、通过 TransactionTemplate 并配合使用事务回调 TransactionCallback 指定具体的持久化操作,就可以通过编程方式实现事务管理,而无须关注资源获取、复用、释放、事务同步和异常处理等操作。

2.Spring单数据源和多数据源

Spring 深刻地认识到,大部分应用都是基于单数据源的,只有为数不多的应用需要使用多数据源的JTA 事务
1、因此,在单数据源的情况下,Spring 直接使用底层的数据源管理事务。
2、只有在面对多数据源的应用时,Spring 才寻求 Java EE 应用服务器的支持,通过引用应用服务器中的JNDI资源完成 JTA 事务。

3.事务管理关键抽象

在 Spring 事务管理 SPI ( Service Provider Interface)的抽象层主要包括3 个接口,分别是 Platform TransactionManager、 TransactionDefinition 和 TransactionStatus, 它们位于 org.springframework.transaction 包中。通过图11-3 可以描述这三者的关系。

1.TransactionDefinition

2.TransactionStatus

Transaction Status 代表一个事务的具体运行状态。事务管理器可以通过该接口获取事务运行期的状态信息,也可以通过该接口间接地回滚事务,它相比于在抛出异常时回滚事务的方式更具可控性。该接口继承于 SavepointManager 接口,SavepointManager 接口基于 JDBC 3.0 保存点的分段事务控制能力提供了嵌套事务的机制。

TransactionStatus 扩展了 SavepointManager 接口,并提供了以下几个方法。
● boolean hasSavepoint():判断当前事务是否在内部创建了一个保存点,该保存点是为了支持 Spring 的嵌套事务而创建的。
● boolean isNewTransaction():判断当前事务是否是一个新的事务,如果返回 false,则表示当前事务是一个已经存在的事务,或者当前操作未运行在事务环境中。
● boolean isCompleted():判断当前事务是否已经结束(己经提交或回滚)。
● boolean isRollbackOnly():判断当前事务是否己经被标识为 rollback-only。
● void setRollbackOnly():将当前事务设置为 rollback-only。通过该标识通知事务管理器只能将事务回滚,事务管理器将通过显式调用回滚命令或抛出异常的方式回滚事务。

3.PlatformTransactionManager

4.Spring 的事务管理器实现类

Spring 将事务管理委托给底层具体的持久化实现框架来完成。因此,Spring 为不同的持久化框架提供了 PlatformTransactionManager 接口的实现类,如表11-2 所示。
这些事务管理器都是对特定事务实现框架的代理,这样就可以通过 Spring 所提交的高级抽象对不同种类的事务实现使用相同的方式进行管理,而不用关心具体的实现。

如何实现事务管理?要实现事务管理:
1、首先要在 Spring 中配置好相应的事务管理器;
2、为事务管理器指定数据资源及一些其他事务管理控制属性。

5.事务同步管理器

Spring 将 JDBC 的 Connection、 Hibernate 的 Session 等访问数据库的连接或会话对象统称为资源,这些资源在同一时刻是不能多线程共享的。因为资源在多线程下是不安全的(文章篇首有对Connection的解释)。

为了让 DAO、 Service 类可能做到 singleton, Spring 的事务同步管理器类 org.springframework.transaction.support.TransactionSynchronizationManager 使用 ThreadLocal 为不同事务线程提供了独立的资源副本,同时维护事务配置的属性和运行状态信息。事务同步管理器是 Spring 事务管理的基石,不管用户使用的是编程式事务管理,还是声明式事务管理,都离不开事务同步管理器。

Spring 框架为不同的持久化技术提供了一套从 TransactionSynchronizationManager中获取对应线程绑定资源的工具类,如表11-3 所示。
这些工具类都提供了静态的方法,通过这些方法可以获取和当前线程绑定的资源,
1.如 DataSourceUtils.getConnection(DataSource dataSource)方法可以从指定的数据源中获取和当前线程绑定的 Connection,
2.而 Hiberate 的 SessionFactoryUtils.getSession(SessionFactorysessionFactory, boolean allowCreate)方法则可以从指定的 SessionFactory 中获取和当前线程绑定的 Session。

四、项目中的事务配置

此处略,可以看语雀版文章或者Spring-API官方文档。

此处略,并不代表不重要。恰恰相反,编程式事务与声明式事务的代码的代码配置是非常非常重要的。它对你探讨Spring事务源码,以及后面学习Springboot关于事务自动配置的原理,都起着很关键的作用

1.编程式事务

2.声明式事务

总结

待补充


转载:https://blog.csdn.net/qq_43783527/article/details/128998408
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场