EBS OAF开发中的Java 实体对象(Entity Object)<三>
回滚
OAF对提交和commit动作实现为”所有或者什么都不做”的事务方式。不论错误的严重程度,如果数据库提交或者commit失败,OAF会:
l 发出一个JDBC回滚来释放数据库锁。
注意:这不对中间层的状态有不利的影响。
l 重置视图对象行状态这样可以对这个事务做第二次提交尝试。
注意:这意味着你不需要明显的回滚失败的实体对象事务;如果提交或者commit失败,OAF自动显示一个用户友好的错误消息。下面的示例演示了一个commit并且稍后在用户选择了Apply按钮之后显示了一个”确认”对话框.
// In the root application module public void apply() { getTransaction()Commit(); } // In the controller public void processFormData(OAPageContext pageContext, OAWebBean webBean) { super.processFormRequest(webBean); // Handle the user pressing the "Apply" button if (pageContext.getParameter("Apply") != null) { OAApplicationModule am = pageContext.getRootApplicationModule(); // No need for any special exception handling. You can just display the // confirmation message because the OAF won‘t reach this code if the post/commit // fails. am.invokeMethod("apply"); OAException confirmMessage = new OAException("ICX", "FWK_TBX_T_SUPPLIER_CREATE_CONF", null, OAException.CONFIRMATION, null); pageContext.putDialogMessage(confirmMessage); } }
回滚方法
要手工清除中间层的视图对象和实体对象缓存,可以调用AM中的getTransaction().rollback()。这也会回滚所有数据库修改并清空你在事务中缓存的所有值。当创建实体对象时,可以参考Supporting the Browser Back Button来理解这个功能。
如果你在任何运行PL/SQL procedure,并且需要明确的在数据库层的回滚而又不影响中间层,可以在AM中调用getTransaction().executeCommand("rollback")。
注意:就像BC4J NativeJDBC Statement Management,描述的,Transaction.rollback()通过调用vo.clearCache()会关闭关联视图对象查询集合的JDBC ResultSets(cursors)。因此,如果你以下面的顺序调用这些方法:
Vo.executeQuery();
Transaction.rollback();
vo.next();
SQL异常”ORA-O1002:fetch out ofsequence”,经常是由数据库回滚调用导致的无效的打开的游标,但是在这里不会发生,因为Transaction.rollback()关闭了游标,但是vo.next()会通过重新运行视图对象的查询来强制重新打开一个新的有效的游标。
Transaction.rollback()会即回滚数据库状态也会回滚中间层业务对象状态,下面这些直接的JDBC调用不会回滚任何中间层的业务对象状态,因此不会关闭打开的JDBC游标:
i. Transaction.executeCommand("rollback")调用。
ii. BC4J的”回滚到保存点”调用是在当Transaction.postChanges() 或者Transaction.commit()调用时有验证或者提交错误时由内部发出的。尽管在这种情况下实体对象或者视图对象数据和用户修改都保留着,但实体的提交状态被改回到已修改的状态,因此用户可以再次尝试提交。
BC4J框架不能弥补导致无效JDBC和数据库游标的回滚限制,这些游标是在当数据库保存点或由于回滚调用之后打开的游标。因此,如果你需要使用Transaction.executeCommand("rollback"),, 请先参考M52 model codingstandards.
如果你需要覆盖提交处理或者EntityImpl的beforeCommit,要确定先参考了Inappropriate Post Handling.
不合适的提交处理
避免在下面的任何情形下掉用executeQuery()方法:
l 在视图对象的EntityImpl的提交处理方法(postChanges,beforePost, afterPost).
l 在beforeCommit方法,然后尝试从同一个视图对象中使用vo.next, vo.first()等等来获取行。
原因是因为当Transaction.postChanges()或者Transaction.commit()由于验证或者提交错误失败时,会发出一个回滚到数据库保存点,并失效所有在保存点之后打开的游标。
相应的,我们建议下面的编码行为:
选项1:如果你需要对一个视图对象调用executeQuery().
1. 在调用相应的基类方法或者成功调用Transaction.commit()方法之后,在EntityImpl.afterCommit()方法中调用executeQuery().
2. 如果视图对象在Transaction.commit()调用之前被获取和使用,那么在成功调用Transaction.postChanges()之后调用executeQuery().
选项2:如果你必须要在EntityImpl’s 提交处理中调用executeQuery(),那么你也必须像下面来重载handlePostChangesError以便关闭无效的游标:
protected void handlePostChangesError() { super.handlePostChangesError(); ViewObject vo = <get the view object where query was issued>; vo.clearCache(); }
接下来的行导航方法比如vo.next()会强制重新进行查询。
你可以看到,选项1更容易实现。
对象版本号列
OBJECT_VERSION_NUMBER(OVN)列(对应于EO中ObjectVersionNumber属性)用于在数据库中锁定行。每个可以被用户更新或者删除的表都必须有一个OVN列。当一个新的行插入到表中时,OVN列的值被设为一个微不足道的值,一般是1. OVN的值每当行被更新时会增加。这个值为一直保持,直到下次的更新或删除,并且从不会减小或者重设为之前的值。
每当客户查询一个行时,行的OVN值一般是和其他属性一块返回。如果对象(行)被客户修改了并提交到数据库,数据库上的OVN的值就会和提交的OVN的值进行比较:
l 如果值是一样的,那么提交到服务器的行状态就和客户查询行时的状态是一样的。因为没有更改发生,在服务器上行就被提交了并且OVN编号也被增加了。
l 如果值不同(数据库的值比提交的值大),那么表明另一个用户已经更改并提交到了数据库。因此,当前的修改提交是不允许的,因为另一个用户的修改可能被覆盖被丢失。
下面就是这个概念的一个示例
第一步:两个用户,用户A和用户B,从服务器读取值。OVN属性值也其它属性一块被读取.
第二步:用户A改变了值并且尝试提交修改到服务器.
第三步:提交的OVN值会和服务器上的OVN值进行比较:
情形1:如果OVN值一致,那么提交就成功了并且OVN的值会增加。
情形2:用户B在用户A提交之前更新并提交了记录。用户A然后提交他的记录,那么OVN值不匹配,提交会失败并抛出’record changed by another user’错误。
注意:OVN值不少唯一的,因此不能用于替代行的主键。表中的多个行可以有相同的OVN值。一个OVN值表明了一个主键行的特定版本。还有,数据库锁不会被延迟锁方法所覆盖。这就阻止了为提交的修改被其它用户所覆盖。
标准规则
当在你的实体对象创建ObjectVersionNumber属性时使用下面的规则:
规则1:如果数据库表有OBJECT_VERSION_NUMBER列,那么映射它到ObjectVersionNumber属性。这个属性是可选的。
规则2:如果数据库列表不是OBJECT_VERSION_NUMBER并且如果你有一个同等的列但是不同名的表列,那么通过BC4J实体对象向导映射这个数据库列到ObjectVersionNumber属性名。
你也可以使用其它的列比如LAST_UPDATE_DATE来实现你的加锁机制。但是,即使你使用当前时间更新LAST_UPDATE_DATE,它也可能是不安全的因为两个用户可能在同一时刻更新相同的记录并且结束时的LAST_UPDATE_DATE的值一样。因此使用OBJECT_VERSION_NUMBER列来用于这个目的更好。
规则3:使用实体对象向导指定ObjectVersionNumber属性作为”更改指示器”,并把它加入到视图对象的SQL中。这个属性值作为更改指示器基于锁会和数据库列的值做比较。如果没有属性标记为更改指示器,那么所有的列属性值都和数据库中相关的列的值做比较。
OAF框架在通用的OAEntityImplJava类中更新ObjectVersionNumber属性。就是,createObjectVersionNumber()和updateObjectVersionNumber()方法自动做初始化和更新值。BC4J框架会处理锁,包括比较原先属性值和数据库中表列的值来侦测过时的数据。