`

Spring事物配置

阅读更多
一.Spring事物配置:
Spring 2.0的事务配置主要有以下两种方式,
一种是声明式事务配置,
一种是编程式事务配置,

这里主要介绍声明式事务配置.
使用Spring时,无论你选择编程式还是声明式的事务管理,定义一个正确的 PlatformTransactionManager 实现都是至关重要的。
按照Spring的良好风格,这种重要定义都是通过IoC实现的。一般来说,选择PlatformTransactionManager实现时需要知道当前的
工作环境,如JDBC、JTA、Hibernate等。下面的例子来自Spring示例应用——jPetStore——中的dataAccessContext-local.xml文
件,其中展示了一个局部PlatformTransactionManager实现是怎么定义的(仅限于纯粹JDBC环境)
我们必须先定义一个JDBC DataSource,然后使用Spring的DataSourceTransactionManager,并传入指向DataSource的引用。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}" />
  <property name="password" value="${jdbc.password}" />
</bean>
PlatformTransactionManager bean的定义如下:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>
    
     我们也可以很容易地使用Hibernate局部事务,就像下面的Spring框架的 PetClinic 示例应用中的例子一样)。这种情况下,
我们需要定义一个Hibernate的 LocalSessionFactoryBean,应用程序从中获取到Hibernate Session 实例。DataSource
的bean定义同上例类似(这里不再展示)。不过,如果是一个JEE容器提供的 DataSource,它将由JEE容器自身,而不是
Spring框架来管理事务。这种情况中'txManager' bean的类型为 HibernateTransactionManager。同样地,
DataSourceTransactionManager 需要一个指向 DataSource 的引用,而 HibernateTransactionManager 需要一个
指向 SessionFactory 的引用。
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mappingResources">
    <list>
      <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
    </list>
  </property>
  <property name="hibernateProperties">
    <value>
          hibernate.dialect=${hibernate.dialect}
        </value>
  </property>
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>


     声明式事务管理大多数Spring用户选择声明式事务管理。这是对应用代码影响最小的选择,因此也最符合 非侵入式
轻量级容器的理念。Spring的声明式事务管理是通过Spring AOP实现的,因为事务方面的代码与Spring绑定并以一种样板
式风格使用,不过尽管如此,你一般并不需要理解AOP概念就可以有效地使用Spirng的声明式事务管理。从考虑EJB CMT和
Spring声明式事务管理的相似以及不同之处出发是很有益的。它们的基本方法是相似的:都可以指定事务管理到单独的方
法;如果需要可以在事务上下文调用 setRollbackOnly() 方法。
  不同之处在于:
·不像EJB CMT绑定在JTA上,Spring声明式事务管理可以在任何环境下使用。只需更改配置文件,它就可以
  和JDBC、JDO、Hibernate或其他的事务机制一起工作。
·Spring的声明式事务管理可以被应用到任何类(以及那个类的实例)上,不仅仅是像EJB那样的特殊类。
·Spring提供了声明式的回滚规则:EJB没有对应的特性,我们将在下面讨论。回滚可以声明式的控制,不仅仅是编程式的。
·Spring允许你通过AOP定制事务行为。例如,如果需要,你可以在事务回滚中插入定制的行为。你也可以增加任意的通
  知,就象事务通知一样。使用EJB CMT,除了使用setRollbackOnly(),你没有办法能够影响容器的事务管理。
·Spring不提供高端应用服务器提供的跨越远程调用的事务上下文传播。如果你需要这些特性,我们推荐你使用EJB。
   然而,不要轻易使用这些特性。通常我们并不希望事务跨越远程调用。 回滚规则的概念比较重要:它使
   我们能够指定什么样的异常(和throwable)将导致自动回滚。我们在配置文件中声明
   式地指定,无须在Java代码中。同时,我们仍旧可以通过调用 TransactionStatus 的
   setRollbackOnly() 方法编程式地回滚当前事务。通常,我们定义一条规则,声明
   MyApplicationException 必须总是导致事务回滚。这种方式带来了显著的好处,它使你的业
   务对象不必依赖于事务设施。典型的例子是你不必在代码中导入Spring API,事务等。

对EJB来说,默认的行为是EJB容器在遇到 系统异常(通常指运行时异常)时自动回滚当前事务。
EJB CMT遇到 应用异常(例如,除了 java.rmi.RemoteException 外别的checked exception)时并不会自动回滚。
默认式Spring处理声明式事务管理的规则遵守EJB习惯(只在遇到unchecked exceptions时自动回滚),但
通常定制这条规则会更有用。

列子:

清单1:
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
   
   <!--Hibernate SessionFatory-->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingDirectoryLocations">
            <list>
                <value>classpath:com/sailing/domain/hbm/</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
                <prop key="hibernate.cache.provider_class">${hibernate.cache.provider_class}</prop>
            </props>
        </property>
    </bean>


    <!--Hibernate TransactionManager-->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <!--
    <bean id="tocharFunction" class="com.sailing.framework.hibernate.TOCharFunction">
        <property name="tocharFunction" value="{$hibernate.to_char_function}"/>
    </bean>
    -->
</beans>
清单2
<?xml version="1.0" encoding="gb2312"?>
<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-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
       default-autowire="byName" default-lazy-init="true">

    <!-- 支持 @Transactional 标记 -->
    <tx:annotation-driven/>

    <!-- 支持 @AspectJ 标记-->
    <aop:aspectj-autoproxy/>
   
    <aop:config proxy-target-class="true">
       <aop:advisor pointcut="execution(* com.sailing.app.uupms..*.*(..))"
                   advice-ref="txAdvice"/>
    </aop:config>
    <!-- 基本事务定义,使用transactionManager作事务管理,默认get*方法的事务为readonly,其余方法按默认设置.
            默认的设置请参考Spring文档事务一章. -->
    <tx:advice id="txAdvice">
       <tx:attributes>
<!--查询事务 -->
            <tx:method name="get*" read-only="true" />
           <tx:method name="find*" read-only="true"/>
           <tx:method name="load*" read-only="true"/>

<!--新增、修改、删除事务 -->
            <tx:method name="save*" read-only="false" rollback-for="Exception"/>
            <tx:method name="add*" read-only="false" rollback-for="Exception"/>
            <tx:method name="append*" read-only="false" rollback-for="Exception"/>

            <tx:method name="update*" read-only="false" rollback-for="Exception"/>
            <tx:method name="modify*" read-only="false" rollback-for="Exception"/>
            <tx:method name="edit*" read-only="false" rollback-for="Exception"/>
            <tx:method name="data*" read-only="false" rollback-for="Exception"/>

            <tx:method name="remove*" read-only="false" rollback-for="Exception"/>
             <tx:method name="delete*" read-only="false" rollback-for="Exception"/>
            <tx:method name="del*" read-only="false" rollback-for="Exception"/>
          
           <tx:method name="*"/>
       </tx:attributes>
    </tx:advice>
   



</beans>

其中
<aop:config proxy-target-class="true">
       <aop:advisor pointcut="execution(* com.sailing.app.uupms..*.*(..))"
                   advice-ref="txAdvice"/>
    </aop:config>

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?) 
其中带问号的modifiers-pattern?(public/protected) 和 declaring-type-pattern? throws-pattern? 可以不填
可见execution(* com.sailing.app.uupms..*.*(..))代表com.sailing.app.uupms包下以及子包下的所有类
以execution(* *..BookManager.save(..))为列子

第一颗* 代表ret-type-pattern 返回值可任意,
*..BookManager 代表任意Pacakge里的BookManager类。
如果写成com.xyz.service.* 则代表com.xyz.service下的任意类
com.xyz.service..* com.xyz.service则代表com.xyz.service及其子package下的任意类
save代表save方法,也可以写save* 代表saveBook()等方法
(..) 匹配0个参数或者多个参数的,任意类型
(x,..) 第一个参数的类型必须是X
(x,,,s,..) 匹配至少4个参数,第一个参数必须是x类型,第二个和第三个参数可以任意,第四个必须是s类型。
注 意name-pattern千万不要写成*..*Manager ,这样子的话会把所有第三方类库的Manager比如Spring的
PlatformTranstationManager 也加入aop,非常危险。所以最好还是加上项目的package前缀,如org.springside
回滚在前面的章节里,概述了如何在你的应用里为类特别是服务层的类指定事务性的基本方法。这一章将描述
在一个简单的声明式配置中如何你才能控制事务的回滚。
一个容易的(和被推荐的)方法是在Spring框架的事务架构里指出当context的事务里的代码抛出 Exception 时
事务进行回滚。Spring框架的事务基础架构代码将从调用的堆栈里捕获到任何未处理的 Exception,并将标识事
务将回滚。
然而,请注意Spring框架的事务基础架构代码将默认地 只 在抛出运行时和unchecked exceptions时才标识事务
回滚。 也就是说,当抛出一个 RuntimeException 或其子类例的实例时。(Errors 也一样 - 默认地 - 标识事
务回滚。)从事务方法中抛出的Checked exceptions将 不 被标识进行事务回滚。
就是这些默认的设置;严格规定了哪些 Exception 类型将被标识进行事务回滚。
下面的XML配置片断里示范了如何配置一个checked,应用程序指定的 Exception 类型来标识事务回滚。
<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
         <tx:method name="get*" read-only="false" rollback-for="NoProductInStockException"/>
         <tx:method name="*"/>
  </tx:attributes>
</tx:advice>

第二方法是在Spring框架的事务架构里通过 编程式 方式指出一个事务将被回滚。 虽然这个非常简单,但是这个
方法对于Spring框架的事务架构来说,在你的代码是高入侵的和紧藕合的 下面的代码片断里示范了Spring框架
管理事务的编程式回滚:
public void resolvePosition() {
    try {
        // some business logic...
    } catch (NoProductInStockException ex) {
        // trigger rollback programmatically
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

强烈推荐你尽可能地使用声明式事务回滚方法。 编程式方法的回滚对你来说是可见,如果你需要它你就可以使用,
但是使用它就直接违反了在你的应用中使用一个纯基于POJO的模型。
<tx:advice/>有关的设置这一节里将描述通过 <tx:advice/> 标签来指定不同的事务性设置。
默认的 <tx:advice/> 设置如下:
·事务传播设置是 REQUIRED
·隔离级别是 DEFAULT
·事务是 读/写
·事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
·任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚
这些默认的设置当然也是可以被改变的。 <tx:advice/> 和 <tx:attributes/> 标签里的 <tx:method/>
各种属性设置总结如下:
    Table 9.1. <tx:method/> 有关的设置属性是否需要?默认值描述name是与事务属性关联的方法名。
    通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:'get*'、'handle*'、'on*Event'等等。
propagation

REQUIRED
事务传播行为
isolation

DEFAULT
事务隔离级别
timeout

-1
事务超时的时间(以秒为单位)
read-only

false
事务是否只读?
rollback-for


将被触发进行回滚的 Exception(s);以逗号分开。
如:'com.foo.MyBusinessException,ServletException' no-rollback-for
不 被触发进行回滚的 Exception(s);以逗号分开。
如:'com.foo.MyBusinessException,ServletException'

Spring常见错误问题1:
Exceptionin thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory at org.springframework.util.ClassUtils.<clinit>(ClassUtils.java:67) at org.springframework.core.io.DefaultResourceLoader.<init>(DefaultResourceLoader.java:52) at org.springframework.context.support.AbstractApplicationContext.<init>(AbstractApplicationContext.java:184) at org.springframework.context.support.AbstractRefreshableApplicationContext.<init>(AbstractRefreshableApplicationContext.java:80) at org.springframework.context.support.AbstractXmlApplicationContext.<init>(AbstractXmlApplicationContext.java:58) at
需要加上:commons-logging.jar log4j-1.2.11.jar
问题2:
Exceptionin thread "main" org.springframework.beans.factory.BeanDefinitionStoreException:
Unexpected exception parsing XML document from class path resource [text.xml];
nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
Caused by: java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
at java.lang.Class.forName0(Native Method)
需要加上:aspectjweaver.jar
问题3:
Exceptionin thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'logBean' defined in class path resource [text.xml]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.
Caused by: org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.
需要加上:cglib-2.1.3.jar

问题4:
xception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'logBean' defined in class path resource [text.xml]: Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/objectweb/asm/Type
Caused by: java.lang.NoClassDefFoundError: org/objectweb/asm/Type
at net.sf.cglib.core.TypeUtils.parseType(TypeUtils.java:180)
需要加上:asm.jar

问题5:
CGLIB Enhancement failed: com.bowen.domain.Schools
java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V
at net.sf.cglib.core.ClassEmitter.begin_class(ClassEmitter.java:77)
at net.sf.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:173)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
解决:
由于不同版本让我想到了可能会因为其它三方包是不同版本引起的最新的MyEclipse,所以里面的
Hibernate也是最新的3.1(它里面还带有一个3.0版本的)删除多余的包 Hibernate3.1
问题6:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in resource [/WEB-INF/dataAccessContext-hibernate.xml] of ServletContext: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: javax/transaction/TransactionManager
java.lang.NoClassDefFoundError: javax/transaction/TransactionManager
原因:缺少jta.jar

问题7:
java.lang.NoClassDefFoundError: org/dom4j/Attribute
缺dom4j.jar
java.lang.NoClassDefFoundError: net/sf/ehcache/CacheException
缺ehcache.jar
java.lang.NoClassDefFoundError: net/sf/cglib/core/KeyFactory
缺cglib-full.jar

二.事物的传播特性:
    srping用到的另外一项技术就是AOP(Aspect-Oriented Programming, 面向切面编程),它是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程)的补充。AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点。在应用 AOP 编程时, 仍然需要在定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里。每个事物逻辑位于一个位置, 代码不分散,便于维护和升级,业务模块更简洁, 只包含核心业务代码。
    现实中使用spring最多的就是声明式事务配置功能。下面就来了解其aop在事务上应用。首先要了解的就是AOP中的一些概念:
      Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象。
      joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)。
      Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义。
      Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。
      Target(目标对象):代理的目标对象。
      Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入。
      Introduction(引入):在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field。
    所谓AOP,我的理解就是应该是这样一个过程,首先需要定义一个切面,这个切面是一个类,里面的方法就是关注点(也是通知),或者说里面的方法就是用来在执行目标对象方法时需要执行的前置通知,后置通知,异常通知,最终通知,环绕通知等等。有了切面和通知,要应用到目标对象,就需要定义这些通知的切入点,换句话说就是需要对哪些方法进行拦截,而这些被拦截的方法就是连接点,所谓连接点也就是在动态执行过程,被织入切面的方法(至少在spring中只能对方法进行拦截)。因此,在动态过程中通知的执行就属于织入过程,而被织入这些通知的对象就是目标对象了。
    通常应用中,被织入的都是事务处理,对事务的织入不是普通简单的织入,它有着事务特有的特性——
事务的传播特性:
      1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启新的事物。
      2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
      3. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
      4. PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
      5. PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
      6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
      7.(spring)PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
    这些都是事务特有的特性,比如前面分析的,如果两个在代码上不相关的操作,需要放在同一个事务中,这就需要利用到传播特性了,这时后调用的方法的传播特性的值就应该是PROPAGATION_REQUIRED。在spring中只需要进行这样的配置,就实现了生命式的事物处理。
    最后一点需要提及的就是Spring事务的隔离级别:
      1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
      2. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
      3. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
      4. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
      5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 除了防止脏读,不可重复读外,还避免了幻像读。
    除了第一个是spring特有的,另外四个与JDBC的隔离级别相对应。第二种隔离级别会产生脏读,不可重复读和幻像读,特别是脏读,一般情况下是不允许的,所以这种隔离级别是很少用到的。大多说数据库的默认格里基本是第三种。它能消除脏读,但是可重复读保证不了。第四种隔离级别也有一些数据库作为默认的隔离级别,比如MySQL。最后一种用的地方不多,除非是多数据访问的要求特别高,否则轻易不要用它,因为它会严重影响数据库的性能。


0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics