事务管理面试题

为了描述事务的概念,我们拿买电影票来举例。买一张电影票通常有一下步骤:

检查剩余座位的数量,确定是否能给你提供你需要的座位个数
每卖出一张票,可用座位的数量就应该减一
付款
售票员把票给你

如果一切顺利的话,你就可以欣赏到一场一鸣惊人的电影,而影院也增加了收入。但是如果有环节出差错了怎么办呢?比如说:你用来付款的信用卡没钱了?显然,你不会拿到票,影院也拿不到钱。但是如果说座位的数量在下个人购买之前没有被恢复到原来的状态,那么电影也许因为人为原因而不会满场了。或者如果出现这样的情况:一切都很顺利,但是发放票的时候出了问题。你只好乖乖的呆在家里看电视了,而且还损失了一小笔钱。

为了保证剧院和你都不受到损失,上面的操作应该用事务封装起来。作为事务,它应该被看成是一个单独的动作,以保证要么所有的操作都成功,或者所有的操作都回滚到初始的状态。

在软件中,事务有着举足轻重的地位,确保数据和资源保持一致的状态。如果没有事务,那么数据有可能因为应用程序的业务逻辑而变成脏数据,或者变成与其他数据不统一的数据。

事务他是如何工作的,有以下四个因素:在软件开发的一个重要传统里,可以用一个单词首字母的缩写来描述一个事务:ACID,简言之,ACID代表

  

原子性(Atomic)事务由一个或多个动作绑定起来作为一个单独的工作单元。原子性保证事务中所有的操作要么都执行,或者都不执行。如果所有的动作都执行了,那么事务就是成功的,如果其中有一个动作失败了,那么整个事务都失败,而且要执行回滚操作。

一致性(Consistent)一旦事务结束(可能成功了也可能失败了),那么系统所模拟的业务逻辑要处于一致的状态。数据不应该被实体关系破坏。

隔离性(Isolated)事务应该允许多个用户操作一个数据,一个用户的操作应该不受另一个用户操作的影响。因此事务之间应该是相互隔离的,以阻止他们在操作中同时读写同一数据。(一般是以乐观锁来实现这一特性的)
持久性(持久性)一旦事务完成,事务执行的结果就应该被保存到数据库中,这样即使因为某一原因系统崩了,数据还能保存下来。传统上是把结果保存到数据库或者其他某种格式的持久化介质中。

     

在刚才的电影票的例子中,如果任何一个步骤失败的话事务可以所有取消操作的结果来保证原子性。原子性可以通过保证系统的数据从来没有不一致的状态,和从来没有部分执行的状况来保证一致性。隔离性可以通过阻止其他并发事务在你正在购买你的座位的时候把这个座位从你那里偷走来保证一致性。

最终的效果就是持久性,因为它们已经被保存到存储介质中去了。如果系统崩了或有其他类似的事件发生的话你也不需要担心事务的结果会丢失。

    二、理解spring对事务处理的支持

和ejb(https://blog.csdn.net/xufei512/article/details/52703113)一样,spring也提供了对包括的两种事务的支持,但是spring的事务管理能力超过了ejb(https://zhidao.baidu.com/question/30278121.html)的事务管理能力。

Spring对代码级事务管理的支持很大程度上不同于ejb。与ejb不同的是,ejb和jta实现是联系在一起的,spring使用的是一种招回机制以从从事务代码种抽象出真实的事务实现。事实上,spring的事务管理甚至不需要一个jta的实现。如果你的应用程序只使用一个单一的持久资源,那么你就可以使用持久机制提供的事务支持来处理你的事务。这包括jdbc,hibernate,jdo和ojb。然而,如果你的程序有涉及到多个资源的事务需求,spring可以用第三方的jta实现来提供一个分布式的事务支持。(

在5.2种我们将讨论spring对代码级事务管理的支持)

 在你的代码中,代码级事务管理提供给你精确的弹性来定义的事务边界,声明式事务在其事务规则中帮助你减弱了操作带来的影响。Spring对声明式事务处理的支持让我们想起了ejb的容器管理事务(cmt)。两者都允许你们显式的定义事务的边界。但是spring的声明式事务超出了cmt,因为它允许你声明附加的属性,比如说隔离级别和timeouts。(在5.3中我们将阐述spring的声明式事务管理)

选择代码级事务管理还是声明式事务管理就决定了你是选择了对程序(fine-grained)严密的控制还是选择了方便。当你把事务编写到你的代码中时,你就可以精确的控制事务的边界,在你需要的地方精确的开始和结束事务。一般的做法是,你不需要代码级提供的对程序的精密的控制时就选择把你的事务声明到一个上下文环境中去(就是说选择声明式事务管理)。

不管你是否选择把事务写到你的bean中还是当作一个切面来声明它们,你将使用一个spring的事务管理来作为接口插入到平台具体的事务实现中。让我们看一下spring的事务管理是如何用平台的具体的事务实现把你从事务处理中解放出来的。

5.1.3 介绍spring的事务管理

Spring并没有直接管理事务,取而代之的是,它提供了另一个选择,把事务管理部分的责任委托给平台具体的事务实现,这些事务实现可能是jta提供的或者也可以是持久机制提供的。

这里我们将着重介绍spring对hibernate的事务支持。

Hibernate的事务

如果你的程序是用hibernate来实现持久化的,那么你要使用HibernateTransactionManager这个类。用下面提供的xml文件来在程序中声明这个类。

sesssionFactory这个property应该填上一个hibernate的SessionFactory,这里最好取名为sessionFactory。上面有如何创建一个hibernate的SessionFactory的详细的讲解。

HibernateTransactionManager把事务管理的责任委托给一个net.sf.hibernate.Transaction对象,这个对象可以hibernate的session对象得到。类似的,当一个事务失败了,事务对象就会调用rollback()这个方法执行回滚操作。

5.2在spring中开发事务处理程序

CourseService类的enrollStudentInCourse()方法在一个学生注册一门课程的过程中要执行多个步骤,如果其中的任何一个步骤出错,那么所有的操作都必须回滚到改方法执行前的状态。换句话说,enrollStudentInCourse()方法必须要用事务包装起来。

把事务加到你的代码中的途径之一就是利用spring的TransactionTemplate类把事务边界以代码的形式写到程序中。就像spring中的其他模板类一样,TransactionTemplate使用了一个招回机制。下面的代码告诉你怎么用TransactionTemplate来包装你的代码。

Public void enrollStudentInCourse(){
transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus ts){
try{
//do stuff <-Runs within doInTransaction()
}catch(Exceptin e){
Ts.setRollbackOnly(); <-Calls setRollbackOnly() to roll back
}
Return null ; <-if successful transaction is committed
}
});
}

你以实现TransactionCallback接口为开始。因为TransactionCallback只需要实现一个方法,作为一个匿名内部类来说实现这个方法通常是最简单的。把这些代码放在doInTransaction()方法中的上下文事务环境中,再把这个doInTransaciton方法放在你的程序要执行上面这些代码的地方(不理解就看上面这段代码是怎么做的)。

在TransactionTemplate实例中使用execute()方法将执行包含在TransactionCallback实例中的代码。如果你的代码遇到问题,那么调用TransactionStatus对象的setRollbackOnly() 方法将回滚事务。否则,如果doInTransaction()方法成功返回的话,事务将会被提交。

TransactionTemplate实例是怎么得到的呢?这是一个不错的疑问,它应该被注入到CourseServiceImpl类中,像下面那样:

注意一下,transactionTemplate这个bean有一个transactionManager的property(属性)。隐藏在表象之下的是,TransactionTemplate使用了一个PlatformTransactionManager(这是一个接口)的实现来处理平台具体的事务管理的细节。这里我们注册了一个名为transactionManager的bean的引用,这个bean可以是任意一种PlatformTransactionManager接口的实现(比如说DataSourceTransactionManager,HibernateTransactionManager,JdoTransactionManager,JtaTransactionManager,或者是PersistenceBrokerTransactionManager)。

当你需要完全控制事务边界的时候代码级事务管理是很好的。但是,正如你上面所看到的,这样做有点烦,你不得不更改enrollStudentInCourse()方法的实现,为了得到spring的代码级事务的支持你不得不使用spring的具体的类。

通常你的事务需求不需要那么精密的控制事务边界。那就是为什么一般你选择在程序外面声明你的事务的原因了(比如说在spring的配置文件中)。下面我们将要讲解spring的声明式事务管理。

5.3 声明事务
在不久之前,声明式事务管理只有在ejb容器中才能实现。但是现在spring也提供了对pojo的声明式事务管理的支持。这是一个spring的一个意义重大的特性,因为你的应用程序不再只是因为要得到声明式原子性操作而需要复杂而且重量级的ejb了。

Spring对声明式事务的支持实际上是spring的aop框架的一个实现。Aop显然很适合来实现声明式事务管理,因为事务是系统级的服务,应该是在应用程序主功能的上面一层。你可以把spring的事务认作是包装了一个方法的切面。

为了在你的应用程序中使用声明式的事务,你要用TransactionProxyFactoryBean这个类。这个动态工厂bean类似ProxyFactoryBean,不一样的是这个bean在上下文事务环境中包装方法是有一个具体的目的的。你也可以自己创建一个ProxyFactoryBean来处理事务,但是用TransactionProxyFactoryBean更简单,因为它是专门为处理声明式事务而设计的。下面将告诉你如何声明一个TransactionProxyFactoryBean:

Com.springinaction.training.service.CourseService ////interface implemented by proxy

////bean being proxied

/////transaction manager

////transaction attribute source

注意一下,这个bean有一个courseService的id。这是为了当应用程序需要得到一个courseService时,那么可以从应用程序上下文环境中得到被TransactionProxyFactoryBean包装过的courseService实例。 原来的courseServicebean应该重命名一下,避免和bean 的id冲突。任何名字都可以,但是把目标bean的名字取成“目标bean的代理+Target“这种格式是一个约定成俗的规定。所以,courseServiceTarget是合适的:

除了它的目标bean之外,TransactionProxyFactoryBean还有两个合作者。transactionManager这个property暗示着一个使用明确的上下文事务环境的PlatformTransactionManager的实例。这个PlatformTransactionManager可以是任何一个具体TransactionManager。

而transactionAttributeSource这个property是一个TransactionAttributeSource 这个bean的引用。为了理解transaction attribute sources的工作原理,你必须先要理解transaction attributes。那么,让我们来仔细看一下transaction attribute是怎么工作的吧。

5.3.1 理解事务的属性(transaction attributes)

在spring中,一个transaction attribute是事务方针如何作用在一个方法上的一种描述。这种描述可以包括一种或是多种以下的参数
Propagation behavior(传播行为)
Isolation level(隔离级别)
Read-only hints(只都标示)
The transaction timeout period(失效周期)

Propagation behavior(传播行为)
传播行为在尊重客户端和调用的方法的基础上来定义事务的边界的。Spring定义了7种截然不同的传播行为,如下表所列:

1 PROPAGATION_NOT_SUPPORTED
表示方法没有事务需求,如果在方法的执行过程前已经有一个已存在的事务,那么在方法的执行过程中,该事务应该暂停。如果使用的是
JTATransactionManager的话,必须要有权使用TransactionManager类

2 PROPAGATION_REQUIRED
表示当前的方法有事务需求。如果在方法执行前已经存在一个事务,那么这个方法将在这个事务中执行。否则就开始一个新的事务。

3 PROPAGATION_REQUIRES_NEW
表示当前的方法必须在自己的事务中运行。一个新的事务会开始,而且如果方法执行之前已经有事务存在,那么这个事务在方法执行的过程中会暂停。如果使用JTATransactionManager的话,有权使用TransactionManager类是必须的。

4 PROPAGATION_SUPPORTS
表示当前方法不需要上下文事务环境,但可以运行在方法执行之前已经存在的事务中。

5 PROPAGATION_MANDATORY
表示方法必须运行在一个事务中,如果没有这样一个事务,将会抛出异常。

6 PROPAGATION_NESTED
表示如果在进程中已经存在一个事务,那么这个方法应该运行在一个嵌套事务之中。一个嵌套的事务可以从放入该嵌套的事务处单独的提交或回滚,如果没有事务在嵌套事务中,那就要有PROPAGATION_REQUIRED的行为。要注意这种传播行为是问题最多的。为你的资源管理者参考一下文档然后决定是否支持嵌套事务。

7 PROPAGATION_NEVER
表示当前的方法不应该在一个上下文事务环境中运行。如果有这么一个事务在进程中的话,将会抛出异常。

上面大多数的传播行为看上去是很熟悉的。那是因为它们反映出了ejb中容器事务管理(cmt)的传播规则。比如,spring的PROPAGATION_REQUIRES_NEW相当与cmt的requiresNew.spring加了一个cmt中没有的传播行为,PROPAGATION_NESTED,这样做是为了能支持嵌套事务。

一个新的事务是否应该创建或者暂停,或者一个方法是否应该在一个上下文事务环境中执行,这都是一个问题,而传播规则则从根本上回答了这些问题。

举个例子,如果一个方法被声明成PROPAGATION_REQUIRES_NEW这个传播行为,这就意味者事务边界和方法自己的边界是一样的:当方法开始时事务就开始了,当方法返回或者是抛出了异常那么事务就结束。如果方法被声明成PROPAGATION_REQUIRED,那么事务的边界取决于一个事务是否已经在运行中。

Isolation levels隔离级别

在一个典型的应用程序中,多事务并发是很平常的,它们经常操作同一数据而且完成自己的工作。并发是必需的,但是可能导致以下的问题:

Dirty read(脏读)
当一个事务在读取数据,但这个数据已经被另一个事务改写但是该事务还是没有提交(就是说这个事务还没有结束),这时候就会发生脏读。如果这个改动待会被回滚,那么第一事务所持有的数据将是无效的。

Nonrepeatable read(不可重复读)
当一个事务执行同一个查询命令时两次以上,但是每次查询的数据都不一样,不可重复读就发生了。者通常是因为另外一个并发的事务在几次查询之间正在更新数据造成的。

Phantom reads(虚读)
虚读类似于不可重复读。当事务T1读了几行数据,然后另一个并发的事务T2插入了几行。在后来的查询中,T1发现以前不存在的那些行。

在一个理想的情况下,事务应该相互完全隔离,阻止这些问题的发生。然而,完全的隔离会影响性能,因为它涉及到在数据库中把行(row)锁住(有时会是锁住整张表(table))。带有侵略性的锁会阻碍并发,要求事务相互等待来完成它们的工作(不会是死锁吧,呵呵)。

认识到完全的隔离会影响到性能,而且不是所有的应用程序都需要完全的隔离,在对待事务隔离这个问题上,有时要求一些弹性也是合理的。因此有如下几种隔离级别:

1 ISOLATION_DEFAULT
使用数据存储系统默认的隔离级别

2 ISOLATION_READ_UNCOMMITTED
在一个事务没有被提交之前允许你读取该事务改变过的数据,但可能 导致脏读虚读和不可重复读

3 ISOLATION_READ_COMMITTED
允许从已提交的并发事务中读取。脏读是被禁止的,但是虚 读和不 可重复都是可以的
4 ISOLATION_REPEATABLE_READ
多次读取同一个域会产生相同的结果,除非被事务自己改变了。脏 读和不可重复读是被禁止的,虚读可以发生。
5 ISOLATION_SERIALIZABLE
这个完全的适应acid的隔离级别保证了脏读,不可重复读,和虚读 都被禁止,这个是所有隔离级别中最缓慢的一种,因为它很具代表性的在涉及到事务的所有表上都加了锁(对表加的锁)。

ISOLATION_READ_UNCOMMITTED是最有效的隔离级别,但是只是最小程度的隔离了事务,把事务向脏读,不可重复读和虚读开放了。另一个极端,ISOLATION_SERIALIZABLE防止了所有隔离问题的形式,但是,效率是最低的。

要知道并不是所有的资源管理器都支持上面所列的隔离级别。参考你的资源管理器的文档来决定那些隔离级别是允许的。

Read-only
如果事务只对应底层数据仓库只执行都操作,那么数据仓库也许可以利用事务只读的特性来执行某些优化。通过把一个事务声明成read-only,你可以给底层数据仓库机会去应用那些优化措施,而这些措施是数据仓库确定对优化来说是合适。

因为read-only的优化是当一个事务开始时由底层数据仓库来应用的,它只对那些把事务声明成read-only这个传播行为的方法才有意义,这个传播行为可能会开始一个新的事务(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED).

此外,如果你使用hibernate作为你的持久机制,把一个事务声明成read-only的话会导致hibernate中的flush模式被设成FLUSH_NEVER。这样就告诉了hibernate要用数据库来避免对象的非必须的同步。延迟所有的更新直到事务结束。

Transaction timeout

最后,还有一个你可以选择来设置事务属性的是timeout。假设你的事务运行的时间变的长的出人意料。因为事务涉及到对底层数据仓库的锁,所以长时间的事务运行可能会占用数据库资源,而这种占用有不是必须的。你可以声明一个事务在一定的秒数之后自动回滚,而不是等它执行结束。

因为timeout是从事务开始的时候开始算起的,它只对那些用传播行为在方法上把事务声明成timeout才有意义,这个传播行为可能会开始一个新的事务(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED)。

5.3.2 声明一个简单事务的策略
TransactionProxyFactoryBean参考一个方法的事务的属性来决定怎样管理那个方法上的事务策略。但是TransactionProxyFactoryBean从哪里得到一个方法的事务属性的呢。

如你所见,TransactionProxyFactoryBean有一个transactionAttributeSource的property。这个property就代表着一个TransactionAttributeSource的实例。一个TransactionAttributeSource就是作为一个引用来查找设置在方法上的事务属性的。

一个TransactionAttributeSource就是由下面的接口来定义的:

public interface TransactionAttributeSource{
public TransactionAttribute getTransactionAttribute(
java.lang.reflect.Method method,
java.lang.Class targetClass);
}

getTransactionAttribute这个方法用来为一个特定的方法寻找其事务的属性,前提是给出目标类和方法。这个TransactionAttribute返回的就表示着事务策略应该被应用到方法上。

现在让我们在应用程序的xml配置文件中定义一下transactionAttributeSource这个bean,如下:

瞧,随这个transactionAttributeSource这个bean的声明,所有由TransactionProxyFactoryBean的目标类来代理的方法都在一个上下文事务环境中执行了。但是要注意你没有具体指定哪个方法需要事务,或者甚至没有指定要应该什么样的事务策略。因为我们决定在这里用MatchAlwaysTransactionAttributeSource这个类。

MatchAlwaysTransactionAttributeSource 可能是TransactionAttributeSource这个接口最简单的实现了。当这个类的getTransactionAttribute()方法被调用,它每次都会返回同样的TransactionAttribute,不管方法被包装在什么样的事务中(一般来说是:PROPAGATION_REQUIRED和ISOLATION_DEFAULT)。那就是比赛中的MatchAlwaysTransactionAttributeSource的“MatchAlways”部分。

改变默认的TransactionAttribute

就像前面提到的那样,MatchAlwaysTransactionAttributeSource的getTransactionAttribute()方法将返回一个事务策略为PROPAGATION_REQUIRED/ISOLATION_DEFAULT的事务属性。如果你想要MatchAlwaysTransactionAttributeSource返回一个不同默认值的TransactionAttribute,你可以在transactionAttribute的property中写另一个TransactionAttribute。

举个例子,为了得到以PROPAGATION_REQUIRES_NEW和ISOLATION_PEPEATABLE_READ作为事务策略的TransactionAttribute,把下面这个xml片断放到spring的配置文件中:

PROPAGATION_REQUIRES_NEW
ISOLATION_REPEATABLE_READ

myTransactionAttribute这个bean定义了一个定制的事务属性。propagationBehaviorName这个property设置了传播行为,isolationLevelName这个property设置了隔离级别。这个bean被关联到MatchAlwaysTransactionAttributeSource的transactionAttribute这个property中,为得是覆盖掉原来默认得事务属性。

注意一下,不管怎样,你可以改变MatchAlwaysTransactionAttributeSource事务属性的参数,它将一直返回同样的事务属性,而不管方法是否已经被事务处理。

当你有一个相关的简单的应用程序的时候用MatchAlwaysTransactionAttributeSource是不错的,而且它把所有的方法都应用相同的事务策略是没有问题的。但是在更复杂点的应用程序中,你需要对不同的方法使用不同的事务策略。如果是那样的话,你需要更细致有条理的控制需要使用哪些策略。所以让我们看其他的TransactionAttributeSource,这个TransactionAttribute允许你在方法的基础上声明事务策略。

5.4通过方法的名字来声明事务

Ejb规范的关键特性之一就是容器事务管理(cmt)。使用cmt,就使在ejb部署描述的时候声明事务策略成为可能。举例来说,想象一下,我们用ejb重写了spring的教学程序。我们已经声明了一个CourseSerivceBean的enrollStudentInCourse()方法需要事务处理,使用的是如下的方式来定义到ejb-jar.xml中的。

CourseServiceBean
enrollStudentInCourse
RequiresNew

Spring参照了ejb的事务声明模式,提供了几种让你在pojos上声明事务策略的事务属性来源。我们将先从NameMatchTransactionAttributeSource处开始,一个允许你在pojos上声明事务的事务属性来源,这个方法让我们想起了ejb的cmt。

5.4.1使用NameMatchTransactionAttributeSource

Spring的ejb等价物就是NameMatchTransactionAttributeSource。这个事务属性来源允许你在方法名字的基础上来声明事务属性。举例来说,声明enrollStudentInCourse()方法来获得一个requires new的传播行为,代替transactionAttributeSource这个bean的声明,方法如下:

PROPAGATION_REQUIRES_NEW

因为这个bean被命名为transactionAttributeSource,所以它应该注册到TransactionProxyFactoryBean的transactionAttributeSource这个property中去,就像上面的5.3.2部分中的MatchAlwaysTransactionAttributeSource一样。当TransactionProxyFactoryBean需要知道怎样在方法上执行事务的时候,它会去参考这个事务属性来源(就是NameMatchTransactionAttributeSource)。

NameMatchTransactionAttributeSource的properties这个property描述的是方法名称和对应的事务property描述符间的关系。事务property描述符遵循下面的格式:

在上面的例子中,只有传播行为被指定了。但是如你所看到的,许多事务属性的其他参数可以在事务属性描述符中定义。让我们看看事务属性描述符的其他组件。

指定事务隔离级别

在到达这一点之前,你已经看到如何使用NameMatchTransactionAttributeSource去声明一个事务传播行为。如果在ejb的cmt中,一切就结束了,但是在spring中,你还可以声明更多。

举例来说,想象一下,除了requires new这个传播行为,你的enrollStudentInCourse()方法还需要repeatable read这个隔离级别。所有你需要做的就是把ISOLATION_REPEATABLE_READ加到事务的property中,用豆号把传播行为和隔离级别分开:

PROPAGATION_REQUIRES_NEW,ISOLATION_REPEATABLE_READ

使用read-only事务

等一下,还有更多。你也可以把事务声明成只读的,只需要在事务属性列表上添加“read-only”举例来说,为了给getCompletedCourses方法声明一个被只读访问优化的事务,可以像下面这么写:

PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ,readOnly

指定回滚规则

最后,事务可以在异常的基础上声明为回滚或是不回滚。这个异常是在事务执行的过程中抛出来的。默认的情况是,事务只在运行时异常发生时回滚且在已检查的异常的情况下不会回滚。然而,你也可以指定事务在具体的已检查异常发生时回滚。

比如,当一个CourseException异常抛出时,为了使事务一直回滚,可以改变事务属性,如下所示:
PROPAGATION_REQUIRES_NEW,ISOLATION_REPEATBLE_READ,-CourseException

注意到CourseException前面加了一个负的标志“-”。异常前面可以负的标志“-”或是正的标志“+”。负异常抛出时将导致回滚。另已方面,当正异常被抛出时,暗示着即使这种异常被抛出,事务也应该被提交。你甚至可以把运行时异常标示成正异常来组织回滚(但是要仔细考虑你是不是真想这么做)。

使用通配符来匹配

就像ejb一样,你也可以使用通配符来为多个符合一个式样的方法声明事务策略。比如,给所有以get开头的方法应用supports这个传播行为,像下面这么用

PROPAGATION_SUPPORTS
NameMatchTransactionAttributeSource是模仿ejb的cmt的主要途径。只靠pojo和甚至更多的能量。在5.5中,我们将加快讨论spring其他事务属性来源的速度。首先,让我们看一下,你如何直接用TransactionProxyFactoryBean来声明一个名字匹配的事务,不用声明NameMatchTransactionAttributeSource

名字匹配事务的快捷方式

到目前为止,我们已经告诉你如何用通过一个已经定义好的bean实例来使用一个NameMatchTransactionAttributeSource,何如何命名transactionAttributeSource。用这种方法,transactionAttributeSource这个bean将注册到TransactionProxyFactoryBean的transactionAttributeSource这个property中。这个方法能很好的工作,但是还有更简单的途径来做到这一点。

当它出现的时候,TransactionProxyFactoryBean也有一个transactionAttributes这个property。你可以直接把事务的properties注册到TransactionProxyFactoryBean的transactionAttributes这个property中去,来取代把NameMatchTransactionAttributeSource注册到这个property中。就像下面那样:
PROPAGATION_REQUIRES_NEW

把事务的properties注册到transactionProperties这个property和把NameMatchTransactionAttributeSource注册到transactionAttributeSource这个property的功能是一样的。在表象之下,TransactionProxyFactoryBean初始化了它自己的NameMatchTransactionAttributeSource而且通过注册它的transactionProperties这个property到NameMatchTransactionAttributeSource的setProperties()方法中来传递properties的。结果就是,你不需要创建单独的transactionAttributeSource这个bean。

5.5用元数据来声明事务
到目前为止,你已经知道如何在spring的xml配置文件中声明事务了。事实证明这样比把事务处理代码写到程序中受到更少的侵入(低耦合)。然而,这样做,你不能不把方法的事务策略声明到一个和你方法定义分离的文件中。

猜你喜欢

转载自www.cnblogs.com/yan562474129/p/9119369.html
今日推荐