七、ejb 程式設計式事務管理(bean管理事務)
就是要程式員手動控制事務的開啟、送出、復原等操作。
避免使用的方法
在處于事務中時,不要調用 java.sql.Connection 或 javax.jms.Session 接口的 commit() 或 rollback()。
同樣,不要調用 EJBContext 接口的 getRollBackOnly() 和 setRollBackOnly()。容器将抛出一個異常,原因是:
您可以通過調用 javax.transaction.UserTransaction 的 getStatus() 方法來獲得事務的狀态。這等同于調用 getRollBackOnly。
可以使用 javax.transaction.UserTransaction 接口的 rollback() 方法來復原事務。這等同于調用 setRollbackonly()。
與 bean 管理的事務相比較,容器管理的事務更簡單且代碼更少。但是在使用容器管理的事務時存在以下情況:您的企業 bean 方法既可以參與到事務中,也可以不參與。如果您需要更粗略的邏輯,并在基于特定有效性邏輯的情況下使用粗略邏輯送出事務或復原事務,那麼您應該使用 bean 管理的事務。bean 管理的事務可使您對事務邊界進行全面控制。
會話 bean 或消息驅動 bean(MDB) 可以使用 bean 管理的事務。實體 bean 不能使用 bean 管理的事務。這是因為 EJB 容器控制了加載或存儲實體 bean 的資料的時間。
在 bean 管理的事務中,容器必須允許bean 執行個體在一個方法中連續執行幾個事務,但是要記住,不能執行嵌套事務。如果您試圖啟動一個新事務而 bean 執行個體還沒有送出前一個事務,那麼容器将抛出一個異常。
您可以使用兩種類型的 bean 管理的事務:
JTA 事務
JDBC 事務
JTA 是事務管理器和分布式事務處理系統所涉及的其他元件之間的一個接口規範。通過使用接口,不必使用事務管理器的特有 API 就可以單獨地劃分事務。
所有 EJB 容器都必須支援 JTAAPI。對于一名開發人員,這允許您将事務指令傳達給 EJB 容器,并以通用、簡單的方式提供事務指令。此方法使您不必擔心事務服務的底層工作。
javax.transaction.UserTransaction
使用 bean 管理的 JTA 事務時,可使用 javax.transaction.UserTransaction接口來劃分事務。在該接口上,有三個有趣的方法:
begin() —— 建立一個新的事務,并将該事務與目前線程關聯起來。
commit() —— 送出與目前線程有關聯的事務。
rollback() —— 強行復原與目前線程有關聯的事務。
在 begin() 和 commit() 之間發生的所有更新都是在一個事務中完成的。
UserTransaction 接口上的其他方法包括:
getStatus() —— 檢索與目前線程有關的事務的狀态。傳回值是 javax.transaction.Status 接口上定義的常數。
setRollbackOnly()—— 強行使事務終止。
setTransactionTimeout(int)—— 設定事務中止前能運作的最大次數。這在避免死鎖時很有用。
請參閱本文的參考資料 部分,獲得 Sun 公司的 JavaDocs 中有關 UserTransaction 和 UserStatus 接口的連結。
如何使用 bean 方法獲得UserTransaction 的最初引用呢?基于企業bean 的類型,您可從 bean 上下文中獲得接口:
對于會話 bean,可從javax.ejb.EJBContext 調用 getUserTransaction()。
對于 MDB,可從MessageDrivenContext.getUserTransaction() 調用 getUserTransaction()。
如何通過 JNDI 擷取UserTransaction 接口
publicMySessionBean implements SessionBean {
publicsomeMethodOnMyBean()
{
Context initCtx = new InitialContext();
UserTransaction utx =(UserTransaction)initCtx.lookup(
"java:comp/UserTransaction");
utx.begin();
...
utx.commit();
}
...
}
通常,在同一個方法中啟動事務并送出該事務是一個好主意。這有助于您跟蹤事務開始和結束的地方。同樣,要使事務開放的時間盡可能的短。事務需要系統資源,是以如果保持事務長時間的開放,可能會影響多使用者性能。
使用 JTA 事務的最大好處是它允許您跨越多個不同資料庫的多個更新。但要記住,JTA 實作不支援嵌套事務。
會話 bean Java 代碼示例
public classMySessionEJB implements SessionBean {
EJBContextejbContext;
public voidupdateTwoDatabases(...) {
DataSource dbOneDataSource = null;
DataSource dbTwoDataSource = null;
Connection dbOneConnection = null;
Connection dbTwoConnection= null;
Statement dbOneStatement = null;
Statement dbTwoStatement = null;
String sqlUpdateDbOne = null;
String sqlUpdateDbTwo = null;
javax.transaction.UserTransaction ut =null;
try {
InitialContext initCtx = newInitialContext();
// retrieve our first Connection and
// prepare a statement
dbOneDataSource = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/dbOneDS");
dbOneConnection =dbOneDataSource.getConnection();
// setup SQL here,
// perhaps we're using parameterizedqueries
sqlUpdateDbOne = ...
dbOneStatement =
dbOneConnection.prepareStatement(sqlUpdateDbOne);
// retrieve our second Connection and
dbTwoDataSource = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/dbTwoDS");
dbTwoConnection =dbTwoDataSource.getConnection();
sqlUpdateDbTwo = ...
dbTwoStatement =
dbTwoConnection.prepareStatement(sqlUpdateDbTwo);
// Now setup a JTA transaction toencompass both
// database updates //
ut = ejbContext.getUserTransaction();
// Start our transaction here
ut.begin();
dbOneStatement.executeUpdate();
dbTwoStatement.executeUpdate();
// commit the transaction
ut.commit();
// cleanup
dbOneStatement.close();
dbTwoStatement.close();
dbOneConnection.close();
dbTwoConnection.close();
catch (Exception e) {
//something went wrong
try {
//rollback the exception
ut.rollback();
} catch (SystemException se) {
//Rollback has failed!
throw new EJBException
("Attempted to rollback, butit failed: "
+ se.getMessage());
}
//Lastly throw an exception to reportwhat went wrong
throw new EJBException
("Transaction failed: "+ ex.getMessage());
}
JDBC 事務的用法
public voidupdateFirstName (int customerId, String firstName) {
try {
//getConnection creates a JDBC connectionfor us
Connection con = getConnection();
con.setAutoCommit(false);
String sql = "update customer setfirstName = ? where customerId = ?";
PreparedStatement updateCustomerName= con.prepareStatement(sql);
updateCustomerName.setString(1,firstName);
updateCustomerName.setInt(2,customerId);
updateCustomerName.executeUpdate();
con.commit();
} catch (Exception ex) {
try {
con.rollback();
throw new EJBException("Customername update
failed: " + ex.getMessage());
} catch (SQLException sqx) {
throw newEJBException("Rollback failed: " +
sqx.getMessage());
}
是以可以用來封裝遺留 JDBC 代碼的常用方法是:
在連接配接對象上将 setAutoCommit 屬性設定為 false。
在執行 SQL 之後調用連接配接上的commit 方法。
如果出現任何錯誤,則調用連接配接上的 rollback 方法。
此外,JDBC 事務不是實作 bean 管理的事務的首選方式。如果不需要重用現有 JDBC 代碼,則應該考慮使用 JTA 事務。