Let's go inside

this blog is deprecated as a result of laziness.
posts - 59, comments - 2, trackbacks - 0, articles - 0

TrailBlazer第13天--Transactions & App Transactions

Posted on 2006-07-28 06:11 Earth 阅读(388) 评论(0)  编辑  收藏 所属分类: JavaEE5/EJB3

Transaction Annotation

在EJB 3.0 的应用中,transaction properties最常声明在session bean定义的方法中。如果一个方法requires transaction, 所有这个方法中的操作(包括database updates)仅在方法正常退出时才被提交。如果在这个方法或调用这个方法的方法中抛出了application exception,transaction manager会回滚所有的变化(i.e., database updates). 声明transaction所用的标注是@TransactionAttribute,可以带如下参数:

REQUIRED: 有则用之,没有就创建一个新的用。
MANDATORY: the caller method必须自带事务. 否则,将抛出一个错误。
REQUIRESNEW: 被标注的方法必须在新的事务中执行. 如果the caller method自带事务, 则之前的那个事务将被挂起.
SUPPORTS: 有则用之,没有就算了。
NOT_SUPPORTED: 有则出错。

如果一个方法没有事务标注, 则它使用EJB 3.0 container指定的默认值:REQUIRED

在EJB 3.0中,EntityManager必须运行在一个事务性的context中以保证数据库完整性(database integrity). transaction manager总是在事务被提交的时候同步数据库(在current thread之后或在next database query之前)。你也可以在一个事务中调用EntityManager.flush()随时同步数据库。
 
Transaction Rollback
   
当应用抛出一个RuntimeException的时候transation将失败,这通常与数据库有关或者也有可能是一个ApplicationException.这里是一个自定义ApplicationException的例子。你可以在任何地方抛出它以引发事务失败---即使并不是数据库错误。

@ApplicationException(rollback = true )
public   class  TransException  extends  Exception {
  
public  TransException () { }
}

当一个事务失败的时候,database会回滚到事务开始前的状态,即使你在事务中调用了flush()。所有被管理的entity beans均处在detached的状态。如果你想在事务失败后继续使用这些entity beans, 你不得不手工设置它们的ID为零。
 
A Transactional Method Example
    在下面的例子中,我们在updateExchangeRate()的for循环中产生随机的application exceptions,因为方法被声明为事务性的,这些exceptions将导致所有的更新失败。

@Stateless
public   class  TransCalculator  implements  Calculator {
  @PersistenceContext
  
protected  EntityManager em;
  
//   
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  
public   void  updateExchangeRate ( double  newrate)  throws  Exception {
    Collection 
< TimedRecord >  rc  =  
      em.createQuery(
" from TimedRecord r " ).getResultList();
    
int  size  =  rc.size ();
    
    
for  (Iterator iter  =  rc.iterator(); iter.hasNext();) {
      TimedRecord r 
=  (TimedRecord) iter.next();
      r.setSaving(r.getSaving() 
*  newrate);
      r.setResult(r.getResult() 
*  newrate);

      
//  Emulate a failure
      
      
//  Calculate failure probability for each loop
      
//  in order for the overall failure probability
      
//  to be 50%
       double  prob  =  Math.pow ( 0.5 1 . / size);
      
if  (Math.random()  >  prob) {
        
//  Emulated failure causes rollback
         throw   new  TransException ();
        
        
//  Or throw a RuntimeException to trigger rollback
      }
    }
  }

点击下面的按钮以载入新的汇率更新程序。试着多次更新汇率的值你将看到要么所有的记录被更新,要么所有的记录都没有被更新。永远不会出现只有部分记录被更新的情况,即使异常是在循环的中期进行的。出现异常的概率为50%

**************************************************************************************

第二小节:Application Transactions

介绍 
 标准的JTA事务是基于线程的。事务在线程结束时要么commit要么rollback.在线程结束时,事务被提交;数据的变更将flush到database;并且persistence context被销毁。对于有些web应用来说,这种"one thread"的限制令人感到不快。比如,我们考虑一个购物车程序。用户需要浏览一系列的页面:选择商品,输入信用卡的资料,输入货运地址,预览最终的结算,最后结束整个事务。每一次页面提交在服务器端都会被一个独立的线程处理。但是事务必须在最后一个页面提交时才能被commit.

在 EJB 3.0中, 你可以把多线程的database updates缓存到EntityManager中,只在应用结束一次逻辑会话时commit the changes in a batch(比如在购物车checkout的时候)。在这个例子中,我们将模拟一次多页面aplication transaction.

The sample application
  这次的示例应用程序是对前面“currency exchange rate update”例子一次小小的改动,当你提交更改一次新的currency exchange rate后,应用更新所有entity beans对象中的计算记录但是不会把变化flush到database,然后应用转到另外一个页面询问是否更新这些记录的timestamp,如果你选择“yes”,这些bean对象的timestamp属性将被更新同时在a single batch中flush到database.

The extended EntityManager
 为了使上面的事务工作,你首先要指定 @PersistenceContext的type属性为PersistenceContextType.EXTENDED.这告诉container EntityManager在跨线程区域内维护它的persistence context (也就是它所管理的entity beans对象)

@Stateful
public   class  ApptransCalculator  implements  Calculator, Serializable {
  @PersistenceContext(
      type
= PersistenceContextType.EXTENDED
  )
  
protected  EntityManager em;
  
//     
}

Commit the transaction
 然后,我们通过使用参数NOT_SUPPORTED告诉JTA事务处理器不要在线程结束时提交更新.EntityManager应该仅在stateful session bean销毁时更新数据库. 你应该还记得我们可以通过调用用@Remove标记的方法来销毁一个statuful session bean.我们在用@Remove标记的方法中显示的调用EntityManager.flush()来send in all updates to the database。

@Stateful
public   class  ApptransCalculator  implements  Calculator, Serializable {
  
//   
  @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
  
public   void  updateExchangeRate ( double  newrate) {
    
//   
  }
  @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
  
public   void  updateTimestamp () {
    
//   
  }
  @Remove
  
public   void  checkout()  throws  Exception {
    em.flush ()
  }
}

只有注册用户登录后才能发表评论。


网站导航: