J2EE事务策略 - 模型和策略概述

很多时候,程序员、设计师和架构师都混淆了事务模型和事务策略的概念。我通常会问一些架构师或者技术领导人他们的项目中使用的事务策略。我经常得到3中回答。有时是:“哦,我们的程序实际上没有使用事务。”有些时候是:“嗯,我实际上不知道你说的是什么意思。”通常情况下,我得到的是一个肯定的回答“我们使用声明式事务”。但是,就像你将在本文中看到的,术语声明式事务指的是事务模型,而不是事务策略。

 

Java平台支持的3种事务模型是:

  • 本地事务模型
  • 编程式事务模型
  • 声明式事务模型

这3种模型描述了在Java平台中事务的基本行为和他们怎么实现的。但是,他们只是提供了事务处理的规则和语义,事务模型怎么应用完全由你决定。例如,你什么时候应该使用事务属性REQUIRED或者MANDATORY?什么时候以及在哪儿指定事务回滚指令?什么时候应该考虑编程式事务模型或者声明式事务模型?怎么为高性能系统优化事务?事务模型本身不能回答这些问题。而且,你必须通过开发自己的事务策略或者使用本文介绍的4种事务策略来解决这些问题。

 

正如你在本系列的第一篇文章中所看到的,很多常见的事务陷阱会影响事务的行为,因此降低数据的完整性和一致性。类似的,缺乏有效的事务策略也会对数据的完整性和一致性产生负面影响。在本文中介绍的事务模型是开发有效的事务策略的基础。理解这些模型之间的区别以及他们怎么工作对于理解使用他们的事务策略是至关重要的。在介绍这三种事务模型后,我将介绍适用于大部分业务应用程序的四种事务策略,从简单的web程序到大型高速的事务处理系统。Transanction Strategies系列的后续文章将详细介绍这些策略的细节。

 

本地事务模型

本地事务模型的名字来源于这样一个事实,事务由底层的数据库资源管理器管理,而不是你的应用程序运行的容器或者框架。这该模型中,你管理的是连接(connection)而不是事务。正如你在"Understanding transaction pitfalls,"中了解到的,当你使用类似于Hibernate,TopLink或者JPA等OR映射框架时,你不能使用本地事务模型。你可以在使用DAO,基于JDBC的框架或者数据库存储过程是使用他们。

 

你可以以两种方式中的一种使用本地事务模型:让数据库管理连接,或者由程序管理连接。通过把JDBC Connection对象的autoCommit属性设置为true(默认值)让数据库管理连接。这就会告诉底层的DBMS在插入、更新或者删除完成时提交事务,或者在失败时回滚。清单1展示了该技术,它在TRADE表中插入一条股票交易的订单信息。

 

Listing 1. Local transactions with a single update

 

 

注意,在清单1中autoCommit属性被设置为true,告诉DBMS在每一个语句后应该提交本地事务。如果你在一个逻辑工作单元(LUW, logic unit of work)中只有单一的数据库维护活动,这个技术可以工作的很好。但是,假设processTrade() 方法也需要更新ACCT表中的交易余额值。在这种情况下,两种数据库行为是独立的,在更新ACCT表之前插入到TRADE表中的记录会被提交。如果更新ACCT表失败了将没有办法回滚插入到TRADE表中的记录,结果就造成了数据库中的数据不一致。

 

这个情况导致出现了第二种技术:由程序管理连接。在这种技术中,你将把Connection对象的autoCommit属性设置为false,然后手动提交或者回滚事务。清单2展示了该技术:

 

Listing 2. Local transactions with multiple updates

 

 

注意,在清单2中把autoCommit属性设置为false,这就告诉底层DBMS连接将有代码管理,而不是数据库。在这种情况下,如果一切正常,你必须调用Connection对象的commit()方法 ;如果发生了异常就要调用rollback()方法。通过这种方式,你就可以把数据库中的两个活动组织到一个逻辑工作单元中。

 

尽管本地事务模型在现在看来有些过时了,但是它是本文后面描述的一种事务策略的重要元素。

 

编程式事务模型

编程式事务模型来源于这样的事实,由程序员负责管理事务。不像本地事务模型,在编程式事务模型中,由你管理事务,并且它独立于底层数据库连接。

 

类似于清单2,在该模型中,有程序员负责从事务管理器中获得一个事物,启动事务,提交事务,或者在异常发生时回滚事务。你可能会猜到,这可能会导致在你的应用程序的业务逻辑中出现大量易于出错的代码,尽管如此,有些事务策略需要编程式事务模型。

 

尽管概念相同,编程式事务模型在Spring框架和EJB3.0规范中的实现方式是不同的。我将会展示在EJB3.0中该模型的实现,然后再介绍在Spring框架中的实现。

 

EJB3.0中的编程式事务

在EJB3.0中,你通过JNDI查找javax.transaction.UserTransaction从事务管理器(或者说容器)中获得一个事务。你可以通过调用begin()方法启动事务,调用commit()方法提交事务,或者在错误发生时调用rollback()方法回滚事务。在该模型中,容器不会自动提交或者回滚事务,而是由程序员通过编程实现。清单3展示了EJB3.0中使用JPA实现编程式事务模型的一个例子:

 

Listing 3. Programmatic transactions using EJB 3.0

 

在Java企业版容器中的无状态会话Bean中使用编程式事务模型时,你必须告诉容器你使用的是编程式事务。这可以通过使用@TransactionManagement标记和把事务类型设置为BEAN的方法实现。如果你不适用该标记,容器将假设你使用的是EJB3.0中默认的声明式事务管理。当在无状态会话bean环境之外的客户层使用编程式事务模型时,你不需要设置事务类型。

 

Spring中的编程式事务模型

在Spring框架中,有两种方式实现编程式事务模型,一种是通过Spring的TransactionTemplate,另一种是直接使用Spring平台的事务管理器。因为我不是一个匿名内部类和难度代码的推崇者,所以我使用Spring中的第二种技术展示编程式事务模型。

 

Spring中至少有九种事务管理器。你可能最长使用的是DataSourceTransactionManager, HibernateTransactionManager, JpaTransactionManager和JtaTransactionManager。我的代码示例使用JPA,因此我将展示JtaTransactionManager的配置。

 

为了在Spring中配置JtaTransactionManager,直接使用springframework.orm.jpa.JpaTransactionManager类在应用程序上下文XML文件中定义bean,并且添加一个到JPA实体管理器工厂bean的引用。然后,如果你的业务逻辑bean由Spring管理,把事务管理器注入到bean中,就像代码清单4所示:

 

 Listing 4. Defining the Spring JPA transaction manager

 

如果你的应用程序类不是由Spring管理,你可以在你的方法中通过调用Spring上下文的getBean()方法得到一个到事务管理器的引用。

 

在源代码中,你可以使用管理器得到一个事物,一旦完成了所有的更新,你可以调用commit()方法提交事务或者调用rollback()方法回滚事务。清单5展示了该技术:

 

Listing 5. Using the Spring JPA transaction manager

 

 注意在清单5中Spring框架和EJB3.0.在Spring中,通过调用事务管理器的getTransaction()方法获得事务。匿名的DefaultTransactionDefinition 类包含了事务的细节和行为,包括事务名称、隔离级别、传播模式和事务超时值等。在本例中我只是简单的使用默认值,name值是空字符串,底层数据库默认的隔离级别(通常是READ_COMMITTED),事务的传播模式是PROPAGATION_REQUIRED和数据库的默认超市值。注意commit()和rollback()方法由事务管理器调用,而不是事务本身。

 

声明式事务模型

声明式事务模型,又称为容器管理的事物,是Java平台中最常用的事物模型。在该模型中,容器负责启动、提交和回滚事务。程序员只负责指定事务的行为。在本系列的第一篇文章中讨论的事物缺陷大多都和声明式事务模型相关。

 

Spring框架和EJB3.0都使用标记指定事务的行为,Spring使用@Transactional标记,而EJB3.0使用@TransactionAttribute标记。在使用声明式事务模型时,在发生检查异常时,容器不会自动回滚事务。程序员必须检查异常发生时在哪儿以及何时回滚事务。在Spring框架中,你可以通过在@Transactional标记上使用rollbackFor属性来指定;在EJB中,可以通过调用SessionContext的setRollbackOnly()方法指定。 清单6展示了EJB中声明式事务模型的用法:

 

Listing 6. Declarative transactions using EJB 3.0

 

清单7展示了Spring中声明式事务模型的使用:

 

Listing 7. Declarative transactions using Spring

 

事务属性

除了回滚指令,你还必须指定事务属性,它定义了事务的行为。无论使用Spring框架还是EJB,Java平台都支持6种事务属性:

  • Required
  • Mandatory
  • RequiresNew
  • Supports
  • NotSupported
  • Never

在描述每种事务属性时,我假设有一个正在应用事务属性的方法methodA()。

当在methodA()方法上指定事务属性Required时,如果在已有事务中调用方法methodA(),那么将使用已有事务。否则,methodA()将启动一个新的事务,如果事务由方法methodA()启动,那它必须由方法methodA()来终结(提交或回滚)。这是最常使用的事物属性,也是Spring框架和EJB3.0中的默认事务属性。不行的是,它在很多情况下北被错误的使用,导致了数据完整性和一致性问题。在后续文章中将要讨论的每种事务策略中,我将详细讨论该事务属性。

 

如果在方法methodA()上指定了事务属性Mandatory并且在已有事务中调用它,那么将使用已有事务。但是,如果在没有事务的情况下调用methodA(),将会抛出TransactionRequiredException异常,表明在调用发放methodA()之前必须存在事务。该事务属性用在本文后面将要讨论的Client Orchestration事务策略中。

 

 我发现Supports属性是另一个程序员没有完全理解的事物属性。如果在方法methodA()上指定了该属性,当methodA()在已有事务中调用时,以后事务将被使用。如果methodA()没有在事务环境中调用,那么将不会启动事务。该属性主要用于数据库中的只读操作,那么在这种情况下为什么不直接指定NotSupported代替它呢?毕竟,NotSupported可以确保该方法不在事务中执行。答案很简单,在已有事务中调用查询操作将会读取事务日志(也就是已被更新但未提交的数据),但是在非事务环境下只能读取表中未被改变的数据。例如,例如,你向TRADE表中插入一条交易记录,接着获取所有的交易记录列表,那么未提交的交易记录将出现在列表中。但是,如果你使用类似NotSupported的属性,将会使查询从表中而不是事务日志中读取记录。在前面的例子中,将看不到新插入但为提交的交易记录。这可能不是一件坏事情,取决于你的使用情况和业务逻辑。

 

NotSupported属性表明在指定的方法上将不使用或者启动事务,不管是不是已经存在一个事物。如果在methodA()上指定了NotSupported属性并且methodA()在已有事务中调用,在method()结束之前,已有事务将被挂起,methodA()结束时,已有事务被恢复。只有很少的情况下使用该属性,主要和调用数据库中的存储过程有关。如果你在一个已有事务中调用存储过程并且存储过程中包含BEGIN TRANS,或者在Sybase中,运行在unchained模式,将会抛出一个异常表明不能在一个已有事务中启动新的事务(也就是说不支持嵌套事务)。几乎所有的把JTS作为JTA默认实现的容器---而不是java平台不支持嵌套事务。如果你不能改变数据库中的存储过程,你可以指定NotSupported来挂起已有事务从而避免一场发生。结果是,你不在具有更新的原子性,作为平衡,它可以把你带出困难境地。

 

Never属性或许是所有属性中最有趣的一个,除了一个重要的不同外,它和NotSupported一样:当在已有事务中调用一个被指定为NotSupported的方法时,将会抛出一个异常表明该方法不允许有事务。我遇到的唯一需要使用它的情况是在单元测试中,当你调用一个特殊的方法时,它提供了一种简单快捷地验证是否已经存在事务的方法。

 

事务策略

本文中描述的事务模型形成了将要介绍的事务策略的基础,在构建事务策略之前理解模型之间的差别以及它们怎么工作是非常重要的。在大多数业务应用程序中都可以使用的主要事务策略是:

  • Client Orchestration transaction strategy
  • API Layer transaction strategy
  • High Concurrency transaction strategy
  • High-Speed Processing transaction strategy

在这儿将简要讨论下每种事务策略,在本系列后续文章中将详细讨论每一种事务策略。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值