事务Transactions

事务Transactions

典型的企业应用存取存储在一个或多个数据库中的信息。因为这些信息对于业务操作来说非常重要,它必须是准确的,及时的并且可靠的。如果在同一时间允许多个程序更新同一条数据,那么数据完整性就会荡然无存。如果业务处理使业务数据被部分更新,那么数据完整性也无从谈起。为了避免这两种情况发生,软件事务保证了数据完整性。事务能处理多个程序的并发访问。在系统失败时,事务能确保数据在恢复之后能处于连续状态。

什么是事务?

为了完成一个业务事务,程序可能需要完成多个步骤。一个商业应用,比如,从一个帐号里转帐到另外一个账户上,步骤如下面伪码所示:

begin transaction 
   debit checking account 
   credit savings account
   update history log 
commit transaction 

这三个步骤要么都完成,要么一个都没有完成。否则,数据完整性就不能保证。因为在一个事务里面的这些步骤,是一个统一的整体。一个事务总是被定义成不可分割的工作单元。

一个事务可以以两种方式结束:提交或者回滚。事务提交,数据更改就会被保存。如果事务中的 statement失败,则事务回滚,在 statement中的数据更改都会被撤销。在上面的伪码中,举个例子,如果在 credit步骤中,磁盘驱动器坏了,事务就会回滚并且撤销 debit statement所做的数据更改。尽管事务失败了,数据完整性仍能得到保障,因为帐号金额还是平衡的。

在前面的伪码中,begincommit statement标识了事务的边界。当设计一个企业bean时,你需要决定如何通过指定容器管理事务(CMT)或bean管理事务(BMT)来设定事务边界。

容器管理事务

在一个标识为容器管理事务的企业bean中,EJB容器设定了事务边界。在任何企业bean中你都可以用容器管理事务,比如session beanmessage-driven bean。容器管理事务简化了企业bean的开发,因为企业bean代码不必明确标识事务边界,代码中不必包含begin end transactionstatements

缺省地,如果没有指定事务边界,则企业bean应用容器管理事务边界。

典型地,容器在企业 bean方法开始之前立刻就会启动事务。在方法退出之前事务会提交。每个方法都能关联到一个单一的事务。在一个方法里面,嵌套的或者多个事务都是不允许的。

容器管理事务不需要所有方法都关联到事务中。在开发一个企业bean时,你需要指定这个bean的哪一个方法需要关联到事务中,通过设定事务属性就能指定。

采用了容器管理事务的企业bean绝对不能在用任何和容器管理事务边界冲突的其他事务管理方法。这些方法的例子有java.sql.Connectioncommit, setAutoCommit, rollback方法,或者javax.jms.Sessioncommit and rollback方法。如果你需要控制事务边界,你必须用应用管理事务边界。

应用CMT生命的企业bean,也绝不能用javax.transaction.UserTransaction接口。


事务属性

事务属性控制着事务范围。图34-1表明为什么范围控制很重要。在这个图中,method-A启动了一个事务,然后调用Bean-2method-B。当method-B执行的时候,它是在method-A启动的事务范围里面运行呢,还是启动一个新的事务?答案由method-B的事务属性决定。

 











Figure 34-1事务范围

事务属性可以具有以下几个值:

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

如果运行事务中的客户调用企业bean的方法,则方法运行于客户的事务中。如果客户没有关联到事务中,容器会在运行入方法之前启动一个新的事务。

Required属性是CMT中企业bean所有的事务属性中隐含的事务属性。一般除非你需要重载其他的事务属性,否则不需要指定Required属性。因为事务属性是声明性的,你可以随后很简单地更改它。

RequiresNew

如果客户端运行在事务里,并且调用了企业bean的方法,容器就会执行下列步骤:

1, 挂起客户端事务

2, 启动一个新的事务

3, 委派这个方法的调用

4, 在方法结束后重新恢复客户断事务

如果客户端没有关联到一个事务中,容器就会在运行这个方法之前的启动一个新的事务。

如果你想确保这个方法总是运行在一个新的事务中,你就可以用RequiresNew属性。

Mandatory

如果运行于事务中的客户调用了企业bean的方法,方法在客户的事务中执行。如果客户没有关联到事务中,容器就会抛出TransactionRequiredException

如果企业bean方法必须用客户事务则采用Mandatory属性。

NotSupported

如果运行在一个事务里的客户调用了企业bean的方法,容器会在调用之前中止客户事务。在调用结束后,容器会恢复客户事务。

如果客户没有关联到一个事务中,容器不会在运行到方法前启动一个新的事务。

NotSupported属性标识不需要事务的方法。因为事务会带来更高的性能支出,所以这个属性可以提高性能。

Supports

如果运行在事务中的客户调用了企业bean方法,这个方法运行于客户事务中。如果客户没有关联到事务中,容器不会在运行这个方法之前启动一个新的事务。

因为方法的事务行为是多变的,所以你要小心使用Supports属性。

Never

如果运行在事务中的方法调用了企业bean的方法,容器会抛出RemoteException。如果客户没有关联到事务,则容器不会在运行入方法之前启动一个新的事务。

事务属性概要

Table 34-1概述了事务属性的作用,T1T2都由容器控制。事务T1关联到客户,客户调用企业bean中的方法。在绝大多数场景,客户是另外一个企业bean。事务T2会在方法执行之前由容器启动。

 

在最后一列中,None意味着业务方法不会在容器控制的事务中运行。可是,在这些业务方法的数据库调用中,业务可以被DBMS的事务管理器控制。


Table 34-1 Transaction Attributes and Scope 

Transaction Attribute

Client's Transaction

Business Method's Transaction

Required

None

T2

T1

T1

RequiresNew

None

T2

T1

T2

Mandatory

None

error

T1

T1

NotSupported

None

None

T1

None

Supports

None

None

T1

T1

Never

None

None

T1

Error

Setting Transaction Attributes

事务属性由注解于企业beanjavax.ejb.TransactionAttribute注解指定,被设定为javax.ejb.TransactionAttributeType常量的某一个值。

如果你用@TransactionAttribute注解企业bean,则指定的TransactionAttributeType会被应用到这个企业bean的所有方法上。而在一个方法上的注解只在这一个方法上有效。如果一个注解同时在类和方法上,则方法上的注解覆盖类上的注解。

TransactionAttributeType常量封装了前面小节描述的事务属性。

  • Required: TransactionAttributeType.REQUIRED
  • RequiresNew: TransactionAttributeType.REQUIRES_NEW
  • Mandatory: TransactionAttributeType.MANDATORY
  • NotSupported: TransactionAttributeType.NOT_SUPPORTED
  • Supports: TransactionAttributeType.SUPPORTS
  • Never: TransactionAttributeType.NEVER

 

以下代码片段示范了如何使用 @TransactionAttribute 注解:

@TransactionAttribute(NOT_SUPPORTED)
@Stateful
public class TransactionBean implements Transaction {
...
  @TransactionAttribute(REQUIRES_NEW)
  public void firstMethod() {...}

  
  
   
    
  
  
  @TransactionAttribute(REQUIRED)
  public void secondMethod() {...}

  
  
   
    
  
  
  public void thirdMethod() {...}

  
  
   
    
  
  
  public void fourthMethod() {...}
}

在这个例子中,TransactionBean类的事务属性被设定为NotSupportedfirstMethod为设定为RequiresNewsecondMethod被设定为Required。因为在方法上的@TransactionAttribute覆盖了类上的@TransactionAttribute,调用firstMethod会创建一个新的事务,调用secondMethod会在当前事务中进行或创建一个新的事务。thirdMethodfourthMethod不会发生在事务中。

CMT中回滚事务

CMT中,有2种方法回滚事务。第一种,系统异常抛出,容器自动回滚事务;第二种调用EJBContext 接口的setRollbackOnly方法,bean方法就会通知容器回滚事务。如果bean抛出业务异常,除非调用setRollbackOnly否则容器不会自动回滚事务。

同步Session Bean实例变量

可选的SessionSynchronization接口,允许有状态session bean实例接受事务同步通知。例如,你可以用数据库中的响应值同步企业bean实例变量。容器调用SessionSynchronization的方法afterBeginbeforeCompletionafterCompletion,在事务的主要阶段。

afterBegin方法通知企业bean实例新事务开始。容器在调用业务方法之前立即调用afterBegin方法。

容器在业务方法结束之后,但事务提交之前调用beforeCompletion方法。beforeCompletion方法是在事务提交之前回滚事务的最后机会(通过调用setRollbackOnly的方式)。

afterCompletion方法指明事务结束了。它有个boolean的返回值,分别标识事务提交(true)和回滚(false)。


不允许在CMT中出现的方法

你不能调用任何可能和容器设定的事务边界相冲突的方法。禁止的方法列表如下:

  • java.sql.Connection commit, setAutoCommit, rollback方法
  • javax.ejb.EJBContext getUserTransaction method of方法
  • javax.transaction.UserTransaction的所有方法

可是,你可以在应用管理事务(AMT)中用这些方法设定事务边界。

Bean管理事务Bean-Managed Transactions

Bean管理事务边界中,session beanMDB中的代码明确地标明了事务边界。尽管CMT的企业bean只需要较少的代码量,但它们有一个限制:当方法执行时,要么被关联到一个事务,要么就不能。如果这个限制使你的bean的编码变难的话,你就需要考虑使用容器管理事务了。

下面的伪码图示了Bean管理事务能使你得到的好处。通过检查变量条件,伪码在业务方法中决定是否启动或停止不同的事务。

begin transaction
...
update table-a
...
if (condition-x)
   commit transaction
else if (condition-y)
   update table-b
   commit transaction
else
   rollback transaction
   begin transaction
   update table-c

   commit transaction

当编写容器管理事务的session bean MDB时,你需要决定是否采用JDBCJTA事务,下面小节将讨论这两种事务类型。

 

JTA Transactions

JTAJava Transaction API的简称。这个API使你用事务管理器实现独立的规则划定事务边界。应用服务器用Java Transaction Service (JTS)实现了事务管理器。但你的代码不能直接调用JTS的方法。你的代码调用JTA的方法,而由JTA调用底层的JTS

JTA事务由Java EE事务管理器控制。你可能想用JTA事务来更新多个厂商提供的不同的数据库。一个确定的DBMS的事务管理器可能不能和不同的数据库合作。可是,Java EE事务管理器有一个限制:它不支持嵌套的事务。换句话说,在一个事务结束前是不能启动另一个事务的。

为了指定JTA事务,你需要调用javax.transaction.UserTransaction 接口的begin, commit, rollback方法。


没有提交事务就返回

在容器管理事务的无状态session bean中,业务方法在返回之前必须提交或回滚事务。可是,有状态session bean没有这个约束。

应用JTA事务的有状态session beanbean实例和事务直接的关联要在多个客户调用直接保持。即便客户调用的每个业务方法打开并关闭了数据库连接,这种关联还是要被保持到这个实例结束事务。

JDBC事务的有状态session bean中,JDBC连接在多个调用之间保持了bean实例和事务。如果连接关闭,这种关联也就不存在了。

Bean管理事务中不允许的方法 

Bean管理事务中不要调用EJBContext接口的getRollbackOnlysetRollbackOnly方法。这些方法只能在CMT中使用。对于Bean管理事务,应该调用UserTransaction 接口的getStatusrollback方法。

事务超时

CMT中,你可以通过设定domain.xml文件中的超时时间属性来控制事务超时间隔,domain.xml在你的应用服务器的安装目录中的config目录下。例如:你可以像下面这样设定5秒超时:

timeout-in-seconds=5

这样设定好之后,如果事务在5秒中没有完成,则EJB容器就会把它回滚。

当应用服务器刚被安装时,超时值被设定为0

timeout-in-seconds=0 

如果超时时间设定为0,则事务不会超时。

只有在CMT事务中,超时属性才有效。对于由bean管理的JTA事务,你可以调用UserTransaction接口的setTransactionTimeout方法。


更新多个数据库

Java EE 事务管理器除了控制容器管理JDBC事务,还控制所有企业bean事务。Java EE 事务管理器允许在一个事务中更新多个数据库。下面的图显示了两个在同一事务中更新多个数据库的场景

在图34-2中,客户调用Bean-A中的业务方法。这个方法启动了事务,更新数据X,更新数据库Y,并且调用Bean-B的业务方法。第二个业务方法更新数据库Z,并且把控制权返回到要提交事务的Bean-A中。所有的这3个数据库更新发生在同一个事务中。


 

 

Figure 34-2 Updating Multiple Databases
在图34-3中,客户调用Bean-A中的业务方法。这个业务方法启动一个事务,并更新数据库X。这样,Bean-A调用Bean-B中的方法,Bean-B在另外一个远程Java EE服务器上。Bean-B中的方法更新数据库Y。Java EE应用服务器确保两个数据库更新在同一事务中完成。

 

Figure 34-3 在Java EE服务器中更新多个数据库
Transactions in Web Components
你可以用 java.sql.Connection或javax.transaction.UserTransaction接口在web组件中标识事务。这两个接口和在bean管理事务中使用的接口是一样的。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值