spring事务管理器设计思想(一)
在最近做的一个项目里面,涉及到多数据源的操作,比较特殊的是,这多个数据库的表结构完全相同,由于我们使用的ibatis框架作为持久化层,为了防止每一个数据源都配置一套规则,所以重新实现了数据源,根据线程变量中指定的数据库连接名称来获取实际的数据源。
一个简单的实现如下:
public class ProxyDataSource implements DataSource {
/** 数据源池配置 */
private Map<String, DataSource> dataSourcePoolConfig;
public Connection getConnection() throws SQLException {
return createDataSource().getConnection();
}
private synchronized DataSource createDataSource() {
String dbName = DataSourceContextHolder.getDbName();
return dataSourcePoolConfig.get(dbName);
}
每次调用spring事务管理器之前,设置DataSourceContextHolder.set(“dbName”)
事务提交之后在调用 DataSourceContextHolder.clear() 方法即可
但是这样设计实际使用过程中也会遇到一些典型的问题,这就是在仔细了解spring中持久化层的设计之后,才能明白所产生的问题的原因。下面主要总结一下spring 持久化的设计。
Jdbc基本的编程模型
由于任何持久化层的封装实际上都是对java.sql.Connection等相关对象的操作,一个典型的数据操作的流程如下:

但在我们实际使用spring和ibatis的时候,都没有感觉到上面的流程,其实spring已经对外已经屏蔽了上述的操作,让我们更关注业务逻辑功能,但是我们有必要了解其实现,以便能够更好运用和定位问题。
开启事务:
在开启事务的时候,我们需要初始化事务上下文信息,以便在业务完成之后,需要知道事务的状态,以便进行后续的处理,这个上下文信息可以保存在 ThreadLocal里面,包括是否已经开启事务,事务的超时时间,隔离级别,传播级别,是否设置为回滚。这个信息对应用来说是透明的,但是提供给使用者编程接口,以便告知业务结束的时候是提交事务还是回滚事务。
获取连接
首先来看看spring如何获取数据库连接的,对于正常情况来看,获取连接直接调用DataSource.getConnection()就可以了,我们在自己实现的时候也肯定会这么做,但是需要考虑两种情况(这里面先不引入事务的传播属性):
1 还没有获取过连接,这是第一次获取连接
2 已经获取过连接,不是第一次获取连接,可以复用连接
解决获取数据库连接的关键问题就是如何判断是否已经可用的连接,而不需要开启新的数据库连接,同时由于数据库连接需要给后续的业务操作复用,如何保持这个连接,并且透明的传递给后续流程。对于一个简单的实现就是使用线程上下文变量ThrealLocal来解决以上两个问题。
具体的实现是:在获取数据库连接的时候,判断当前线程线程变量里面是否已经存在相关连接,如果不存在,就创新一个新的连接,如果存在,就直接获取其对应的连接。在第一次获取到数据库连接的时候,我们还需要做一些特殊处理,就是设置自动提交为false。在业务活动结束的时候在进行提交或者回滚。这个时候就是要调用connection.setAutoCommit(false)方法。

执行sql
这一部分和业务逻辑相关,通过对外提供一些编程接口,可以让业务决定业务完成之后如何处理事务,比较简单的就是设置事务状态。
提交事务:
在开启事务的时候,事务上下文信息已经保存在线程变量里面了,可以根据事务上下文的信息,来决定是否是提交还是回滚。其实就是调用数据库连接Connection.commit 和 Connection.rollback 方法。然后需要清空线程变量中的事务上下文信息。相当于结束了当前的事务。
关闭连接:
关闭连接相对比较简单,由于当前线程变量保存了连接信息,只需要获取连接之后,调用connection.close方法即可,接着清空线程变量的数据库连接信息。
上面几个流程是一个简单的事务处理流程,在spring中都有对应的实现,见TransactionTemplate.execute方法。Spring定义了一个TransactionSynchronizationManager对象,里面保存了各种线程变量信息,
//保存了数据源和其对应连接的映射,value是一个Map结构,其中key为datasource,value为其打开的连接 private static final ThreadLocal resources //这个暂时用不到,不解释 private static final ThreadLocal synchronizations //当前事务的名字 private static final ThreadLocal currentTransactionName //是否是只读事务以及事务的隔离级别(这个一般我们都用不到,都是默认界别) private static final ThreadLocal currentTransactionReadOnly private static final ThreadLocal currentTransactionIsolationLevel //代表是否是一个实际的事务活动,这个后面将) private static final ThreadLocal actualTransactionActive
在获取连接的时候,可见DataSourceUtils.doGetConnection()方法,就是从调用TransactionSynchronizationManager.getResource(dataSource)获取连接信息,如果为空,就直接从调用dataSource.getConnection()创建新的连接,后面在调用
TransactionSynchronizationManager.bindResource(dataSource,conn)绑定数据源到线程变量,以便后续的线程在使用。
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();
在提交事务的时候,见 DataSourceTransactionManager.doCommit方法,其实就是获取事务状态信息以及连接信息,调用conn.commmit方法,比较简单。
但是实际上,spring事务管理远远比上述复杂,我们没有考虑以下几种情况:
1 如果当前操作不需要事务支持,也就是每次执行一次,就自动进行提交。如何在同一个架构里面兼容这两种情况。比如就是简单的query操作。
2 一个业务活动跨越多个事务,每个事务的传播级别配置不一样。后面会拿一个例子来说明
对于第一个问题,比较好解决,首先就是根据线程变量里面获取数据源对应的连接,如果有连接,就复用。如果没有,就创建连接。在判断当前是否存在活动的事务上下文,如果存在事务信息,设置conn.setAutoCommit(false),然后设置线程上下文,绑定对应的数据源。如果不存在事务信息,就直接返回连接给应用。
这样就会带来一个新的问题,就是连接如何进行关闭。根据最开始的分析,在存在事务上下文的情况下,直接从获取线程获取对应的数据库连接,然后关闭。在关闭的也需要也进行判断一下即可。在spring里面,在事务中获取连接和关闭连接有一些特殊的处理,主要还是和其jdbc以及orm框架设计兼容。在jdbcTemplate,IbatiTemplate每执行一次sql操作,就需要获取conn,执行sql,关闭conn。如果不存在事务上下文,这样做没有任何问题,获取一次连接,使用完成,然后就是比。但是如果存在事务上下文,每次获取的conn并不一定是真实的物理连接,所以关闭的时候,也不能直接关闭这数据库连接。Spring的中定义一个ConnectionHandle对象,这个对象持有一个数据库连接对象,以及该连接上的引用次数(retain属性)。每次复用一次就retain++ 操作,没关闭一次,就执行retain-- 操作,在retain 为0的时候,说明没有任何连接,就可以进行真实的关闭了。
spring事务管理器设计思想(一)的更多相关文章
- spring事务管理器设计思想(二)
上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当前没有事务,就新建一个事务.这是最常见 ...
- spring事务管理器设计思想(2)
spring事务管理器设计思想(二) 上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当 ...
- 【面试】足够“忽悠”面试官的『Spring事务管理器』源码阅读梳理(建议珍藏)
PS:文章内容涉及源码,请耐心阅读. 理论实践,相辅相成 伟大领袖毛主席告诉我们实践出真知.这是无比正确的.但是也会很辛苦. 就像淘金一样,从大量沙子中淘出金子一定是一个无比艰辛的过程.但如果真能淘出 ...
- Spring事务管理器的应对
Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋.像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不太方便:而iBatis则通过模板化技术让你方便地控制SQ ...
- Spring事务管理器分类
Spring并不直接管理事务,事实上,它是提供事务的多方选择.你能委托事务的职责给一个特定的平台实现,比如用JTA或者是别的持久机制.Spring的事务管理器可以用下表表示: 事务管理器的实例 目标 ...
- spring事务管理器的源码和理解
原文出处: xieyu_zy 以前说了大多的原理,今天来说下spring的事务管理器的实现过程,顺带源码干货带上. 其实这个文章唯一的就是带着看看代码,但是前提你要懂得动态代理以及字节码增强方面的知识 ...
- Spring事务管理器
1.创建实体和接口 public class Bank { private Integer id; private String name; private String manay; public ...
- 阿里面试挂了,就因为面试官说我Spring 事务管理(器)不熟练?
前言 事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一.但除了八股文中需要熟读并背诵的那些个传播行为之外,背后的"为什么"和核心原理更为重要. 写这篇文章之前,我 ...
- 跟我学Spring3(9.2):Spring的事务之事务管理器
原文出处: 张开涛9.2.1 概述 Spring框架支持事务管理的核心是事务管理器抽象,对于不同的数据访问框架(如Hibernate)通过实现策略接口PlatformTransactionManage ...
随机推荐
- 移动端css知识总结--字体,毛玻璃效果,input和disabled
移动端字体使用: font-family: Helvetica,sans-serif;我看这也是天猫使用的 透过背景看其他元素模糊,自身元素不模糊:-webkit-backdrop-filter: s ...
- 转:Delphi2010新发现-类的构造和析构函数功能
Delphi2010发布了. 虽然凭着对Delphi的热爱第一时间就安装了,但是现在可能是年纪大了,对新事物缺乏兴趣了.一直都没有仔细研究. 今天有点时间试了一下新功能. 本来C#和Delphi.NE ...
- BZOJ2763 [JLOI2011]飞行路线(SPFA + DP)
题目 Source http://www.lydsy.com/JudgeOnline/problem.php?id=2763 Description Alice和Bob现在要乘飞机旅行,他们选择了一家 ...
- WPF 实现圆形进度条
项目中用到圆形进度条,首先就想到使用 ProgressBar 扩展一个,在园子里找到迷途的小榔头给出的思路和部分代码,自己加以实现. 进度小于60显示红色,大于60则显示绿色.效果如下: 基本思路: ...
- 【转】iOS开发 -- Apple Pay
技术博客原地址:http://www.cnblogs.com/dashunzi/p/ApplePay.html#top 原技术博客中有源码和视频,有感兴趣的朋友可以研究一下! 一.什么是Apple P ...
- servlet中session的使用
1.获取session HttpSession session=request.getSession(); session.setAttribute("variety", vari ...
- Linux内核笔记——内存管理之块内存分配
内核版本:linux-2.6.11 伙伴系统 伙伴系统是linux用于满足对不同大小块物理内存分配和释放请求的解决方案. 内存管理区 linux将物理内存分成三个内存管理区,分别为ZONE_DMA Z ...
- UVA - 11604 General Sultan 题解
题目大意: 有若干模式串,将某些模式串拼接起来(一个可以使用多次)形成一个长模式串,判断能否有两种或更多种不同的拼法拼成相同的模式串. 思路: 神奇的构图,暴力的求解. 可以发现,若有不同的拼法,则一 ...
- iOS tableview自定义cell上添加按钮实现删除功能
在删除的时候,先删除数据源,再删除cell 但是,会发现一直崩: numberOfRowsInSection 解决方案:
- 正则表达式在python中的应用
一.常用符号 . :匹配任意字符,换行符\n除外 * :匹配前一个字符0次或无限次 ? :匹配前一个字符0次货1次 .* :贪心算法 .*? :非贪心算法 () :括号内的数据作为结果返回 ...