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
转载:http://pengchua.javaeye.com/blog/142879
问题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