对于第二个问题,涉及到事务的传播级别,定义以下:spring
PROPAGATION_REQUIRED-- 若是当前没有事务,就新建一个事务。这是最多见的选择。
PROPAGATION_SUPPORTS-- 若是当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY-- 若是当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,若是当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,若是当前存在事务,则抛出异常。 数据库
在开启事务以前,正常状况下须要作两个事情jvm
一:获取当前事务上下文信息分布式
二:获取将要开启事务的传播属性spa
根据以上两个信息,来判断程序的处理方式,具体方式以下:debug
而处理流程则是以下:设计
其中上图标中英文简称对应的事务传播属性以下:日志
RE: PROPAGATION_REQUIRED-- 若是当前没有事务,就新建一个事务。这是最多见的选择。 code
SPT PROPAGATION_SUPPORTS-- 若是当前没有事务,就以非事务方式执行。
MA: PROPAGATION_MANDATORY-- 若是当前没有事务,就抛出异常。
RE_NEW: PROPAGATION_REQUIRES_NEW--新建事务,若是当前存在事务,把当前事务挂起。
NOT_SPT: PROPAGATION_NOT_SUPPORTED--以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。
NEVER: PROPAGATION_NEVER--以非事务方式执行,若是当前存在事务,则抛出异常。
经过上面发现,只有新建立资源的时候,才会开启事务,在其余的状况下,只须要返回事务状态信息就能够了。其实这个状态信息,就是事务的上下文信息。
事务上下文
经过上面的分析,每次启动事务的时候,都会判断当前是否存在事务,要么抛出异常,不然都会创新事务上下文,可是对于数据源的处理方式则是不同的,这个要根据当前事务传播属性和新的事务传播属性共同决定。
事务上下文信息究竟是什么,这个彻底是能够自定义的,在spring中,主要是表现为TransactionStatus,也就是事务状态信息。里面保存了事务相关的信息,
//事务对象信息,使用普通数据源的话,是DataSourceTransactionObject对象,保存//了事务对应的链接信息 private final Object transaction; //是不是新开启的事务信息,只有调用了开启事务的方法,这个才为true private final boolean newTransaction; //这个是事务同步器,不是事务要关心的,spring在事务提交以前或者以后座的hook private final boolean newSynchronization; //是不是只读事务 private final boolean readOnly; //日志debug信息,彻底没有放在这里 private final boolean debug; //挂起的事务信息,若是没有,则为空 private final Object suspendedResources;
经过事务状态信息,就能够彻底知道当前事务的全部信息,包括事务的对应的数据源链接信息,是不是新建立的事务,是不是只读事务,以及以前挂起的事务信息。这些对事务管理器起来讲,都是必须的信息。可是我的以为也存在一些问题,首先spring这个事务状态信息有两个使用者,一个是spring自己事务管理器使用,另一个是应用程序接口。对应用程序接口暴露出来的状态信息以及内部使用的事务上下文信息应该隔离出来,避免应用程序人为的修改了事务上下文的属性信息。固然,能够用过接口的方式进行避免,可是若是知道实现原理的话,彻底能够经过强制转化为实现对象,从而破坏事务其余的信息致使程序异常。固然,正常状况下不太可能有人会如此无聊。
从上面的分析来看,spring的事务管理器(这里都特指DataSourceTransactionManager)主要的工做流程就是建立事务信息,绑定数据源,获取数据库链接,提交活回滚事务,释放数据库链接,解绑数据源。其实整个事务管理器作的事情无非就是这些。让应用者更关注业务逻辑,而不是复杂的事务管理。
DataSourceTransactionManager事务管理器自己实现了ResouceManager的功能,就是返回对应的其注册的datasrouce。这是一种一对一的映射关系,也就是说一个事务管理器只能注册一个数据源,不支持多数据源的管理。一旦事务管理器开启事务,就和具体的数据源绑定了,你只能经过其对应的数据源获取数据库链接。因此在事务上下文里面操做多个数据库,是不可能的。同时也只支持单一物理数据源,也就是说一个数据源只能返回同一个数据库链接,不支持在同一个事务里面经过同一个逻辑数据源跨越多个物理库操做。下面的操做想经过ProxyDataSource切换实际的数据源的方式没法实现的。
for(String dbName : dbNames){
DataSourceContextHolder.set(“dbName”);
doSomeThing();
DataSourceContextHolder.clear();
}
想要支持跨库的事务操做,能够经过如下几种方式操做:
1 使用JtaTransactionManager,经过jta服务提供商来实现跨库事务
2 改写ProxyDataSource,经过返回其本身实现的Connection来实现跨库的事务。简单的说,返回一个逻辑的Connection,这个connection自己持有多个物理connection
3 本身实现TransactionManager,能够注册多个资源管理器,本身对多个数据源进行管理。
事务上下文的扩展
正常情经过况下,事务上下文信息都是保存在内存之中,至关于只可以支持单个jvm。能够想象一下,假设事务管理器把事务上下文信息持久化,而且经过远程调用的方式,把事务上下文信息传递给另一个jvm,经过这样的设计思想,能够支持跨jvm间的事务一致性,也就是咱们所说的分布式系统的事务。固然,这只是一中简单的想法,具体的实现会至关复杂,须要考虑点也有不少。