全面分析 Spring 的编程式事务管理及声明式事务管理

一、Spring 事务属性剖析

事务治理至关主要。它担保了用户的每次操作都是靠得住的,即便发生了异常的情形,也不至于损坏后台数据的完整性。

在 Spring 中,事务是经由进程 TransactionDefinition 接口来界定的。该接口包括与事务属性有关的方法。具体如清单1所示:

清单1. TransactionDefinition 接口中的方法

public interface TransactionDefinition{
int getIsolationLevel();
int getPropagationBehavior();
int getTimeout();
boolean isReadOnly();
}

1、事务隔离级别

隔离级别是指若干个并发的事务之间的隔离水平。TransactionDefinition 接口中有五个隔离级:

TransactionDefinition.ISOLATION_DEFAULT:这是默许值,是底层数据库的默许隔离级别。对除一部分数据库而言这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表现一个事务可以读取其他一个事务修改但还没有提交的数据。该级别不能避免脏读和可重复读,所以很少行使该隔离级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表现一个事务只能读取其他一个事务已提交的数据。该级别可以避免脏读。TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表现一个事务在悉数进程可以多次重复实行某个查询,而且每次前往的记载都沟通。该级别可以避免脏读和可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:一切的事务顺次一一实行,该级别可以避免脏读、可重复读和幻读。常日情形下也不会用到该级别。

2、事务流传举动

所谓事务的流传举动是指,假定在当前事务之前,一个事务已存在,此时有若干选项可以指定一个事务性方法的执行步骤。在TransactionDefinition中包括了以下几个流传举动的常量:
TransactionDefinition.PROPAGATION_REQUIRED:假定当前存在事务,则介入该事务;假定当前没有事务,则树立一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:树立一个新的事务,假定当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:假定当前存在事务,则介入该事务;假定当前没有事务,则以非事务的体式格式连续运转。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务体式格式运转,假定当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务体式格式运转,假定当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:假定当前存在事务,则介入该事务;假定当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:假定当前存在事务,则树立一个事务作为当前事务的嵌套事务来运转; 假定当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
这里需要指出的是,后面的六种事务流传举动是 Spring 从 EJB 中引入的,他们同享沟通的概念。而 PROPAGATION_NESTED是 Spring 所独有的。以 PROPAGATION_NESTED 启动的事务内嵌于内部事务中(假定存在内部事务的话),此时,内嵌事务并非一个自力的事务,它依托于内部事务的存在,只需经由进程内部的事务提交,才华激 起内部事务的提交,嵌套的子事务不能零丁提交。假定熟习 JDBC 中的留存点(SavePoint)的概念,那嵌套事务就很随意疏忽理解了,其实嵌套的子事务就是留存点的一个运用,一个事务中可以包括多个留存点,每一个 嵌套子事务。其他,内部事务的回滚也会致使嵌套子事务的回滚。

3、事务超时
所谓事务超时,就是指一个事务所准许实行的最长时间,假定超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示,其单元是秒。

4、事务的只读属性
事务的只读属性是指,对事务性成本中包括只读操作或是读写操作。所谓事务性成本就是指那些被事务治理的成本,好比数据源、 JMS 成本,和自己界定的事务性成本等等。假定一定只对事务性成本中表示只读操作,那么我们可以将事务符号为只读的,以提高事务处置责罚的功效。在 TransactionDefinition 中以 boolean 类型来表示该事务能否是只读。


二、Spring 事务治理 API 剖析

Spring 框架中,触及到事务治理的 API 中最主要的有三个:TransactionDefinition、PlatformTransactionManager、 TransactionStatus。所谓事务治理,其实就是“依照给定的事务划定来实行提交或回滚操作”。“给定的事务划定”就是用 TransactionDefinition表现的,“依照……来实行提交或回滚操作”就是用 PlatformTransactionManager 来表现,而 TransactionStatus 用于表现一个运转着的事务的形态。


1、TransactionDefinition
它包括了事务的静态属性,Spring 为我们供应了一个默许的完成类:DefaultTransactionDefinition。假定该类不能满足需求,可以由TransactionDefinition 接口来完成本人的事务界定。
2、PlatformTransactionManager
PlatformTransactionManager 用于实行具体的事务操作。

清单2. PlatformTransactionManager 接口中的方法

public interface PlatformTransactionManager{
  TransactionStatus getTransaction(TransactionDefinition definition)
   throws TransactionException;
   void commit(TransactionStatus status)throws TransactionException;
   void rollback(TransactionStatus status)throws TransactionException;
}

PlatformTransactionManager 的完成类有以下:

1)DataSourceTransactionManager:适用于行使JDBC和iBatis中止数据经久化操作的情形。
2)HibernateTransactionManager:适用于行使Hibernate中止数据经久化操作的情形。
3)JpaTransactionManager:适用于行使JPA中止数据经久化操作的情形。
其他还有JtaTransactionManager 、JdoTransactionManager、JmsTransactionManager等等。
假定我们行使JTA中止事务治理,我们可以经由进程 JNDI 和 Spring 的 JtaTransactionManager 来获得一个容器治理的 DataSource。JtaTransactionManager 不需求晓得 DataSource 和其他特定的成本,因为它将行使容器供应的全局事务治理。而对其他事务治理器,好比DataSourceTransactionManager,在界说时 需求供应底层的数据源作为其属性,也就是 DataSource。与 HibernateTransactionManager 对应的是 SessionFactory,与 JpaTransactionManager 对应的是 EntityManagerFactory 等等。

3、TransactionStatus
PlatformTransactionManager.getTransaction(…) 往传递一个 TransactionStatus 对象。TransactionStatus 接口供给了一个庞杂的掌握事务实行和查询事务形态的方法。
清单3. TransactionStatus 接口中的方法

public  interface TransactionStatus{
   boolean isNewTransaction();
   void setRollbackOnly();
   boolean isRollbackOnly();
}

三、编程式事务治理
Spring 的编程式事务治理概述
在 Spring 中,编程式事务治理对基于 POJO 的运用来说是独一选择。用过 Hibernate 的人都晓得,我们需求在代码中显式挪用beginTransaction()、commit()、rollback()等事务治理相关的方法,这就是编程式事务治理。在底层,Spring 仍然将事务操作请托给底层的经久化框架来实行。
1、基于底层 API 的编程式事务治理
PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三个接口。
清单4. 基于底层 API 的事务治理示例代码

public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionDefinition txDefinition;
private PlatformTransactionManager txManager;
......
public boolean transfer(Long fromId, Long toId, double amount) {
TransactionStatus txStatus = txManager.getTransaction(txDefinition);
boolean result = false;
try {
result = bankDao.transfer(fromId, toId, amount);
txManager.commit(txStatus);
} catch (Exception e) {
result = false;
txManager.rollback(txStatus);
System.out.println("Transfer Error!");
}
return result;
}
}

清单5. 基于底层API的事务治理示例设置配备文件

<bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
<property name="txManager" ref="transactionManager"/>
<property name="txDefinition">
<bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>
</property>
</bean>


2、基于 TransactionTemplate 的编程式事务治理
这类事务治理格式很随意疏忽理解,但使人头疼的是,事务治理的代码散落在营业逻辑代码中,损坏了原有代码的整体性,而且每一个方法都包括了相反的启动事务、提交/回滚事务的样板代码。幸而,Spring 也意想到了这些,并供应了简化的方法,这就是 Spring 在数据接见层异经罕有的模板回调方法。如清单6所示:
清单6. 基于 TransactionTemplate 的事务治理示例代码

public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionTemplate transactionTemplate;
......
public boolean transfer(final Long fromId, final Long toId, final double amount) {
return (Boolean) transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus status) {
Object result;
try {
result = bankDao.transfer(fromId, toId, amount);
} catch (Exception e) {
status.setRollbackOnly();
result = false;
System.out.println("Transfer Error!");
}
return result;
}
});
}
}

清单 7. 基于 TransactionTemplate 的事务治理示例设置配备文件
<bean id="bankService"
class="footmark.spring.core.tx.programmatic.template.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>

TransactionTemplate 的 execute() 方法有一个 TransactionCallback 类型的参数,该接口中有一个 doInTransaction() 方法,我们以匿名内部类的体式格式完成 TransactionCallback 接口,并在其 doInTransaction() 方法中书写业务逻辑代码。doInTransaction() 方法有一个TransactionStatus 类型的参数,setRollbackOnly() 方法将事务标识为回滚的,以实行事务回滚。
TransactionCallback 接口有一个子接口 TransactionCallbackWithoutResult,该接口中有一个 doInTransactionWithoutResult() 方法,TransactionCallbackWithoutResult 接口用于如果不想返回结果( resultObject ),则可以用 TransactionCallbackWithoutResult 来实现 TransactionCallback 接口。

四、声明式事务治理
1、Spring 的声明式事务治理概述
Spring 的声明式事务治理在底层是建立在 AOP 的之上的。
声明式事务治理优点:不需要在业务逻辑代码中搀杂事务治理的代码,只需在设置配备文件中做相关的事务划定声明(或基于标注的体式格式),即可以将事务划定运用到业务逻辑中。因为事务治理本身就是一个范例的横切逻辑,恰是 AOP 的用武之地。
和编程式事务比照,声明式事务独一缺少地方是,后者的最细粒度只能浸染到方法级别,没法做到像编程式事务那样可以浸染到代码块级别。

1、基于TransactionInterceptor 的声明式事务治理
最初,Spring 供应了 TransactionInterceptor 类来实行声明式事务治理功用。
清单 8. 基于 TransactionInterceptor 的事务治理示例配置文件

<beans...>
......
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="bankServiceTarget"
class="footmark.spring.core.tx.declare.origin.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<bean id="bankService"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="bankServiceTarget"/>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
......
</beans>

我们还需求设置配备铺排一个 ProxyFactoryBean 来组装 target 和advice。这也是范例的 Spring AOP 的做法。经由ProxyFactoryBean 生成的代理类就是织入了事务治理逻辑后的目的类。至此,声明式事务治理就算是完成了。我们没有对业务逻辑代码中有任何操作,一切设置均在配置文件中完成,这就是声明式事务的最大优点。

流传举动 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,致使回滚的异常]

流传举动是独一必需设置的属性,其他都可以疏忽,Spring为我们供应了合理的默许值。
1)流传举动的取值必需以“PROPAGATION_”开首,具体包括:PROPAGATION_MANDATORY、 PROPAGATION_NESTED、PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED、 PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_SUPPORTS,共七种取 值。
2)隔离级其他取值必需以“ISOLATION_”开首,具体包括:ISOLATION_DEFAULT、 ISOLATION_READ_COMMITTED、ISOLATION_READ_UNCOMMITTED、 ISOLATION_REPEATABLE_READ、ISOLATION_SERIALIZABLE,共五种取值。
3)假定事务是只读的,那末我们可以指定只读属性,行使“readOnly”指定。否则我们不需求设置该属性。
4)超时属性的取值必需以“TIMEOUT_”开首,后面跟一个int类型的值,泄漏表现超不时辰,单元是秒。
5)不影响提交的异常是指,即便事务中抛出了这些类型的异常,事务任然正常提交。必需在每一个异常的名字后面加上“+”。异常的名字可所以类名的一部份。好比“+RuntimeException”、“+tion”等等。
6)致使回滚的异常是指,当事务中抛出这些类型的异常时,事务将回滚。必需在每一个异常的名字后面加上“-”。异常的名字可所以类名的悉数或部份,好比“-RuntimeException”、“-tion”等等。
以下是两个示例:

<property name="*Service">
PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,TIMEOUT_20,
+AbcException,+DefException,-HijException
</property>

以上表达式意思:针对一切方法名以 Service 收尾的方法,行使 PROPAGATION_REQUIRED 事务流传举动,事务的隔离级别是 ISOLATION_READ_COMMITTED,超不时辰为20秒,当事务抛出 AbcException 或 DefException 类型的异常,则仍然提交,当抛出 HijException 类型的异常时必需回滚事务。这里没有指定”readOnly”,表现事务不是只读的。

<property name="test">PROPAGATION_REQUIRED,readOnly</property>

以上表达式意思:针对一切方法名为 test 的方法,行使 PROPAGATION_REQUIRED 事务流传举动,而且该事务是只读的。除此之外,其他的属性均行使默许值。

2、基于 TransactionProxy 的声明式事务治理
后面的声明式事务虽然好,然则却存在一个异常恼人的成就:设置配备铺排文件太多。我们必需针对每一个目的对象设置配备铺排一个 ProxyFactoryBean;其他,虽然可以经由进程父子 Bean 的体式格式来复用 TransactionInterceptor 的设置配备铺排,然则理想的复用几率也不高;这样,加上目的对象本人,每一个营业类可以需求对应三个 <bean/> 设置配备铺排,跟着营业类的增多,设置配备铺排文件将会变得越来越重除夜,治理设置配备铺排文件又成了成就。
为了减缓这个成就,Spring 为我们供应了 TransactionProxyFactoryBean,用于将TransactionInterceptor 和 ProxyFactoryBean 的设置配备铺排合二为一。如清单9所示:
清单9. 基于 TransactionProxyFactoryBean 的事务治理示例设置配备铺排文件

<beans......>
......
<bean id="bankServiceTarget"
class="footmark.spring.core.tx.declare.classic.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<bean id="bankService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target" ref="bankServiceTarget"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
......
</beans>

3、基于 <tx> 命名空间的声明式事务治理
后面两种声明式事务设置配备铺排体式格式奠基了 Spring 声明式事务治理的基石。在此根蒂根抵上,Spring 2.x 引入了 <tx> 命名空间,连络行使 <aop> 命名空间,带给垦荒人员设置配备铺排声明式事务的全新体验,设置配备铺排变得加倍庞杂和无邪。其他,得益于 <aop> 命名空间的切点表达式支持,声明式事务也变得加倍丁壮夜。
如清单10所示:
清单10. 基于 <tx> 的事务治理示例设置配备铺排文件

beans......>
......
<bean id="bankService" 
class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<tx:advice id="bankAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/>
<aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
</aop:config>
......
</beans>

假定默许的事务属性就可以知足要求,那末代码简化为如清单 11 所示:
清单 11. 简化后的基于 <tx> 的事务治理示例设置配备铺排文件

<beans......>
......
<bean id="bankService"
class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<tx:advice id="bankAdvice" transaction-manager="transactionManager">
<aop:config>
<aop:pointcut id="bankPointcut" expression="execution(**.transfer(..))"/>
<aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
</aop:config>
......
</beans>

因为行使了切点表达式,我们就不需求针对每一个营业类树立一个署理对象了。其他,假定设置配备铺排的事务治理器 Bean 的名字取值为“transactionManager”,则我们可以省略 <tx:advice> 的 transaction-manager 属性,因为该属性的默许值即为“transactionManager”。

4、基于 @Transactional 的声明式事务治理
除基于命名空间的事务设置配备铺排体式格式,Spring 2.x 还引入了基于 Annotation 的体式格式,具体次要触及@Transactional 标注。@Transactional 可以浸染于接口、接口方法、类和类方法上。算作用于类上时,该类的一切 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别行使该标注来笼盖类级其他界说。如清单12所示:
清单12. 基于 @Transactional 的事务治理示例设置配备铺排文件

@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId, double amount) {
return bankDao.transfer(fromId, toId, amount);
}

pring 行使 BeanPostProcessor 来处置责罚 Bean 中的标注,是以我们需求在设置配备铺排文件中作以下声明来激活该后处置责罚 Bean,如清单13所示:
清单13. 启用后处置责罚Bean的设置配备铺排

<tx:annotation-driven transaction-manager="transactionManager"/>

与后面相反,transaction-manager 属性的默许值是 transactionManager,假定事务治理器 Bean 的名字即为该值,则可以省略该属性。
虽然 @Transactional 注解可以浸染于接口、接口方法、类和类方法上,然则 Spring 小组建议不要在接口或接口方法下行使该注解,因为这只需外行使基于接口的署理时它才会生效。其他, @Transactional 注解理应只被运用到 public 方法上,这是由 Spring AOP 的实质决意的。假定你在 protected、private 或默准许见性的方法下行使 @Transactional 注解,这将被疏忽,也不会抛出任何异常。

基于 <tx> 命名空间和基于 @Transactional 的事务声明体式格式各有优瑕玷。基于 <tx> 的体式格式,其优点是与切点表达式连络,功用丁壮夜。行使切点表达式,一个设置配备铺排可以婚配多个方法,而基于 @Transactional 的体式格式必需在每一个需求行使事务的方法或类上用 @Transactional 标注,虽然可以除夜多半事务的划定礼貌是不合的,然则对 @Transactional 而言,也没法重用,必需一一指定。其他一方面,基于 @Transactional 的体式格式行使起来异常庞杂清晰了了,没有进修成本。垦荒人员可以凭证需求,任选个中一种行使,甚至也可以凭证需求夹杂行使这两种体式格式。

假定不是对遗留代码中止珍重,则不建议再行使基于 TransactionInterceptor 和基于TransactionProxyFactoryBean 的声明式事务治理体式格式,然则,进修这两种体式格式异常无益于对底层完成的理解。

虽然上面共枚举了四种声明式事务治理体式格式,然则这样的划分只是为了便于理解,其实后台的完成体式格式是一样的,只是用户行使的体式格式不合而已。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值