浅琢Container-Managed Transaction

来源:互联网 发布:贺林院士怎么样 知乎 编辑:程序博客网 时间:2024/06/10 03:14

事务处理分Container-Managed Transaction和Bean-Managed Transaction。

Container-Managed Transaction

由EJB容器来管理事务的开始与结束。在这种情形底下,事务可以处理各种企业Bean:会话,实体或者消息驱动。由EJB容器来管理,简化了开发,代码无需显式地声明事务处理的开始和结束。
在企业Bean的某个方法调用之前,容器立即启用一个事务,在该方法退出的时候,容器会提交事务。每个方法都会有一个特定的事务来处理,每个事务都由容器自动管理。但是在方法体内部,不支持嵌套的或者多个事务。
基于容器的事务管理并不强迫每个方法都必须有一个事务来监督,我们可以设置事务属性来区别对待各自的方法。

Transaction Attributes

事务属性决定了事务的作用范围,有以下值:
 Required
 RequiresNew
 Mandatory
 NotSupported
 Supports
 Never

Required:
在Required情况下,假设Client在一个事务中运行并且调用了一个企业Bean的某个方法,那么这个方法也将由这个事务来处理。反之,如果Client运行的时候没有事务来管理,那么在调用Bean的方法之前,容器会自动启用一个事务机制。

RequiresNew:
如果Client在一个事务处理中调用Bean的方法,容器会采取如下步骤来处理:
1)挂起Client的事务
2)启用新的事务
3)访问Bean方法
4)Bean方法结束之后恢复Client的事务
如果Client没有使用transaction,那么容器会在Client调用Bean之前启用一个新的事务。

Mandatory:
如果Client在事务中调用Bean方法,那么Bean方法会在该事务中处理。否则,容器会抛异常错:TransactionRequiredException

NotSupported:
如果Client使用transaction,此时它在调用Bean方法时,容器会挂起该事务。在Bean结束后,恢复事务。如果Client没有使用transaction,容器也不会自动启用新的事务。
在这种属性之下,事务不起任何作用,会大大提高运行性能。

Supports
如果Client使用了事务处理,它在调用Bean方法时,该方法也会交由该事务来处理。反之,Client没有使用事务,容器也不会启用新的事务。

Never
不允许任何事务存在。一旦存在,容器会抛RemoteException。


何时设置事务属性

因为transaction attributes是保存在容器的descriptor中的,所以各级开发人员在任何时候都可以修改它。事实上,只有企业Bean的开发人员才能修改该属性,因为只有他们明白事务需要如何处理。所以不要指望企业应用发布人员在deploy的时候去指定。

Roll back事务

有两种方法可以回滚。一是系统在抛出异常的时候,容器可以自动取消该事务的执行;二是显式地代码声明:
EJBContext(interface).setRollbackOnly

举例代码:
public void transferToSaving(double amount) throws InsufficientBalanceException {
  checkingBalance -= amount;
  savingBalance += amount;
  try {
    updateChecking(checkingBalance);
    if (checkingBalance < 0.00) {
      context.setRollbackOnly();
      throw new InsufficientBalanceException();
    }
    updateSaving(savingBalance);
  } catch (SQLException ex) {
    throw new EJBException ("Transaction failed due to SQLException: " + ex.getMessage());
  }
}

使用银行卡在借记账户和消费账户之间转账,某个时刻,消费卡被转账成透支状态(checkingBalance < 0.00),就马上取消该事务。
注意在该方法中,实体Bean是很容易被rollback,因为涉及的是数据库操作。对于一个会话Bean来说,必须额外处理,方法是重置SessionSynchronization接口的一个实例变量。

会话Bean的回滚

使用SessionSynchronization可以同步化对应着数据库中数据的各个实例变量。容器按照如下的顺序来调用SessionSynchronization的各个方法。
afterBegin, beforeCompletion, afterCompletion
afterBegin通知实例有一个新的事务已经开始,在调用各种商业逻辑之前,容器立即执行afterBegin。可以说,afterBegin是最好的加载各个数据变量的方法,例如:

public void afterBegin() {
  System.out.println("afterBegin()");
  try {
    checkingBalance = selectChecking();
    savingBalance = selectSaving();
  } catch (SQLException ex) { throw new EJBException("afterBegin Exception: " + ex.getMessage());}
}

该方法加载了两个变量checkingBalance 和savingBalance 。

在各种商业逻辑执行完后,容器立即调用beforeCompletion方法。此时,该方法是能够回滚事务的最后一个地方,如果它还没有执行更新数据库之类的操作,回滚就是有效的。最后容器调用afterCompletion,根据boolean变量来判断是commit还是rollback,例子如下:

public void afterCompletion(boolean committed) {
  System.out.println("afterCompletion: " + committed);
  if (committed == false) {
    try {
      checkingBalance = selectChecking();
      savingBalance = selectSaving();
    } catch (SQLException ex) {
      throw new EJBException("afterCompletion SQLException: " + ex.getMessage());
    }
  }
}

此处rollback是仍然调用和afterBegin中一样的两个方法,即重新从数据库中加载数据。

一些注意点

使用容器来自动管理事务处理,必须显式地杜绝一些容易干扰事务处理的方法,比如jdbc中commit, setAutoCommit, 和rollback方法;javax.ejb.EJBContext中的getUserTransaction方法;javax.transaction.UserTransaction的任何调用等。

原创粉丝点击