事务管理是企业应用开发中确保数据完整性和一致性的关键技术。对于并发和分布式坏境中从不可预期的错误中恢复来说,事务管理特别重要。Spring作为一个企业应用框架,在不同的事务管理API之上提供了一个抽象层。Spring支持编程式和声明式两种事务管理。编程式事务管理通过在业务方法中嵌入控制事务提交和回滚的事务管理代码来实现,自己定义事务提交和回滚的规则,因此这样代码会很多重复,不能为不同的应用程序启用和禁用事务管理;而声明式事务管理在大部分情况下比编程式事务管理好,它通过声明将事务管理代码从业务方法中分离出来,但其灵活性不如编程式事务管理。spring支持的事务属性包括传播行为、隔离级别、回滚规则、事务超时和事务的只读属性。

事务是一系列作为单独工作单元处理的操作,其有四个关键属性: ACID

原子性:事务是一个包含一系列的原子操作,事务的原子性确保这些操作全部完成或者完全无效。

一致性:一旦事务的所有操作结束,事务就被提交。然后你的数据和资源将处于遵循业务规则的一致状态。

隔离性:因为同时在相同数据集上可能有许多事务处理,每个事务应该与其他事务隔离,避免数据损坏。

持久性:一旦事务完成,它的结果应该能承受任何系统错误。通常,事务的结果被写入持续性存储。

我们可以使用Jdbc API如setAutoCommit,commit,rollback来进行事务管理,Spring的事务支持提供了一组技术独立的机制,包括事务管理器(PlatformTransactionManager)、事务模板(TransactionTemplate)和事务声明支持,以简化事务管理。

Spring的事务抽象包括3个主要接口,即PlatformTransactionManager、TransactionDefinition、TransactionStatus。PlatformTransactionManager负责界定事务边界,TransactionDefinition负责定义事务相关属性,包括隔离级别、传播行为等,前者参照后则的属性定义来开启相关事务,事务开启后到事务结束的事务状态是由TransactionStatus负责,我们也可以通过TransactionStatus对事务进行有限的控制。

1.选择一个事务管理器

Spring的核心事务管理抽象基于PlatformTransactionManager接口,它封装了一组用于事务管理的技术独立方法。其提供了三种处理事务的方法:

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException

void commit(TransactionStatus status) throws TransactionException

void rollback(TransactionStatus status) throws TransactionException

其用于所有Spring事务管理器的通用接口,如果仅仅处理一个数据源并且用jdbc进行访问,使用DataSourceTransactionManager;如果JTA进行javaEE应用服务器上的事务管理,就应该使用JtaTransactionManager,其对于分布式事务是合适的。如果使用ORM框架访问数据库,应该选取对应的事务管理器,如HibernateTransactionManager或JpaTransactionManager。可以使用IoC容器来进行配置,如

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

	<property name="dataSource" ref="dataSource" />

</bean>

2.编程式事务管理

Spring的事务管理器提供一个技术独立的API,通过getTransaction()启动新事务,通过调用commit()和rollback()方法管理事务。类似如下代码:

...

	TransactionDefinition def = new DefaultTransactionDefintion();
TransactionStatus status = transactionManager.getTransaction(def);
......
transactionManager.commit(status);
...
transactionManager.rollback(status);

...

配置如下:

<beans>

       	 <bean id="transactionManager" class="org.springframework.jdbc.datasourdce.DataSourceTransactionManager">
             <property name="dataSource" ref="dataSource" />
         </bean>    
        
         <bean id="xxxx" class="yyyyy>
                ...
                <property name="transactionManager" ref="transactionManager"/>
         </bean>
     </beans>

Spring还提供了事务模板 TransactionTemplate帮助控制整个事务管理过程和事务异常处理。只要在实现TransactionCallback<T>接口的回调类里封装代码,并传递给TransactionTemplate的execute方法执行就可以了。使用如下:

...

	private TransactionTemplate transactionTemplate ;
...
transactionTemplate.execute(new TransactionCallbackWithoutResult(){
        protected void doInTransactionWithoutResult(TransactionStatus status){
        ....
         });

...

如果想使用IoC容器注入transactionTemplate的话,则为其提供set方法,配置如下:

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate>

      	  <property name="transactionManager" ref="transactionManager" />
</bean>
      <bean id="xxxx" class="yyyyy">
     ...
     <property name="transactionTemplate" ref="transactionTemplate"/>
      ...

</bean>

3.声明式事务管理

Spring 2.x之后使用AOP方法进行声明式管理事务,通过tx schema中定义的<tx:advice>元素来配置,因此事先要在xml文件中加入schema的约束。该方式对应基于Schema的aop配置,需在<aop:config>元素中声明一个通知器(Advisor),将通知与切入点关联。如下配置:

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

        <tx:attributes>
             <tx:method name="xyz"/>
        </tx:attributes>    
    </tx:advice>
...
    <aop:config>
        ...
     <aop:advisor advice-ref="xyzTxAdvice" pointcut-ref="zyz" />
        ...
    </aop:config>
    <bean id="transactionManager" class="org.springframework.jdbc.datasourdce.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
    </bean>    

除此之外,Spring允许仅通过@Transactional注解你的事务性方法,并且启用<tx:annotation-driven>元素声明事务。最好只注解公开方法。

配置如下:

<tx:annotation-driven transaction-manager="transactionManager"/>

	<bean id="transactionManager" class="org.springframework.jdbc.datasourdce.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />

</bean>

4.设置事务属性

传播属性 事务传播在事务性方法被另一个方法调用时发生的,其由事务属性propagation指定,spring定义了7中传播行为,这些行为在org.springframework.transaction.TransactionDefinition接口中定义。如下:

REQUIRED:如果现有的事务正在进行,当前方法应该在这个事务中运行,否则重新开启一个事务,并在自己的事务中运行。

REQUIRED_NEW:当前方法必须启动新事务,并在自己的事务中运行,如果现有的事务正在运行,则应该挂起。

SUPPORTS:如果现有事务正在运行,当前方法应该运行在该事务中,否则,它没有必要运行在事务中。

NOT_SUPPORTED:当前方法不应该运行在事务中。如果现在事务正在进行,它应该挂起。

MANDATORY:当前方法必须运行一个事务中。如果没有事务在进行中,将抛出一个异常。

NEVER:当前方法不应该运行于事务中。如果现有事务在运行中,将抛出异常。

NESTED: 现有事务不应该运行于事务中。如果现有事务在运行中,将抛出异常。

这些传播行为,并不是所有的事务管理器都支持的,而且不同的隔离级别,也会限制传播行为。 传播属性的配置三种如下:

<tx:advice>

   	 <tx:attributes>
         <tx:method name="..." propagation="REQUIRED"/>
     </tx:attributes>
    </tx:advice>      <property name="transactionAttributes">
      <props>
             <prop key="...">PROPAGATION_REQUIRED</prop>
       </props>    
     </property> DefaultTransactionDefinition  def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 

隔离属性 当相同应用或者不同应用的多个失误同时在同一数据集上操作时,可能发生许多不可预期的问题,必须至少能够幻想隔离的预期方式。并发产生的问题有:

脏数据:两个事务,后一个事务读取到了前一个事物还没有提交的字段,若前一个事务出现回滚,则后一个事务读取的字段是无效的。

不可重复读: 两个事务,前一个事务读取一个字段,后一个事务更新了这个字段,前一事务再去读相同的字段,数据不一致。

幻读:两个事务,前一个事务读取一些数据行,后一个事务新添加了数据行,前一个事务再去读,读到更多的数据行。

更新丢失:两个事务,更新同一个字段数据,前一个事务进行提交,后一个事务之后进行提交,前一个事务的更新会被覆盖掉。

理论上应该完全隔离,但是会对性能造成很大的影响,因为事务是顺序进行的。Spring支持5中隔离级别,这些级别在org.springframework.transaction.transaction.TransactionDefinition 接口中定义。如下:

DEFAULT:使用低层数据库的默认隔离级别,对于大多数数据库来说,默认的隔离级别是READ_COMMITTED

READ_UNCOMMITTED:允许事务读取其他事物未提交修改。可能发生脏读、不可重复读和幻读,该级别下读操作不加S锁

READ_COMMITTED:仅允许事务读取已经提交的修改。能避免脏读,但不可重复读和幻读可能发生,该级别下读操作需要加S锁,但在语句执行完之后释放S锁

REPEATABLE_READ:确保食物能够多次从一个字段读到相同值,在其他事务的更新被禁止。能避免脏读和不可重复读,幻读可能发生,该级别下读操作需要加S锁,但在事务提交之后才释放S锁。

SERIALIZABLE:确保一个事物能从表中多次读取相同的行。在事务期间,其他事务的行为均被禁止。能避免所有并发问题,但性能低,在该级别下又添加了一个范围锁,保证事务的两次查询一样。

配置如下:

<tx:advice ...>

   	 <tx:attributes>
         <tx:method name="*" isolation="REPEATABLE_READ"/>    
        </tx:attributes>
    </tx:advice>     <property name="transactionAttributes">
     <props>
         <prop key="...">
                 PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ    
           </prop>    
      </props>
    </property> DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);

Rollback、超时和只读事务属性 我们有时候需要自己设置回滚异常,而不是遇到异常或者错误的时候才让事务回滚。通过设置rollback事务属性来指定。若不指定则按照默认规则处理。如果事务长时间占用资源势必会影响性能,因此同样需要优化和改进引用性能,timeout事务属性表示事务在被强制回滚之前存活的时间,read-only属性表示该事务仅仅读取而不更新数据。这些配置如下:

可直接在@Transactional注解中设置rollbackFor和noRollbackFor属性定义,属性被声明为Class[],可以指定超过一个异常,也支持直接设置timeout和readOnly。

其他如下:

<tx:advice ...>

      <tx:attributes>
        <tx:method name="...">
            rollback-for="java.io.IOException"
            no-rollback-for="java.lang.ArithmeticException"/>    
            timeout="30"
            read-only="true"
        </tx:method>    
       </tx:attributes>
   </tx:advice>    <property name="transactionAttributes">
     <prop key="...">
           PROPAGATION_REQUIRED,-java.io.IOException,+java.lang.ArithmeticException,timeout_30,readOnly
     </prop>    
   </property> RuleBasedTransactionAttribute attr = new RuleBasedTransactionAttribute();
attr.getRollbackRules().add(new RollbackRuleAttribute(IOException.class));
attr.getRollbackRules().add(new NoRollbackAttribute(ArithmeticException));
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setTimeout(30);
def.setReadOnly(true);

Spring3之事务管理的更多相关文章

  1. Spring3数据库事务管理机制

    Spring对事务的解决办法其实分为2种:编程式实现事务,AOP配置声明式解决方案. http://jinnianshilongnian.iteye.com/blog/1496953 Spring提供 ...

  2. spring3.0事务管理配置

    转载:http://war-martin.iteye.com/blog/1396335 第一种配置方法:基于XML的事务管理 这种方法不需要对原有的业务做任何修改,通过在XML文件中定义需要拦截方法的 ...

  3. Spring3 (事务管理)

    简介: 1.事务管理.2.整合Junit.3.整和Web 1       事务管理 1.1   回顾事务 l  事务:一组业务操作ABCD,要么全部成功,要么全部不成功. l  特性:ACID 原子性 ...

  4. [转]Spring3核心技术之事务管理机制

    原文地址:http://chouyi.iteye.com/blog/1675199 Spring对事务的解决办法其实分为2种:编程式实现事务,AOP配置声明式解决方案. http://jinnians ...

  5. 跟我学Spring3(9.2):Spring的事务之事务管理器

    原文出处: 张开涛9.2.1 概述 Spring框架支持事务管理的核心是事务管理器抽象,对于不同的数据访问框架(如Hibernate)通过实现策略接口PlatformTransactionManage ...

  6. Spring3.0配置多个事务管理器(即操作多个数据源)的方法

    大多数项目只需要一个事务管理器.然而,有些项目为了提高效率.或者有多个完全不同又不相干的数据源,最好用多个事务管理器.机智的Spring的Transactional管理已经考虑到了这一点,首先分别定义 ...

  7. Spring的事务管理

    事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 ...

  8. Spring第13篇—–Spring整合Hibernate之声明式事务管理

    不容置疑的我们可以知道Spring的事务管理是通过AOP(AOP把我们的事务管理织入到我们的业务逻辑里面了)的方式来实现的,因为事务方面的代码与spring的绑定并以一种样板式结构使用.(面向切面编程 ...

  9. spring的annotation-driven配置事务管理器详解

    http://blog.sina.com.cn/s/blog_8f61307b0100ynfb.html ——————————————————————————————————————————————— ...

随机推荐

  1. DICOM医学图像处理:DIMSE消息发送与接收“大同小异”之DCMTK fo-dicom mDCM

    背景: 从DICOM网络传输一文开始,相继介绍了C-ECHO.C-FIND.C-STORE.C-MOVE等DIMSE-C服务的简单实现,博文中的代码给出的实例都是基于fo-dicom库来实现的,原因只 ...

  2. ArcScene三维制作

    转自:http://www.360doc.com/content/11/0818/10/7534722_141363009.shtml 1.0 添加地图数据 数据显示效果: 1.1 设置图层的显示顺序 ...

  3. 《Nagios系统监控实践》一书出版

    本书是我的第一本译著,有此机会实属机缘巧合.虽然使用Nagios只有一年多的时间,但是作为用户,我深感其设计的简洁与高效—没有一丝多余的东西.因为工作的关系,要求对各个领域都有所了解,所以没有仔细地阅 ...

  4. 关于hibernate映射过程中的笔记

    MySQL遇到check the manual that corresponds to your MySQL server version for the right syntax错误 You hav ...

  5. Deep learning:三十四(用NN实现数据的降维)

    数据降维的重要性就不必说了,而用NN(神经网络)来对数据进行大量的降维是从2006开始的,这起源于2006年science上的一篇文章:reducing the dimensionality of d ...

  6. phpMyAdmin 中数据库替换问题

    将原来的数据库删除,然后进入data文件夹中修改名字, 但是后来前台出现错误: Zend_Db_Statement_Exception Object ( [_previous:Zend_Excepti ...

  7. 2014-02-27WPF学习笔记

    //StackPanel布局 //创建时间:11:33 页面布局:Orientation默认纵向:Vertical水平为:Horizontal <Button> <Button.Co ...

  8. Apache Hadoop 源码阅读

    总之一句话,这些都是hadoop-2.2.0的源代码里有的.也就是不光只是懂理论,编程最重要,还是基本功要扎实啊.... 在hadoop-2.2.0的源码里,按Ctrl + Shift + T . 跳 ...

  9. [Objective-c 基础 - 2.7] 构造方法、重写init方法

    A.id 万能指针,可以指向任何对象,实质是NSObject的指针,使用的时候不用加上*   B.NSObject中得类方法new 1.完整地创建一个可用对象步骤 (1)分配存储空间 + alloc ...

  10. 为什么只有在用Visual Studio启动程序时会抛出InvalidOperationException异常

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:为什么只有在用Visual Studio启动程序时会抛出InvalidOperationExceptio ...