zoukankan      html  css  js  c++  java
  • spring事务管理

      在软件开发领域,全有或全无的操作被称为事务(transaction)。事务允许你将几个操作组合成一个要么全部发生要么全部不发生的工作单元。如果一切顺利,事务将会成功。但是有任何一件事情出错的话,所发生的行为将会被清除干净,就像什么事情都没有发生一样。

      在传统的软件开发中,人们创建了一个术语来描述事务:ACID。简单来说,ACID表示4个特性。

      1)原子性(Atomic):事务是由一个或多个活动所组成的一个工作单元。原子性确保事务中的所有操作全部发生或全部不发生。如果所有的活动都成功了,事务也就成功了。如果任意一个活动失败了,整个事务也失败并回滚。

      2)一致性(Consistent):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态。现实的数据不应该被损坏。

      3)隔离性(Isolated):事务允许多个用户对相同的数据进行操作,每个用户的操作不会与其他用户纠缠在一起。因此,事务应该被彼此隔离,避免发生同步读写相同数据的事情(注意的是,隔离性往往涉及到锁定数据库中的行或表)。

      4)持久性(Durable):一旦事务完成,事务的结果应该持久化,这样就能从任何的系统崩溃中恢复过来。这一般会涉及将结果存储到数据库或其他形式的持久化存储中。

      与EJB类似,Spring为事务管理提供了丰富的功能支持,包括编码方式和声明方式。但是,Spring的事务管理能力超过了EJB。

      Spring对编码式事务的支持与EJB有很大的区别。不像EJB与Java事务API(Java Trasaction API,JTA)耦合在一起那样,Spring通过回调机制将实际的事务实现从事务性的代码中抽象出来。实际上,Spring对事务的支持甚至不需要JTA的实现。如果你的应用程序只使用一种持久化资源,Spring可以使用持久化机制本身所提供的事务性支持,这包括了JDBC、Hibernate以及JPA。但是如果应用程序的事务跨多个资源,那么Spring会使用第三方的JTA实现来支持分布式(XA)事务。

      编码式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。Spring对声明式事务的支持会让人联想起EJB的容器管理事务(container-managed transaction,CMT)。它们都允许你声明式地定义事务边界。但是Spring的声明式事务要胜过CMT,因为它允许你声明额外的一些属性,例如隔离级别和超时。

      选择编码式事务还是声明式事务很大程度上是在细粒度控制和易用性之间进行权衡。当通过编码实现事务控制时,你能够精确控制事务的边界,它们的开始和结束完全取决于你的需求。通常,我们不需要编码式事务所提供的细粒度控制,而会选择在上下文定义文件中声明事务。

      无论在选择将事务编码到Bean中还是将选择其定义为切面,你都需要使用Spring事务管理器与平台相关的事务进行交互。

      Spring并不直接管理事务,而是提供了多种事务管理器,它们将事务管理的职责委托给JTA或其他持久化机制所提供的平台相关的事务实现。

      为了使用事务管理器,需要将其声明在应用程序上下文中。

      如果在应用程序中你直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTrasactionManager,你需要使用如下的XML将其装配到应用程序的上下文定义中:

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

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

    </bean>

      如果应用程序的持久化是通过Hibernate实现的,那么你需要使用HibernateTrasactionManager。对于Hibernate3,需要在Spring上下文定义中添加如下的<bean>声明:

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

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

    </bean>

      如果应用程序的持久化是通过JPA实现的,你需要在Spring中这样配置JpaTransactionManager:

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

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

    </bean>

    1、声明式事务

      Spring对声明式事务的支持是通过使用Spring AOP框架实现的。这是很自然的一件事,因为事务是在应用程序主要功能之上的系统服务。Spring提供了3种方式来声明事务边界。Spring AOP、TransactionProxyFactoryBean的代理Bean及Spring的tx命名空间和@Transactional注解。在最新版本的Spring中,TransactionProxyFactoryBean已基本被淘汰。

      在Spring中,声明式事务是通过事务属性(Transaction Attribute) 来定义的。事务属性描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图:

      尽管Spring提供了多种声明式事务的机制,但是所有的方式都依赖这五个参数来控制如何管理事务策略。

      事务的第一个方面是传播行为(propagation behavior)。传播行为定义了客户端与被调用方法之间的事务边界。传播规则回答了这样一个问题,即新的事务应该被启动还是被挂起,或者方法是否要在事务环境中运行。Spring定义了7种不同的传播行为。如图:

      声明式事务的第二个维度就是隔离级别(isolation level)。隔离级别定义了一个事务可能受其他并发事务影响的程度。在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必需的,但可能会导致一下问题。

      1)脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。

      2)不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间更新了数据。

      3)幻读(Phantorn read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一事务(T1)就会发现多了一些原本不存在的记录。

      在理想情况下,事务之间是完全隔离的,从而可以防止这些问题发生。但是完全的隔离会导致性能问题,因为它通常会涉及锁定数据库中的记录(有时候甚至是整张表)侵占性的锁定会阻碍并发性,要求事务互相等待以完成各自的工作。Spring事务的隔离级别如下:

      声明式事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行读操作,数据库可以利用事务的只读特性来进行一些特定的优化。

      声明式事务的第四个特性是事务超时。为了使应用程序很好地运行,事务不能运行太长的时间。

      声明式事务的最后一个特性是回滚规则,这些规则定义了哪些异常导致事务回滚而哪些不会。默认情况下,事务只有在遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚。但是你可以声明事务在遇到特定的检查型异常时像遇到运行异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

      

      在早期版本的Spring中,声明事务需要装配一个名为TransactionProxy-FactoryBean的特殊Bean。TransactionProxyFactoryBean的问题在于使用它会导致非常冗长的Spring配置文件。已基本不使用。Spring现在提供了一个tx配置命名空间,借助它可以极大的简化Spring中的声明式事务。配置如下:

    <tx:advice id="txAdvice">

      <tx:attributes>

        <tx:method name="save*" propagation="REQUIRED"/>

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

      </tx:attributes>

    </tx:advice>

      除了<tx:advice>配置元素,tx命名空间还提供了<tx:annotation-driven>元素。使用<tx:annotation-driven>元素时,通常只需要一行XML:

    <tx:annotation-driven/>

      我们可以通过transactionmanager属性来指定特定的事务管理器:

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

      <tx:annotation-driven>元素告诉Spring检查上下文中所有的Bean并查找使用@Transactional注解的Bean,而不管这个注解试试用在类级别上还是方法级别上。对于每一个使用@Transactional注解的Bean,<tx:annotation-driven>会自动为它添加事务通知。通知的事务属性是通过@Transactional注解的参数来定义的。如:

      

  • 相关阅读:
    快速排序法的C#实现
    SQL语句执行效率及分析(note)
    如何在C#中运行数学表达式字符串
    TSQL删除重复数据,保留一条
    C#对象序列化XML时报错:反射类型XXX时出错
    c#如何扩展类型的内置方法
    把数字转换成阿拉伯数字大写的程序
    使用C#格式化字符串
    Silverlight中自己定义实现的双击方法
    原来是这样:C#中new一个对象时,发生了什么事?
  • 原文地址:https://www.cnblogs.com/jizhuan/p/6603707.html
Copyright © 2011-2022 走看看