Skip Headers
Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers
10g (10.1.3.1.0)

Part Number B25947-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

5.10 Working with Master/Detail Data

So far you've worked with a single view object that queries a single USERS table. In practice, many queries you'll need to work with will involve multiple tables that are related by foreign keys. There are two ways you can use view objects to handle this situation, you can either:

Figure 5-22 illustrates the different "shape" that these two options produce. The join is a single "flattened" result. The master/detail linked queries produce a multilevel result.

Figure 5-22 Difference Between Join Query Result and Linked Master/Detail Queries

Image shows difference between queries

5.10.1 How to Create a Read-Only View Object Joining Tables

To create a read-only view object joining two tables, use the Create View Object wizard. As an example, you'll create a view object named OpenOrPendingServiceRequests that joins the SERVICE_REQUEST and USER tables. For each service request, you'll display the email of the user who created the request.

In step 1 ensure that you've selected Read-only Access, and in step 2 on the SQL Statement page, enter the SQL query statement that joins the desired tables. If you want interactive assistance to build up the right SQL statement, you can click on the Query Builder button.

5.10.1.1 Using the Query Builder to Simplify Creating Joins

As shown in Figure 5-23, on the Quick-pick objects page of the query builder dialog, you can see the tables in your schema, including the foreign keys that relate them to other tables. To include columns in the select list of the query, click on them in the Available list and shuttle them to the Selected list. Figure 5-23 shows the result of selecting the SVR_ID, PROBLEM_DESCRIPTION, and ASSIGNED_TO columns from the SERVICE_REQUESTS table, along with the EMAIL column from the USERS table. In the SERVICE_REQUESTS table, beneath the SVR_CREATED_BY_USR_FK foreign key, select the EMAIL column from the USERS table and the query builder automatically determines the required join clause for you.

Figure 5-23 Using the View Object Query Builder to Define a Join

Image of SQL Statement page of View Object Query Builder

On the WHERE Clause page of the query builder, shuttle the STATUS column into the WHERE clause box, and complete the job by adding the IN ('Open','Pending') yourself. Click OK in the query builder to create the following query:

Example 5-14 Creating a Query Using SQL Builder

SELECT 
    SERVICE_REQUESTS.SVR_ID SVR_ID, 
    SERVICE_REQUESTS.PROBLEM_DESCRIPTION PROBLEM_DESCRIPTION, 
    SERVICE_REQUESTS.ASSIGNED_TO ASSIGNED_TO, 
    USERS.EMAIL EMAIL 
FROM 
    SERVICE_REQUESTS INNER JOIN USERS
      ON SERVICE_REQUESTS.CREATED_BY = USERS.USER_ID 
WHERE 
    SERVICE_REQUESTS.STATUS IN ('Open', 'Pending')

Notice the EMAIL column in the query. It represents the email of the person who created the service request, but its column name is not as descriptive as it could be. In Section 5.2.3.2, "Working with Queries That Include SQL Expressions", you learned one way to affect the default Java-friendly name of the view object attributes by assigning a column alias. Here you can adopt an alternative technique. You'll use one of the later panels in the Create View Object wizard to rename the view object attribute directly as part of the creation process. Renaming the view object here saves you from having to edit the view object again, when you already know the different attribute names that you'd like to use.

Click Next four times to get to the Attributes Settings page. Select the Email attribute in the Select Attribute dropdown list at the top of the page and change the value in the Name field to CreatedByEmail. Then click Finish to create the OpenOrPendingServiceRequests view object. An OpenOrPendingServiceRequests.xml component definition file is created to save the view object's declarative settings.

5.10.1.2 Testing the Join View

To test the new view object, edit the UserService application module and on the Data Model page, add an instance of the OpenOrPendingServiceRequests to the data model. Instead of accepting the default OpenOrPendingServiceRequests1 instance name, change the instance name to AllOpenOrPendingServiceRequests. After doing this, you can launch the Business Components Browser and verify that the join query is working as expected.

5.10.2 How to Create Master/Detail Hierarchies Using View Links

When your needs call for showing the user a set of master rows, and for each master row a set of coordinated detail rows, then you can create view links to define how you want the master and detail view objects to relate. Assume you want to link the UserList view object to the OpenOrPendingServiceRequests view object to create a master/detail hierarchy of users and the related set of open or pending service requests that have been assigned to them.

To create the view link, use the Create View Link wizard. The wizard is available from the New Gallery in the Business Tier > ADF Business Components category. In step 1, on the Name page provide a name for the view link and a package where its definition will reside. Given its purpose, a name like RequestsAssignedTo is a fine name, and for simplicity keep it in the same devguide.examples package as the view objects.

In step 2, on the View Objects page, select a "source" attribute to use from the view object that will act as the master. Figure 5-24 shows selecting the UserId attribute from the Users view object in this role. Next, select a corresponding destination attribute from the view object that will act as the detail. Since you want the detail query to show service requests that are assigned to the currently selected user, select the AssignedTo attribute in the OpenOrPendingServiceRequests to play this role. Finally, click Add to add the matching attribute pair to the table of source and destination attribute pairs below. If there were multiple attribute pairs required to define the link between master and detail, you could repeat these steps to add additional source/target attribute pairs. For this example, the one (UserId,AssignedTo) pair is all that's required.

Figure 5-24 Defining Source/Target Attribute Pairs While Creating a View Link

Image of step 2 of the Create View Link editor

In step 3, on the View Link SQL page, you can preview the view link SQL predicate that will be used at runtime to access the correlated detail rows from the destination view object for the current row in the source view object.

In step 4, on the View Link Properties page you control whether the view link represents a one-way relationship or a bidirectional one. Notice in Figure 5-25 that in the Destination group box for the OpenOrPendingServiceRequests view object, the Generate Accessor In View Object: Users box is checked. In contrast, in the Source group box for the Users view object, the Generate Accessor In View Object: OpenOrPendingServiceRequests box is not checked. By default, a view link is a one-way relationship that allows the current row of the source (master) to access a set of related rows in the destination (detail) view object. These checkbox settings indicate that you'll be able to access a detail collection of rows from the OpenOrPendingServiceRequests for the current row in the Users view object, but not vice versa. For this example, a default one-way view link will be fine, so leave the other checkbox unchecked.

Figure 5-25 View Link Properties Control Name and Direction of Accessors

Image of step 4 of the View Link Properties editor

The Accessor Name field in the destination group box indicates the name you can use to programmatically access the related collection of OpenOrPendingServiceRequests rows for the current row in Users. By default the accessor name will be OpenOrPendingServiceRequests, matching the name of the destination view object. To make it more clear that the related collection of service requests is a collection of requests that are assigned to the current user, as you can see in Figure 5-25 you can change the name of the accessor to AssignedRequests.

To create the view link, click Finish.

5.10.3 What Happens When You Create Master/Detail Hierarchies Using View Links

When you create a view link, JDeveloper creates the XML component definition file that represents its declarative settings and saves it in the directory that corresponds to the name of its package. In Section 5.10.2, the view link was named RequestsAssignedTo in the devguide.examples package, so the XML file created will be ./devguide/examples/RequestsAssignedTo.xml under the project's source path. This XML file contains the declarative information about the source and target attribute pairs you've specified.

In addition to saving the view link component definition itself, JDeveloper also updates the XML definition of the source view object in the view link relationship to add information about the view link accessor you've defined. As a confirmation of this, you can select the Users view object in the Application Navigator and inspect its details in the Structure window. As illustrated in Figure 5-26, you now see the new AssignedRequests accessor in the ViewLink Accessor category.

Figure 5-26 Structure Window Showing Details of the Users View Object

Image of Structure window showing details for users

5.10.4 What You May Need to Know About View Links

To work with view links effectively, there are a few more things you may need to know, including: that view link accessor attributes return a RowSet, how to access a detail collection using the view link accessor, and how to enable active master/detail coordination in the date model.

5.10.4.1 View Link Accessor Attributes Return a RowSet

At runtime the getAttribute() method on a Row allows you to access the value of any attribute of a row in the view object's result set by name. The view link accessor behaves like an additional attribute in the current row of the source view object, so you can use the same getAttribute() method to retrieve its value. The only practical difference between a regular view attribute and a view link accessor attribute is its data type. Whereas a regular view attribute typically has a scalar data type with a value like 303 or ahunold, the value of a view link accessor attribute is a row set of zero or more correlated detail rows. Assuming that curUser is a Row from some instance of the Users view object, you can write a line of code to retrieve the detail row set of open or pending assigned requests:

RowSet reqs = (RowSet)curUser.getAttribute("AssignedRequests");

Note:

If you generate the custom Java class for your view row, the type of the view link accessor will be RowIterator. Since at runtime the return value will always be a RowSet, it is safe to cast the view link attribute value to a RowSet.

5.10.4.2 How to Access a Detail Collection Using the View Link Accessor

Once you've retrieved the RowSet of detail rows using a view link accessor, you can loop over the rows it contains using the same pattern used the view object's row set of results:step

while (reqs.hasNext()) {
  Row curReq = reqs.next();
  System.out.println("--> (" + curReq.getAttribute("SvrId") + ") " + 
                     curReq.getAttribute("ProblemDescription"));
}

If you use JDeveloper's Refactor > Duplicate... functionality on the existing TestClient.java class, you can easily "clone" it to create a TestClient2.java class that you'll modify as shown in Example 5-15 to make use of these new techniques. Notice that the lines left in the main() method are setting a dynamic WHERE clause to restrict the UserList view object instance to show only users whose USER_ROLE has the value technician. The second change was enhancing the executeAndShowResults() method to access the view link accessor attribute and print out the request number (SvrId) and ProblemDescription attribute for each one.

Example 5-15 Programmatically Accessing Detail Rows Using the View Link Accessor

package devguide.examples.client;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
public class TestClient2 {
  public static void main(String[] args) {
    String amDef = "devguide.examples.UserService";
    String config = "UserServiceLocal";
    ApplicationModule am =
     Configuration.createRootApplicationModule(amDef, config);
    ViewObject vo = am.findViewObject("UserList");
    // Add an extra where clause with a new named bind variable
    vo.setWhereClause("user_role = :TheUserRole");
    vo.defineNamedWhereClauseParam("TheUserRole", null, null);
    vo.setNamedWhereClauseParam("TheUserRole", "technician");
    // Show results when :TheUserRole = 'technician'
    executeAndShowResults(vo);
    Configuration.releaseRootApplicationModule(am, true);
  }
  private static void executeAndShowResults(ViewObject vo) {
    System.out.println("---");
    vo.executeQuery();
    while (vo.hasNext()) {
      Row curUser = vo.next();
      // Access the row set of details using the view link accessor attribute
      RowSet reqs = (RowSet)curUser.getAttribute("AssignedRequests");
      long numReqs = reqs.getEstimatedRowCount();
      System.out.println(curUser.getAttribute("UserId") + " " + 
                         curUser.getAttribute("Email")+" ["+
                         numReqs+" requests]");
      while (reqs.hasNext()) {
        Row curReq = reqs.next();
        System.out.println("--> (" + curReq.getAttribute("SvrId") + ") " + 
                           curReq.getAttribute("ProblemDescription"));
      }
    }
  }
}

Running TestClient2 shows the following results in the Log window. Each technician is listed, and for each technician that has some open or pending service requests, the information about those requests appears beneath their name.

---
303 ahunold [0 requests]
304 bernst [2 requests]
--> (102) Washing Machine does not turn on
--> (108) Freezer full of frost
305 daustin [1 requests]
--> (104) Spin cycle not draining
307 dlorentz [0 requests]
306 vpatabal [2 requests]
--> (107) Fridge is leaking
--> (112) My Dryer does not seem to be getting hot

If you run TestClient2 with debug diagnostics enabled, you will see the SQL queries that the view object performed. The view link WHERE clause predicate is used to automatically perform the filtering of the detail service request rows for the current row in the UserList view object.

5.10.4.3 How to Enable Active Master/Detail Coordination in the Data Model

You've seen that the process of defining a view link introduces a view link attribute in the source view object, which enables programmatic navigation to a row set of correlated details. In this scenario, the view link plays a passive role, simply defining the information necessary to retrieve the coordinated detail row set when your code requests it. The view link accessor attribute is present and programmatically accessible in any result rows from any instance of the view link's source view object. In other words, programmatic access does not require modifying the UserService application module's data model.

However, since master/detail user interfaces are such a frequent occurrence in enterprise applications, the view link can be also used in a more active fashion to avoid having to coordinate master/detail screen programmatically. You opt to have this active master/detail coordination performed by explicitly adding an instance of a view-linked view object to your application module's data model.

To accomplish this, edit the UserService application module and open the Data Model page. As shown in Figure 5-27, you'll see that the Available View Objects list now shows the OpenOrPendingServiceRequests view object twice: once on its own, and once as a detail view object via the RequestsAssignedTo view link.

Figure 5-27 Adding a Detail View Object to the Data Model

Image shows adding a detail view object to data model

To add a detail instance of a view object:

  1. In the Data Model list on the right, select the instance of the Users view object in the Data Model list that you want to be the actively-coordinating master

    The data model has only one instance of the Users view object named UserList, so select the UserList instance.

  2. In the Available View Objects list, select the OpenOrPendingServiceRequests node that is indented beneath the Users view object.

  3. Enter a name for the detail instance you're about to create in the Name field below the Available View Objects list. As shown in Figure 5-27, call the instance RequestsAssigned.

  4. Click the Add Instance button > to add the detail instance to the currently selected master instance in the data model, with the name you've chosen.

After following these steps, your Data Model list will look like what you see in Figure 5-28.

Figure 5-28 UserService Data Model with View Linked View Object

Image of UserService data model with view linked view object

The easiest way to see the effect of this active master/detail coordination is to launch the Business Components Browser on the UserService by choosing Test from its context menu in the Application Navigator. Figure 5-29 shows the browser window you will see. The data model tree shows the view link instance that is actively coordinating the UserList view object instance with the RequestsAssigned view object instance. It has the default view link instance name of RequestsAssignedTo1. Double-clicking this view link instance node in the tree opens the master/detail panel that you see in Figure 5-29. You'll see that when you use the toolbar buttons to navigate in the master view object — changing the view object's current row as a result — the coordinated set of details is automatically refreshed and the user interface stays in sync.

Figure 5-29 Experimenting with Active Data Model Master/Detail Coordination

Image of Business Components Browser with active data model

If you also double-click on the AllOpenOrPendingServiceRequests view object instance that you added earlier, a second tab will open to show its data. Notice that it is another instance of the same devguide.examples.Users view object; however, since it is not being actively coordinated by a view link, its query is not constrained by the current row in the UserList.

So far you've seen a view link that defines a basic master/detail relationship between two view objects. Keep in mind that by creating more view links you can achieve master/detail hierarchies of any complexity, including:

  • Multilevel master/detail/detail

  • Master with multiple (peer) details

  • Detail with multiple masters

The steps to define these more complex hierarchies are the same as the ones covered here, you just need to create it one view link at time.