VO 初始化探析
目录
第一部分 插入新行前的初始化
1 仅插入型VO的初始化
2 插入查询VO的初始化
第二部分 执行查询前的初始化
1 避免无条件查询
2 避免多余的查询
正文
第一部分 插入新行前的初始化
在处理插入初始化之前,我们必须确定一件事情,就是当前我们要处理的这个VO,它是只用作插入操作呢,还是即用作插入又用作查询操作。比如一个EmployeeCreateVO,这个VO仅仅在创建新员工的页面使用,不用做其他用途,那么我们认为它是一个只做插入操作的VO,反之,它就是一个即查询又插入的VO.理清楚这一点,对合理初始化VO很重要。
1仅插入型的VO的初始化
我们可以利用如下代码:
// 检查VO是否有行,如果有行了,我们就不用执行括号里的语句了。vo.getFetchedRowCount()是检查当前缓存中VO的行数,包括新插入的行,它不执行数据库查询,仅仅检查内存中已经取出来的行。如VO已经被插入过行了,那么getFetchedRowCount肯定大于0,就无需再调用setMaxFetchSize了
if (vo.getFetchedRowCount() == 0) {
//此方法是设置VO的每次查询行数的上限,但是如果参数为0的话,此方法会设置mPreparedForExecution标识为TRUE。这个标识是OAViewObjectImpl的一个成员变量,表示VO是否准备好执行查询,初始值是FALSE。当VO执行了executeQuery()之后,该标识值也会被设为TRUE。
vo.setMaxFetchSize(0);
}
// 执行插入操作
Row row = vo.createRow();
vo.insertRow(row);
//遵循代码标准M69,在所有新插入的行后立即将起设置为STATUS_INITIALIZED状态,让此行处于脱管状态,不参与事务提交,不参与验证,直到在页面上对这行数据进行更新,然后OA框架自动把状态设回为STATUS_NEW,以重新参与事务和验证。
row.setNewRowState(Row.STATUS_INITIALIZED);
有时候我们会遇到这个错误:“违反了 OA 钝化结构编码标准。未正确准备全名为 EmployeeAM.EmployeeFullVO1 的视图对象以供插入行。必须先调用视图对象中的 setMaxFetchSize(0) 或 executeQuery()(或最终执行视图对象查询的任何等效方法),然后才能插入新行。”
我估计是OAF框架在遇到插入行的时候,会检查当前VO有没有行,如果没有行,会检查在这句代码前面是否执行了executeQuery()或setMaxFetchedSize(0)语句(而不是检查mPreparedForExecution的值是否为TRUE),如果没有执行这样的语句,那么就报告违反钝化编码标准。当然这只是我的个人猜测,仅供参考。
对于只包含瞬时属性的VO,为了避免OAF中的一些已知的BUG,我们在对其进行初始化的时候最好像下面这样编写代码:
if (vo.getFetchedRowCount() == 0) {
vo.setMaxFetchSize(0);
vo.executeQuery();
... // Insert rows into the view object.
}
2带数据库查询的VO的初始化
首先,我们必须注意,在这种既用于查询又用于插入的VO情况下,我们必须先执行executeQuery(),然后再insertRow(),否则会导致一些钝化上的问题。
提示1:
总的来说,尽量为“创建**”或“插入**”型页面单独创建一个VO,比如单独创建一个创建员工的VO,EmployeeCreateVO.
提示2:
如果实在需要插入和查询或更新都公用一个VO,那么在插入前,应该给executeQuery加上条件。
if (!vo.isExecuted()){
vo.executeQuery();
// 或者调用vo.initQuery(..),以便根据你自己WHERE条件进行查询。
}
Row row=vo.createRow()
…其他初始化操作
注意:根据我个人经验,由于mPreparedForExecution标识变量并不会在rollback()后变为false,会造成未执行executeQuery()就进行insertRow()操作,那么OA框架就会报告“违反了 OA 钝化结构编码标准。未正确准备全名为 EmployeeAM.EmployeeFullVO1 的视图对象以供插入行。必须先调用视图对象中的 setMaxFetchSize(0) 或 executeQuery()(或最终执行视图对象查询的任何等效方法),然后才能插入新行。”,所以在进行插入操作前,通过vo.isPreparedForExecution来作为判断条件不是最好的选择,可以选择用vo.isExecuted来代替。
第二部分 执行查询前的初始化
1避免无条件查询
一般情况下,我们可以使用下面的方法来避免无条件执行查询操作
if (!vo.isPreparedForExecution()) { //带条件执行查询操作
vo.executeQuery();
// 或者调用vo.initQuery(..),以便根据你自己WHERE条件进行查询。
}
无条件执行查询操作会导致一些事务状态的丢失,比如,对瞬时属性(transient view object attributes)数据的更新会丢失,VO的current Row和current Range会被重设。这也就从一定解释了“翻页后在有删除确认对话框的页面中无法删除行的案例”,具体参考我的另一篇文章《翻页后在有删除确认对话框的页面中无法删除行的案例》。
但是,有几种情况你不应该给查询加上条件:
1.你的页面是只读的,你不在乎VO的事务状态,你只希望每次渲染页面的时候都能看到数据库中的最新数据。
2.如果这个查询是在processFormRequest中调用的,例如,页面上有一个“GO”按钮,希望点击的时候查询出满足我需要的数据,那么这个时候也不要给他加上面的条件。因此,我们可以这样理解,上面的“带条件执行查询操作”仅用于在processRequest中对页面包含的VO的数据进行初始化操作。
3.加了这个条件后,在保持同一个AM的范围内,这个vo只被查询一次,以后不会再被查询,如果想重新执行查询,得到最新的数据,那么需要释放AM。
对于下钻页面(Drilldown Target Page),比如搜索结果页面中点击员工姓名下钻到员工的详细信息,我们也不能依赖于isPreparedForExecution的判断,它太容易返回true了,以至于我们的查询得不到执行,我们更喜欢用vo.findByKey()这个方法,这个方法是根据VO下面的EO主键来查询,首先会检查缓存中有没有这条数据,没有才去执行数据库查询。注意,下钻页面的VO和搜索页面的VO最好不要使用同一个,以免下钻后,影响搜索页面的状态。
public void initDetails(Number employeeNumber) {
EmployeeFullVOImpl vo = getEmployeeFullVO1();
Number[] keys = { employeeNumber };
Row[] rows = vo.findByKey(new Key(keys), 1);
// findByKey不会改变currentRow指针,所以需要我们手动去更改,否则页面上不会显示值。
if ((rows != null) && (rows.length > 0)) {
vo.setCurrentRow(rows[0]);
} } // end initDetails()
2避免多余查询
涉及个性化,暂不讨论
/////////////////////////////////////////////////////////////////////////////////////////////////////////
一: 插入时不进行查询
1. 当你插入的 VO 没有从数据库中查询数据的时候,你必须使用 setMaxFetchSize(0) 去 初始化他。当 setMaxFetchSize 设 置为 0 的时候则 VO 会创建一个空的 Rowset 但是查询不会执行。(注:每次进行这个初始化之前请确认 VO 不存在任何行)
if (vo.getFetchedRowCount() == 0)
{vo.setMaxFetchSize(0);}
Row row = vo.createRow();
vo.insertRow(row);
row.setNewRowState(Row.STATUS_INITIALIZED);
2. 如果你有一个 VO 包含,可变的数据。并且你可能会提交保存有这个 VO 数据的事务。(应用程序属性 VO 是一个很好的例子 PVO 实现 PPR 的 VO 保存程序状态的)你需要添加以下代码来保证数据不会被不注意的清除。
if (vo.getFetchedRowCount() == 0) { vo.setMaxFetchSize(0); vo.executeQuery();
}
3. 作为一个规定不要使用 WHERE 1=0 来强迫执行一个没有意义的查询。但是在 Master-Details 的关系中是个例外,需要使用 WHERE 1=0 并且执行 executeQuery() 来达到 DetailsVO 插入的初始化目的。而是用 setMaxFetchSize ( 0 ) 则会造成不利的影响。
二:插入时 VO 执行查询
1. 对于那些即用来插入,又用来查询的 VO 必须在插入前执行 executeQuery() 。如果在查询前就执行了 insertRow(row) ,插入的行可能不会被正确的保存,所以可能在激活后丢失
三:避免无条件的初始化
当 processRequest() 方法被重新进入,无限制的初始化会重置导致事务状态的丢失。用户对临时 VO 属性的修改会丢失。以下代码可以可以避免这种情况
if (!vo.isPreparedForExecution()){ vo.executeQuery();}
注:有些情况并不能依靠 isPreparedForExecution() 去检查,比如当你在使用了 Transacton.rollback() or ViewObject.clearCache() 后,
马上插入数据如果不显式或者隐式的查询 VO 你就会丢失这些行。 Transacton.rollback() or ViewObject.clearCache() 会重新 Vo 的查询状态。所以为了防止不必要的数据丢失使用 OAViewObject.isExecuted() 代替