8 -- 深入使用Spring -- 6...2 Spring支持的事务策略

      8.6.2 使用XML Schema配置事务策略

        Spring 同时支持编程式事务策略和声明式事务策略,通常都推荐采用声明式事务策略。

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

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

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

        ⊙ EJB的CMT无法提供声明式回滚规则:而通过配置文件,Spring可指定事务在遇到特定异常时自动回滚。

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

        Spring 2.x 的XML Schema方式提供了简洁的事务配置策略,Spring 2.x提供了 tx:命名空间 来配置事务管理,tx:命名空间下提供了<tx:advice.../>元素来配置事务增强处理,一旦使用该元素配置了事务增强处理,就可直接使用<aop:advisor.../>元素启用自动代理了。

        配置<tx:advice.../>元素时除了需要transaction-manager属性指定事务管理器之外,还需要配置一个<attributes.../>子元素,该子元素里又可包含多个<method.../>子元素。

        配置<tx:advice.../>元素的重点就是配置<method.../>子元素,实际上每个<method.../>子元素都为一批方法指定了所需的事务定义,包括事务传播属性、事务隔离属性、事务超时属性、只读事务、对指定异常回滚、对指定异常不回滚等。

        配置<method.../>子元素可以指定如下属性:

          ⊙ name : 必选属性,与该事务语义关联的方法名。该属性支持使用通配符,例如:‘add*’、‘delete*’、‘get*byId’、‘update*’等。

          ⊙ propagation : 指定事务传播行为,该属性值可为Propagation枚举类的任一枚举值。该属性的默认值为Propagation.REQUIRED。

          ⊙ isolation : 指定事务隔离级别,该属性值可为Isolation枚举类的任一枚举值。默认值为Isolation.DEFAULT。

          ⊙ timeout : 指定事务超时的时间(以秒为单位),指定-1意味着不超时,该属性默认值是-1.

          ⊙ read-only : 指定事务是否只读。该属性的默认值是false。

          ⊙ rollback-for : 指定触发事务回滚的异常类(应使用全限定类名),该属性可指定多个异常类,多个异常类之间以英文逗号隔开。

          ⊙ no-rollback-for : 指定不触发事务回滚的异常类(应使用全限定类名),该属性可指定多个异常类,多个异常类之间以英文逗号隔开。

        <method.../>子元素的propagation属性用于指定事务传播行为,Spring支持的事务传播行为如下:

          ⊙ PROPAGATION_MANDATORY(强制的) : 要求调用方法的线程必须处于事务环境中,否则抛出异常。

          ⊙ PROPAGATION_NEVER(决不) : 不允许调用该方法的线程处于事务中,如果调用该方法的线程处于事务环境中,则抛出异常。

          ⊙ PROPAGATION_SUPPORTS(支持) : 如果当前执行线程处于事务环境中,则使用当前事务,否则不使用事务。          

          ⊙ PROPAGATION_NOT_SUPPORED (被支持的): 如果调用该方法的线程处于事务环境中,则先暂停当前事务,然后执行该方法。

          ⊙ PROPAGATION_NESTED(嵌套的) : 即使执行该方法的线程已处于事务环境中,也依然启动新的事务,方法在嵌套的事务里执行;即使执行该方法的线程并未处于事务环境中,也启动新的事务,然后执行该方法,此时与PROPAGATION_REQUIRED相同。

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

          ⊙ PROPAGATION_REQUIES_NEW (需求): 该方法要求在新的事务环境中执行,如果当前执行线程已处于事务环境中,则先暂停当前事务,启动新事物后执行该方法;如果当前调用线程不处于事务环境中,则启动新的事务后执行方法。

        Class : NewsDaoImpl

package edu.pri.lime._8_6_2.dao;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

public class NewsDaoImpl implements NewsDao {

    private DataSource ds;

    public void setDs(DataSource ds) {
        this.ds = ds;
    }

    @Override
    public void insert(String title, String content) {
        JdbcTemplate jt = new JdbcTemplate(ds);
        jt.update("insert into news_inf values(null,?,?)", title, content);
        // 两次插入的数据违反唯一键约束
        jt.update("insert into news_inf values(null,?,?)", title, content);
        // 如果没有事务控制,则第一条记录可以被插入
        // 如果增加事务控制,将发现第一条记录也插不进去
    }

}

         XML :

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
    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-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">


    <!-- 扫描Spring的组件 -->
    <context:component-scan base-package="edu.pri.lime._8_6_2.dao"/>
    
    <!-- 定义数据源Bean,使用C3P0数据源实现,并注入数据源的必要信息 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost/spring"/>
        <property name="user" value="root"/>
        <property name="password" value="System"/>
        <property name="maxPoolSize" value="40"/>
        <property name="minPoolSize" value="2"/>
        <property name="initialPoolSize" value="2"/>
        <property name="maxIdleTime" value="30"/>
    </bean>
    
    <!-- 配置JDBC数据源的局部事务管理器,使用DataSourceTransactionManager类 -->
    <!-- 该类实现了PlatformTransactionManager接口,是针对采用数据源连接的特定实现 -->
    <!-- 配置DataSourceTransactionmanager时需要依赖注入DataSource的引用 -->
    <bean id="transactionManager" class="org.spring.framework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 配置一个业务逻辑Bean -->
    <bean id="newsDao" class="edu.pri.lime._8_6_2.dao.NewsDaoImpl">
        <property name="ds" ref="dataSource"/>
    </bean>
    
    <!-- 配置事务增强处理Bean,指定事务管理器 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 用于配置详细的事务定义 -->
        <tx:attributes>
            <!-- 所有以get开头的方法是只读的 -->
            <tx:method name="get*" read-only="true"/>
            <!-- 其他方法使用默认的事务设置,指定超时时长为5秒 -->
            <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="5"/>
        </tx:attributes>
    </tx:advice>
    
    <!-- AOP配置的元素 -->
    <aop:config>
        <!-- 配置一个切入点,匹配edu.pri.lime._8_6_2.dao包下所有以Impl结尾的类里所有方法的执行 -->
        <aop:pointcut expression="execution(* edu.pri.lime._8_6_2.dao.*Impl.*(..))" id="myPointcut"/>
        <!-- 指定在myPointcut切入点应用txAdvice事务增强处理 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
    </aop:config>
</beans>

        Class : SpringTest

package edu.pri.lime._8_6_2;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import edu.pri.lime._8_6_2.dao.NewsDao;
import edu.pri.lime._8_6_2.dao.NewsDaoImpl;

public class SpringTest {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("app_8_6_2_tx.xml");
        // 获取事务代理Bean
        NewsDao dao = ctx.getBean("newsDao", NewsDaoImpl.class);
        // 执行插入操作
        dao.insert("lime", "Oracle");
    }
}

        当使用<tx:advisor.../>为目标Bean生成事务代理之后,SpringAOP将会把负责事务操作的增强处理织入目标Bean的业务方法中。

        Spring 不仅支持对接口的代理,而且也可对具体类生成代理,只要设置proxytargetClass属性为true既可以了。如果目标Bean没有实现热河接口,proxyTargetClass属性默认被设为true,此时Spring会对具体类生成代理。当然,通常建议面向接口编程,而不要面向具体的实现类编程。

        当采用<aop:advisor.../>元素将Advice和切入点绑定时,实际上是由Spring提供的Bean后处理器完成的。Spring提供了BeanNameAutoProxyCreator、DefaultAdvisorAutoProxyCreator两个Bean后处理器,它们都可以对容器中的Bean执行后处理(为他们织入切面中包含的增强处理)。当配置<aop:advisor.../>元素时传入一个txAdvice事务增强处理,所以Bean后处理器将为所有Bean实例里匹配切入点的方法织入事务操作的增强处理。

        在这种声明式事务策略下,Spring也允许为不同的业务逻辑方法指定不同的事务策略:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
    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-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">


    <!-- 扫描Spring的组件 -->
    <context:component-scan base-package="edu.pri.lime._8_6_2.dao"/>
    
    <!-- 定义数据源Bean,使用C3P0数据源实现,并注入数据源的必要信息 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost/spring"/>
        <property name="user" value="root"/>
        <property name="password" value="System"/>
        <property name="maxPoolSize" value="40"/>
        <property name="minPoolSize" value="2"/>
        <property name="initialPoolSize" value="2"/>
        <property name="maxIdleTime" value="30"/>
    </bean>
    
    <!-- 配置JDBC数据源的局部事务管理器,使用DataSourceTransactionManager类 -->
    <!-- 该类实现了PlatformTransactionManager接口,是针对采用数据源连接的特定实现 -->
    <!-- 配置DataSourceTransactionmanager时需要依赖注入DataSource的引用 -->
    <bean id="transactionManager" class="org.spring.framework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 配置一个业务逻辑Bean -->
    <bean id="newsDao" class="edu.pri.lime._8_6_2.dao.NewsDaoImpl">
        <property name="ds" ref="dataSource"/>
    </bean>
    
    <!-- 配置两个事务增强处理 -->
    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    
    <tx:advice id="noTxAdvice">
        <tx:attributes>
            <tx:method name="*" propagation="NEVER"/>
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <!-- 配置一个切入点,匹配userService Bean 中所有方法的执行 -->
        <aop:pointcut expression="bean(userService)" id="txOperation"/>
        <!-- 配置一个切入点,匹配包下所有以Impl结尾的类中所有方法的执行 -->
        <aop:pointcut expression="execution(* edu.pri.lime._8_6_2.dao.*Impl.*(..))" id="noTxOperation"/>
        <!-- 将txOperation切入点和defaultTxAdvice切面绑定在一起 -->
        <aop:advisor advice-ref="defaultTxAdvice" pointcut-ref="txOperation"/>
        <!-- 将noTxOperation切入点和noTxAdvice切面绑定在一起 -->
        <aop:advisor advice-ref="noTxAdvice" pointcut-ref="noTxOperation"/>
    </aop:config>
    
    <!-- 配置第一个业务逻辑Bean,该Bean的名字为userService,匹配txOperation切入点将被织入defaultTxAdvice切面里的增强处理 -->
    <bean id="userService" class=""></bean>
    <!-- 配置第二个业务逻辑Bean,实现类位于包下,将被织入noTxAdvice切面里的增强处理 -->
    <bean id="anotherFooService" class=""/>
</beans>

        如果想让事务在遇到特定的cheched异常时自动回滚,则可借助于rollback-for属性。

        在默认情况下,只有当方法引发运行时异常和unchecked异常时,Spring事务机制才会自动回滚事务。也就是说,只有当抛出一个RuntimeException或其子类实例,或Error对象时,Spring才会自动回滚事务。如果事务方法抛出checked异常,则事务不会自动回滚。

        通过使用rollback-for属性可强制Spring遇到特定checked异常时自动回滚事务:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
    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-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"
                rollback-for="exception.NoItemException" />
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>


</beans>

        如果想让Spring遇到特定runtime异常时强制不会滚事务,则可通过no-rollback-for属性来指定:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
    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-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"
                no-rollback-for="exception.AuctionException" />
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>


</beans>

啦啦啦

啦啦啦

啦啦啦

啦啦啦

啦啦啦

啦啦啦

啦啦啦

啦啦啦

啦啦啦

posted @ 2017-03-21 21:33  limeOracle  阅读(355)  评论(0编辑  收藏  举报