现在的位置: 首页 > 综合 > 正文

spring 的事务管理

2014年09月05日 ⁄ 综合 ⁄ 共 9981字 ⁄ 字号 评论关闭

文章转自:http://blog.csdn.net/stone_star/article/details/1247226

通常通过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属性来限定事务代理来代 理指定接口也是可以的(一般来说是个好想法)。也可以通过从 org.springframework.aop.framework.ProxyConfig继承或所有AOP代理工厂共享
的属性来定制TransactionProxyFactoryBean的行为。

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

TransactionProxyFactoryBean允许你通过 “preInterceptors”和“postInterceptors”属性设置“前”或“后”通知来提供额外的 拦截行为。可以设置任意数量的“前”和“后”通知,它们的类型可以是 Advisor(可以包含一个切入点),MethodInterceptor或被当前Spring配置支持的通知类型
(例如ThrowAdviceAfterReturningtAdviceBeforeAdvice, 这些都是默认支持的)。这些通知必须支持实例共享模式。如果你需要高级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声明式事务管理的两种方式

传统的:
 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

抱歉!评论已关闭.