|
Correctly Implementing Multi-Level, Dynamic Master Detail
A user wrote in to ask why there master-detail-detail queries were not producing the expected XML output using the writeXML() API provided on the ADF View Object API. They said that in their tests, it worked as expected when they created the view links at design time between the design-time created view object, but in their situation they had to create the view objects and view links dynamically, and the writeXML() wasn't behaving as they expected. Their code looked like this:
package test; import oracle.jbo.client.Configuration; import oracle.jbo.*; import org.w3c.dom.*; import oracle.xml.parser.v2.*; import oracle.jbo.domain.Number; public class TestClient { static final String MASTER_QUERY = "select deptno, dname from dept where deptno = :0"; static final String DETAIL_QUERY = "select empno, ename, deptno from emp"; static final String REPORTS_QUERY = "select empno, ename,mgr from emp"; public static void main(String[] args) throws Throwable { String _am = "test.TestModule", _cf = "TestModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(_am,_cf); ViewObject master = am.createViewObjectFromQueryStmt("Department",MASTER_QUERY); master.setWhereClauseParam(0,new Number(10)); ViewObject detail = am.createViewObjectFromQueryStmt("Employee",DETAIL_QUERY); ViewObject reports = am.createViewObjectFromQueryStmt("DirectReport",REPORTS_QUERY); ViewLink master_detail = am.createViewLinkBetweenViewObjects( "master_detail", "EmpsInDept", master, new AttributeDef[]{master.findAttributeDef("DEPTNO")}, detail, new AttributeDef[]{detail.findAttributeDef("DEPTNO")}, null); ViewLink mgr_reports = am.createViewLinkBetweenViewObjects( "mgr_reports", "DirectReports", detail, new AttributeDef[]{detail.findAttributeDef("EMPNO")}, reports, new AttributeDef[]{reports.findAttributeDef("MGR")}, null); Node n = master.writeXML(-1,XMLInterface.XML_OPT_ALL_ROWS); ((XMLNode)n).print(System.out); Configuration.releaseRootApplicationModule(am,true); } }
But it was producing output without the 2nd level details appearing as expected in the XML like this:
<Department> <DepartmentRow> <DEPTNO>10</DEPTNO> <DNAME>ACCOUNTING</DNAME> <EmpsInDept> <EmployeeRow> <EMPNO>7782</EMPNO> <ENAME>CLARK</ENAME> <DEPTNO>10</DEPTNO> </EmployeeRow> <EmployeeRow> <EMPNO>7839</EMPNO> <ENAME>KING</ENAME> <DEPTNO>10</DEPTNO> </EmployeeRow> <EmployeeRow> <EMPNO>7934</EMPNO> <ENAME>MILLER</ENAME> <DEPTNO>10</DEPTNO> </EmployeeRow> </EmpsInDept> </DepartmentRow> </Department>
The writeXML() mechanism is using the view link accessor attributes to access detail collections. The view object used to construct the rowset of employee details is not the same VO instance that the user had created dynamically here. It is a new one that the framework creates on the fly, to which it's added the view link where clause to coordinate the details. So, the trick to get this dynamic situation working is to create the second-level view link between the view link accessor attribute's view object, instead of just on the "Employees" view object itself. Here is the modified code that shows how I got it working.
package test; import oracle.jbo.client.Configuration; import oracle.jbo.*; import org.w3c.dom.*; import oracle.xml.parser.v2.*; import oracle.jbo.domain.Number; public class TestClientNotWorking { static final String MASTER_QUERY = "select deptno, dname from dept where deptno = :0"; static final String DETAIL_QUERY = "select empno, ename, deptno from emp"; static final String REPORTS_QUERY = "select empno, ename,mgr from emp"; public static void main(String[] args) throws Throwable { String _am = "test.TestModule", _cf = "TestModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(_am,_cf); ViewObject master = am.createViewObjectFromQueryStmt("Department",MASTER_QUERY); master.setWhereClauseParam(0,new Number(10)); ViewObject detail = am.createViewObjectFromQueryStmt("Employee",DETAIL_QUERY); ViewObject reports = am.createViewObjectFromQueryStmt("DirectReport",REPORTS_QUERY); ViewLink master_detail = am.createViewLinkBetweenViewObjects( "master_detail", "EmpsInDept", master, new AttributeDef[]{master.findAttributeDef("DEPTNO")}, detail, new AttributeDef[]{detail.findAttributeDef("DEPTNO")}, null); /* Retrieve the first row of the master rowset */ Row firstDept = master.first(); /* Get iterator over details collection via the view link accessor attribute */ RowSetIterator empsInDeptRowSet = (RowSetIterator)firstDept.getAttribute("EmpsInDept"); /* Get view object created by the framework to handle view link traversal */ ViewObject viewObjectForAccessorRowSet = empsInDeptRowSet.getRowSet().getViewObject(); /* Now, create the 2nd-level detail between this view object, and reports */ ViewLink mgr_reports = am.createViewLinkBetweenViewObjects( "mgr_reports", "DirectReports", viewObjectForAccessorRowSet, new AttributeDef[]{detail.findAttributeDef("EMPNO")}, reports, new AttributeDef[]{reports.findAttributeDef("MGR")}, null);
Node n = master.writeXML(-1,XMLInterface.XML_OPT_ALL_ROWS); ((XMLNode)n).print(System.out); Configuration.releaseRootApplicationModule(am,true); } }
Now it produces the expected, three-level XML output:
<Department> <DepartmentRow> <DEPTNO>10</DEPTNO> <DNAME>ACCOUNTING</DNAME> <EmpsInDept> <EmployeeRow> <EMPNO>7782</EMPNO> <ENAME>CLARK</ENAME> <DEPTNO>10</DEPTNO> <DirectReports> <DirectReportRow> <EMPNO>7934</EMPNO> <ENAME>MILLER</ENAME> <MGR>7782</MGR> </DirectReportRow> </DirectReports> </EmployeeRow> <EmployeeRow> <EMPNO>7839</EMPNO> <ENAME>KING</ENAME> <DEPTNO>10</DEPTNO> <DirectReports> <DirectReportRow> <EMPNO>7566</EMPNO> <ENAME>JONES</ENAME> <MGR>7839</MGR> </DirectReportRow> <DirectReportRow> <EMPNO>7698</EMPNO> <ENAME>BLAKE</ENAME> <MGR>7839</MGR> </DirectReportRow> <DirectReportRow> <EMPNO>7782</EMPNO> <ENAME>CLARK</ENAME> <MGR>7839</MGR> </DirectReportRow> </DirectReports> </EmployeeRow> <EmployeeRow> <EMPNO>7934</EMPNO> <ENAME>MILLER</ENAME> <DEPTNO>10</DEPTNO> </EmployeeRow> </EmpsInDept> </DepartmentRow> </Department>
|