EJB 3事务之容器管理事务(CMT)BEAN管理事务

来源:互联网 发布:jackson 空json对象 编辑:程序博客网 时间:2024/05/05 06:30

容器管理事务(Container-Managed Transaction, CMT ):容器管理事务允许组件自动征集(enlist )到事务中,也就是说,EJB 组件从不需要显式地给出begin 、commit 、abort 语句,EJB 容器会替开发者考虑这些内容。EJB 容器会依据EJB 组件提供者指定的事务行为来界定相应的事务边界。在使用容器管理事务时,EJB 容器会拦截客户请求,并自动为EJB 组建启动新的事务,也就是说,容器会通过begin 语句调用底层事务系统,从而启动事务。随后,容器会将业务请求委派给EJB 组件,组件中的业务操作将运行在这一事务中。处于事务中的EJB 组件能够执行任何业务逻辑,如写入数据库、发送异步信息、调用其他的EJB 组件等。一旦在处理业务过程中出现问题,则EJB 组建需要通知EJB 容器去回滚事务。当EJB 组建完成业务处理后,会将控制权交回给EJB 容器。随后,EJB 容器能够通过commit 或abort 语句调用底层事务系统。

通过使用@TransactionAttribute 注释或部署描述符,开发者能够指定事务属性。EJB 容器通过分析事务属性便能够知道如何处理EJB 组件的事务需求。EJB 事务属性的取值有:

(1 )Required ,如果EJB 组件必须总是运行在事务中,则应该使用Required 模式。如果已经有事务在运行,则EJB 组件参与其中;如果没有事务运行,则EJB 容器会为EJB 组件启动新的事务。

Required 是默认和最常使用的事务属性值。这个值指定必须在事务之内调用EJB 方法。如果从非事务性客户端调用方法,那么容器会在调用方法之前开始事务,并且在方法返回时结束事务。另一方面,如果调用者从事务性上下文调用方法,那么方法会联结已有事务。在从客户段传播事务的情况下,如果我们的方法表示应该回滚事务,那么容器不仅回回滚整个事务,而且会向客户端抛出异常,从而让客户端知道它开始的事务已经被另一个方法回滚了。

(2 )Requires_New ,当客户调用EJB 时,如果总是希望启动新的事务,则应该使用RequiresNew 事务属性,如果客户在调用EJB 组件时已经存在事务,则当前事务会被挂起,进而容器启动新的事务,并将调用请求委派给EJB 组件。也就是说,如果客户端已经有了事务,那么它暂停该事务,知道方法返回位置,新事务是成功还是失败都不会影响客户端已有的事务。EJB 组件执行相应的业务操作,容器会提交或回滚事务,最终容器将恢复原有的事务,当然,如果客户在调用EJB 组件时不存在事务,则不需要执行事务的挂起或恢复操作。

RequiresNew 事务属性非常有用。如果EJB 组件需要事务的ACID 属性,并且将EJB 组件运行在单个独立的工作单元中,从而不会将其他外部逻辑也包括在当前的事务中,则必须使用RequiredNew 事务属性。如果需要事务,但是不希望事务的回滚影响客户端,就应该使用它。另外,当不希望客户端的回滚影响你的时候,也应该使用这个值。日志记录是个很好的例子,即使父事务回滚,你也希望把错误情况记录到日志中,另一方面,日志记录细小调试信息的失败不应该导致回滚整个事务,并且问题应该仅限于日志记录组件内。

(3 )Supports ,如果某个EJB 组件使用了Supports 事务属性,则只有调用它的客户已经启用了事务时,这一EJB 组件才会运行在事务中。如果客户并没有运行在事务中,则EJB 组建也不会运行在事务中。Supports 同Required 事务属性很相似,但是,Required 要求EJB 组件必须运行在事务中。如果使用Support 事务属性,EJB 组建很可嫩没有运行在事务中。

(4 )Mandatory ,Mandatory 事务属性要求调用EJB 组件的客户必须已经运行在事务中。如果从非事务性客户端调用使用Mandatory 属性的EJB 方法,那么客户将接受到系统抛出的javax.ejb.EJBTransactionRequiredException 异常。EJB 组件使用Mandatory 事务属性是非常安全的,它能够保证EJB 组建运行在事务中。如果客户没有运行在事务中,则不能够调用到应用了Mandatory 事务属性的EJB 组件。但是,Mandatory 事务属性要求第3 方(及客户)在调用EJB 组件前必须启动了事务。EJB 容器并不会为Mandatory 事务属性自动启动新事务,这是同Support 事务属性的最主要区别。

(5 )NotSupported ,如果EJB 组件使用了NotSupport 事务属性,它根本不会参与到事务中。如果调用者使用相关联的事务调用方法,容器就会暂停事务,调用方法,然后再方法返回时恢复事务。通常,此属性只用于非实物性的自动确认模式中,支持JMS 提供者的MDB 。

(6 )Never ,如果EJB 组件使用Never 事务属性,它就不能够参与到事务中,而且,如果调用它的客户已经处于事务中,则容器会将javax.ejb.EJBException 异常抛给客户。

事务效果图,其中,T1 和T2 是2 个不同的事务,T1 是客户请求传递的事务,T2 是容器启动的事务,通过下表,能够理解各种事务属性在影响事务长度和范围方面所起的重要作用。

事务属性
客户事务
EJB 组件事务

事务属性

客户事务

EJB 组件事务

Required

T1

T2

T1

RequiresNew

T1

T2

T2

Supports

T1

T1

Mandatory

T1

错误

T1

NotSupported

T1

Never

T1

错误

使用CMT 实现Transaction :

view plaincopy to clipboardprint?
import javax.annotation.Resource;  
import javax.ejb.SessionContext;  
import javax.ejb.Stateless;  
import javax.ejb.TransactionManagement;  
import javax.ejb.TransactionManagementType;  
import javax.ejb.TransactionAttribute;  
import javax.ejb.TransactionAttributeType;  
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)  
public class OrderManagerBean {  
        @Resource
        private SessionContext context;  
          
        @TransactionAttribute(TransactionAttributeType.REQUIRED)  
        public void placeSnagItOrder(Item item, Customer customer) {  
                try{  
                        if(bidsExisting(item)) {  
                                validateCredit(customer);  
                                chargeCustomer(customer, item);  
                                removeItemFromBidding(item);  
                        }  
                } catch(Exception e) {  
                        context.setRollbackOnly();  
                }  
        }  
}

CMT 的最大优势也是其最大弱点是,使用CMT ,会限制你将事务边界设置在业务方法开始和结束的位置,依靠容器决定何时开始、提交和回滚事务。另一方面,BMT 允许你通过变成确切指定这些细节,使用与JDBC 事务模型类似的语义。

例子:

view plaincopy to clipboardprint?
import javax.annotation.Resource;  
import javax.ejb.SessionContext;  
import javax.ejb.Stateless;  
import javax.ejb.TransactionManagement;  
import javax.ejb.TransactionManagementType;  
import javax.transaction.UserTransaction;  
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)  
public class OrderManagerBean {  
          
        @Resource
        private SessionContext context;  
          
        public void placeSnagItOrder(Item item, Customer customer) {  
                UserTransaction userTransaction = context.getUserTransaction();  
                try{  
                        userTransaction.begin();  
                        if(!bidsExisting(item)) {  
                                validateCredit(customer);  
                                chargeCustomer(customer, item);  
                                removeItemFromBidding(item);  
                        }  
                        userTransaction.commit();  
                } catch (Exception e) {  
                        userTransaction.rollback();  
                        e.printStackTrace();  
                }  
        }  
}


原文链接:http://hi.baidu.com/fishfast/blog/item/410e43af2fe0dff0faed5063.html
原创粉丝点击