Spring的事务管理

来源:互联网 发布:思科mac地址绑定 编辑:程序博客网 时间:2024/06/04 18:47
 

6.3  Spring的事务
Spring的事务管理不需与任何特定的事务API耦合。对不同的持久层访问技术,编程式事务提供一致的事务编程风格,通过模板化的操作一致性地管理事务。声明式事务基于SpringAOP实现,却并不需要程序开发者成为AOP专家,亦可轻易使用Spring的声明式事务管理。

6.3.1  Spring支持的事务策略
Spring事务策略是通过PlatformTransactionManager接口体现的,该接口是Spring事务策略的核心。该接口的源代码如下:

public interface PlatformTransactionManager

{

    //平台无关的获得事务的方法

    TransactionStatus getTransaction(TransactionDefinition definition)

        throws TransactionException;

    //平台无关的事务提交方法

    void commit(TransactionStatus status) throws TransactionException;

    //平台无关的事务回滚方法

    void rollback(TransactionStatus status) throws TransactionException;

}

PlatformTransactionManager是一个与任何事务策略分离的接口,随着底层不同事务策略切换,应用必须采用不同的实现类。PlatformTransactionManager接口没有与任何事务资源捆绑在一起,它可以适应于任何的事务策略,结合Spring的IoC容器,可以向PlatformTransactionManager注入相关的平台特性。

PlatformTransactionManager接口有许多不同的实现类,应用程序面向与平台无关的接口编程,对不同平台的底层支持,由PlatformTransactionManager接口的实现类完成。从而,应用程序无须与具体的事务API耦合。因此,使用PlatformTransactionManager接口,可将代码从具体的事务API中解耦出来。

即使使用特定容器管理的JTA,代码依然无须执行JNDI查找,无须与特定的JTA资源耦合在一起。通过配置文件,JTA资源传给PlatformTransactionManager的实现类。因此,程序的代码可在JTA事务管理和非JTA事务管理之间轻松切换。

在PlatformTransactionManager接口内,包含一个getTransaction(TransactionDefinitiondefinition)方法,该方法根据一个TransactionDefinition参数,返回一个TransactionStatus对象。TransactionStatus对象表示一个事务。TransactionStatus被关联在当前执行的线程。

getTransaction(TransactionDefinitiondefinition)返回的TransactionStatus对象,可能是一个新的事务,也可能是一个已经存在的事务对象。如果当前执行的线程已经处于事务管理下,返回当前线程的事务对象,否则,返回当前线程的调用堆栈已有的事务对象。

TransactionDefinition接口定义了一个事务规则,该接口必须指定如下几个属性值:

  ● 事务隔离,当前事务和其他事务的隔离程度。例如,这个事务能否看到其他事务未提交的数据等。

  ●事务传播,通常,在事务中执行的代码都会在当前事务中运行。但是,如果一个事务上下文已经存在,有几个选项可指定该事务性方法的执行行为。例如,大多数情况下,简单地在现有的事务上下文中运行;或者挂起现有事务,创建一个新的事务。Spring提供EJB CMT(Contain ManagerTransaction,容器管理事务)中所有的事务传播选项。

  ● 事务超时,事务在超时前能运行多久。事务的最长持续时间。如果事务一直没有被提交或回滚,将在超出该时间后,系统自动回滚事务。

  ● 只读状态,只读事务不修改任何数据。在某些情况下(例如使用Hibernate时),只读事务是非常有用的优化。

TransactionStatus代表事务本身,它提供了简单的控制事务执行和查询事务状态的方法。这些方法在所有的事务API中都是相同的。TransactionStatus接口的源代码如下:

public interface TransactionStatus

{

    //判断事务是否是新建的事务

    boolean isNewTransaction();

    //设置事务回滚

    void setRollbackOnly();

    //查询事务是否已有回滚标志

    boolean isRollbackOnly();

}

Spring的事务管理由PlatformTransactionManager的不同实现类完成。在Spring上下文中配置PlatformTransactionManager Bean时,必须针对不同环境提供不同的实现类。

下面提供不同的持久层访问环境,及其对应的PlatformTransactionManager实现类的  配置。

JDBC数据源的局部事务策略:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 定义数据源Bean,使用C3P0数据源实现 -->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

    <!-- 指定连接数据库的驱动 -->

    <property name="driverClass" value="com.mysql.jdbc.Driver"/>

    <!-- 指定连接数据库的URL -->

    <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

    <!-- 指定连接数据库的用户名 -->

    <property name="user" value="root"/>

    <!-- 指定连接数据库的密码 -->

    <property name="password" value="32147"/>

    <!-- 指定连接数据库连接池的最大连接数 -->

    <property name="maxPoolSize" value="40"/>

    <!-- 指定连接数据库连接池的最小连接数 -->

    <property name="minPoolSize" value="1"/>

    <!-- 指定连接数据库连接池的初始化连接数 -->

    <property name="initialPoolSize" value="1"/>

    <!-- 指定连接数据库连接池的连接最大空闲时间 -->

    <property name="maxIdleTime" value="20"/>

</bean>

<!-- 配置JDBC数据源的局部事务管理器 -->

<!-- 使用DataSourceTransactionManager 类,该类实现PlatformTransactionManager接口 -->

<!-- 针对采用数据源连接的特定实现 -->

<bean id="transactionManager"

        class="org.springframework.jdbc.datasource.
        DataSourceTransactionManager">

        <!-- DataSourceTransactionManager  bean需要依赖注入一个DataSource
        bean的引用 -->

         <property name="dataSource" ref="dataSource"/>

    </bean>

</beans>

对于容器管理JTA数据源,全局事务策略的配置文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置JNDI数据源Bean -->

    <bean id="dataSource" class="org.springframework.jndi.
    JndiObjectFactoryBean">

    <!--  容器管理数据源的JNDI -->

         <property name="jndiName" value="jdbc/jpetstore"/>

    </bean>

    <!-- 使用JtaTransactionManager类,该类实现PlatformTransactionManager接
    口 -->

    <!-- 针对采用全局事务管理的特定实现 -->

    <!-- JtaTransactionManager不需要知道数据源,或任何其他特定资源 -->

    <!-- 因为它使用容器的全局事务管理 -->

    <bean id="transactionManager"

        class="org.springframework.transaction.jta.
        JtaTransactionManager" />

</beans>

对于采用Hibernate持久层访问策略时,局部事务策略的配置文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.
    ComboPooledDataSource" destroy-method="close">

        <!-- 指定连接数据库的驱动 -->

        <property name="driverClass" value="com.mysql.jdbc.Driver"/>

        <!-- 指定连接数据库的URL -->

        <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

        <!-- 指定连接数据库的用户名 -->

        <property name="user" value="root"/>

        <!-- 指定连接数据库的密码 -->

        <property name="password" value="32147"/>

        <!-- 指定连接数据库连接池的最大连接数 -->

        <property name="maxPoolSize" value="40"/>

        <!-- 指定连接数据库连接池的最小连接数 -->

        <property name="minPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的初始化连接数 -->

        <property name="initialPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的连接最大空闲时间 -->

        <property name="maxIdleTime" value="20"/>

    </bean>

    <!-- 定义Hibernate的SessionFactory -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.
    LocalSessionFactoryBean">

        <!-- 依赖注入SessionFactory所需的数据源,正是上文定义的dataSource -->

        <property name="dataSource" ref="dataSource"/>

        <!-- mappingResources属性用来列出全部映射文件 -->

        <property name="mappingResources">

              <list>

                  <!-- 以下用来列出所有的PO映射文件 -->

                <value>lee/MyTest.hbm.xml</value>

              </list>

        </property>

          <!-- 定义Hibernate的SessionFactory的属性 -->

        <property name="hibernateProperties">

             <props>

                <!-- 指定Hibernate的连接方言 -->

                <prop key="hibernate.dialect">org.hibernate.dialect.
                MySQLDialect</prop>

                <!-- 是否根据Hibernate映射创建数据表时,选择create、update、
                create-drop -->

                  <prop key="hibernate.hbm2ddl.auto">update</prop>

             </props>

        </property>

    </bean>

    <!-- 配置Hibernate的局部事务管理器 -->

    <!-- 使用HibernateTransactionManager类,该类是PlatformTransactionManager
    接口,针对采用Hibernate持久化连接的特定实现 -->

    <bean id="transactionManager"

      class="org.springframework.orm.hibernate3.
      HibernateTransactionManager">

            <!-- HibernateTransactionManager  Bean需要依赖注入一个
            SessionFactorybean的引用 -->

         <property name="sessionFactory" ref="sessionFactory"/>

     </bean>

</beans>

对于采用Hibernate持久层访问策略时,全局事务策略的配置文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置JNDI数据源Bean -->

    <bean id="dataSource" class="org.springframework.jndi.
    JndiObjectFactoryBean">

        <!--  容器管理数据源的JNDI -->

         <property name="jndiName" value="jdbc/jpetstore"/>

    </bean>

    <!--定义Hibernate的SessionFactory -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.
    LocalSessionFactoryBean">

        <!-- 依赖注入SessionFactory所需的数据源,正是上文定义的dataSource Bean -->

        <property name="dataSource" ref="dataSource"/>

        <!-- mappingResources属性用来列出全部映射文件 -->

        <property name="mappingResources">

              <list>

                  <!-- 以下用来列出所有的PO映射文件 -->

                <value>lee/MyTest.hbm.xml</value>

              </list>

        </property>

          <!-- 定义Hibernate的SessionFactory的属性 -->

        <property name="hibernateProperties">

             <props>

                <!-- 指定Hibernate的连接方言 -->

                <prop key="hibernate.dialect">org.hibernate.dialect.
                MySQLDialect</prop>

                <!-- 是否根据Hiberante映射创建数据表时,选择create、update、
                create-drop -->

                  <prop key="hibernate.hbm2ddl.auto">update</prop>

             </props>

         </property>

    </bean>

    <!-- 使用JtaTransactionManager类,该类是PlatformTransactionManager接口,
            针对采用数据源连接的特定实现 -->

    <!--  JtaTransactionManager不需要知道数据源,或任何其他特定资源,

            因为使用容器的全局事务管理 -->

    <bean id="transactionManager"

           class="org.springframework.transaction.jta.
           JtaTransactionManager" />

</beans>

不论采用哪种持久层访问技术,只要使用JTA数据源,Spring事务管理器的配置都是一样的,因为它们都采用的是全局事务管理。

可以看到,仅仅通过配置文件的修改,就可以在不同的事务管理策略间切换,即使从局部事务到全局事务的切换。

提示:Spring所支持的事务策略非常灵活,Spring的事务策略允许应用程序在不同事务策略之间自由切换,即使需要在局部事务策略和全局事务策略之间切换,只需要修改配置文件,而应用程序的代码无须任何改变。这种灵活的设计,又何尝不是因为面向接口编程带来的优势,可见面向接口编程给应用程序更好的适应性。

6.3.2  Spring事务策略的优势
虽然在上面的配置片段中,仅仅配置了JDBC局部事务管理器、Hibernate局部事务管理器、JDBC全局事务管理器等。但Spring支持大部分持久化策略的事务管理器。

不论采用何种持久化策略,Spring都提供了一致的事务抽象,因此,应用开发者能在任何环境下,使用一致的编程模型。无须更改代码,应用就可在不同的事务管理策略中切换。Spring同时支持声明式事务管理和编程式事务管理。

使用编程式事务管理,开发者使用的是Spring事务抽象,而无须使用任何具体的底层事务API。Spring的事务管理将代码从底层具体的事务API中抽象出来,该抽象可以使用在任何底层事务基础之上。

使用声明式策略,开发者通常书写很少的事务管理代码,因此,不依赖Spring或任何其他事务API。Spring的声明式事务无须任何额外的容器支持,Spring容器本身管理声明式事务。使用声明事务策略,无须在业务代码中书写任何事务代码,可以让开发者更好地专注于业务逻辑的实现。Spring管理的事务支持多个事务资源的跨越,但无法支持跨越远程调用的事务上下文传播。

6.3.3  使用TransactionProxyFactoryBean创建事务代理
Spring同时支持编程式事务策略和声明式事务策略,大部分时候,都推荐采用声明式事务策略,使用声明事务策略的优势十分明显:

  ● 声明式事务能大大降低开发者的代码书写量。而且声明式事务几乎不需要影响应用的代码。因此,无论底层事务策略如何变化,应用程序无须任何改变。

  ● 应用程序代码无须任何事务处理代码,可以更专注于业务逻辑的实现。

  ● Spring则可对任何POJO的方法提供事务管理,而且Spring的声明式事务管理无须容器的支持,可在任何环境下使用。

  ● EJB的CMT无法提供声明式回滚规则。而通过配置文件,Spring可指定事务在遇到特定异常时自动回滚。Spring不仅可在代码中使用setRollbackOnly回滚事务,也可在配置文件中配置回滚规则。

  ● 由于Spring采用AOP的方式管理事务,因此,可以在事务回滚动作中插入用户自己的动作,而不仅仅是执行系统默认的回滚。

提示:本节不打算全面介绍Spring的各种事务策略,因此本节不会介绍编程式事务。如果读者需要更全面了解Spring事务的相关方面,请参阅笔者所著的《Spring2.0宝典》     一书。

对于采用声明式事务策略,可以使用TransactionProxyFactoryBean来配置事务代理Bean。正如它的类名所暗示的,它是一个工厂Bean,工厂Bean用于生成一系列的Bean实例,这一系列的Bean实例都是Proxy。

可能读者已经想到了,既然TransactionProxyFactoryBean产生的是代理Bean,可见这种事务代理正是基于Spring AOP组件的。配置TransactionProxyFactoryBean时,一样需要指定目标Bean。

每个TransactionProxyFactoryBean为一个目标Bean生成事务代理,事务代理的方法改写了目标Bean的方法,就是在目标Bean的方法执行之前加入开始事务,在目标Bean的方法正常结束之前提交事务,如果遇到特定异常则回滚事务。

TransactionProxyFactoryBean创建事务代理时,需要了解当前事务所处的环境,该环境属性通过PlatformTransactionManager实例传入,而相关事务传入规则在TransactionProxy-FactoryBean的定义中给出。

下面给出声明式事务配置文件的完整代码:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.
    ComboPooledDataSource" destroy-method="close">

        <!-- 指定连接数据库的驱动 -->

        <property name="driverClass" value="com.mysql.jdbc.Driver"/>

        <!-- 指定连接数据库的URL -->

        <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

        <!-- 指定连接数据库的用户名 -->

        <property name="user" value="root"/>

        <!-- 指定连接数据库的密码 -->

        <property name="password" value="32147"/>

        <!-- 指定连接数据库连接池的最大连接数 -->

        <property name="maxPoolSize" value="40"/>

        <!-- 指定连接数据库连接池的最小连接数 -->

        <property name="minPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的初始化连接数 -->

        <property name="initialPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的连接最大空闲时间 -->

        <property name="maxIdleTime" value="20"/>

    </bean>

    <!-- 定义Hibernate的SessionFactory -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.
    LocalSessionFactoryBean">

        <!-- 依赖注入SessionFactory所需的数据源,正是上文定义的dataSource -->

        <property name="dataSource" <ref="dataSource"/>

        <!-- mappingResources属性用来列出全部映射文件 -->

        <property name="mappingResources">

              <list>

                  <!-- 以下用来列出所有的PO映射文件 -->

                <value>lee/Person.hbm.xml</value>

              </list>

        </property>

          <!-- 定义Hibernate的SessionFactory属性 -->

        <property name="hibernateProperties">

             <props>

                <!-- 指定Hibernate的连接方言 -->

                <prop key="hibernate.dialect">org.hibernate.dialect.
                MySQLDialect</prop>

                <!-- 是否根据Hiberante映射创建数据表时,选择create、update、
                create-drop -->

                  <prop key="hibernate.hbm2ddl.auto">update</prop>

             </props>

        </property>

    </bean>

    <!-- 配置DAO Bean,该Bean将作为目标Bean使用 -->

    <bean id="personDAOTarget" class="lee.PersonDaoImpl">

        <!-- 采用依赖注入来传入SessionFactory的引用 -->

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <!-- 配置Hibernate的事务管理器 -->

    <!-- 使用HibernateTransactionManager类,该类实现PlatformTransactionManager
    接口,针对采用Hibernate持久化连接的特定实现 -->

    <bean id="transactionManager"

        class="org.springframework.orm.hibernate3.
        HibernateTransactionManager">

        <!-- HibernateTransactionManager Bean,它需要依赖注入一个SessionFactory
        Bean的引用 -->

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <!--  配置personDAOTarget Bean的事务代理 -->

    <bean id="personDAO"

        class="org.springframework.transaction.interceptor.
        TransactionProxyFactoryBean">

          <!-- 依赖注入PlatformTransactionManager的bean引用,此处使用
          Hibernate的bean -->

          <!-- 局部事务器,因此transactionManager 传入Hibernate事务管理器的
          引用 -->

          <property name="transactionManager" ref="transactionManager"/>

          <!-- 需要生成代理的目标bean -->

          <property name="target" ref="personDAOTarget"/>

          <!-- 指定事务属性 -->

          <property name="transactionAttributes">

              <props>

                  <!-- 以下部分为定义事务回滚规则 -->

                    <prop key="insert*">PROPAGATION_REQUIRED,
                  -MyCheckedException</prop>

                    <prop key="update*">PROPAGATION_REQUIRED</prop>

                    <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

            </props>

        </property>

    </bean>

</beans>

在上面的定义文件中,没有对DAO对象采用Service层包装。通常情况下,DAO层上应有一层Service层。事务代理则以Service层Bean为目标Bean。此处为了简化配置,TransactionProxyFactoryBean直接以DAObean作为目标bean,这一点不会影响事务代理的生成。

事务回滚规则部分定义了三个回滚规则:

第一个回滚规则表示所有以insert开始的方法,都应该满足该事务规则。PROPAGATION_REQUIRED事务传播规则指明,该方法必须处于事务环境中,如果当前执行线程已处于事务环境下,则直接执行;否则,启动新的事务然后执行该方法。该规则还指定,如果方法抛出MyCheckedException的实例及其子类的实例,则强制回滚。MyCheckedException前的“-”表示强制回滚;“+”则表示强制提交,即某些情况下,即使抛出异常也强制提交;

第二个回滚规则表示所有以update开头的方法,都遵守PROPAGATION_REQUIRED的事务传播规则;

第三个回滚规则表示除前面规定的方法外,其他所有方法都采用PROPAGATION_ REQUIRED事务传播规则,而且只读。

常见的事务传播规则有如下几个:

  ● PROPAGATION_MANDATORY,要求调用该方法的线程必须处于事务环境中,否则抛出异常。

  ● PROPAGATION_NESTED,如果执行该方法的线程已处于事务环境下,依然启动新的事务,方法在嵌套的事务里执行。如果执行该方法的线程序并未处于事务中,也启动新的事务,然后执行该方法,此时与PROPAGATION_REQUIRED相同。

  ● PROPAGATION_NEVER,不允许调用该方法的线程处于事务环境下,如果调用该方法的线程处于事务环境下,则抛出异常。

  ● PROPAGATION_NOT_SUPPORTED,如果调用该方法的线程处在事务中,则先暂停当前事务,然后执行该方法。

  ● PROPAGATION_REQUIRED,要求在事务环境中执行该方法,如果当前执行线程已处于事务中,则直接调用;如果当前执行线程不处于事务中,则启动新的事务后执行该方法。

  ● PROPAGATION_REQUIRES_NEW,该方法要求有一个线程在新的事务环境中执行,如果当前执行线程已处于事务中,先暂停当前事务,启动新的事务后执行该方法;如果当前调用线程不处于事务中,则启动新的事务后执行该方法。

  ● PROPAGATION_SUPPORTS,如果当前执行线程处于事务中,则使用当前事务,否则不使用事务。

程序里原来使用personDAO的地方,无须变化。因为,配置文件里将personDAO目标Bean的id改成personDAOTarget,为TransactionProxyFactoryBean工厂Bean所产生的代理Bean命名为personDAO。该代理Bean会包含原有personDAO的所有方法,而且为这些方法增加了不同的事务处理规则。

程序面向PersonDaoImpl类所实现的接口编程,TransactionProxyFactoryBean生成的代理Bean也会实现TransactionProxyFactoryBean接口。因此,原有的程序中调用DAO组件的代码无须任何改变。程序运行时,由事务代理完成原来目标Bean完成的工作。

事实上,Spring不仅支持对接口的代理,整合CGLIB后,Spring甚至可对具体类生成代理。只要设置proxyTargetClass属性为true就可以。如果目标Bean没有实现任何接口,proxyTargetClass属性默认被设为true,此时Spring会对具体类生成代理。当然,通常建议面向接口编程,而不要面向具体的实现类编程。

6.3.4  使用继承简化事务配置
仔细观察配置文件中两个事务代理Bean的配置时,发现两个事务代理Bean的大部分配置完全相同,如果配置文件中包含大量这样的事务代理Bean配置,配置文件将非常臃肿。考虑到大部分事务代理Bean的配置都大同小异,可以使用Bean继承来简化事务代理的配置。

正如前面部分介绍的,Bean继承是将所有子Bean中相同的配置定义成一个模板,并将此模板Bean定义成一个抽象Bean。考虑所有事务代理Bean中,有如下部分是大致相   同的:

  ● 事务代理Bean所使用的事务管理器。

  ● 事务传播规则。

因此,现在配置文件中定义如下的事务代理模板Bean,其配置代码如下:

<!-- 定义所有事务代理Bean的模板 -->

<bean id="txProxyTemplate" abstract="true"

        class="org.springframework.transaction.interceptor.
        TransactionProxyFactoryBean">

    <!-- 为事务代理Bean注入生成代理所需的PlatformTransactionManager实例 -->

    <property name="transactionManager" ref="transactionManager"/>

        <!-- 定义生成事务代理通用的事务属性 -->

        <property name="transactionAttributes">

            <props>

                <!-- 所有的方法都应用PROPAGATION_REQUIRED的事务传播规则 -->

                <prop key="*">PROPAGATION_REQUIRED</prop>

            </props>

    </property>

</bean>

而真正的事务代理Bean,则改为继承上面的事务模板Bean。考虑到将目标Bean定义在Spring容器中可能增加未知的风险,因此将目标Bean定义成嵌套Bean。

<!-- 让事务代理Bean继承模板Bean -->

<bean id="personDAO" parent="txProxyTemplate">

    <!-- 这里采用嵌套Bean的方式来定义目标Bean,当然也可以引用已存在的Bean -->

    <property name="target">

        <bean class="lee.personDAO"/>

    </property>

</bean>

此时的personDAO Bean无须具体地定义事务属性,它将在其父BeantxProxyTemplate中获取事务定义属性。此处采用嵌套Bean来定义目标Bean,因此,并未将目标Bean直接暴露在Spring的上下文中让其他模块调用。当然,也可采用一个已经存在的Bean作为目标Bean;子Bean的事务属性定义,完全可覆盖事务代理模板里的事务属性定义。如下例所示:

<!-- 让事务代理bean继承模板Bean -->

<bean id="personDAO" parent="txProxyTemplate">

    <!-- 这里,采用引用已存在的bean的方式来定义目标Bean -->

    <property name="target" ref ="personDAOTarget"/>

    <!-- 覆盖事务代理模板bean中的事务属性定义 -->

    <property name="transactionAttributes">

        <props>

            <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>

            <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

            <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>

         </props>

    </property>

</bean>

可见,采用Bean继承方式定义事务代理的方式,可以很好地简化事务代理的配置,可以避免配置事务代理Bean时的冗余配置。

提示:使用Bean继承可以很好地简化事务代理Bean的配置,通过将各事务代理Bean共同的配置信息提取成事务模板Bean,可以让实际的事务代理Bean的配置更加简洁;而且,配置方式相当直观。尽量将目标Bean配置成嵌套Bean,这样的方式可以保证更好的内聚性。

如果读者还记得前面介绍的AOP知识,应该知道还有一种更加简洁的配置,就是利用Bean后处理器,让Bean后处理器为容器中其他Bean自动创建事务代理。

6.3.5  使用自动创建代理简化事务配置
回顾6.2.6节和6.2.7节,读者可能已经想到如何自动创建代理。是的,正是通过6.2.6节和6.2.7节所给出的两个自动代理创建类来生成事务代理。

正如前文已经提到的,使用BeanNameAutoProxyCreator和DefaultAdvisorAutoProxy- Creator来创建代理时,并不一定是创建事务代理,关键在于传入的拦截器,如果传入事务拦截器,将可自动生成事务代理。

下面是使用BeanNameAutoProxyCreator自动生成事务代理的配置文件:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及相应的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.
    ComboPooledDataSource" destroy-method="close">

        <!-- 指定连接数据库的驱动 -->

        <property name="driverClass" value="com.mysql.jdbc.Driver"/>

        <!-- 指定连接数据库的URL -->

        <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

        <!-- 指定连接数据库的用户名 -->

        <property name="user" value="root"/>

        <!-- 指定连接数据库的密码 -->

        <property name="password" value="32147"/>

        <!-- 指定连接数据库连接池的最大连接数 -->

        <property name="maxPoolSize" value="40"/>

        <!-- 指定连接数据库连接池的最小连接数 -->

        <property name="minPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的初始化连接数 -->

        <property name="initialPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的连接最大空闲时间 -->

        <property name="maxIdleTime" value="20"/>

    </bean>

    <!-- 使用JDBC的局部事务策略 -->

    <bean id="transactionManager"

        class="org.springframework.jdbc.datasource.DataSource-
        TransactionManager">

        <!-- 为事务管理器注入所需的数据源Bean -->

        <property name="dataSource" ref="dataSource"/>

    </bean>

    <!-- 配置目标Bean,该目标Bean将由Bean后处理器自动生成代理 -->

    <bean id="test1" class="lee.TransactionTestImpl">

        <!-- 依赖注入目标Bean所必需的数据源Bean -->

        <property name="ds" ref="dataSource"/>

    </bean>

    <!-- 配置目标Bean,该目标Bean将由Bean后处理器自动生成代理 -->

    <bean id="test2" class="lee.TestImpl">

        <!-- 依赖注入目标Bean所必需的数据源Bean -->

        <property name="ds" ref="dataSource"/>

    </bean>

    <!-- 配置事务拦截器Bean -->

    <bean id="transactionInterceptor"

        class="org.springframework.transaction.interceptor.
        TransactionInterceptor">

        <!-- 事务拦截器bean需要依赖注入一个事务管理器 -->

        <property name="transactionManager" ref="transactionManager"/>

        <property name="transactionAttributes">

            <!-- 下面定义事务传播属性 -->

            <props>

                <prop key="insert*">PROPAGATION_REQUIRED</prop>

                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <prop key="*">PROPAGATION_REQUIRED</prop>

            </props>

        </property>

    </bean>

    <!-- 定义BeanNameAutoProxyCreator的Bean后处理器 -->

    <bean class="org.springframework.aop.framework.autoproxy.
    BeanNameAutoProxyCreator">

    <!-- 指定对满足哪些bean name的bean自动生成业务代理 -->

        <property name="beanNames">

            <!-- 下面是所有需要自动创建事务代理的Bean -->

            <list>

                <value>test1</value>

                <value>test2</value>

            </list>

            <!-- 此处可增加其他需要自动创建事务代理的Bean -->

        </property>

        <!-- 下面定义BeanNameAutoProxyCreator所需的拦截器 -->

        <property name="interceptorNames">

            <list>

                <value>transactionInterceptor</value>

                <!-- 此处可增加其他新的Interceptor -->

            </list>

        </property>

    </bean>

</beans>

如果配置文件中仅有两个目标Bean,可能不能很清楚地看出这种自动创建代理配置方式的优势,但如果有更多目标Bean需要自动创建事务代理,则可以很好地体会到这种配置方式的优势:配置文件只需要简单地配置目标Bean,然后在BeanNameAutoProxyCreator配置中增加一行即可。

提示:使用BeanNameAutoProxyCreator可以自动创建事务代理,使用DefaultAdvisor- AutoProxyCreator也可自动创建事务代理。关于后一个Bean后处理器的配置方式

再次表示感谢

6.3  Spring的事务
Spring的事务管理不需与任何特定的事务API耦合。对不同的持久层访问技术,编程式事务提供一致的事务编程风格,通过模板化的操作一致性地管理事务。声明式事务基于SpringAOP实现,却并不需要程序开发者成为AOP专家,亦可轻易使用Spring的声明式事务管理。

6.3.1  Spring支持的事务策略
Spring事务策略是通过PlatformTransactionManager接口体现的,该接口是Spring事务策略的核心。该接口的源代码如下:

public interface PlatformTransactionManager

{

    //平台无关的获得事务的方法

    TransactionStatus getTransaction(TransactionDefinition definition)

        throws TransactionException;

    //平台无关的事务提交方法

    void commit(TransactionStatus status) throws TransactionException;

    //平台无关的事务回滚方法

    void rollback(TransactionStatus status) throws TransactionException;

}

PlatformTransactionManager是一个与任何事务策略分离的接口,随着底层不同事务策略切换,应用必须采用不同的实现类。PlatformTransactionManager接口没有与任何事务资源捆绑在一起,它可以适应于任何的事务策略,结合Spring的IoC容器,可以向PlatformTransactionManager注入相关的平台特性。

PlatformTransactionManager接口有许多不同的实现类,应用程序面向与平台无关的接口编程,对不同平台的底层支持,由PlatformTransactionManager接口的实现类完成。从而,应用程序无须与具体的事务API耦合。因此,使用PlatformTransactionManager接口,可将代码从具体的事务API中解耦出来。

即使使用特定容器管理的JTA,代码依然无须执行JNDI查找,无须与特定的JTA资源耦合在一起。通过配置文件,JTA资源传给PlatformTransactionManager的实现类。因此,程序的代码可在JTA事务管理和非JTA事务管理之间轻松切换。

在PlatformTransactionManager接口内,包含一个getTransaction(TransactionDefinitiondefinition)方法,该方法根据一个TransactionDefinition参数,返回一个TransactionStatus对象。TransactionStatus对象表示一个事务。TransactionStatus被关联在当前执行的线程。

getTransaction(TransactionDefinitiondefinition)返回的TransactionStatus对象,可能是一个新的事务,也可能是一个已经存在的事务对象。如果当前执行的线程已经处于事务管理下,返回当前线程的事务对象,否则,返回当前线程的调用堆栈已有的事务对象。

TransactionDefinition接口定义了一个事务规则,该接口必须指定如下几个属性值:

  ● 事务隔离,当前事务和其他事务的隔离程度。例如,这个事务能否看到其他事务未提交的数据等。

  ●事务传播,通常,在事务中执行的代码都会在当前事务中运行。但是,如果一个事务上下文已经存在,有几个选项可指定该事务性方法的执行行为。例如,大多数情况下,简单地在现有的事务上下文中运行;或者挂起现有事务,创建一个新的事务。Spring提供EJB CMT(Contain ManagerTransaction,容器管理事务)中所有的事务传播选项。

  ● 事务超时,事务在超时前能运行多久。事务的最长持续时间。如果事务一直没有被提交或回滚,将在超出该时间后,系统自动回滚事务。

  ● 只读状态,只读事务不修改任何数据。在某些情况下(例如使用Hibernate时),只读事务是非常有用的优化。

TransactionStatus代表事务本身,它提供了简单的控制事务执行和查询事务状态的方法。这些方法在所有的事务API中都是相同的。TransactionStatus接口的源代码如下:

public interface TransactionStatus

{

    //判断事务是否是新建的事务

    boolean isNewTransaction();

    //设置事务回滚

    void setRollbackOnly();

    //查询事务是否已有回滚标志

    boolean isRollbackOnly();

}

Spring的事务管理由PlatformTransactionManager的不同实现类完成。在Spring上下文中配置PlatformTransactionManager Bean时,必须针对不同环境提供不同的实现类。

下面提供不同的持久层访问环境,及其对应的PlatformTransactionManager实现类的  配置。

JDBC数据源的局部事务策略:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 定义数据源Bean,使用C3P0数据源实现 -->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

    <!-- 指定连接数据库的驱动 -->

    <property name="driverClass" value="com.mysql.jdbc.Driver"/>

    <!-- 指定连接数据库的URL -->

    <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

    <!-- 指定连接数据库的用户名 -->

    <property name="user" value="root"/>

    <!-- 指定连接数据库的密码 -->

    <property name="password" value="32147"/>

    <!-- 指定连接数据库连接池的最大连接数 -->

    <property name="maxPoolSize" value="40"/>

    <!-- 指定连接数据库连接池的最小连接数 -->

    <property name="minPoolSize" value="1"/>

    <!-- 指定连接数据库连接池的初始化连接数 -->

    <property name="initialPoolSize" value="1"/>

    <!-- 指定连接数据库连接池的连接最大空闲时间 -->

    <property name="maxIdleTime" value="20"/>

</bean>

<!-- 配置JDBC数据源的局部事务管理器 -->

<!-- 使用DataSourceTransactionManager 类,该类实现PlatformTransactionManager接口 -->

<!-- 针对采用数据源连接的特定实现 -->

<bean id="transactionManager"

        class="org.springframework.jdbc.datasource.
        DataSourceTransactionManager">

        <!-- DataSourceTransactionManager  bean需要依赖注入一个DataSource
        bean的引用 -->

         <property name="dataSource" ref="dataSource"/>

    </bean>

</beans>

对于容器管理JTA数据源,全局事务策略的配置文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置JNDI数据源Bean -->

    <bean id="dataSource" class="org.springframework.jndi.
    JndiObjectFactoryBean">

    <!--  容器管理数据源的JNDI -->

         <property name="jndiName" value="jdbc/jpetstore"/>

    </bean>

    <!-- 使用JtaTransactionManager类,该类实现PlatformTransactionManager接
    口 -->

    <!-- 针对采用全局事务管理的特定实现 -->

    <!-- JtaTransactionManager不需要知道数据源,或任何其他特定资源 -->

    <!-- 因为它使用容器的全局事务管理 -->

    <bean id="transactionManager"

        class="org.springframework.transaction.jta.
        JtaTransactionManager" />

</beans>

对于采用Hibernate持久层访问策略时,局部事务策略的配置文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.
    ComboPooledDataSource" destroy-method="close">

        <!-- 指定连接数据库的驱动 -->

        <property name="driverClass" value="com.mysql.jdbc.Driver"/>

        <!-- 指定连接数据库的URL -->

        <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

        <!-- 指定连接数据库的用户名 -->

        <property name="user" value="root"/>

        <!-- 指定连接数据库的密码 -->

        <property name="password" value="32147"/>

        <!-- 指定连接数据库连接池的最大连接数 -->

        <property name="maxPoolSize" value="40"/>

        <!-- 指定连接数据库连接池的最小连接数 -->

        <property name="minPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的初始化连接数 -->

        <property name="initialPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的连接最大空闲时间 -->

        <property name="maxIdleTime" value="20"/>

    </bean>

    <!-- 定义Hibernate的SessionFactory -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.
    LocalSessionFactoryBean">

        <!-- 依赖注入SessionFactory所需的数据源,正是上文定义的dataSource -->

        <property name="dataSource" ref="dataSource"/>

        <!-- mappingResources属性用来列出全部映射文件 -->

        <property name="mappingResources">

              <list>

                  <!-- 以下用来列出所有的PO映射文件 -->

                <value>lee/MyTest.hbm.xml</value>

              </list>

        </property>

          <!-- 定义Hibernate的SessionFactory的属性 -->

        <property name="hibernateProperties">

             <props>

                <!-- 指定Hibernate的连接方言 -->

                <prop key="hibernate.dialect">org.hibernate.dialect.
                MySQLDialect</prop>

                <!-- 是否根据Hibernate映射创建数据表时,选择create、update、
                create-drop -->

                  <prop key="hibernate.hbm2ddl.auto">update</prop>

             </props>

        </property>

    </bean>

    <!-- 配置Hibernate的局部事务管理器 -->

    <!-- 使用HibernateTransactionManager类,该类是PlatformTransactionManager
    接口,针对采用Hibernate持久化连接的特定实现 -->

    <bean id="transactionManager"

      class="org.springframework.orm.hibernate3.
      HibernateTransactionManager">

            <!-- HibernateTransactionManager  Bean需要依赖注入一个
            SessionFactorybean的引用 -->

         <property name="sessionFactory" ref="sessionFactory"/>

     </bean>

</beans>

对于采用Hibernate持久层访问策略时,全局事务策略的配置文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置JNDI数据源Bean -->

    <bean id="dataSource" class="org.springframework.jndi.
    JndiObjectFactoryBean">

        <!--  容器管理数据源的JNDI -->

         <property name="jndiName" value="jdbc/jpetstore"/>

    </bean>

    <!--定义Hibernate的SessionFactory -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.
    LocalSessionFactoryBean">

        <!-- 依赖注入SessionFactory所需的数据源,正是上文定义的dataSource Bean -->

        <property name="dataSource" ref="dataSource"/>

        <!-- mappingResources属性用来列出全部映射文件 -->

        <property name="mappingResources">

              <list>

                  <!-- 以下用来列出所有的PO映射文件 -->

                <value>lee/MyTest.hbm.xml</value>

              </list>

        </property>

          <!-- 定义Hibernate的SessionFactory的属性 -->

        <property name="hibernateProperties">

             <props>

                <!-- 指定Hibernate的连接方言 -->

                <prop key="hibernate.dialect">org.hibernate.dialect.
                MySQLDialect</prop>

                <!-- 是否根据Hiberante映射创建数据表时,选择create、update、
                create-drop -->

                  <prop key="hibernate.hbm2ddl.auto">update</prop>

             </props>

         </property>

    </bean>

    <!-- 使用JtaTransactionManager类,该类是PlatformTransactionManager接口,
            针对采用数据源连接的特定实现 -->

    <!--  JtaTransactionManager不需要知道数据源,或任何其他特定资源,

            因为使用容器的全局事务管理 -->

    <bean id="transactionManager"

           class="org.springframework.transaction.jta.
           JtaTransactionManager" />

</beans>

不论采用哪种持久层访问技术,只要使用JTA数据源,Spring事务管理器的配置都是一样的,因为它们都采用的是全局事务管理。

可以看到,仅仅通过配置文件的修改,就可以在不同的事务管理策略间切换,即使从局部事务到全局事务的切换。

提示:Spring所支持的事务策略非常灵活,Spring的事务策略允许应用程序在不同事务策略之间自由切换,即使需要在局部事务策略和全局事务策略之间切换,只需要修改配置文件,而应用程序的代码无须任何改变。这种灵活的设计,又何尝不是因为面向接口编程带来的优势,可见面向接口编程给应用程序更好的适应性。

6.3.2  Spring事务策略的优势
虽然在上面的配置片段中,仅仅配置了JDBC局部事务管理器、Hibernate局部事务管理器、JDBC全局事务管理器等。但Spring支持大部分持久化策略的事务管理器。

不论采用何种持久化策略,Spring都提供了一致的事务抽象,因此,应用开发者能在任何环境下,使用一致的编程模型。无须更改代码,应用就可在不同的事务管理策略中切换。Spring同时支持声明式事务管理和编程式事务管理。

使用编程式事务管理,开发者使用的是Spring事务抽象,而无须使用任何具体的底层事务API。Spring的事务管理将代码从底层具体的事务API中抽象出来,该抽象可以使用在任何底层事务基础之上。

使用声明式策略,开发者通常书写很少的事务管理代码,因此,不依赖Spring或任何其他事务API。Spring的声明式事务无须任何额外的容器支持,Spring容器本身管理声明式事务。使用声明事务策略,无须在业务代码中书写任何事务代码,可以让开发者更好地专注于业务逻辑的实现。Spring管理的事务支持多个事务资源的跨越,但无法支持跨越远程调用的事务上下文传播。

6.3.3  使用TransactionProxyFactoryBean创建事务代理
Spring同时支持编程式事务策略和声明式事务策略,大部分时候,都推荐采用声明式事务策略,使用声明事务策略的优势十分明显:

  ● 声明式事务能大大降低开发者的代码书写量。而且声明式事务几乎不需要影响应用的代码。因此,无论底层事务策略如何变化,应用程序无须任何改变。

  ● 应用程序代码无须任何事务处理代码,可以更专注于业务逻辑的实现。

  ● Spring则可对任何POJO的方法提供事务管理,而且Spring的声明式事务管理无须容器的支持,可在任何环境下使用。

  ● EJB的CMT无法提供声明式回滚规则。而通过配置文件,Spring可指定事务在遇到特定异常时自动回滚。Spring不仅可在代码中使用setRollbackOnly回滚事务,也可在配置文件中配置回滚规则。

  ● 由于Spring采用AOP的方式管理事务,因此,可以在事务回滚动作中插入用户自己的动作,而不仅仅是执行系统默认的回滚。

提示:本节不打算全面介绍Spring的各种事务策略,因此本节不会介绍编程式事务。如果读者需要更全面了解Spring事务的相关方面,请参阅笔者所著的《Spring2.0宝典》     一书。

对于采用声明式事务策略,可以使用TransactionProxyFactoryBean来配置事务代理Bean。正如它的类名所暗示的,它是一个工厂Bean,工厂Bean用于生成一系列的Bean实例,这一系列的Bean实例都是Proxy。

可能读者已经想到了,既然TransactionProxyFactoryBean产生的是代理Bean,可见这种事务代理正是基于Spring AOP组件的。配置TransactionProxyFactoryBean时,一样需要指定目标Bean。

每个TransactionProxyFactoryBean为一个目标Bean生成事务代理,事务代理的方法改写了目标Bean的方法,就是在目标Bean的方法执行之前加入开始事务,在目标Bean的方法正常结束之前提交事务,如果遇到特定异常则回滚事务。

TransactionProxyFactoryBean创建事务代理时,需要了解当前事务所处的环境,该环境属性通过PlatformTransactionManager实例传入,而相关事务传入规则在TransactionProxy-FactoryBean的定义中给出。

下面给出声明式事务配置文件的完整代码:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.
    ComboPooledDataSource" destroy-method="close">

        <!-- 指定连接数据库的驱动 -->

        <property name="driverClass" value="com.mysql.jdbc.Driver"/>

        <!-- 指定连接数据库的URL -->

        <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

        <!-- 指定连接数据库的用户名 -->

        <property name="user" value="root"/>

        <!-- 指定连接数据库的密码 -->

        <property name="password" value="32147"/>

        <!-- 指定连接数据库连接池的最大连接数 -->

        <property name="maxPoolSize" value="40"/>

        <!-- 指定连接数据库连接池的最小连接数 -->

        <property name="minPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的初始化连接数 -->

        <property name="initialPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的连接最大空闲时间 -->

        <property name="maxIdleTime" value="20"/>

    </bean>

    <!-- 定义Hibernate的SessionFactory -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.
    LocalSessionFactoryBean">

        <!-- 依赖注入SessionFactory所需的数据源,正是上文定义的dataSource -->

        <property name="dataSource" <ref="dataSource"/>

        <!-- mappingResources属性用来列出全部映射文件 -->

        <property name="mappingResources">

              <list>

                  <!-- 以下用来列出所有的PO映射文件 -->

                <value>lee/Person.hbm.xml</value>

              </list>

        </property>

          <!-- 定义Hibernate的SessionFactory属性 -->

        <property name="hibernateProperties">

             <props>

                <!-- 指定Hibernate的连接方言 -->

                <prop key="hibernate.dialect">org.hibernate.dialect.
                MySQLDialect</prop>

                <!-- 是否根据Hiberante映射创建数据表时,选择create、update、
                create-drop -->

                  <prop key="hibernate.hbm2ddl.auto">update</prop>

             </props>

        </property>

    </bean>

    <!-- 配置DAO Bean,该Bean将作为目标Bean使用 -->

    <bean id="personDAOTarget" class="lee.PersonDaoImpl">

        <!-- 采用依赖注入来传入SessionFactory的引用 -->

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <!-- 配置Hibernate的事务管理器 -->

    <!-- 使用HibernateTransactionManager类,该类实现PlatformTransactionManager
    接口,针对采用Hibernate持久化连接的特定实现 -->

    <bean id="transactionManager"

        class="org.springframework.orm.hibernate3.
        HibernateTransactionManager">

        <!-- HibernateTransactionManager Bean,它需要依赖注入一个SessionFactory
        Bean的引用 -->

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <!--  配置personDAOTarget Bean的事务代理 -->

    <bean id="personDAO"

        class="org.springframework.transaction.interceptor.
        TransactionProxyFactoryBean">

          <!-- 依赖注入PlatformTransactionManager的bean引用,此处使用
          Hibernate的bean -->

          <!-- 局部事务器,因此transactionManager 传入Hibernate事务管理器的
          引用 -->

          <property name="transactionManager" ref="transactionManager"/>

          <!-- 需要生成代理的目标bean -->

          <property name="target" ref="personDAOTarget"/>

          <!-- 指定事务属性 -->

          <property name="transactionAttributes">

              <props>

                  <!-- 以下部分为定义事务回滚规则 -->

                    <prop key="insert*">PROPAGATION_REQUIRED,
                  -MyCheckedException</prop>

                    <prop key="update*">PROPAGATION_REQUIRED</prop>

                    <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

            </props>

        </property>

    </bean>

</beans>

在上面的定义文件中,没有对DAO对象采用Service层包装。通常情况下,DAO层上应有一层Service层。事务代理则以Service层Bean为目标Bean。此处为了简化配置,TransactionProxyFactoryBean直接以DAObean作为目标bean,这一点不会影响事务代理的生成。

事务回滚规则部分定义了三个回滚规则:

第一个回滚规则表示所有以insert开始的方法,都应该满足该事务规则。PROPAGATION_REQUIRED事务传播规则指明,该方法必须处于事务环境中,如果当前执行线程已处于事务环境下,则直接执行;否则,启动新的事务然后执行该方法。该规则还指定,如果方法抛出MyCheckedException的实例及其子类的实例,则强制回滚。MyCheckedException前的“-”表示强制回滚;“+”则表示强制提交,即某些情况下,即使抛出异常也强制提交;

第二个回滚规则表示所有以update开头的方法,都遵守PROPAGATION_REQUIRED的事务传播规则;

第三个回滚规则表示除前面规定的方法外,其他所有方法都采用PROPAGATION_ REQUIRED事务传播规则,而且只读。

常见的事务传播规则有如下几个:

  ● PROPAGATION_MANDATORY,要求调用该方法的线程必须处于事务环境中,否则抛出异常。

  ● PROPAGATION_NESTED,如果执行该方法的线程已处于事务环境下,依然启动新的事务,方法在嵌套的事务里执行。如果执行该方法的线程序并未处于事务中,也启动新的事务,然后执行该方法,此时与PROPAGATION_REQUIRED相同。

  ● PROPAGATION_NEVER,不允许调用该方法的线程处于事务环境下,如果调用该方法的线程处于事务环境下,则抛出异常。

  ● PROPAGATION_NOT_SUPPORTED,如果调用该方法的线程处在事务中,则先暂停当前事务,然后执行该方法。

  ● PROPAGATION_REQUIRED,要求在事务环境中执行该方法,如果当前执行线程已处于事务中,则直接调用;如果当前执行线程不处于事务中,则启动新的事务后执行该方法。

  ● PROPAGATION_REQUIRES_NEW,该方法要求有一个线程在新的事务环境中执行,如果当前执行线程已处于事务中,先暂停当前事务,启动新的事务后执行该方法;如果当前调用线程不处于事务中,则启动新的事务后执行该方法。

  ● PROPAGATION_SUPPORTS,如果当前执行线程处于事务中,则使用当前事务,否则不使用事务。

程序里原来使用personDAO的地方,无须变化。因为,配置文件里将personDAO目标Bean的id改成personDAOTarget,为TransactionProxyFactoryBean工厂Bean所产生的代理Bean命名为personDAO。该代理Bean会包含原有personDAO的所有方法,而且为这些方法增加了不同的事务处理规则。

程序面向PersonDaoImpl类所实现的接口编程,TransactionProxyFactoryBean生成的代理Bean也会实现TransactionProxyFactoryBean接口。因此,原有的程序中调用DAO组件的代码无须任何改变。程序运行时,由事务代理完成原来目标Bean完成的工作。

事实上,Spring不仅支持对接口的代理,整合CGLIB后,Spring甚至可对具体类生成代理。只要设置proxyTargetClass属性为true就可以。如果目标Bean没有实现任何接口,proxyTargetClass属性默认被设为true,此时Spring会对具体类生成代理。当然,通常建议面向接口编程,而不要面向具体的实现类编程。

6.3.4  使用继承简化事务配置
仔细观察配置文件中两个事务代理Bean的配置时,发现两个事务代理Bean的大部分配置完全相同,如果配置文件中包含大量这样的事务代理Bean配置,配置文件将非常臃肿。考虑到大部分事务代理Bean的配置都大同小异,可以使用Bean继承来简化事务代理的配置。

正如前面部分介绍的,Bean继承是将所有子Bean中相同的配置定义成一个模板,并将此模板Bean定义成一个抽象Bean。考虑所有事务代理Bean中,有如下部分是大致相   同的:

  ● 事务代理Bean所使用的事务管理器。

  ● 事务传播规则。

因此,现在配置文件中定义如下的事务代理模板Bean,其配置代码如下:

<!-- 定义所有事务代理Bean的模板 -->

<bean id="txProxyTemplate" abstract="true"

        class="org.springframework.transaction.interceptor.
        TransactionProxyFactoryBean">

    <!-- 为事务代理Bean注入生成代理所需的PlatformTransactionManager实例 -->

    <property name="transactionManager" ref="transactionManager"/>

        <!-- 定义生成事务代理通用的事务属性 -->

        <property name="transactionAttributes">

            <props>

                <!-- 所有的方法都应用PROPAGATION_REQUIRED的事务传播规则 -->

                <prop key="*">PROPAGATION_REQUIRED</prop>

            </props>

    </property>

</bean>

而真正的事务代理Bean,则改为继承上面的事务模板Bean。考虑到将目标Bean定义在Spring容器中可能增加未知的风险,因此将目标Bean定义成嵌套Bean。

<!-- 让事务代理Bean继承模板Bean -->

<bean id="personDAO" parent="txProxyTemplate">

    <!-- 这里采用嵌套Bean的方式来定义目标Bean,当然也可以引用已存在的Bean -->

    <property name="target">

        <bean class="lee.personDAO"/>

    </property>

</bean>

此时的personDAO Bean无须具体地定义事务属性,它将在其父BeantxProxyTemplate中获取事务定义属性。此处采用嵌套Bean来定义目标Bean,因此,并未将目标Bean直接暴露在Spring的上下文中让其他模块调用。当然,也可采用一个已经存在的Bean作为目标Bean;子Bean的事务属性定义,完全可覆盖事务代理模板里的事务属性定义。如下例所示:

<!-- 让事务代理bean继承模板Bean -->

<bean id="personDAO" parent="txProxyTemplate">

    <!-- 这里,采用引用已存在的bean的方式来定义目标Bean -->

    <property name="target" ref ="personDAOTarget"/>

    <!-- 覆盖事务代理模板bean中的事务属性定义 -->

    <property name="transactionAttributes">

        <props>

            <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>

            <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

            <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>

         </props>

    </property>

</bean>

可见,采用Bean继承方式定义事务代理的方式,可以很好地简化事务代理的配置,可以避免配置事务代理Bean时的冗余配置。

提示:使用Bean继承可以很好地简化事务代理Bean的配置,通过将各事务代理Bean共同的配置信息提取成事务模板Bean,可以让实际的事务代理Bean的配置更加简洁;而且,配置方式相当直观。尽量将目标Bean配置成嵌套Bean,这样的方式可以保证更好的内聚性。

如果读者还记得前面介绍的AOP知识,应该知道还有一种更加简洁的配置,就是利用Bean后处理器,让Bean后处理器为容器中其他Bean自动创建事务代理。

6.3.5  使用自动创建代理简化事务配置
回顾6.2.6节和6.2.7节,读者可能已经想到如何自动创建代理。是的,正是通过6.2.6节和6.2.7节所给出的两个自动代理创建类来生成事务代理。

正如前文已经提到的,使用BeanNameAutoProxyCreator和DefaultAdvisorAutoProxy- Creator来创建代理时,并不一定是创建事务代理,关键在于传入的拦截器,如果传入事务拦截器,将可自动生成事务代理。

下面是使用BeanNameAutoProxyCreator自动生成事务代理的配置文件:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及相应的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.
    ComboPooledDataSource" destroy-method="close">

        <!-- 指定连接数据库的驱动 -->

        <property name="driverClass" value="com.mysql.jdbc.Driver"/>

        <!-- 指定连接数据库的URL -->

        <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

        <!-- 指定连接数据库的用户名 -->

        <property name="user" value="root"/>

        <!-- 指定连接数据库的密码 -->

        <property name="password" value="32147"/>

        <!-- 指定连接数据库连接池的最大连接数 -->

        <property name="maxPoolSize" value="40"/>

        <!-- 指定连接数据库连接池的最小连接数 -->

        <property name="minPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的初始化连接数 -->

        <property name="initialPoolSize" value="1"/>

        <!-- 指定连接数据库连接池的连接最大空闲时间 -->

        <property name="maxIdleTime" value="20"/>

    </bean>

    <!-- 使用JDBC的局部事务策略 -->

    <bean id="transactionManager"

        class="org.springframework.jdbc.datasource.DataSource-
        TransactionManager">

        <!-- 为事务管理器注入所需的数据源Bean -->

        <property name="dataSource" ref="dataSource"/>

    </bean>

    <!-- 配置目标Bean,该目标Bean将由Bean后处理器自动生成代理 -->

    <bean id="test1" class="lee.TransactionTestImpl">

        <!-- 依赖注入目标Bean所必需的数据源Bean -->

        <property name="ds" ref="dataSource"/>

    </bean>

    <!-- 配置目标Bean,该目标Bean将由Bean后处理器自动生成代理 -->

    <bean id="test2" class="lee.TestImpl">

        <!-- 依赖注入目标Bean所必需的数据源Bean -->

        <property name="ds" ref="dataSource"/>

    </bean>

    <!-- 配置事务拦截器Bean -->

    <bean id="transactionInterceptor"

        class="org.springframework.transaction.interceptor.
        TransactionInterceptor">

        <!-- 事务拦截器bean需要依赖注入一个事务管理器 -->

        <property name="transactionManager" ref="transactionManager"/>

        <property name="transactionAttributes">

            <!-- 下面定义事务传播属性 -->

            <props>

                <prop key="insert*">PROPAGATION_REQUIRED</prop>

                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <prop key="*">PROPAGATION_REQUIRED</prop>

            </props>

        </property>

    </bean>

    <!-- 定义BeanNameAutoProxyCreator的Bean后处理器 -->

    <bean class="org.springframework.aop.framework.autoproxy.
    BeanNameAutoProxyCreator">

    <!-- 指定对满足哪些bean name的bean自动生成业务代理 -->

        <property name="beanNames">

            <!-- 下面是所有需要自动创建事务代理的Bean -->

            <list>

                <value>test1</value>

                <value>test2</value>

            </list>

            <!-- 此处可增加其他需要自动创建事务代理的Bean -->

        </property>

        <!-- 下面定义BeanNameAutoProxyCreator所需的拦截器 -->

        <property name="interceptorNames">

            <list>

                <value>transactionInterceptor</value>

                <!-- 此处可增加其他新的Interceptor -->

            </list>

        </property>

    </bean>

</beans>

如果配置文件中仅有两个目标Bean,可能不能很清楚地看出这种自动创建代理配置方式的优势,但如果有更多目标Bean需要自动创建事务代理,则可以很好地体会到这种配置方式的优势:配置文件只需要简单地配置目标Bean,然后在BeanNameAutoProxyCreator配置中增加一行即可。

提示:使用BeanNameAutoProxyCreator可以自动创建事务代理,使用DefaultAdvisor- AutoProxyCreator也可自动创建事务代理。关于后一个Bean后处理器的配置方式

再次表示感谢

转自:http://blog.csdn.net/gabriel80/archive/2008/05/23/2473880.aspx

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 气泡机打不了气怎么办 膝盖好冷好凉怎么办吃什么好 卸妆水进眼睛了怎么办 外出没带卸妆水怎么办? 种睫毛卸睫膏弄眼睛里面了 怎么办 化了妆没卸妆水怎么办 痘痘变成了黑痣怎么办 痘痘形成的痣怎么办 图片文件重命名改不了怎么办 想给宝宝改名字怎么办 洗照片像素过低怎么办 用ps改尺寸照片变形怎么办 平安银行卡三次密码错误怎么办 平安银行卡密码忘了怎么办 平安银行行用卡多次还款怎么办 平安银行大润发卡还不上怎么办 八载图片被投诉怎么办 ps用替换颜色后怎么办 三星s8百度闪退怎么办 吃了过敏的东西怎么办 过敏了痒的厉害怎么办 脸吃麻辣过敏了怎么办 脸过敏发红痒怎么办急救 脸过敏怎么办快速治疗方法 下巴起噶的过敏怎么办 药物过敏脸肿了怎么办 吃虾过敏全身痒怎么办 全身过敏怎么办 痒的厉害 吃小龙虾过敏全身痒怎么办 脸过敏肿起来了怎么办 小孩对灰尘树木过敏怎么办 小孩过敏怎么办痒的很 脸皮肤过敏很痒怎么办 浅粉色绵羊皮衣不好清洗怎么办 皮料染上其他颜色怎么办 宝宝的银锁发黑怎么办 pu鞋子蹭坏了怎么办 包的油边脱胶了怎么办 pu包有点破皮怎么办 买房子不给过户怎么办 布艺床头有味道怎么办