七、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 事务
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
// prepare a statement
dbTwoDataSource = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/dbTwoDS");
dbTwoConnection =dbTwoDataSource.getConnection();
// setup SQL here,
// perhaps we're using parameterizedqueries
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 事务。