如何覆写yii model 事务,添加自动事务控制

Spring中的事务控制学习中(转) - will - ITeye博客
博客分类:
事务管理(Transaction Management)是一个很深的研究方向,而本章最终目的是为了阐述spring的事务管理抽象的理念以及相关内容,所以,你大可放心,我不会为你准备一本类似于“砖头”的书籍, 但为了能够在整个讲解的过程中始终有一个平滑的过渡,有关事务(Transaction)的一些基本概念还是有必要简单介绍一下的。
1.1. 有关事务(Transaction)的楔子
1.1.1. 认识事务本身
为了说明“什么是事务”,我觉得先从事务所针对的目的说起,会比较容易切入。
对于一个软件系统来说,需要相应的数据资源(比如,数据库,文件系统等)来保存系统状态,在对系统状态所依托的数据资源进行访问的时候,为了保证系统始终处于一个“正确”的状态[], 我们就必须对这些访问操作进行一些必要的限定,以此来保证系统状态的完整性。
事务就是以可控的方式对数据资源进行访问的一组操作,为了保证事务执行前后数据资源所承载的系统状态始终处于“正确”状态,事务本身持有四个限定属性, 即原子性(Atomicity),一致性(Consistency),隔离性(Isolation)以及持久性(Durability),也就是常说的事务的ACID属性:
事务的原子性(Atomicity)
原子性要求事务所包含的全部操作是一个不可分割的整体,这些操作要么全部提交成功,要么只要其中一个操作失败,就全部“成仁”(“一颗老鼠屎搅坏一锅汤”好像形容这种情况比较贴切哦)。 如果把整个事务的操作比做“钢七连”,那我们的口号就得从“不抛弃,不放弃”改成“要么不抛弃,要么就全部放弃”了。
事务的一致性(Consistency)
一致性要求事务所包含的操作不能违反数据资源的一致性检查,数据资源在事务执行之前处于一个数据的一致性状态,那么,事务执行之后也需要依然保持数据间的一致性状态。 对于一个证券系统来说,如果顾客银行账户和证券账户资金总和为10万的话(银行账户初始8万,证券账户初始2万),从银行账户的8万转账5万到证券账户的事务操作结束之后, 银行账户会剩余3万,证券账户为7万,两个账户的总和依然是10万,如果事务操作结束后,整个数据状态不是这个样子,那么就说系统处于不一致状态,而使用事务其中一个目的就是为了避免这种不一致性状态的产生。
事务的隔离性(Isolation)[]
事务的隔离性主要规定了各个事务之间相互影响的程度。隔离性概念主要面向对数据资源的并发访问(Concurrency),并兼顾影响事务的一致性。当两个事务或者更多事务同时访问同一数据资源的时候, 不同的隔离级别决定了各个事务对该数据资源访问的不同行为。
不出意外的话,我们可以为事务指定四种类型的隔离级别,隔离程度按照从弱到强分别为“Read Uncommitted”,“Read Committed”,“Repeatable Read”和“Serializable”:
Read Uncommitted.
最低的隔离级别,Read Uncommitted最直接的效果就是一个事务可以读取另一个事务并未提交的更新结果。
Read Uncommitted是以较低的隔离度来寻求较高的性能,其本身无法避免以下几个问题:
脏读(Dirty Read). 如果一个事务中对数据进行了更新,但事务还没有提交,另一个事务可以“看到”该事务没有提交的更新结果,这样造成的问题就是,如果第一个事务回滚,那么,第二个事务在此之前所“看到”的数据就是一笔脏数据。
不可重复读取(Non-Repeatable Read).
不可重复读取是指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。如果事务1在事务2的更新操作之前读取一次数据,在事务2的更新操作之后再读取同一笔数据一次,两次结果是不同的,所以,Read Uncommitted也无法避免不可重复读取的问题。
幻读(Phantom Read)[].
幻读是指同样一笔查询在整个事务过程中多次执行后,查询所得的结果集是不一样的。幻读针对的是多笔记录。在Read Uncommitted隔离级别下, 不管事务2的插入操作是否提交,事务1在插入操作之前和之后执行相同的查询,取得的结果集是不同的,所以,Read Uncommitted同样无法避免幻读的问题。
Read Committed.
Read Committed通常是大部分数据库采用的默认隔离级别,它在Read Uncommitted隔离级别基础上所做的限定更进一步, 在该隔离级别下,一个事务的更新操作结果只有在该事务提交之后,另一个事务才可能读取到同一笔数据更新后的结果。 所以,Read Committed可以避免Read Uncommitted隔离级别下存在的脏读问题, 但,无法避免不可重复读取和幻读的问题。
Repeatable Read.
Repeatable Read隔离级别可以保证在整个事务的过程中,对同一笔数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,也不管其他事务对同一笔数据的更新提交与否。 Repeatable Read隔离级别避免了脏读和不可重复读取的问题,但无法避免幻读。
Serializable.
最为严格的隔离级别,所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别, 但同时也是性能最差的隔离级别,因为所有的事务在该隔离级别下都需要依次顺序执行,所以,并发度下降,吞吐量上不去,性能自然就下来了。 因为该隔离级别极大的影响系统性能,所以,很少场景会使用它。通常情况下,我们会使用其他隔离级别加上相应的并发锁的机制来控制对数据的访问,这样既保证了系统性能不会损失太大,也能够一定程度上保证数据的一致性。
对于数据库来说,通常都有一个默认的隔离级别,大多数情况下都是ReadCommitted,只有Hsqldb使用Read UnCommited作为默认隔离级别。 而且,并非所有的数据库都支持这四种隔离级别,比如Oracle只支持Read Committed和Serializable,如果你指定的隔离级别当前数据库不支持的话, 数据库会采用默认的隔离级别代替你指定的隔离级别。EJB,Spring,JDBC等数据访问方式都允许我们为事务指定以上提到的四种隔离级别,但最终事务是否以指定的隔离级别执行,则由底层的数据资源来决定。
不同的隔离级别设置会对系统的并发性以及数据一致性造成不同的影响,总的来说,隔离级别与系统并发性成反比,与数据一致性成正比。 也就是说,事务隔离度越高,系统并发性越差,进而造成系统性能就越差,不过,隔离度的增高,却可以更好地保证数据的一致性。隔离程度与并发性和一致性的关系如下图:
Figure 1.1. Isolation与Concurrency,Consistency之间的关系
在具体的实践过程中,我们需要根据系统的具体情况来调整隔离度以保证系统性能与数据一致性之间有一个良好的平衡,但总的来说,保证数据的一致性的考虑应该优于对系统性能的考虑。
事务的持久性(Durability)
事务的持久性是指一旦整个事务操作成功提交完成,对数据所做的变更将被记载并不可逆转,多少有点儿“生米煮成熟饭”的意思。 即使发生某些系统灾难或者什么天灾人祸之类的事情,之前事务所做的变更也可以找回并恢复。纵使海枯石烂,我(数据库等资源管理系统)对你(事务)的真心永不变! 通常情况下,数据库等数据资源管理系统会通过冗余存储或者多数据网络备份等方式来保证事务的持久性。
至此,对事务自身的认识就算告一段落了,接着,让我们进一步认识一下与事务有关的“家族成员”吧!
1.1.2. 初识事务家族成员
在一个典型的事务处理场景中,有以下几个参与者:
Resource Manager(RM)
ResourceManager简称RM,它负责存储并管理系统数据资源的状态,比如数据库服务器,JMS消息服务器等都是相应的Resource Manager。
Transaction Processing Monitor(TP Monitor)
Transaction Processing Monitor简称TPM或者TP Monitor,它的职责是在分布式事务场景中协调包含多个RM的事务处理。TP Monitor通常对应特定的软件中间件(Middleware), 随着软件开发技术的进步,TP Monitor的实现也由原来基于过程式的设计与实现转向面向对象的更趋模块化的设计和实现。J2EE规范[]中的应用服务器(Application Server)通常担当的就是TP Monitor的角色。
Transaction Manager(TM)
Transaction Manager简称为TM,它可以认为是TP Monitor中的核心模块,直接负责多RM之间的事务处理的协调工作,并且提供事务界定(Transaction Demarcation)[], 事务上下文传播(transaction context propagation)[]等功能接口。
Application
以独立形式存在的或者运行于容器中的应用程序,可以认为是事务边界的触发点。
实际上,并非每一个事务的场景中都会出现以上提到的所有参与者,如果我们根据整个事务中牵扯的RM的多寡来区分事务类型的话,可以将事务分为两类,即全局事务(Global Transaction)和局部事务(Local Transaction),在这两类事务中,具体的事务参与者是不同的:
全局事务(Global Transaction).
如果整个事务处理过程中有多个Resource Manager的参与,那么就需要引入TP Monitor来协调多个RM之间的事务处理,TP Monitor将采用“两阶段提交(2 Phase Commit)”协议来保证整个事务的ACID属性, 这种场景下的事务我们就称其为全局事务(Global Transaction)或者分布式事务(Distributed Transaction)。全局事务中各个参与者之间的关系如下图所示:
Figure 1.2. 全局事务示意图
所有应用程序提交的事务请求需要通过TP Monitor的调配之后,直接由TM统一协调,TM将使用“两阶段提交(two-phase commit)”协议来协调处理多RM之间的事务处理。 针对“两阶段提交”的描述,最经典的比喻就是西方婚礼的过程,婚礼的牧师或者主持是TM,他会首先询问两位新人(两个RM),是否愿意娶对方为妻(嫁给对方), 如果双方的反馈都是“I do”的时候,牧师将宣布二者结为夫妻(即整个事务提交成功)。如果双方任何一方有疑议,那不好意思,婚礼无法继续进行,整个事务提交失败,双方都要回滚(rollback)到之前的单身状态。
局部事务(Local Transaction).
如果当前事务只有一个Resource Manager参与其中的话,我们就可以称当前事务为局部事务(Local Transaction)。 比如,你在当前事务中只对一个数据库进行更新,或者只向一个消息队列中发送消息的情况,都属于局部事务。
Figure 1.3. 局部事务示意图
因为局部事务只包含一个Resource manager,所以,也就没有必要引入相应的TP Monitor来帮助协调管理多个Resource Manager之间的事务, 应用程序可以直接与RM打交道。通常情况下,相应的RM都有内置的事务支持,所以,在局部事务中,我们更倾向于直接使用RM的内置事务支持, 这样不仅可以极大的减少事务处理的复杂度,也避免了引入TP Monitor来协调多个Resource Manager之间事务的性能负担。
局部事务与全局事务的主要区分在于“事务”中牵扯多少RM,而不是“系统”中实际有多少RM,这是需要我们注意的地方。 即使你系统中存在多个数据库(即RM),只要你当前事务只更新一个数据库的数据,那当前事务就依然应该算作局部事务,而不是全局事务(虽然这种情况下,你也可以启用全局事务)。
实际上,针对单一事务资源的事务管理,你可以在局部事务中直接使用RM内置的事务支持来进行,你也可以引入TP Monitor在分布式事务场景中进行,通常情况下, 各TP Monitor在实现的时候会检测参与事务的RM数目,如果只有单一的RM参与,TP Monitor会做一定的优化,避免采用“两阶段提交”协议的负担, 但即使如此,针对单一事务资源参与的事务,直接采用局部事务中RM内置的事务支持,无论是从复杂度,还是从效率上来看,都要更胜一筹。
到此为止,我们所阐述的都是概念层面的东西,要真正的在系统开发中使用事务,我们需要相应的产品和API支持。 为了让事务“走进现实”,不同的组织或者不同的技术平台会有各自不同的API设计和实现, 但既然Spring(不是Spring .Net)归属于Java一族,其他的平台和组织的解决方案暂且放于一边,我们专门来看Java平台的事务解决方案如何?
1.2. 群雄逐鹿下的Java事务管理
对于应用程序的开发人员来说,更多时候,我们是通过相应产品提供的API接口来访问事务资源,考虑如何在应用的业务逻辑中界定事务边界,而对于各提供商如何在产品中实现事务支持, 则通常不是我们需要关心的问题。所以,以下内容将更多的围绕着各事务处理场景下我们可以通过哪些产品提供的事务处理接口或者标准的事务处理接口来进行事务控制为主线进行。 当然,期间我们也可能提及相应场景下比较受欢迎的几款事务处理的产品实现。
下面我们将按照从局部事务场景到全局事务场景的顺序来看一下,各场景中Java平台为我们都准备了哪些可用的事务处理API。
1.2.1. Java平台的局部事务支持
在Java的局部事务场景中,系统中事务管理的具体处理方式会随着所使用的数据访问技术的不同而各异,我们不是使用专用的事务API来管理事务, 而是通过当前使用的数据访问技术所提供的基于“connection” [] 的API来管理事务。
数据库资源的局部事务管理.
要在对数据库的访问过程中进行事务管理,每一种数据访问技术都提供了特定于它自身的事务管理API,比如JDBC是Java平台访问关系数据库最基础的API,如果直接使用JDBC进行数据访问的话,我们可以将数据库连接(java.sql.Connection)的自动提交(AutoCommit)功能设置为false,改为手动提交来控制整个事务的提交或者回滚:
Connection connection =
boolean rollback =
connection = dataSource.getConnection();
connection.setAutoCommit(false);
// do data access with JDBC
catch(SQLException e)
e.printStackTrace(); // don't do this
rollback =
if(connection != null)
if(rollback)
connection.rollback();
} catch (SQLException e) {
e.printStackTrace(); // don't do this
connection.close();
} catch (SQLException e) {
e.printStackTrace(); // don't do this
而如果我们使用Hibernate进行数据访问,我们就得使用Hibernate的Session进行数据访问期间的事务管理[]:
Session session =
Transaction transaction =
session = sessionFactory.openSession();
transaction = session.beginTransaction();
// do data access with Hibernate
session.flush();
catch(HibernateException e)
transaction.rollback();
session.close();
同样的,如果我们使用JDO,TopLink甚至JPA进行数据访问的话,这些数据访问技术也都在他们数据访问API之上提供了相应的事务管理支持。
消息服务资源的局部事务管理.
在使用JMS进行消息处理的过程中,我们可以通过JMS的javax.jms.Session来控制整个处理过程的事务:
boolean rollback =
Connection
Session session =
con = cf.createConnection();
session = con.createSession(true, Session.AUTO_ACKNOWLEDGE);
// process Messages with JMS API
catch (JMSException e) {
e.printStackTrace();// don't do this
rollback =
if(con != null)
if(rollback)
session.rollback();
} catch (JMSException e1) {
e1.printStackTrace(); // don't do this
con.close();
} catch (JMSException e) {
e.printStackTrace(); // don't do this
我们在通过javax.jms.Connection的createSession方法创建javax.jms.Session的时候, 将该方法的第一个参数指定为true要求创建一个事务型的javax.jms.Session实例,然后就可以根据情况提交和回滚(rollback)事务了。
以上两种情况之外的时候,我们可能需要借助于Java Connector Architecture(JCA)来管理局部事务,JCA允许通过javax.resource.spi.LocalTransaction接口暴露局部事务控制接口。 但是,JCA只能与JavaEE的应用服务器集成,所以,通过JCA访问事务资源的应用程序需要绑定到相应的JavaEE服务器。
1.2.2. Java平台的分布式事务支持
Java平台上的分布式事务管理主要是通过Java Transaction API(JTA)或者Java Connector Architecture(JCA)提供支持的。
1.2.2.1. 基于JTA的分布式事务管理
JTA是Sun提出的标准化分布式事务访问的Java接口规范。不过,JTA规范定义的只是一套Java接口定义,具体的实现留给了相应的提供商去实现,各JavaEE应用服务器需要提供对JTA的支持, 另外,除了可以使用绑定到各JavaEE应用服务器的JTA实现之外,Java平台上也存在几个独立的并且比较成熟的JTA实现产品,这包括:
(http://jotm.objectweb.org/index.html)
Atomikos(/home.html)
(/jbosstm/)
使用JTA进行分布式事务管理通常有两种方式,直接使用JTA接口的编程事务管理以及基于应用服务器的声明性事务管理。
1.2.2.1.1. JTA编程事务管理
使用JTA进行分布式事务的编程式事务管理通常使用javax.transaction.UserTransaction接口进行,各应用服务器都提供了针对它的JNDI查找服务。
下面是典型的使用UserTransaction进行事务管理的代码片断:
UserTransaction ut = (UserTransaction)ctx.lookup("javax.transaction.UserTransaction");
ut.begin();
// 事务操作
ut.commit();
} catch (NamingException e) {
e.printStackTrace();
} catch (NotSupportedException e) {
e.printStackTrace();
} catch (SystemException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (RollbackException e) {
e.printStackTrace();
} catch (HeuristicMixedException e) {
e.printStackTrace();
} catch (HeuristicRollbackException e) {
e.printStackTrace();
如果是在EJB中使用UserTransaction的话,你可以直接通过javax.ejb.EJBContext获得UserTransaction的引用:
UserTransaction ut = ctx.getUserTransaction();
在EJB中直接编程使用UserTransaction主要是BMT(Bean Managed Transaction)的情况下, 不过,在EJB中使用JTA进行分布式管理有比直接使用编程方式更吸引人的方式,那就是在CMT(Container Managed Transacton)情况下,EJB容器提供的声明性的事务管理。
1.2.2.1.2. JTA声明性事务管理
如果使用EJB进行声明性的分布式事务管理的话(限于CMT的情况),JTA的使用则只限于EJB容器的内部,对于应用程序来说则完全就是透明的, 现在唯一需要做的工作实际上就是在相应的部署描述符中指定相应的事务属性即可:
&enterprise-beans&
&display-name&Your Enterprise Java Bean&/display-name&
&ejb-name&YourBean&/ejb-name&
&home&...&/home&
&remote&...&/remote&
&ejb-class&...YourBean&/ejb-class&
&session-type&Stateless&/session-type&
&transaction-type&Container&/transaction-type&
&/session&
&/enterprise-beans&
&assembly-descriptor&
&container-transaction&
&ejb-name&YourBean&/ejb-name&
&method-name&*&/method-name&
&trans-attribute&Required&/trans-attribute&
&/container-transaction&
&/assembly-descriptor&
&transaction-type&指定了我们让EJB容器来管理的事务,并且,&trans-attribute&规定所有方法执行都需要相应事务, 现在,应用程序内部再也不用充斥着各种事务管理的代码了。
1.2.2.2. 基于JCA的分布式事务管理
JCA规范主要面向Enterprise Information System(EIS)的集成,通过为遗留的EIS系统和JavaEE应用服务器指定统一的通信标准, 二者就可以实现各种服务上的互通。
当应用服务器通过JCA将一些EIS系统集成进来之后,我们就可以让EIS系统中的事务资源也加入到JavaEE应用的全局事务中来。 实际上,要在应用程序中控制这种跨越多个系统的分布式事务,我们最终还是通过JTA来进行的,JCA更多的是提供资源的集成, 所以,从这一点儿来说,在Java平台上管理分布式事务,JTA是唯一标准接口。
1.2.3. 继续前行之前的反思
你也看到了,Java平台提供的事务管理API足够丰富,可谓高中低档一应俱全,这当然有助于我们根据合适场景选用合适的事务管理API, 但,在实际使用过程中,过多的事务管理策略的选择也会造成一些问题:
局部事务的管理绑定到了具体的数据访问方式
使用JDBC进行数据访问,你需要通过java.sql.Connection来控制局部事务; 使用Hibernate进行数据访问,你需要通过org.hibernate.Session和org.hibernate.Transaction来管理局部事务, 至于其他的数据访问方式,自然也是要使用它们特定的数据访问API来控制局部事务。
这样直接导致的问题就是事务管理代码与数据访问代码甚至业务逻辑代码混杂,因为局部事务场景下,我们使用的是数据访问API进行事务控制。 如果实际使用中不能通过合适的方式对事物管理的代码与数据访问代码或者业务逻辑代码进行逻辑上的隔离, 将直接导致数据访问代码和业务逻辑代码的可重用性降低,甚至事务管理代码在数据访问层和业务服务层的到处散落。
当前的情况是,各种数据访问方式只提供了简单的事务API,但没有更高层次的抽象来帮助我们隔离事务与数据访问两个方面的过紧耦合。
事务的异常处理
事务处理过程中出现的异常应该都是不可恢复的,所以,应该抛出“unchecked exception”,并且有一个统一的父类,便于客户端处理。 但是现在的情况是:
没有一个统一的事务相关异常体系,使用特定API管理事务的时候,你就需要捕捉这些 API特定的异常并处理;
许多事务管理代码在使用过程中抛出的依然还是“checked exception”强制客户端代码来捕捉并处理, 从UserTransaction的使用上你就可以看出来, 从JNDI查找到事务的开始和结束等操作,简单的事务界定操作,却引出七八个异常需要处理,任何一个人在使用UserTransaction进行编程式事务管理的时候也不会认为这样的API设计很好用吧?!
事务处理API的多样性
对于开发人员来说,所谓对事务的管理,最多也就是界定一下事务的边界,规定事务什么地方开始,什么地方结束,可是,要达到这一个目的,你却要在各种数据访问API或者JTA之间徘徊。
各种事务管理API的存在给了我们更多选择,但没有一个统一的方式来抽象单一的事务管理需求,反而让这种多种选择的优势变得繁杂而不易管理。
CMT声明式事务的局限
EJB容器提供的CMT特性是比较受欢迎的事务界定管理方式,因为业务对象中不需要混杂任何事务管理代码,所有的事务管理都通过一些简单的配置交由容器来管理。 CMT提供的声明式的事务管理很好的分离了事务管理与具体的数据资源访问之间的耦合,使得开发人员能够分别专心于业务逻辑和数据访问逻辑的开发。
但CMT形式的声明式事务管理有一个令人惋惜的限制,那就是,你必须借助于EJB容器才能得到这种声明式事务的好处。 如果你的应用程序想要使用声明式的事务管理,却不得不花费不少银子来购买应用服务器厂商的授权,或者,即使是使用开源的应用服务器, 但你的应用程序在此之前并没有特别强烈的必须应用服务器的需求,这些情况下,引入应用服务器的做法应该不太容易令人接受吧?
总之,要使用CMT的声明式事务管理,强制要求引入EJB容器的支持(当然,你也得以EJB的形式提供组件实现),某些情况下是不合适的。
鉴于这些问题,我们应该考虑对目前的状况进行改进,以便简化开发过程,提高整个过程中的开发效率:
我们能否对众多的基于数据访问API的局部事务操作进行一个合理的抽象,以便隔离事务管理与数据资源访问之间的过分耦合?
能否在合适的地方将各种场景下事务处理抛出的“checked exception”进行一个合适的转译,以屏蔽事务处理过程中因使用不同的事务API所造成的差异性?
既然对于我们来说,事务管理的需求很简单,基本上就是事物界定的工作,那我们能否对事务的界定操作进行统一抽象,以屏蔽各种事务管理API的差异性,使得事务管理能够以统一的编程模型来进行?
既然声明式的事务管理如此诱人,那么能否突破必须依赖EJB容器的限制,寻求一种能够为普通的Java对象(POJO)提供声明式事务的方式那?
如果这些问题也是你在考虑的话,那么,恭喜你,你不用从头去探索这些问题的解决方法了,因为spring的事务抽象框架正是我们所要寻找的解决方案。
1.3. 一统河山后的Spring事务管理
spring的事务框架将开发过程中事务管理相关的关注点进行适当的分离,并对这些关注点进行合理的抽象,最终打造了一套使用方便却功能强大的事务管理“利器”。 通过spring的事务框架,我们可以按照统一的编程模型来进行事务编程,却不用关心所使用的数据访问技术以及具体要访问什么类型的事务资源; 并且,spring的事务框架与spring提供的数据访问支持可以紧密结合,更是让你在事务管理与数据访问之间游刃有余,而最主要的,结合spring的AOP框架, spring的事务框架为我们带来了只有CMT才有的使用声明式事务管理的待遇,却无需绑定到任何的应用服务器上。
其他溢美之词咱就先放一边,还是赶快进入正题吧!
1.3.1. spring事务王国的架构
spring的事务框架设计理念的基本原则在于:让事务管理的关注点与数据访问关注点相分离:
当你在业务层使用事务的抽象API进行事务界定的时候,你不需要关心事务将要加诸于上的事务资源是什么,对不同的事务资源的管理将由相应的框架实现类来操心;
当你在数据访问层对可能参与事务的数据资源进行访问的时候,只需要使用相应的数据访问API进行数据访问,却不需要关心当前的事务资源如何参与事务或者是否需要参与事务,这同样将由事务框架类来打理;
当以上两个关注点被清晰的分离出来之后,对于我们开发人员来说,唯一需要关心的,就只是通过抽象后的事务管理API对当前事务进行界定而已:
public class FooService
private PlatformTransactionManager transactionM
public void serviceMethod()
TransactionDefinition definition = ...;
TransactionStatus txStatus = getTransactionManager().getTransaction(definition);
// dao1.doDataAccess();
// dao2.doDataAccess();
catch(DataAccessException e)
getTransactionManager().rollback(txStatus);
catch(OtherNecessaryException e)
getTransactionManager().rollback(txStatus);
getTransactionManager().commit(txStatus);
public PlatformTransactionManager getTransactionManager() {
return transactionM
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionM
纵使你数据访问方式如何变换,我事务管理实现可以岿然不动。只要有了这样一个统一的事务管理编程模型,剩下的声明式事务管理自然就是锦上添花之作啦! 从此之后,事务管理就是事务管理,数据访问只关心数据访问,再也不用因为他们之间的纠缠而烦恼。
1.3.1.1. 统一中原的过程
org.springframework.transaction.PlatformTransactionManager是spring事务抽象架构的核心接口,他的主要作用在于为应用程序提供事务界定的统一方式。 既然事务界定的需要很简单,那么PlatformTransactionManager的定义看起来也不会过于复杂:
public interface PlatformTransactionManager
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionE
void commit(TransactionStatus status) throws TransactionE
void rollback(TransactionStatus status) throws TransactionE
PlatformTransactionManager是整个事务抽象策略的顶层接口,它就好象我们的战略蓝图,而战略的具体实施则将由相应的PlatformTransactionManager实现类来执行。
spring的事务框架针对不同的数据访问方式以及全局事务场景,提供了相应的PlatformTransactionManager实现类,当每一个实现类的职责完成之后,spring事务框架的“统一大业”就算完成了。 在深入了解各个PlatformTransactionManager实现类的奥秘之前,我们不妨先考虑一下,如果让我们来实现一个PlatformTransactionManager,要如何去做那?
不妨先以针对JDBC数据访问方式的局部事务管理为例。对于层次划分清晰的应用来说,我们通常都是将事务管理放在Service层,而将数据访问逻辑放在DAO层,这样做的目的在于可以不用因为将事务管理代码放在DAO层而降低数据访问逻辑的重用性, 也可以在Service层根据相应逻辑来决定提交(commit)或者回滚(rollback)事务。一般的Service对象可能需要在同一个业务方法中调用多个数据访问对象的方法,类似于这样的情况:
Figure 1.4. 普通情况下的事务管理代码
因为JDBC的局部事务控制是由同一个java.sql.Connection来控制的,所以,要保证两个DAO的数据访问方法处于一个事务中,我们就得保证他们使用的是同一个java.sql.Connection,要做到这一点, 通常采用称之为“connection passing”的方式,即为同一个事务中的各个dao的数据访问方法传递当前事务对应的同一个java.sql.Connection,这样,我们的业务方法以及数据访问方法都得做一定的修改:
Figure 1.5. connection-passing方式的事务管理代码
我们只要把java.sql.Connection的获取并设置autoCommit状态的代码以及使用java.sql.Connection提交事务的代码重构到原来的开启事务以及提交事务的方法中,针对JDBC的局部事务管理的整合看起来离成功也就是咫尺之遥了。 不过,这看起来的咫尺之遥,实际上却依然遥远。
使用这种方式,最致命的一个问题就在于,不但事务管理代码无法摆脱java.sql.Connection的纠缠,而且数据访问对象的定义要绑定到具体的数据访问技术上来。 现在是使用JDBC进行数据访问,你要在数据访问方法中声明对java.sql.Connection的依赖,那要是使用HIbernate的话,是不是要声明对Session的依赖那?显然,这样的做法是不可行的。 不过好消息是,传递Connection的理念是对的,只不过,我们具体实施过程中所采用的方法不对头。
要传递java.sql.Connection,我们可以将整个事务对应的java.sql.Connection实例放到统一的一个地方去,无论是谁,要使用该资源,都从这一个地方来获取,这样就解除了事务管理代码和数据访问代码之间通过java.sql.Connection的“直接”耦合。 具体一点儿说就是,我们在事务开始之前取得一个java.sql.Connection,然后将这个Connection绑定到当前的调用线程,之后,数据访问对象在使用Connection进行数据访问的时候,就可以从当前线程上去获得这个事务开始的时候绑定的Connection实例, 当所有的数据访问对象全部使用这个绑定到当前线程的Connection完成了数据访问工作,我们就使用这个Connection实例提交或者回滚事务,然后解除它到当前线程的绑定。
Figure 1.6. java.sql.Connection绑定到线程示意图
这个时候的java.sql.Connection就像那大河上的一条船,从启航(事务开始)到航程结束(事务完成),在这整个期间,大河沿岸都可以与该船打交道,而至于说你是发“木船”&还是发“铁轮”, 那由你来决定了,发JDBC的船,那就是Connection,发Hibernate的船,那就是Session...
假设TransactionResourceManager就是我们存放java.sql.Connection(或者其他事务资源)的地方,那么,它看起来可能的样子如下(过多的逻辑检验代码略去):
public class TransactionResourceManager
private static ThreadLocal resources = new ThreadLocal();
public static Object getResource()
return resources.get();
public static void bindResource(Object resource)
resources.set(resource);
public static Object unbindResource()
Object res = getResource();
resources.set(null);
对于我们要实现的针对JDBC的PlatformTransactionManager,只需要在事务开始将java.sql.Connection通过我们的TransactionResourceManager绑定,然后在事务结束解除绑定即可:
public class JdbcTransactionManager implements PlatformTransactionManager
private DataSource dataS
public JdbcTransactionManager(DataSource dataSource)
this.dataSource = dataS
public TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException {
connection = dataSource.getConnection();
TransactionResourceManager.bindResource(connection);
return new DefaultTransactionStatus(connection,true,true,false,true,null);
catch (SQLException e)
throw new CannotCreateTransactionException("can't get connection for tx",e);
public void rollback(TransactionStatus txStatus) throws TransactionException {
Connection connection = (Connection)TransactionResourceManager.unbindResource();
connection.rollback();
catch (SQLException e)
throw new UnexpectedRollbackException("rollback failed with SQLException",e);
connection.close();
} catch (SQLException e) {
// log information but can do nothing
public void commit(TransactionStatus txStatus) throws TransactionException {
Connection connection = (Connection)TransactionResourceManager.unbindResource();
catch (SQLException e)
throw new TransactionSystemException("commit failed with SQLException",e);
connection.close();
} catch (SQLException e) {
// log information but can do nothing
因为Connection在事务开始和结束期间都可以通过我们的TransactionResourceManager获得,所以,所有的DAO层数据访问对象在使用JDBC进行数据访问的时候,就可以直接从TransactionResourceManager来获得数据库连接并进行数据访问,这样就可以保证在整个事务期间,所有的数据访问对象对应的同一个Connection。
public class FooJdbcDao implements IDao
public void doDataAccess()
Connection con = (Connection)TransactionResourceManager.getResource();
至此,我们完成了PlatformTransactionManager具体实现类并解除了它与相应数据访问对象之间通过java.sql.Connection的直接耦合,进行事务控制的时候,我们只需要为Service对象提供相应的PlatformTransactionManager实现类, Service对象中的事务管理功能就算大功告成了,而不需要关心到底对应的是什么样的事务资源,甚至什么样的数据访问方式。
当然,为了便于读者理解spring抽象层的实现原理,以上的代码实例都是简化后的模型,所以,不要试图将他们应用于生产环境。 原型代码永远都是原型代码,要做的事情还有许多,比如:
“如何保证PlatformTransactionManager的相应方法被以正确的顺序调用,如果哪一个方法没有被正确调用,也会造成资源泄漏以及事务管理代码混乱的问题。”
在稍后为读者介绍使用spring进行编程事务管理的时候,你将看到spring是如何解决这个问题的。
“数据访问对象的接口定义不会因为最初的‘connection passing’方式而改变契约了,但是,现在却要强制使用每个数据访问对象使用TransactionResourceManager来获取数据资源接口, 另外,如果当前数据访问对象对应的数据方法不想参与跨越多个数据操作的事务的时候,甚至于不想(或不能)使用类似的事务管理支持,是否就意味着无法获得connection进行数据访问了那? ”
不知道你是否还记得我们在介绍spring的数据访问一章内容的时候,曾经提到的org.springframework.jdbc.datasource.DataSourceUtils工具类, 当时我们只是强调了DataSourceUtils提供的异常转译能力,实际上,DataSourceUtils最主要工作却在于对connection的管理,DataSourceUtils会从类似TransactionResourceManager的类(spring中对应org.springframework.transaction.support.TransactionSynchronizationManager)那里 获取Connection资源,如果当前线程之前没有绑定任何connection,他就通过数据访问对象的DataSource引用获取新的connection,否则就使用绑定的那个connection。 这就是为什么要强调,当我们要使用spring提供的事务支持的时候,必须通过DataSourceUtils来获取连接的原因,因为它提供了spring事务管理框架在数据访问层需要提供的基础设施中不可或缺的一部分,而JdbcTemplate等类内部已经使用DataSourceUtils来管理连接了,所以,我们不用操心这些细节。 从这里,你也应可以看出,spring的事务管理与它的数据访问框架是紧密结合的。
对应Hibernate的SessionFactoryUtils,对应JDO的PersistenceManagerFactoryUtils以及对应其他数据访问技术的Utils类, 他们的作用与DataSourceUtils是相似的,除了提供异常转译功能,他们更多的用于数据访问资源的管理工作,以配合对应的PlatformTransactionManager实现类进行事务管理。
实际上,spring在实现针对各种数据访问技术的PlatformTransactionManager的时候要考虑很多的东西,不像原型以及提出的几个问题所展示的那么简单, 不过,各个实现类的基本思路与原型所展示的是基本吻合的,当我们了解了针对Jdbc的PlatformTransactionManager是如何实现的时候,其他的实现类基本上就是平推了。
1.3.1.2. 和平年代
spring的事务抽象包括三个主要接口,即PlatformTransactionManager,TransactionDefinition以及TransactionStatus,他们之间的关系如下:
Figure 1.7. spring事务抽象接口关系图
三接口以org.springframework.transaction.PlatformTransactionManager为中心,互为犄角,多少有点儿“晋西北铁三角”的味道。 org.springframework.transaction.PlatformTransactionManager负责界定事务边界,org.springframework.transaction.TransactionDefinition负责定义事务相关属性,包括隔离级别,传播行为等, org.springframework.transaction.PlatformTransactionManager将参照org.springframework.transaction.TransactionDefinition的属性定义来开启相关事务,事务开启之后到事务结束期间的事务状态由org.springframework.transaction.TransactionStatus负责, 我们也可以通过org.springframework.transaction.TransactionStatus对事务进行有限的控制。
1.3.1.2.1. TransactionDefinition
1.3.1.2.1.1. TransactionDefinition简介
org.springframework.transaction.TransactionDefinition主要定义了有哪些事务属性可以指定,这包括:
事务的隔离级别(Isolation)
事务的传播行为(Propagation Behavisor)
事务的超时时间(Timeout)
是否为只读事务(ReadOnly)
TransactionDefinition内定义了五个常量用于标志可供选择的隔离级别:
ISOLATION_DEFAULT. 如果指定隔离级别为ISOLATION_DEFAULT,则表示使用数据库默认的隔离级别,通常情况下是“read committed”;
ISOLATION_READ_UNCOMMITTED. 对应“Read Uncommitted”隔离级别,无法避免脏读,不可重复读和幻读;
ISOLATION_READ_COMMITTED. 对应“Read Committed”隔离级别,可以避免脏读,但无法避免不可重复读和幻读;
ISOLATION_REPEATABLE_READ. 对应“Repeatable read”隔离级别,可以避免脏读和不可重复读,但不能避免幻读;
ISOLATION_SERIALIZABLE. 对应“Serializable”隔离级别,可以避免所有的脏读,不可重复读以及幻读,但并发性效率最低;
事务的传播行为(Propagation Behavisor)我们之前没有提到,不过,如果你接触过EJB的CMT的话,对它应该也不会陌生。 事务的传播行为(Propagation Behavisor)表示整个事务处理过程所跨越的业务对象将以什么样的行为参与事务(我们将在声明式事务中更多的依赖于该属性)。 比如,当有FoobarService调用FooService和BarService两个方法的时候,FooService的业务方法和BarService的业务方法可以指定它们各自的事务传播行为:
Figure 1.8. 业务方法的传播行为
FooService的业务方法的传播行为被我们指定为Required,表示如果当前存在事务的话,则加入当前事务,因为FoobarService在调用FooService的业务方法的时候已经启动了一个事务,所以,FooSerivce的业务方法会直接加入FoobarService启动的“事务1”中; BarService的业务方法的传播行为被指定为Required New,表示无论当前是否存在事务,都需要为其重新启动一个事务,所以,它使用的是自己启动的“事务2”。
TransactionDefinition针对事务的传播行为提供了以下几种选择,除了PROPAGATION_NESTED是spring特有的外,其他的传播行为的语义与CMT基本相同:
PROPAGATION_REQUIRED.
如果当前存在一个事务,则加入当前事务;如果不存在任何事务,则创建一个新的事务。总之,要至少保证在一个事务中运行。PROPAGATION_REQUIRED通常作为默认的事务传播行为。
PROPAGATION_SUPPORTS.
如果当前存在一个事务,则加入当前事务;如果当前不存在事务,则直接执行。 对于一些查询方法来说,PROPAGATION_SUPPORTS通常是比较合适的传播行为选择。 如果当前方法直接执行,那么不需要事务的支持;如果当前方法被其他方法调用,而其他方法启动了一个事务的时候,使用PROPAGATION_SUPPORTS可以保证当前方法能够加入当前事务并洞察当前事务对数据资源所做的更新。 比如说,A.service()会首先更新数据库,然后调用B.service()进行查询,那么,B.service()如果是PROPAGATION_SUPPORTS的传播行为, 就可以读取A.service()之前所做的最新更新结果,而如果使用稍后所提到的PROPAGATION_NOT_SUPPORTED,则B.service()将无法读取最新的更新结果,因为A.service()的事务在这个时候还没有提交(除非隔离级别是read uncommitted):
Figure 1.9. PROPAGATION_SUPPORTS可能场景
PROPAGATION_MANDATORY.
PROPAGATION_MANDATORY强制要求当前存在一个事务,如果不存在,则抛出异常。 如果某个方法需要事务支持,但自身又不管理事务提交或者回滚的时候,比较适合使用PROPAGATION_MANDATORY。 你可以参照《JAVA TRANSACTION DESIGN STRATEGIES》一书中对REQUIRED和MANDATORY两种传播行为的比较来更深入的了解PROPAGATION_MANDATORY的可能应用场景。
PROPAGATION_REQUIRES_NEW.
不管当前是否存在事务,都会创建新的事务。如果当前存在事务的话,会将当前的事务挂起(suspend)。 如果某个业务对象所做的事情不想影响到外层事务的话,PROPAGATION_REQUIRES_NEW应该是合适的选择,比如,假设当前的业务方法需要向数据库中更新某些日志信息, 但即使这些日志信息更新失败,我们也不想因为该业务方法的事务回滚而影响到外层事务的成功提交,因为这种情况下,当前业务方法的事务成功与否对外层事务来说是无关紧要的。
PROPAGATION_NOT_SUPPORTED.
不支持当前事务,而是在没有事务的情况下执行。如果当前存在事务的话,当前事务原则上将被挂起(suspend),但要依赖于对应的PlatformTransactionManager实现类是否支持事务的挂起(suspend),更多情况请参照TransactionDefinition的javadoc文档。 PROPAGATION_NOT_SUPPORTED与PROPAGATION_SUPPORTS之间的区别,可以参照PROPAGATION_SUPPORTS部分的实例内容。
PROPAGATION_NEVER.
永远不需要当前存在事务,如果存在当前事务,则抛出异常。
PROPAGATION_NESTED.
如果存在当前事务,则在当前事务的一个嵌套事务中执行,否则与PROPAGATION_REQUIRED的行为类似,即创建新的事务,在新创建的事务中执行。 PROPAGATION_NESTED粗看起来好像与PROPAGATION_REQUIRES_NEW的行为类似,实际上二者是有差别的。 PROPAGATION_REQUIRES_NEW创建的新事务与外层事务属于同一个“档次”,即二者的地位是相同的,当新创建的事务运行的时候,外层事务将被暂时挂起(suspend); 而PROPAGATION_NESTED创建的嵌套事务则不然,它是寄生于当前外层事务的,它的地位比当前外层事务的地位要小一号,当内部嵌套事务运行的时候,外层事务也是出于active状态:
Figure 1.10. PROPAGATION_REQUIRES_NEW与PROPAGATION_NESTED创建的事务的区别
也就是说,PROPAGATION_REQUIRES_NEW新创建的事务虽然是在当前外层事务内执行,但新创建的事务是独立于当前外层事务而存在的,二者拥有各自独立的状态而互不干扰; 而PROPAGATION_NESTED创建的事务属于当前外层事务的内部子事务(sub-transaction),内部子事务的处理内容属于当前外层事务的一部分,而不能独立于外层事务而存在,并且与外层事务共有事务状态,我想这也就是为什么称其为内部嵌套事务的原因。
PROPAGATION_NESTED可能的应用场景在于,你可以将一个大的事务划分为多个小的事务来处理,并且外层事务可以根据各个内部嵌套事务的执行结果来选择不同的执行流程。 比如,某个业务对象的业务方法A.service()可能调用其他业务方法B.service()向数据库中插入一批业务数据,但当插入数据的业务方法出现错误的时候(比如主键冲突),我们可以在当前事务中捕捉前一个方法抛出的异常,然后选择另一个更新数据的业务方法C.service()来执行, 这个时候,我们就可以把B.service()和C.serivce()方法的传播行为指定为PROPAGATION_NESTED[],如果用伪代码演示的话,看起来如下:
* PROPAGATION_REQUIRED
A.service()
// PROPAGATION_NESTED
B.service();
catch(Exception e)
// PROPAGATION_NESTED
C.service();
不过,并非所有的PlatformTransactionManager实现都支持PROPAGATION_NESTED类型的传播行为,现在只有org.springframework.jdbc.datasource.DataSourceTransactionManager在使用JDBC3.0数据库驱动的情况下才支持(当然,数据库和相应的驱动程序也需要提供支持),另外, 某些JtaTransactionManager也可能提供支持,但JTA规范并没有要求提供对嵌套事务的支持。
对TransactionDefinition所提供的这几个传播行为选项的使用,最好是建立在充分理解的基础之上,当然,除非特殊的场景,通常情况下,PROPAGATION_REQUIRED将依然是我们最多的选择。
TransactionDefinition提供了TIMEOUT_DEFAULT常量定义,用来指定事务的超时时间,TIMEOUT_DEFAULT默认值为-1,这会采用当前事务系统默认的超时时间,你将可以通过TransactionDefinition的具体实现类提供自定义的事务超时时间。
TransactionDefinition提供的最后一个重要信息就是将要创建的是否是一个只读(ReadOnly)的事务,如果你需要创建一个只读的事务的话,可以通过TransactionDefinition的相关实现类进行设置。 只读的事务仅仅是给相应的ResourceManager提供一种优化的提示,但最终是否提供优化,则由最终的ResourceManager决定。对于一些查询来说,我们通常会希望它们采用只读事务。
1.3.1.2.1.2. TransactionDefinition相关实现
TransactionDefinition仅仅是一个接口定义,要为PlatformTransactionManager创建事务提供信息,需要有相应的实现类提供支持。 TransactionDefinition的相关实现类虽然不多,但为了便于理解,我们依然将他们划分为“两派”:
Figure 1.11. TransactionDefinition继承层次图
将TransactionDefinition的相关实现类按照“编程式事务场景”和“声明式事务场景”划分为两个分支, 仅仅是出于每个类在相应场景中出现的频率方面考虑的,而不是说“声明式事务场景”的实现类不能在“编程式事务场景”使用。
org.springframework.transaction.support.DefaultTransactionDefinition是TransactionDefinition接口的默认实现类, 他提供了各事务属性的默认值,并且通过它的setter方法,你可以更改这些默认设置。这些默认值包括:
propagationBehavior = PROPAGATION_REQUIRED
isolationLevel = ISOLATION_DEFAULT
timeout = TIMEOUT_DEFAULT
readOnly = false
org.springframework.transaction.support.TransactionTemplate是spring提供的进行编程式事务管理的模板方法类(我们将稍后提到该类的使用), 通过直接继承DefaultTransactionDefinition,我们在使用TransactionTemplate的时候就可以直接通过TransactionTemplate本身提供事务控制属性。
org.springframework.transaction.interceptor.TransactionAttribute是继承自TransactionDefinition的接口定义, 主要面向使用Spring AOP进行声明式事务管理的场合,它在TransactionDefinition定义的基础上添加了一个rollbackOn方法:
boolean rollbackOn(Throwable ex);
这样,我们可以通过声明的方式指定业务方法在抛出哪些的异常的情况下可以回滚(rollback)事务。
TransactionAttribute的默认实现类是DefaultTransactionAttribute,他同时继承了DefaultTransactionDefinition, 在DefaultTransactionDefinition的基础上追加了rollbackOn的实现,DefaultTransactionAttribute的实现指定当异常类型为“unchecked exception”的情况下将回滚(rollback)事务。
DefaultTransactionAttribute之下有两个实现类,即RuleBasedTransactionAttribute以及DelegatingTransactionAttribute。 RuleBasedTransactionAttribute允许我们同时指定多个回滚规则,这些规则以包含org.springframework.transaction.interceptor.RollbackRuleAttribute或者org.springframework.transaction.interceptor.NoRollbackRuleAttribute的List形式提供, RuleBasedTransactionAttribute的rollbackOn将使用传入的异常类型与这些回滚规则进行匹配,然后再决定是否要回滚事务。
DelegatingTransactionAttribute是抽象类,它存在的目的就是被子类化,DelegatingTransactionAttribute会将所有方法调用委派给另一个具体的TransactionAttribute实现类, 比如DefaultTransactionAttribute或者RuleBasedTransactionAttribute,不过,除非不是简单的直接委派(什么附加逻辑都不添加),否则,实现一个DelegatingTransactionAttribute是没有任何意义的。
1.3.1.2.2. TransactionStatus
org.springframework.transaction.TransactionStatus接口定义表示整个事务处理过程中的事务状态, 我们将更多时候在编程式事务中使用该接口。
在事务处理过程中,我们可以使用TransactionStatus进行如下工作:
使用TransactionStatus提供的相应方法查询事务状态;
通过setRollbackOnly()方法标记当前事务以回滚(rollback);
如果相应的PlatformTransactionManager支持Savepoint,可以通过TransactionStatus在当前事务中创建内部嵌套事务;
在稍后为读者介绍如何使用spring进行编程式事务管理部分,可以更直观的了解这些。
TransactionStatus的实现层次比较简单,见下图:
Figure 1.12. TransactionStatus继承层次
org.springframework.transaction.SavepointManager是在JDBC3.0的基础上对Savepoint的支持提供的抽象, 通过继承SavepointManager,TransactionStatus获得可以管理Savepoint的能力,从而支持创建内部嵌套事务。
org.springframework.transaction.support.AbstractTransactionStatus为TransactionStatus的抽象类实现, 主要为其他实现子类提供一些“公共设施”,它下面主要有两个子类, DefaultTransactionStatus和SimpleTransactionStatus, 其中,DefaultTransactionStatus是spring事务框架内部使用的主要TransactionStatus实现类,spring事务框架内的各个TransactionManager的实现大都借助于DefaultTransactionStatus来记载事务状态信息。 SimpleTransactionStatus在spring框架内部的实现中没有使用到,目前来看,主要用于测试目的。
1.3.1.2.3. PlatformTransactionManager
PlatformTransactionManager是spring事务抽象框架的核心组件,关于它的定义以及作用我们之前已经提过了,所以,这部分我们不妨更多的关注一下PlatformTransactionManager整个的层次体系以及针对不同数据访问技术的实现类。
PlatformTransactionManager整个的抽象体系基于Strategy模式,由PlatformTransactionManager对事务界定进行统一抽象,而具体的界定策略的实现则交由具体的实现类。 下面我们先来看一下有哪些实现类可供我们使用...
1.3.1.2.3.1. PlatformTransactionManager实现类概览
PlatformTransactionManager的实现类可以划分到面向局部事务和面向全局事务两个分支:
面向局部事务的PlatformTransactionManager实现类.
spring为各种数据访问技术提供了现成的PlatformTransactionManager实现支持,以下列表给出了各种数据访问技术与它们对应的实现类的关系:
Table 1.1. 数据访问技术与PlatformTransactionManager实现类对应关系
数据访问技术
PlatformTransactionManager实现类
JDBC/iBatis
DataSourceTransactionManager
HibernateTransactionManager
JdoTransactionManager
JPA(Java Persistence API)
JpaTransactionManager
TopLinkTransactionManager
JmsTransactionManager
JCA Local Transaction
CciLocalTransactionManager
在这些实现类当中,CciLocalTransactionManager可能是比较少见的实现,CCI的意思是Common Client Interface, CciLocalTransactionManager主要是面向JCA的局部事务(Local Transaction),本书不打算对JCA的集成做过多的阐述,读者如果在实际项目中需要使用到JCA进行EIS(Enterprise Information System)系统集成,你可以从spring的参考文档获得使用spring提供的JCA集成支持的足够信息。
有了这些实现类,我们在使用spring的事务抽象框架进行事务管理的时候,只需要根据当前使用的数据访问技术选择对应的PlatformTransactionManager实现类即可。
如果你的应用程序需要同时使用Hibernate以及JDBC(或者iBatis)进行数据访问,那么你可以使用HibernateTransactionManager对基于Hibernate和JDBC(或者iBatis)的事务进行统一管理, 只要Hibernate的SessionFactory和JDBC(或者iBatis)引用的是同一个DataSource就行。你能猜到为什么吗?
面向全局事务的PlatformTransactionManager实现类.
org.springframework.transaction.jta.JtaTransactionManager是spring提供的支持分布式事务的PlatformTransactionManager实现。 直接使用JTA规范接口进行分布式事务管理有以下几个问题:
UserTransaction接口使用复杂不说(一长串的异常处理我们之前也见过了),它所暴露的事务管理能力有限,对于事务的挂起(suspend)以及恢复(resume)操作,只有JTA的TransactionManager才支持;
JTA规范并没有明确要求对TransactionManager的支持,这就造成当下各个JTA提供商虽然提供了TransactionManager的实现,但在应用服务器中暴露的位置各有差异,为了进一步支持REQUIRES_NEW和NOT_SUPPORTED之类需要事务挂起以及恢复操作的事务传播行为, 我们需要通过不同的方式来获取不同JTA提供商暴露的TransactionManager实现;
鉴于此,JtaTransactionManager对各种JTA实现提供的分布式事务支持进行了统一封装,只不过它的所有的事务管理操作最终都会委派给具体的JTA实现来完成。
对于典型的基于JTA的分布式事务管理,我们直接使用JtaTransactionManager就可以了,但某些时候,需要更多使用到各JTA产品的TransactionManager特性的时候, 这个时候,我们就可以为JtaTransactionManager注入这些JTA产品的javax.transaction.TransactionManager的实现, 而至于说你是通过应用服务器获取该TransactionManager,还是直接使用本地定义的TransactionManager(比如JOTM或者Atomikos等独立JTA实现产品的TransactionManager), 则完全由你根据当时的场景来决定了。 能够为JtaTransactionManager提供具体的TransactionManager实现为我们扩展JtaTransactionManager提供了很好的一个切入点。
JtaTransactionManager有两个子类OC4JJtaTransactionManager和WebLogicJtaTransactionManager,分别面向基于Oracle OC4J和Weglogic的JTA分布式事务管理, 在这些情况下,使用具体的子类来代替通常的JtaTransactionManager。不过,大多数情况下,使用spring提供的FactoryBean机制来获取不同JTA提供商提供的TransactionManager实现,然后注入JtaTransactionManager使用,是比较好的做法, org.springframework.transaction.jta包下,spring提供了面向JOTM,Weblogic和Websphere的TransactionManager查找FactoryBean实现,如果需要,你也可以根据情况实现其他的TransactionManager实现的查找FactoryBean。
有了spring的事务抽象框架,事务管理策略的转换也变得很简单,通常也只是简单的配置文件变更而已。 如果我们最初只需要处理单一资源的事务管理,那么,局部场景中的面向不同数据访问技术的PlatformTransactionManager实现将是我们的最佳选择,即使后来需要引入分布式资源的事务管理, 对于我们来说,也仅仅是从局部事务场景中的某个PlatformTransactionManager实现转向JtaTransactionManager的变动而已,无论是编程式注入还是通过spring的IoC容器注入,对于应用程序来说都不会造成很大的变动。
1.3.1.2.3.2. 窥一斑而知全豹
PlatformTransactionManager的各个子类在实现的时候基本上遵循统一的结构和理念,所以,我们不妨选择以DataSourceTransactionManager这一实现类作为切入点, 以管中窥豹之势,一探spring的抽象事务框架中各个PlatformTransactionManager实现类的奥秘之所在。
不过,在开始之前,我们有必要先了解几个概念:
transaction object
transaction object承载了当前事务的必要信息,PlatformTransactionManager实现类可以根据transaction object所提供的信息来决定如何处理当前事务。 transaction object的概念类似于JTA规范中的javax.transaction.Transaction定义。
TransactionSynchronization
TransactionSynchronization是可以注册到事务处理过程中的回调(callback)接口,它就像是事务处理的事件监听器, 当事务处理的某些规定时点发生的时候,会调用TransactionSynchronization上的一些方法来执行相应的回调逻辑,比如在事务完成后清理相应的系统资源等操作。
spring事务抽象框架所定义的TransactionSynchronization类似于JTA规范的javax.transaction.Synchronization,但比JTA的Synchronization提供了更多的回调方法, 允许我们对事务的处理添加更多的回调逻辑。
TransactionSynchronizationManager
类似于JTA规范中的javax.transaction.TransactionSynchronizationRegistry, 我们通过TransactionSynchronizationManager来管理TransactionSynchronization,当前事务状态以及具体的事务资源。 在介绍spring事务框架实现原理的原型中,我们提到会将具体的事务资源,比如java.sql.Connection或者hibernate Session绑定到线程,而TransactionSynchronizationManager就是这些资源绑定的目的地,当然,从该类的名字也可以看出, 它更多关注与事务相关的Synchronization的管理。
之所以将他们与JTA规范中定义的接口相提并论是因为,这些概念只限于局部场景中对应的PlatformTransactionManager实现类使用,JtaTransactionManager直接就使用对应的JTA产品提供的对应设施了,JtaTransactionManager的最终工作都是委派给具体的JTA实现产品,记得吗?!
OK,有了这些铺垫,我们开始进入正题...
spring的事务抽象框架是以PlatformTransactionManager作为顶层抽象接口,具体的实现交给不同的实现类,使用对象可以根据当前场景选择使用或者替换哪一个具体的实现类, 从这个层次看,整个框架的设计是以Strategy模式为基础的。 不过,从各个实现类的继承层次上来看,spring事务框架的实现则更多的依赖于模板方法模式:
Figure 1.13. DataSourceTransactionManager的实现层次
org.springframework.transaction.support.AbstractPlatformTransactionManager作为DataSourceTransactionManager的父类, 以模板方法的形式封装了固定的事务处理逻辑,而只将与事务资源相关的操作以protected或者abstract方法的形式下放给DataSourceTransactionManager来实现。 作为模板方法父类,AbstractPlatformTransactionManager替各子类实现了以下固定的事务内部处理逻辑:
判定是否存在当前事务,然后根据判断结果执行不同的处理逻辑;
结合是否存在当前事务的情况,根据TransactionDefinition中指定的传播行为(Propagation)的不同语义执行后继逻辑;
根据情况挂起(suspend)或者恢复(resume)事务;
提交事务之前检查readOnly字段是否被设置,如果是的话,以事务的回滚代替事务的提交;
事务回滚的情况下清理并恢复事务状态;
如果事务的synchonization处于active状态,在事务处理的规定时点触发注册的synchonization回调接口;
这些固定的事务内部处理逻辑大都体现在以下几个主要的模板方法中:
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException
public final void rollback(TransactionStatus status) throws TransactionException
public final void commit(TransactionStatus status) throws TransactionException
protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException
protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder) throws TransactionException
我们可不打算这几个模板方法都捋一遍,毕竟只要了解了前面两个或者三个模板方法的流程,整个事务处理的图景基本上就可以展现在我们眼前了。
我们先从第一个模板方法getTransaction(TransactionDefinition)开始。 getTransaction(TransactionDefinition)的主要目的是开启一个事务,但需要在此之前判断一下之前是否存在一个事务,如果存在,则根据TransactionDefinition中的传播行为决定是挂起当前事务还是抛出异常;同样的,不存在事务的情况下,也需要根据传播行为的具体语义来决定如何处理。 getTransaction(TransactionDefinition)方法的处理逻辑基本上按照下面的流程执行[]:
Procedure 1.1. getTransaction(TransactionDefinition)执行流程
获取transaction object,以判断是否存在当前事务
Object transaction = doGetTransaction();
这行代码有两点需要申明:
获取的transaction object类型会因具体实现类的不同而各异,DataSourceTransactionManager会返回DataSourceTransactionManager.DataSourceTransactionObject类型实例, HibernateTransactionManager会返回HibernateTransactionManager.HibernateTransactionObject类型的实例,等等。 AbstractPlatformTransactionManager不需要知道具体实现类返回的transaction object具体类型是什么,因为最终对transaction object的依赖都将通过方法参数进行传递, 只要具体的实现类在取得transaction object参数后知道如何转型就行,所以,这一步返回的transaction为Object类型;
doGetTransaction()为getTransaction(TransactionDefinition)模板方法暴露给子类来实现的abstract类型方法,DataSourceTransactionManager在实现doGetTransaction()方法逻辑的时候, 会从TransactionSynchronizationManager获取绑定的资源,然后添加到DataSourceTransactionObject之后返回。以此类推,其他AbstractPlatformTransactionManager子类都采用类似的逻辑实现了doGetTransaction()方法。
获取Log类的Debug信息,避免之后的代码重复
boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
logger.debug("Using transaction object [" + transaction + "]");
debugEnabled将以方法参数的形式在各方法调用间传递,以避免每次都调用logger.isDebugEnabled()获取debug日志状态。这一步与具体的事务处理流程关系不大。
检查TransactionDefinition参数合法性
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
如果definition参数为空,则创建一个DefaultTransactionDefinition实例以提供默认的事务定义数据。
根据先前获得transaction object判断是否存在当前事务,根据判定结果采取不同的处理方式
if (isExistingTransaction(transaction)) {
// Existing transaction found -& check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
isExistingTransaction(transaction)默认情况下返回false,该方法需要具体子类根据情况进行覆写(Override), 对于DataSourceTransactionManager来说,它会根据传入的transaction所记载的信息进行判断:
DataSourceTransactionObject txObject = (DataSourceTransactionObject)
return (txObject.getConnectionHolder() != null && txObject.getConnectionHolder().isTransactionActive());
对于HibernateTransactionManager来说,则会将transaction强制转型为HibernateTransactionObject,然后根据HibernateTransactionObject所记载的信息来判断之前是否存在一个事务。 其他具体实现类对isExistingTransaction(transaction)的处理亦是如此。
不管isExistingTransaction(transaction)返回结果如何,实际上,下面的处理主体上都是以TransactionDefinition中的传播行为为中心进行的, 比如同样是PROPAGATION_REQUIRED,在存在当前事务与不存在当前事务两种情况下的处理是不同的,前者会使用之前的事务,后者则会创建新的事务,其他的传播行为的处理也是按照不同的场景分别处理。
如果isExistingTransaction(transaction)方法返回true,即存在当前事务的情况下
由handleExistingTransaction()方法统一处理存在当前事务情况下应该如何创建事务对应的TransactionStatus实例并返回。
如果definition定义的传播行为是PROPAGATION_NEVER,抛出异常并退出
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
这是由TransactionDefinition.PROPAGATION_NEVER的语义决定的。
如果definition定义的传播行为是PROPAGATION_NOT_SUPPORTED,则挂起当前事务,然后返回
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return newTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
newTransactionStatus()方法返回一个DefaultTransactionStatus实例,因为我们挂起了当前事务,而PROPAGATION_NOT_SUPPORTED不需要事务,所以,返回的DefaultTransactionStatus不包含transaction object的信息(构造方法第二个参数)。
如果definition定义的传播行为是PROPAGATION_REQUIRES_NEW,则同样挂起当前事务,并开始一个新的事务并返回
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
SuspendedResourcesHolder suspendedResources = suspend(transaction);
doBegin(transaction, definition);
catch (TransactionException beginEx) {
resume(transaction, suspendedResources);
catch (TransactionException resumeEx) {
logger.error(
"Inner transaction begin exception overridden by outer transaction resume exception", beginEx);
throw resumeEx;
throw beginEx;
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
AbstractPlatformTransactionManager首先将当前事务挂起,然后调用doBegin()方法开始新的事务,如果开始事务过程中出现异常,则恢复之前挂起的事务。 doBegin(transaction, definition)方法为abstract方法,需要具体子类来实现,在DataSourceTransactionManager中, doBegin()方法会首先检查传入的transaction以提取必要信息判断之前是否存在绑定的connection信息,如果没有,则从DataSource中获取新的connection,然后将其AutoCommit状态改为false,并绑定到TransactionSynchronizationManager。 当然,这期间也会牵扯事务定义的应用以及条件检查等逻辑。 当所有一起搞定之后,newTransactionStatus会创建一个包含definition,transaction object以及挂起的事务信息和其它状态信息的DefaultTransactionStatus实例并返回。
如果definition定义的传播行为是PROPAGATION_NESTED,根据情况创建嵌套事务,比如通过Savepoint或者JTA的TransactionManager
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
doBegin(transaction, definition);
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
在这种情况下,会首先通过isNestedTransactionAllowed()方法检查AbstractPlatformTransactionManager的nestedTransactionAllowed属性状态, 如果允许嵌套事务,那么还得分两种情况处理,对于DataSourceTransactionManager来说,因为它支持使用Savepoint创建嵌套事务,所以,会使用TransactionStatus创建相应的Savepoint并返回; 而像JtaTransactionManager则要依赖于具体JTA产品的TransactionManager提供嵌套事务支持。
useSavepointForNestedTransaction()方法默认返回true,即默认使用Savepoint创建嵌套事务,如果具体子类不支持使用Savepoint创建嵌套事务,则需要覆写(Override)该方法,比如JtaTransactionManager。
如果需要检查事务状态匹配情况,则对当前存在事务与传入的defintion中定义的隔离级别与ReadOnly属性进行检查,如果数据不吻合,则抛出异常;
if (isValidateExistingTransaction()) {
// validate isolation
// validate read only
AbstractPlatformTransactionManager的validateExistingTransaction属性默认值为false,如果你想进一步加强事务属性之间的一致性, 可以将validateExistingTransaction属性设置为true,那么这个时候,以上代码即会被触发执行。
剩下的就是其他情况下,直接构建TransactionStatus返回
比如对应PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED的情况。
如果isExistingTransaction(transaction)方法返回false,即不存在当前事务的情况下
当definition中定义的传播行为是PROPAGATION_MANDATORY的时候,抛出异常
因为不存在当前事务,所以根据PROPAGATION_MANDATORY的语义,理当如此。
当definition中定义的传播行为是PROPAGATION_REQUIRED或者PROPAGATION_REQUIRES_NEW或者PROPAGATION_NESTED的时候,开启新的事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
doBegin(transaction, definition);
catch (TransactionException ex) {
resume(null, suspendedResources);
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
之所以在doBegin之前先调用传入null的suspend()方法是因为考虑到如果有注册的Synchronization的话,需要暂时将这些与将要开启的新事务无关的Synchronization先放一边。
剩下的其他情况,则返回不包含任何transaction object的TransactionStatus并返回
这种情况下虽然是空的事务,但有可能需要处理在事务过程中相关的Synchronization。
从getTransaction(TransactionDefinition)的逻辑可以看出,AbstractPlatformTransactionManager更多的关注的是事务处理过程的总体逻辑,而跟具体事务资源相关的处理则交给了具体的子类来实现。
事务处理的完成有两种情况,即回滚事务或者提交事务,AbstractPlatformTransactionManager提供的rollback(TransactionStatus)和commit(TransactionStatus)两个模板方法分别对应这两种情况下的处理。 因为事务提交过程中可能需要处理回滚逻辑,我们不妨以commit(TransactionStatus)的实现流程看一下AbstractPlatformTransactionManager是如何处理事务完成的:
Procedure 1.2. 事务提交执行流程
因为在事务处理过程中,我们可以通过TransactionStatus的setRollbackOnly()方法标记事务回滚,所以,commit(TransactionStatus)在具体提交事务之前会检查rollBackOnly状态, 如果该状态被设置,那么转而执行事务的回滚操作;
rollback(TransactionStatus)的逻辑主要包含三点:
回滚事务! 这当然是必须的啦,不过,这里的回滚事务又分三种情况:
如果是嵌套事务,则通过TransactionStatus释放Savepoint;
如果TransactionStatus表示当前事务是一个新的事务,则调用子类的doRollback(TransactionStatus)方法真正的回滚事务;
doRollback(TransactionStatus)是抽象方法,具体子类必须实现它,DataSourceTransactionManager在实现该方法的时候无疑是调用connection.rollback()啦! 至于说HibernateTransactionManager,会通过它Session上的Transaction的rollback()方法回滚事务,其他子类对doRollback(TransactionStatus)的实现逻辑依此类推。
如果当前存在事务,并且rollbackOnly状态被设置,则调用由子类实现的doSetRollbackOnly(TransactionStatus)方法,各子类实现通常会将当前的transaction object的状态设置为rollBackOnly。
触发Synchronization事件。
rollback的时候出发的事件比commit的时候要少,只有triggerBeforeCompletion(status)和triggerAfterCompletion()。
清理事务资源。这包括:
设置TransactionStatus中的completed为完成状态;
清理与当前事务相关的Synchronization;
调用doCleanupAfterCompletion()释放事务资源,并解除到TransactionSynchronizationManager的资源绑定。对于DataSourceTransactionManager来说,当然是关闭数据库连接,然后解除对DataSource对应资源的绑定。
如果之前有挂起的事务,恢复挂起的事务;
如果rollBackOnly状态没被设置,则执行正常的事务提交操作。
commit(TransactionStatus)方法余下的逻辑与rollback(TransactionStatus)方法基本相似,只是几个具体操作有所差别:
回滚事务现在是提交事务;
提交事务的时候,也会牵扯到几种情况:
决定是否提前检测全局的rollBackOnly标志,如果最外层事务已经被标记为rollBackOnly,并且failEarlyOnGlobalRollbackOnly为true,则抛出异常:
boolean globalRollbackOnly =
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
如果提交事务之前发现TransactionStatus持有Savepoint,则释放之,这实际上是在处理嵌套事务的提交;
如果TransactionStatus表示要提交的事务是一个新的事务,则调用子类的doCommit(TransactionStatus)方法实现提交事务。
doCommit(TransactionStatus)也是AbstractPlatformTransactionManager暴露给子类实现的抽象方法,子类必须实现该方法。 对于DataSourceTransactionManager来说,因为事务的提交有Connection决定,所以会直接调用mit()提交事务。 其他的子类也会使用自身的局部事务API在该方法中实现事务的提交。
也需要出发Synchronization相关事件,不过,触发的事件比rollback(TransactionStatus)中的要多,包括triggerBeforeCommit(), triggerBeforeCompletion(),triggerAfterCommit()和triggerAfterCompletion()。
如果AbstractPlatformTransactionManager的rollbackOnCommitFailure状态被设置为true,则表示如果在事务提交过程中出现异常,需要回滚事务, 所以,当commit(TransactionStatus)方法捕获相应异常并且检测到该字段被设置的时候,需要回滚事务。rollbackOnCommitFailure的默认值是false,表示即使提交过程中出现异常,也不回滚事务。
既然commit(TransactionStatus)与rollback(TransactionStatus)一样,都是意味着事务的完成,那么也需要在最后进行事务资源清理的工作,具体内容可以参照rollback(TransactionStatus)部分。
对于suspend和resume两个方法来说,逻辑更好理解了,前者会把TransactionSynchronizationManager上当前事务对应的Synchroniazation信息以及资源获取到SuspendedResourcesHolder中,然后解除这些绑定; 后者则会将SuspendedResourcesHolder中保持的信息重新绑定到TransactionSynchronizationManager。
实际上,如果将AbstractPlatformTransactionManager中处理Synchroniaztion回调以及事务传播行为的逻辑剥离一下的话,你就会发现,整个的逻辑流程就是开始为您展示的实现原型所表达的。
下图展示了AbstractPlatformTransactionManager需要子类实现或者覆写(Override)的方法:
Figure 1.14. 模板类与实现类之间的纽带
对于各个子类来说,无非就是根据自身需要管理的资源和事务管理API提供这些方法的实现而已。
1.3.2. 使用spring进行事务管理
从Java平台尤其是J2EE平台上事务管理的传统意义上来看,事务管理有两种方式,即编程式事务管理以及声明式事务管理,对于这两种事务管理方式的支持, spring事务框架可以说是“青出于蓝而胜于蓝”。
1.3.2.1. 编程式事务管理
通过spring进行编程式事务管理有两种方式,要么直接使用PlatformTransactionManager,要么使用更方便的TransactionTemplate,二者各有优缺点,但总体上来说,使用TransactionTemplate进行编程式事务管理是推荐的方式。
1.3.2.1.1. 直接使用PlatformTransactionManager进行编程式事务管理
PlatformTransactionManager接口定义了事务界定的基本操作,我们可以直接使用PlatformTransactionManager进行编程式事务管理:
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setTimeout(20);
TransactionStatus txStatus = transactionManager.getTransaction(definition);
// business logic implementation
catch(ApplicationException e)
transactionManager.rollback(txStatus);
catch(RuntimeException e)
transactionManager.rollback(txStatus);
catch(Error e)
transactionManager.rollback(txStatus);
mit(txStatus);
只要为transactionManager提供合适的PlatformTransactionManager实现,然后结合TransactionDefinition开启事务,结合TransactionStatus来回滚或者提交事务就可以完成当前对象的整个事务管理。
直接使用PlatformTransactionManager,你可以完全的控制整个的事务处理过程,但是,缺点也是很明显的, 从抽象事务操作以屏蔽不同事务管理API差异的角度看,PlatformTransactionManager可能已经足够了, 但是,从应用程序开发的角度,却依然过于“底层”,单单是期间的这些异常处理就够我们忙活的了。 如果在每一个需要事务管理的地方,全都采用直接使用PlatformTransactionManager进行事务管理,那重复代码的数量将是惊人的。
鉴于使用PlatformTransactionManager进行事务管理的流程比较固定,各个事务管理期间只有部分逻辑存在差异,我们可以考虑像spring的数据访问层那样,使用模板方法模式 + Callback的方式对直接使用PlatformTransactionManager进行事务管理的代码进行封装, 这就有更方便的编程式事务管理方式,即使用TransactionTemplate的编程式事务管理。
1.3.2.1.2. 使用TransactionTemplate进行编程式事务管理
org.springframework.transaction.support.TransactionTemplate对与PlatformTransactionManager相关的事务界定操作以及相关的异常处理进行了模板化封装, 开发人员更多的关注于通过相应的callback接口提供具体的事务界定内容即可。spring针对TransactionTemplate提供了两个callback接口,TransactionCallback和TransactionCallbackWithoutResult,二者的唯一区别就是是否需要返回执行结果。
使用TransactionTemplate进行事务管理的代码看起来要比直接使用PlatformTransactionManager要简洁并且容易管理的多:
TransactionTemplate txTemplate = ...;
Object result = txTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus txStatus) {
Object result =
// 各种事务操作 ...
txTemplate.execute(new TransactionCallbackWithoutResult(){
protected void doInTransactionWithoutResult(TransactionStatus txStatus) {
// 事务操作1
// 事务操作2
TransactionTemplate会捕捉TransactionCallback或者TransactionCallbackWithoutResult事务操作中抛出的“unchecked exception”并回滚事务,然后将“unchecked exception”抛给上层处理, 所以,现在我们只需要处理特定于应用程序的异常即可,而不用像直接使用PlatformTransactionManager那样对所有}

我要回帖

更多关于 yii model 事务 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信