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?()
??}
}