1. Example
SwiftMsgCollectorBean.handleMsg() -->
SwiftMsgCollectorBean 是容器管理事务, 方法配置为 Required.
代码A:
try {
//4. to overwrite, so delete replica if exist
this.delReplica(refNo);
//5. Format the msg
swiftMsg = this.replaceTabByBlank(swiftMsg);
//6. save the msg
this.insertSwiftMsg(orderNo, refNo, swiftMsg);
} catch (Exception e) {
e.printStackTrace();
}
代码A:即使insertSwiftMsg抛出异常, delReplica 的事务也会提交。
代码B:
try {
//4. to overwrite, so delete replica if exist
this.delReplica(refNo);
//5. Format the msg
swiftMsg = this.replaceTabByBlank(swiftMsg);
//6. save the msg
this.insertSwiftMsg(orderNo, refNo, swiftMsg);
} catch (Exception e) {
sessionContext.setRollbackOnly(); --> 如果insertSwiftMsg抛出异常,也会回滚delReplica的操作。
e.printStackTrace();
}
代码B:如果insertSwiftMsg抛出异常, delReplica 的事务也会回滚。
2. 容器管理事务的提交
一般的理解是:方法运行完毕就提交到数据库了。更详细的验证还在研究中。。。
3. 容器管理事务的回滚
在以下三种情况下,事务将回滚。
第一, 如果产生一个系统异常,容器将自动回滚该事务。
系统异常是指应用程序的支撑服务出现异常。这样的异常例子有:不能得到数据库连接,数据库满造成SQL语句插入失败,一个lookup方法找不到需要的对象等等。如果企业Bean遇到一个系统级的问题,它将抛出javax.ejb.EJBException,容器会把该异常包装到一个 RemoteException异常中返回给客户端。因为EJBException是RutimeException的子类,所以你不需要将它列在 throws子句中。当一个系统异常发生时,EJB容器可能会销毁企业Bean实例,因此系统异常不能被客户端处理,它需要系统管理员的干涉
第二, 由于EJBException作为系统异常,他继承自RutimeException,所以经过验证表明当抛出一个继承自 RutimeException的自定义异常时EJB容器也会自动回滚该事务,抛出普通的继承自Exception的应用程序异常容器不回自动回滚该事务
第三, 通过调用EJBContext接口SetRollbackOnly方法,Bean方法通知容器回滚该事务。如果Bean抛出一个普通的非继承自RutimeException应用异常,事务将不会自动回滚,但可以调用SetRollbackOnly回滚。
以上是网上的一些资料。不清楚是使用什么EJB容器。但是根据 测试,在EAServer中前2中情况不成立。例如:以下代码不会导致事务的回滚:
private void insertSwiftMsg(String orderNo, String refNo, String swiftMsg) throws ArrayIndexOutOfBoundsException, Exception {
if (debug) logger.debug("insertSwiftMsg() OrderNo = " + orderNo + ", refNo = " + refNo);
Connection conn = null;
CallableStatement cstmt = null;
PreparedStatement ps = null;
int rowCount = 0;
try {
if (copsDataSource == null) {
copsDataSource = ServiceLocator.getInstance().getDataSource(JNDINameConstants.JDBC_COPS_DB);
}
conn = copsDataSource.getConnection();
conn = copsDataSource.getConnection();
ps = conn.prepareStatement(SQL_DEL_REPLICA) ;
ps.setString(1, refNo);
rowCount = ps.executeUpdate();
if (debug) logger.debug("Clear replica record in PMT_OUTWARD_MSG(refNo=" + refNo + ")" + ". rowcount = "+ rowCount );
cstmt = conn.prepareCall(SQL_INSERT_SWIFT_MSG);
cstmt.setString(1, orderNo);
cstmt.setString(2, refNo);
cstmt.setString(3, swiftMsg);
cstmt.setString(4, "SYSTEM");
cstmt.executeUpdate();
String[] test = new String[2];
test[0] = "test1";
test[1] = "test2";
test[2] = "test3"; --> Popup ArrayIndexOutOfBoundsException,但是并没有导致前面2个事务的回滚
//throw new PaymentException("create exception by manual"); --> 人为popup RutimeException 子类也是无法导致事务的回滚
}catch (ArrayIndexOutOfBoundsException ae) {
throw ae;
} catch (Exception e) {
throw e;
} finally {
try {
if (cstmt != null) cstmt.close();
if (conn != null) conn.close();
} catch (Exception e) {
logger.error(e.getMessage());
}
}
}
Note: 在EAServer中,可以通过SetRollbackOnly方法回滚事务。请参考前面的代码。
以下资料来自<<Enterprise JavaBeansTM Specification,Version 2.0>>
An enterprise bean with bean-managed transaction demarcation must not use the getRollbackOnly and setRollbackOnly methods of the EJBContext interface. An enterprise bean with bean-managed transaction demarcation has no need to use these methods, because of the following reasons:
(1)An enterprise bean with bean-managed transaction demarcation can obtain the status of a transaction by using the getStatus method of the javax.transaction.User-Transaction interface.
(2)An enterprise bean with bean-managed transaction demarcation can rollback a transaction using the rollback method of the javax.transaction.UserTransaction interface.
The container must throw the java.lang.IllegalStateException if an instance of a bean with bean-managed transaction demarcation attempts to invoke the setRollbackOnly or getRollbackOnly method of the javax.ejb.EJBContext interface.
An enterprise bean with container-managed transaction demarcation can use the setRollbackOnly method of its EJBContext object to mark the transaction such that the transaction can never commit. Typically, an enterprise bean marks a transaction for rollback to protect data integrity before throwing an application exception, if the application exception class has not been specified to automatically cause the container to rollback the transaction.
For example, an AccountTransfer bean which debits one account and credits another account could mark a transaction for rollback if it successfully performs the debit operation, but encounters a failure during the credit operation.