Spring事务管理二

Spring事务管理2

通常通过TransactionProxyFactoryBean设置Spring事务代理。我们需要一个目标对象包装在事务代理中。这个目标对象一般是一个普通Java对象的bean。当我们定义TransactionProxyFactoryBean时,必须提供一个相关的 PlatformTransactionManager的引用和事务属性。 事务属性含有上面描述的事务定义。

<bean id="petStore" 
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager"><ref bean="transactionManager"/></property>
    <property name="target"><ref bean="petStoreTarget"/></property>
    <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>

事务代理会实现目标对象的接口:这里是id为petStoreTarget的bean。(使用 CGLIB也可以实现具体类的代理。只要设置proxyTargetClass属性为true就可以。如果目标对象没有实现任何接口,这将自动设置该属性为true。通常,我们希望面向接口而不是类编程。)使用proxyInterfaces属性来限定事务代理来代 理指定接口也是可以的Spring事务管理二(一般来说是个好想法)。也可以通过从 org.springframework.aop.framework.ProxyConfig继承或所有AOP代理工厂共享 的属性来定制TransactionProxyFactoryBean的行为。

这里的transactionAttributes属性定义在 org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource 中的属性格式来设置。这个包括通配符的方法名称映射是很直观的。注意 insert*的映射的值包括回滚规则。添加的-MyCheckedException 指定如果方法抛出MyCheckedException或它的子类,事务将 会自动回滚。可以用逗号分隔定义多个回滚规则。-前缀强制回滚,+前缀指定提交(这允许即使抛出unchecked异常时也可以提交事务,当然你自己要明白自己 在做什么)。

TransactionProxyFactoryBean允许你通过 “preInterceptors”和“postInterceptors”属性设置“前”或“后”通知来提供额外的 拦截行为。可以设置任意数量的“前”和“后”通知,它们的类型可以是 Advisor(可以包含一个切入点), MethodInterceptor或被当前Spring配置支持的通知类型 (例如ThrowAdvice, AfterReturningtAdviceBeforeAdvice, 这些都是默认支持的)。这些通知必须支持实例共享模式。如果你需要高级AOP特 性来使用事务,如有状态的maxin,那最好使用通用的 org.springframework.aop.framework.ProxyFactoryBean, 而不是TransactionProxyFactoryBean实用代理创建者。

也可以设置自动代理:配置AOP框架,不需要单独的代理定义类就可以生成类的 代理。

附两个spring的事务配置例子:
<prop key="add">
     PROPAGATION_REQUIRES_NEW, -MyException 
</prop>
注:上面的意思是add方法将独占一个事务,当事务处理过程中产生MyException异常或者该异常的子类将回滚该事务。

<prop key="loadAll">
    PROPAGATION_SUPPORTS, ISOLATION_READ_COMMITED, Readonly
</prop>
注:表示loadAll方法支持事务,而且不会读取没有提交事务的数据。它的数据为只读(这样有助于提高读取的性能)

附A Spring中的所有事务策略

PROPAGATION_MANDATORY
PROPAGATION_NESTED 
PROPAGATION_NEVER 
PROPAGATION_NOT_SUPPORTED
PROPAGATION_REQUIRED
PROPAGATION_REQUIRED_NEW
PROPAGATION_SUPPORTS

附B Spring中所有的隔离策略:

ISOLATION_DEFAULT
ISOLATION_READ_UNCOMMITED
ISOLATION_COMMITED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE

Spring事务类型祥解

大家可能在spring中经常看到这样的定义:

 

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

估计有好多朋友还没有弄清楚里面的值的意思,仔细看完下面应该知道自己什么情况下面应该使用什么样的声明。^_^

 

Spring中常用事务类型:

  • PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 
  • PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。 
  • PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。 
  • PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。 
  • PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 
  • PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。 
  • PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
摘要:
Spring和EJB一样,提供了两种事务管理方式:编程式和声明式。在考试系统中我们将使用声明式的事务管理,这是spring推荐的做法。使用这种方式可以体验到spring的强大便捷,而且我们无须在Dao类中编写任何特殊的代码,只需要通过配置文件就可以让普通的java类加载到事务管理中,这个意义是很重大的。 


本文Matrix永久镜像:http://www.matrix.org.cn/resource/article/1/1339.html
说明:本文可能由Matrix原创,也可能由Matrix的会员整理,或者由
Matrix的Crawler在全球知名Java或者其他技术相关站点抓取并永久
保留镜像,Matrix会保留所有原来的出处URL,并在显著地方作出说明,
如果你发觉出处URL有误,请联系Matrix改正.
四、Spring中的事务控制

Spring和EJB一样,提供了两种事务管理方式:编程式和声明式。在考试系统中我们将使用声明式的事务管理,这是spring推荐的做法。使用这种方式可以体验到spring的强大便捷,而且我们无须在Dao类中编写任何特殊的代码,只需要通过配置文件就可以让普通的java类加载到事务管理中,这个意义是很重大的。

Spring中进行事务管理的通常方式是利用AOP(面向切片编程)的方式,为普通java类封装事务控制,它是通过动态代理实现的,由于接口是延迟实例化的,spring在这段时间内通过拦截器,加载事务切片。原理就是这样,具体细节请参考jdk中有关动态代理的文档。本文主要讲解如何在spring中进行事务控制。

动态代理的一个重要特征是,它是针对接口的,所以我们的dao要通过动态代理来让spring接管事务,就必须在dao前面抽象出一个接口,当然如果没有这样的接口,那么spring会使用CGLIB来解决问题,但这不是spring推荐的方式,我们也不做讨论。

参照前面的例子,我们为StudentManager.java定义一个接口,它的内容如下:

/*
 * 创建日期 2005-3-25
 */
package org.bromon.spring.examer.student;

import java.util.List;

import org.bromon.spring.examer.pojo.Student;

/**
 * @author Bromon
 */
public interface StudentManagerInterface
{
    public void add(Student s);
    public void del(Student s);
    public void update(Student s);
    
    public List loadAll();
    public Student loadById(int id);
}

StudentManager也应该做出修改,实现该接口:

public class StudentManager extends HibernateDaoSupport implements StudentManagerInterface

现在需要修改配置文件,用于定义Hibrenate适用的事务管理器,并且把sessionFactory注入进去,同时还需要通过注册一个DefaultTransactionAttribute对象,来指出事务策略。其中sessionFactory的定义已经在本文的第三章中说明。

首先定义一个Hibernate的事务管理器,让它来管理sessionFactory:
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
      <property name="sessionFactory">
         <ref bean="sessionFactory"/>
      </property>
</bean>

下面定义事务管理策略,我们希望把策略定义在方法这个级别上,提供最大的灵活性,本例中将add方法定义为:PROPAGATION_REQUIRES_NEW,这可以保证它将始终运行在一个事务中。

<bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
      <property name="properties">
         <props>
            <prop key="add">
               PROPAGATION_REQUIRES_NEW
            </prop>
         </props>
      </property>
   </bean>

我们不仅可以为add方法定义事务策略,还可以定义事务隔离程度和回滚策略,他们以逗号隔开,比如我们的add事务可以定义为:

<prop key="add">
   PROPAGATION_REQUIRES_NEW,-ExamerException
</prop
>

这个事务策略表示add方法将会独占一个事务,当事务过程中产生ExamerException异常,事务会回滚。

Add/update/del都是写入方法,对于select(读取)方法,我们可以指定较为复杂的事务策略,比如对于loadAll()方法:

<prop key=”loadAll”>
      PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITED,readOnly
   </prop>


该事务的含义为,loadAll方法支持事务,不会读去位提交的数据,它的数据为只读(可提高执行速度)。

如你所见,我们的StudentManagerInterface接口中还有一个loadById(int id)方法,也许我们将来还会有很多的loadByXXXX的方法,难道要意义为他们指定事务策略?太烦人了,他们应该和loadAll()一样,所以我们可以使用通配符,定义所有的loadXXXX方法:

<prop key=”load*”>
      PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITED,readOnly
   </prop>


现在可以定义事务管理器:
<bean id="studentManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
      <property name="target">
         <ref bean="studentManager"/>
      </property>
      <property name="transactionManager">
         <ref bean="transactionManager"/>
      </property>
      <property name="transactionAttributeSource">
         <ref bean="transactionAttributeSource"/>
      </property>
</bean>


这个bean的外观是一个接口(StudentManagerInterface),我们指出了它的具体实现(studentManager),而且为它绑定了事务策略。在客户端使用的时候,获得对象是StudentManagerInterface,所有的操作都是针对这个接口的。测试代码并没有改变,我们虽然修改了很多地方,加入了事务控制,但是客户端并没有受到影响,这也体现了spring的一些优势。测试代码如下:

public void testAdd() 
    {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("springConfig.xml");
        StudentManager sm=(StudentManager)ctx.getBean("studentManager");
        
        Student s=new Student();
        s.setId(1);
        s.setName("bromon");
        s.setPassword("123");
        s.setGrade(1);
        s.setSex(0);
        
        sm.add(s);
}


通过以上的代码可以看出,spring可以简单的把普通的java class纳入事务管理,声明性的事务操作起来也很容易。有了spring之后,声明性事务不再是EJB独有,我们不必为了获得声明性事务的功能而去忍受EJB带来的种种不便。

我所使用的mysql是不支持事务的,你可以更换使用PostgreSQL,有了spring+hibernate,更换db并不像以前那样恐怖了,步骤很简单:

1、 添加PostgreSQL的jdbc驱动
2、 修改dataSource配置,包括驱动名称、url、帐号、密码
3、 修改sessionFactory的数据库dailet为net.sf.hibernate.dialect.PostgreSQLDialect
4、 修改hbm.xml中的主键生成策略为increment

所有的修改都在配置文件中完成,业务代码不需要任何修改,我很满意,How about u?

 

 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二 Spring事务管理二

spring声明式事务管理的两种方式

传统的:
 1 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 2         <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
 3         <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:dev" />
 4         <property name="username" value="kaktos" />
 5         <property name="password" value="kaktos" />
 6     </bean>
 7 
 8     <bean id="txManager"
 9         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
10         <property name="dataSource" ref="dataSource" />
11     </bean>
12 
13     <bean id="businessBean"
14         class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
15         <property name="transactionManager" ref="txManager" />
16         <property name="target" ref="businessBeanTarget" />
17         <property name="transactionAttributes">
18             <props>                
19                 <prop key="*">PROPAGATION_REQUIRED</prop>
20             </props>
21         </property>
22     </bean>
23     
24     <bean id="businessBeanTarget" class="sample.spring.trans.BusinessBean">
25         <property name="dataSource" ref="dataSource" />
26     </bean>

这样做的弊端就是不得不为每个需要事务的bean做一次声明,如果所有的bean都基本上有一致的配置,这样就太繁琐啦。
下面是第二种方式:
 1 <beans>
 2     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 3         <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
 4         <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:dev" />
 5         <property name="username" value="kaktos" />
 6         <property name="password" value="kaktos" />
 7     </bean>
 8 
 9     <bean id="txManager"
10         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
11         <property name="dataSource" ref="dataSource" />
12     </bean>
13 
14     <bean id="matchAllWithPropReq"
15         class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
16         <property name="transactionAttribute" value="PROPAGATION_REQUIRED" />
17     </bean>
18     
19     <bean id="matchAllTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
20         <property name="transactionManager" ref="txManager" />
21         <property name="transactionAttributeSource" ref="matchAllWithPropReq" />
22     </bean>
23 
24     <bean id="autoProxyCreator"
25         class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
26         <property name="interceptorNames">
27             <list>
28                 <idref local="matchAllTxInterceptor" />
29             </list>
30         </property>
31         <property name="beanNames">
32             <list>
33                 <idref local="businessBean" />
34             </list>
35         </property>
36     </bean>
37     
38     <!--  my beans  -->
39     <bean id="businessBean" class="sample.spring.trans.BusinessBean">
40         <property name="dataSource" ref="dataSource" />
41     </bean>
42 </beans>

BeanNameAutoProxyCreator会在applicationcontext初始化后自动为beanNames属性中的bean建立proxy。