Spring的事务管理

 

Spring整合ORM框架时,事务管理用哪个类?是Spring控制事务的提交还是ORM框架控制?同一事务中执行两个更新操作,它的工作原理是怎样的?

 

谈谈Srping的事务隔离和传播

 

 

 

 

最近研究了spring的事务管理,在网上借鉴了很多blog,一一实践后,我把我的实践成果贴出来大家分享,感谢那些对我有帮助的blog的主人。 

 

SPRING的两种事务管理方式:编程式、声明式。通常建议采用声明式事务管理。声明式事务管理的优势非常明显:代码中无需关于关注事务逻辑,让Spring声明式事务管理负责事务逻辑,声明式事务管理无需与具体的事务逻辑耦合,可以方便地在不同事务逻辑之间切换。 

一、 

Spring事务策略 

  Spring事务策略,也就是spring事务管理的实现方式.它有一个统一的抽象接口:  org.springframework.transaction.PlatformTransactionManager 

  此接口的内容如下: 

 Java代码  

 Public interface PlatformTransactionManager()...{   

  TransactionStatue getTransaction(TransactionDefinition    definition) throws TransactionException;   

  Void commit(TransactionStatus status) throws TransactionException;   

  Void rollback(TransactionStatus status) throws TransactionException;   

  }  

 

 Public interface PlatformTransactionManager()...{

  TransactionStatue getTransaction(TransactionDefinition    definition) throws TransactionException;

  Void commit(TransactionStatus status) throws TransactionException;

  Void rollback(TransactionStatus status) throws TransactionException;

  }

1、getTransaction(..)方法根据一个类型为 TransactionDefinition 的参数返回一个 TransactionStatus 对象。返回的 TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务,否则开启一个新的事务)。 

TransactionStatus 接口为处理事务的代码提供一个简单的控制事务执行和查询事务状态的方法。这个概念应该是熟悉的,因为它们在所有的事务API中是相同的: 

Java代码  

public interface TransactionStatus {   

    boolean isNewTransaction();   

    void setRollbackOnly();   

    boolean isRollbackOnly();   

}  

 

public interface TransactionStatus {

    boolean isNewTransaction();

    void setRollbackOnly();

    boolean isRollbackOnly();

}

2、TransactionDefinition接口指定: 

事务隔离:当前事务和其它事务的隔离的程度。 

例如,这个事务能否看到其他事务未提交的写数据? 

事务传播:通常在一个事务中执行的所有代码都会在这个事务中运行。但是,如果一个事务上下文已经存在,有几个选项可以指定一个事务性方法的执行行为:例如,简单地在现有的事务中继续运行(大多数情况);或者挂起现有事务,创建一个新的事务。Spring提供EJB CMT中常见的事务传播选项。 

事务超时: 事务在超时前能运行多久(自动被底层的事务基础设施回滚)。 

只读状态: 只读事务不修改任何数据。只读事务在某些情况下(例如当使用Hibernate时),是一种非常有用的。 

 

二、编程式事务管理 

TransactionTemplate 采用与Spring中别的 模板 同样的方法, 如 JdbcTemplate 。它使用回调机制,将应用代码从样板式的资源获取和释放代码中解放出来, 这样写出的代码是目的驱动的,把精力集中在开发者想要做的事情上。 

TransactionCallbackWithoutResult —— 执行事务没有返回值,例如save、update、delete等等; 

TransactionCallback —— 执行事务处理后有返回值,如find要返回结果集(List); 

使用 TransactionTemplate 绝对会增加你的代码与Spring的事务框架和API间的耦合。 到底编程式事务管理是不是适合你的项目需要由你自己来决定。 

应用的代码必须在一个事务性的上下文中执行。你作为一个应用程序员, 会写一个 TransactionCallback 的实现, (通常会用匿名类来实现 )这样的实现会包含所以你需要在该事务上下文中执行的代码。 然后你会把自己实现TransactionCallback的实例传递给TransactionTemplate暴露的execute(..) 方法。 

 

Java代码  

编程式事务管理代码   

public void updateUser(final  UserInfo userData ) {   

        transactionTemplate   

    .setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);   

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {   

            protected void doInTransactionWithoutResult(TransactionStatus status) {   

                try {   

                    entityDao.merge(userData);   

                } catch (Exception e) {   

                    e.printStackTrace();   

                    status.setRollbackOnly();   

                }   

            }   

        });   

    }  

 

编程式事务管理代码

public void updateUser(final  UserInfo userData ) {

transactionTemplate

.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

protected void doInTransactionWithoutResult(TransactionStatus status) {

try {

entityDao.merge(userData);

} catch (Exception e) {

e.printStackTrace();

status.setRollbackOnly();

}

}

});

}

 

 

 

编程式事务管理 

指定事务设置 

诸如传播模式、隔离等级、超时等等的事务设置都可以在TransactionTemplate中或者通过配置或者编程式地实现。 TransactionTemplate实例默认继承了默认事务设置。 下面有个编程式的为一个特定的TransactionTemplate定制事务设置的例子。 

transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); 

    transactionTemplate.setTimeout(30); 

 

三、声明式事务管理 

大多数Spring用户选择声明式事务管理。这是对应用代码影响最小的选择,因此也最符合 非侵入式 轻量级容器的理念。 

Spring的声明式事务管理是通过Spring AOP实现的,因为事务方面的代码与Spring绑定并以一种样板式风格使用, 不过尽管如此,你一般并不需要理解AOP概念就可以有效地使用Spirng的声明式事务管理。 

特点: 

1、Spring声明式事务管理可以在任何环境下使用,只需改配置文件。 

2、Spring的声明式事务管理可以被应用到任何类上。 

3、Spring提供了声明式的 回滚规则。 

4、Spring允许你通过AOP定制事务行为。 

回滚规则的概念比较重要:它使我们能够指定什么样的异常(和throwable)将导致自动回滚。我们在配置文件中声明式地指定,无须在Java代码中。同时,我们仍旧可以通过调用 TransactionStatus 的 setRollbackOnly() 方法编程式地回滚当前事务。通常,我们定义一条规则, 声明 MyApplicationException 必须总是导致事务回滚。 这种方式带来了显著的好处,它使你的业务对象不必依赖于事务设施。典型的例子是你不必在代码中导入Spring API,事务等。 

Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 

声明式事务管理的配置有5种方式: 

1、每个Bean都有一个代理 

2、所有Bean共享一个代理基类 

3、使用拦截器 

4、使用tx标签配置的拦截器 

5、全注解 

 

 

事务的隔离级别:使用Spring 声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。

org.springframework.transaction包里面的TransactionDefinition类

getTimeout()方法,它返回事务必须在多少秒内完成。 

isReadOnly(),事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。 

getIsolationLevel()方法返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。 

 

在TransactionDefinition接口中定义了四个不同的事务隔离级别:

1) ISOLATION_DEFAULT 这是一个 PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别(读未提交).另外四个与JDBC的隔离级别相对应 

2) ISOLATION_READ_UNCOMMITTED这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。

3) ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。

4) ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)

 

事务的传播性质:

定义了关于客户端和被调用方法的事务边界。传播规则就是在说明新的事务是否要被启动或是挂起,或者方法是否要在事务环境中运行。

在TransactionDefinition接口中定义了七个事务传播行为:

 

1) PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务; 

2) PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行;

3) PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异;

4)PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起;

5) PROPAGATION_NOT_SUPPORTED总是非事务地执行,并挂起任何存在的事务;

6) PROPAGATION_NEVER总是非事务地执行,如果存在一个活动事务,则抛出异常;

7) PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按 TransactionDefinition.PROPAGATION_REQUIRED 属性执行;

 

Spring的声明式事务:

(1) 利用拦截器来管理事务

 <!-- 事务管理器 -->

<beanid="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

     <property name="sessionFactory">

         <ref bean="sessionFactory"/>

     </property>

 </bean>

 

 <!-- 事务拦截器,激活事务管理器所必须的bean -->

 <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> 

        <property name="transactionManager"> 

            <ref bean="transactionManager" /> 

        </property>  

        <!-- 配置事务属性 --> 

       <property name="transactionAttributes"> 

           <props> 

              <prop key="delete*">PROPAGATION_REQUIRED</prop> 

              <prop key="add*">PROPAGATION_REQUIRED</prop> 

              <prop key="update*">PROPAGATION_REQUIRED</prop> 

              <prop key="save*">PROPAGATION_REQUIRED</prop> 

              <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> 

           </props> 

        </property> 

 </bean> 

 <!--BeanNameAutoProxyCreator是个根据bean名生成自动代理的代理创建器,该bean通常需要接受两个参数。一个是指定需要代理的bean,另一个是代理bean所需的事务拦截器 -->

 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

  <!-- 可以是Service或DAO层(最好是针对业务层*Service) -->

 <property name="beanNames">

  <list>

   <value>*Service</value>

  </list>

 </property>

  <property name="interceptorNames">

   <list>

    <value>transactionInterceptor</value>

   </list>

  </property>

 </bean>

 

(2) 利用AOP 方式管理事务

 在Spring中声明式的事务管理是用Spring的AOP来实现的。了解了这些后我们要考虑的是我们的事务到底要加在那一层,

 在我们的项目中往往将事务加到serivce层,这样做的好是:比如在DAO层我有三个方法来对数据进行操作,而我们的一次业务逻辑,

 需要我们要调用这三个方法才能完成这个一业务而这三方法还要放到一个事务中去,

 我们要是将事务配在DAO层我们做法只有将这三个方法分别加上一个事务,这显然违背了我们的业务需求。

 而在我们的service层中可以用一个方法来调用Dao层中的这三个方法,这样我们只要将serive层中的这个方法加上一个事务控制,

 我们业务需求就很容易的解决了。所以我们将事务大多数的要加在service层上。

 

<!-- 以AspectJ方式 定义 AOP -->

 <aop:config proxy-target-class="true">

      <aop:advisor pointcut="execution(* com.*.service.impl.*Manager*.*(..))" advice-ref="txAdvice" />

 </aop:config>

 

 <!-- 基本事务定义,使用transactionManager作事务管理,默认get*方法的事务为readonly,其余方法按默认设置. -->

 <tx:advice id="txAdvice" transaction-manager="transactionManager">

  <tx:attributes>

   <tx:method name="get*" read-only="true" />

   <tx:method name="find*" read-only="true" />

   <tx:method name="del*" propagation="REQUIRED" rollback-for="Exception">

   <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception">

   <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception">

  </tx:attributes>

 </tx:advice>

 

参考:http://www.mianwww.com/html/2009/01/1804.html

关键是结合AOP

 

http://blog.csdn.net/cdl2008sky/article/details/6265682

http://xhfei.iteye.com/blog/724872

 

http://itlee.iteye.com/blog/271111 【可重点参考】

posted on 2013-10-11 15:37  heartstage  阅读(449)  评论(0编辑  收藏  举报

导航