欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring 事务 (二) Spring 声明式事务 配置

程序员文章站 2022-05-21 09:32:25
...

Spring 事务 (二) Spring 声明式事务 配置

 

2.  Spring 声明式事务 配置

 

     由Spring 事务 (一)知道,Spring事务基本概念中,最重要的是

     PlatformTransactionManager,它的

     TransactionStatus getTransaction(TransactionDefinition definition)

     方法根据一个TransactionDefinition生成TransactionStatus.

     这样可知,配置事务,首先需要配置一个PlatformTransactionManager信息,

     然后需要配置 TransactionDefinition信息.而TransactionStatus是返回的值,

     根据前面两个配置由TransactionStatus对象获取相应的配置信息.

     

     所以配置时,主要配置PlatformTransactionManager,TransactionDefinition.

     

     Spring的事务功能由事务代理完成.

     一般的流程是:

     先编写业务接口,

     然后编写一个业务接口的实现类,并配置为bean(targetBean),

     再配置一个TransactionProxyFactoryBean,得到一个代理bean(代理targetBean),

     然后客户端代码中,通过代理bean的id来调用getBean方法,得到TransactionProxyFactoryBean配置的bean.

     

     配置这个代理bean需要传入一个事务管理器(PlatformTransactionManager),一个目标bean(targetBean),

     并指定事务代理的事务属性.

     

     上述配置TransactionProxyFactoryBean的方式为显示的,即配置一个targetBean,和一个有事务行为的代理bean,

     实际上配置了两个bean,而原来的targetBean是不直接使用的,假如代码中不小心使用了targetBean,则会产生问题.

     

     因此Spring还提供了一种基于tx命名空间的方式来配置事务代理bean,这种配置类似于AOP配置,只配置一个bean,

     当根据bean id使用getBean()获取bean实例时,Spring容器自动提供事务代理bean的实例.

     

     http://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/section5.html

     上面链接中,还讨论了使用拦截器实现的事务(非事务代理bean方式),供参考.

     

   

2.1  PlatformTransactionManager配置

 

     PlatformTransactionManager配置,根据不同数据源的使用,以及是局部事务还是全局事务 来分类,

     主要有下面几种配置方式:

     

2.1.1 数据源使用JDBC数据源的局部事务,使用DataSourceTransactionManager类

 

     

<!-- 数据源使用JDBC数据源的局部事务,使用DataSourceTransactionManager类 -->
     <!-- DataSource配置,可配置为c3p0连接池等,这里省略 -->
     <bean id="dataSource" ...>...</bean>
     <bean id="transactionManager" 
           class="org.springframework.jdbc.datasouce.DataSouceTransactionManager">
       <!-- 配置DataSourceTransactionManager时,需要依赖注入DataSource的引用 -->
       <property name="dataSource" ref="dataSource"/>      
     </bean>

 

 

2.1.2  采用全局事务的特定实现

    

     

<!-- 采用全局事务的特定实现 -->
     <bean id="transactionManager" 
           class="org.springframework.transaction.jta.JtaTransactionManager" />

 
 

     

     上面配置,无需指定datasource. 全局事务有JTA实现(比如有的容器weblogic等实现JTA,

     也有专门的开源框架实现JTA,如JOTM)     

 

2.1.3 使用Hibernate的配置

 

     

<!-- 使用Hibernate的配置 -->
     <!-- DataSource配置,可配置为c3p0连接池等,这里省略 -->
     <bean id="dataSource" ...>...</bean>
     <!-- Hibernate的SessionFactory配置,这里省略 --> 
     <bean id="sessionFactory" ...>...</bean>
     <bean id="transactionManager"
           <!-- 针对Hibernate的特定实现类 --> 
           class="org.springframework.orm.hibernate3.HibernateTransactionManager">
       <!-- 配置Hibernate时,需要依赖注入SessionFactory的引用 -->
       <property name="sessionFactory" ref="sessionFactory"/>      
     </bean>

 

     

3 配置 TransactionProxyFactoryBean

 

3.1 配置一个targetBean(业务逻辑bean),和一个有事务行为的代理bean

 

     

<!-- 数据源使用JDBC数据源的局部事务,使用DataSourceTransactionManager类 -->
     <!-- DataSource配置,可配置为c3p0连接池等,这里省略 -->
     <bean id="dataSource" ...>...</bean>
     <bean id="transactionManager" 
           class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <!-- 配置DataSourceTransactionManager时,需要依赖注入DataSource的引用 -->
       <property name="dataSource" ref="dataSource"/>      
     </bean>
     
     <!-- 一个业务逻辑bean -->
     <bean id="newsDao" class="xxx.xxx.NewsDao" >
       <property name="ds" ref="dataSource" />
     </bean>
     
     <!-- 为业务逻辑bean配置事务代理 -->
     <bean id="newsDaoTran" class="org.springframework.transaction.intereptor.TransactionProxyFactoryBean">
       <!-- 注入事务管理器 -->
       <property name="transactionManager" ref="transactionManager" />
       <!-- 指定被代理的目标bean -->
       <property name="target" ref="newDao" />
       <!-- 指定事务属性 -->
       <property name="transactionAttributes">
         <props>
           <prop key="*">PROPAGATION_REQUIRED</prop>
         </props>
       </property>
     </bean>

 

     

     上面transactionAttributes标示注入的是一个properties类型的属性.

     key表示方面名称,可以使用通配符,

     value表示事务规则:

     指定事务属性的取值有较复杂的规则,具体的书写规则如下:

     

     传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]

     

     只有传播行为是必须指定的,其他的可以省略(取默认值)

     

      传播行为是唯一必须设置的属性,其他都可以忽略,Spring为我们提供了合理的默认值。

      传播行为的取值必须以“PROPAGATION_”开头,

        具体包括:

        PROPAGATION_MANDATORY、

        PROPAGATION_NESTED、

        PROPAGATION_NEVER、

        PROPAGATION_NOT_SUPPORTED、

        PROPAGATION_REQUIRED、

        PROPAGATION_REQUIRES_NEW、

        PROPAGATION_SUPPORTS,共七种取值。

      隔离级别的取值必须以“ISOLATION_”开头,

        具体包括:

        ISOLATION_DEFAULT、

        ISOLATION_READ_COMMITTED、

        ISOLATION_READ_UNCOMMITTED、

        ISOLATION_REPEATABLE_READ、

        ISOLATION_SERIALIZABLE,共五种取值。

      如果事务是只读的,那么我们可以指定只读属性,

        使用“readOnly”指定。否则我们不需要设置该属性。

      超时属性的取值必须以“TIMEOUT_”开头,

        后面跟一个int类型的值,表示超时时间,单位是秒。

      不影响提交的异常是指,即使事务中抛出了这些类型的异常,

        事务任然正常提交。必须在每一个异常的名字前面加上“+”。

        异常的名字可以是类名的一部分。比如“+RuntimeException”、“+tion”等等。

      导致回滚的异常是指,当事务中抛出这些类型的异常时,事务将回滚。

        必须在每一个异常的名字前面加上“-”。异常的名字可以是类名的全部或者部分,

        比如“-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 事务传播行为,

      并且该事务是只读的。

      除此之外,其他的属性均使用默认值。

      比如,隔离级别和超时时间使用底层事务性资源的默认值,

      并且当发生未检查异常,则回滚事务,

      发生已检查异常则仍提交事务。

      

      参考:

      http://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/section5.html

      

3.2  基于tx命名空间的方式来配置事务代理bean

      这种配置类似于AOP配置,只配置一个bean,

      当根据bean id使用getBean()获取bean实例时,Spring容器自动提供事务代理bean的实例.

      

      

<?xml version="1.0" encoding="UTF-8"?>
      <!-- 需要使用这样的beans头,指定tx,aop的schema -->  
      <beans xmlns="http://www.springframework.org/schema/beans"  
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
              xmlns:aop="http://www.springframework.org/schema/aop"
              xmlns:tx="http://www.springframework.org/schema/tx"
              xsi:schemaLocation="http://www.springframework.org/schema/beans  
                  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                  http://www.springframework.org/schema/aop 
                  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
                  http://www.springframework.org/schema/tx 
                  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" 
      > 
      
         <!-- 数据源使用JDBC数据源的局部事务,使用DataSourceTransactionManager类 -->
         <!-- DataSource配置,可配置为c3p0连接池等,这里省略 -->
         <bean id="dataSource" ...>...</bean>
         <bean id="transactionManager" 
               class="org.springframework.jdbc.datasouce.DataSouceTransactionManager">
           <!-- 配置DataSourceTransactionManager时,需要依赖注入DataSource的引用 -->
           <property name="dataSource" ref="dataSource"/>      
         </bean>
         
         <!-- 一个业务逻辑bean -->
         <bean id="newsDao" class="xxx.xxx.NewsDao" >
           <property name="ds" ref="newsDao" />
         </bean>
         
         <!-- 
              配置增强处理的bean(相当于切面类),也是Spring自动生成普通
              业务逻辑bean(targetBean)的代理Bean.里面的tx:method配置每个方法的事务属性,
              name配置方法名,可使用通配符.
         -->
         <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!-- 配置详细的事务语义 -->
            <tx:attributes>
              <!-- 表示get开头的方法是只读的 -->
              <tx:method name="get*" read-only="true" />
              <!-- 其他方法使用默认的事务设置,事务传播方式也可以默认么? -->
              <tx:method name="*" />
            </tx:attributes>
         </tx:advice>
         
         <!-- AOP元素配置 -->
         <aop:config>
           <!-- 配置一个切入点 xxx.xxx包下面所有已Imp1结尾的类的所有方法-->
           <aop:pointcut id="myPoint" 
                         expression="execution(* xxx.xxx.*Imp1.*(..))" />
           <!-- 配置 (事务代理)切入点(aop:pointcut) 和 切面类(tx:advice),将二者关联起来  -->
           <aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint" />
         </aop:config>
      </beans>

 

      

      注:

        aop:config中 aop:pointcut配置切入点,表名哪些方法将会被事务代理增强.

        tx:advice 相当于配置一个切面类,并标明对符合条件的方法采用何种事务.

        aop:config中aop:advisor将上述二者关联起来

        

        <tx:attributes>的tx:method的

         name属性是必须的,可以使用通配符,表示与改事务语义管理的方法名.

         另外,还有属性:

         propagation,isolation,timeout,read-only,rollbak-for,no-rallback-for.(P684)

        

      附件中 classes12.jar是oracle的驱动包.