Spring Transaction 分析事务属性

众所周知的ACID属性: 
原子性(atomicity)、一致性(consistency)、隔离性(isolation)以及持久性(durability)。我们无法控制一致性、原子性以及持久性,但可以控制超时,设置事务的只读性以指定隔离级别。 
Spring在TransactionDefinition接口封装了所有这些设置。 

TransactionDefinition接口:

public interface TransactionDefinition {


	int PROPAGATION_REQUIRED = 0;


	int PROPAGATION_SUPPORTS = 1;

	
	int PROPAGATION_MANDATORY = 2;

	
	int PROPAGATION_REQUIRES_NEW = 3;


	int PROPAGATION_NOT_SUPPORTED = 4;

	
	int PROPAGATION_NEVER = 5;


	int PROPAGATION_NESTED = 6;


	
	int ISOLATION_DEFAULT = -1;


	int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

	
	int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;


	int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;


	int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;


	int TIMEOUT_DEFAULT = -1;



	int getPropagationBehavior();


	int getIsolationLevel();


	int getTimeout();


	boolean isReadOnly();


	String getName();

}

 

getTimeout:返回一个事务必须完成的时间限制。 

isReadOnly:表示事务是否只读。 

getIsolationLevel:他对其他事务所看到的数据变化进行控制。 
事务隔离级别: 
隔离级别 说明 
ISOLATION_DEFAULT 默认级别(对大多数数据库来说就是ISOLATION_READ_COMMITTED) 
ISOLATION_READ_UNCOMMITTED 最低的隔离级别。事实上我们不应该隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。 
ISOLATION_READ_COMMITTED 大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入活更新的数据。这意味着在事务的不同点上,如果其他事务修改数据,你会看到不同的数据。 
ISOLATION_REPEATABLE_READ 该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。 
ISOLATION_SERIALIZABLE 代价最大、可靠性最高的隔离级别,所有的事务都是俺顺序一个接一个的执行。 

getPropagationBehavior:指定了当代码请求一个新的事务时Spring所做的事情。 
传播行为指: 
传播行为 说明 
PROPAGATION_REQUIRED 当前如果有事务,Spring就会使用该事务;否则会开始一个新事务。 
PROPAGATION_SUPPORTS 当前如果有事务,Spring就会使用该事务;否则不会开启一个新事务。 
PROPAGATION_MANDATORY 当前如果有事务,Spring就会使用该事务;否则会抛出异常。 
PROPAGATION_REQUIRES_NEW Spring总会开始一个新事务。如果当前有事务,则该事务挂起。 
PROPAGATION_NOT_SUPPORTED Spring不会执行事务中的代码。代码总是在非事务环境下执行,如果当期有事务,则该事务挂起。 
PROPAGATION_NEVER 即使当前有事务,Spring也会在飞事务环境下执行。如果当前有事务,则抛出异常。 
PROPAGATION_NESTED 如果当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与PROPAGATION_REQUIRED一样。

TransactionStatus接口:

package org.springframework.transaction;

import java.io.Flushable;


public interface TransactionStatus extends SavepointManager, Flushable {


	boolean isNewTransaction();


	boolean hasSavepoint();

	
	void setRollbackOnly();

	
	
	boolean isRollbackOnly();


	@Override
	void flush();

	boolean isCompleted();

}

 

setRollbackOnly:将一个事务表示为不可提交的。

PlatformTransactionManager的实现: 

package org.springframework.transaction;


public interface PlatformTransactionManager {


	TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

	
	void commit(TransactionStatus status) throws TransactionException;

	
	void rollback(TransactionStatus status) throws TransactionException;

}

 

 


使用TransactionDefinition和TransactionStatus接口,创建并管理事务。
 
DataSourceTransactionManager控制着从DataSource中获得的JDBC Connection上的事务执行; 
HibernateTransactionManager控制着Hibernate session上的事务执行; 
JdoTransactionManager管理着JDO事务; 
JtaTransactionManager将事务管理委托给JTA。

JDBC:

<!-- 声明事务处理器 -->  
<bean id="transactionManager"  
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
    <property name="dataSource" ref="dataSource"></property>  
</bean>  
  
<!-- 声明事务通知 -->  
<tx:advice id="bookShopTx"  
    transaction-manager="transactionManager">  
    <tx:attributes>  
        <tx:method name="purchase"   
            propagation="REQUIRES_NEW"  
            isolation="READ_COMMITTED"  
            rollback-for="java.lang.ArithmeticException"/>  
    </tx:attributes>  
</tx:advice>  
  
<!-- 声明事务通知需要通知哪些类的那些方法, 即: 那些方法受事务管理 -->  
<aop:config>  
    <!-- 声明切入点 -->  
    <aop:pointcut expression="execution(* cn.partner4java.spring.transaction.BookShopService.*(..))"   
        id="txPointCut"/>  
          
    <!-- 把切入点和事务通知联系起来: 既声明一个增强器 -->  
    <aop:advisor advice-ref="bookShopTx" pointcut-ref="txPointCut"/>  
</aop:config>  

 

Hibernate:

<bean id="sessionFactory"  
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>  
    <property name="dataSource" ref="dataSource"></property>      
</bean>  
  
<bean id="transactionManager"  
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
    <property name="sessionFactory" ref="sessionFactory"></property>      
</bean>  
  
<!-- 事务通知 -->  
<tx:advice id="txAdvice" transaction-manager="transactionManager">  
    <tx:attributes>  
        <tx:method name="new*" propagation="REQUIRED" isolation="DEFAULT" />  
        <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" />  
        <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" />  
        <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" />  
        <tx:method name="bulk*" propagation="REQUIRED" isolation="DEFAULT" />  
        <tx:method name="load*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
        <tx:method name="get*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
        <tx:method name="query*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
        <tx:method name="find*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
        <tx:method name="is*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
          
        <tx:method name="*" propagation="SUPPORTS" isolation="DEFAULT" />  
    </tx:attributes>  
</tx:advice>  
<aop:config>  
        <aop:advisor pointcut="execution(* *..*service*.*(..))" advice-ref="txAdvice" />  
</aop:config>   
  
<context:component-scan base-package="com.bytter"></context:component-scan>  
  
<tx:annotation-driven/>  

 

JPA:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">  
    <property name="dataSource" ref="dataSource" />  
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />  
    <property name="loadTimeWeaver">  
          <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>  
    </property>  
</bean>  
      
  <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
       <property name="entityManagerFactory" ref="entityManagerFactory"/>  
  </bean>  
    
  <tx:annotation-driven transaction-manager="transactionManager"/> 

 

基于 @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);
}

 

Spring 使用 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 的声明式事务管理方式,但是,学习这两种方式非常有利于对底层实现的理解。

虽然上面共列举了四种声明式事务管理方式,但是这样的划分只是为了便于理解,其实后台的实现方式是一样的,只是用户使用的方式不同而已。

猜你喜欢

转载自dk05408.iteye.com/blog/2210561