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

27.6 Using View Objects to Work with Multiple Row Types

In Section 26.6, "Using Inheritance in Your Business Domain Layer" you saw how to create an inheritance hierarchy of User, Technician, and Manager entity objects. Sometimes you will create a view object to work with entity rows of a single type like Technician, which perhaps includes Technician-specific attributes. At other times you may want to query and update rows for users, technicians, and managers in the same row set, working with attributes that they all share in common.


Note:

To experiment with the example described in this section, use the same InheritanceAndPolymorphicQueries project in the AdvancedEntityExamples workspace used in Section 26.6, "Using Inheritance in Your Business Domain Layer".

27.6.1 What is a Polymorphic Entity Usage?

A polymorphic entity usage is one that references a base entity object in an inheritance hierarchy and is configured to handle subtypes of that entity as well. Figure 27-4 shows the results of using a view object with a polymorphic entity usage. The entity-based UserList view object has the User entity object as its primary entity usage. The view object partitions each row retrieved from the database into an entity row part of the appropriate entity object subtype of User. It creates the appropriate entity row subtype based on consulting the value of the discriminator attribute. For example, if the UserList query retrieves one row for user ngreenbe, one row for manager sking, and one row for technician ahunold, the underlying entity row parts would be as shown in the figure.

Figure 27-4 View Object with a Polymorphic Entity Usage Handles Entity Subtypes

Image shows flow of entity subtypes

27.6.2 How To Create a View Object with a Polymorphic Entity Usage

To create a view object with a polymorphic entity usage, follow these steps:

  1. Identify the entity object that represents the base type in the entity inheritance hierarchy you want to work with.

  2. Create an entity-based view object with that base entity as its entity usage.


    Note:

    When an entity-based view object references an entity object with a discriminator attribute, then JDeveloper enforces that the discriminator attribute is included in the query as well (in addition to the primary key attribute).

  3. On the Entity Objects panel of the Create View Object wizard, select the entity usage in the Selected list and click Subtypes...

  • In the Subtypes dialog that appears, shuttle the desired entity subtypes you want to allow from the Available to the Selected list, and click OK

Then click OK to create the view object.

27.6.3 What Happens When You Create a View Object with a Polymorphic Entity Usage

When you create an entity-based view object with a polymorphic entity usage, JDeveloper adds information about the allowed entity subtypes to the view object's XML component definition. For example, when creating the UserList view object above, the names of the allowed subtype entity objects are recorded in an AttrArray tag like this:

<ViewObject Name="UserList" ... >
   <EntityUsage Name="TheUser"
                Entity="devguide.advanced.inheritance.entities.User" >
   </EntityUsage>
   <AttrArray Name="EntityImports">
      <Item Value="devguide.advanced.inheritance.entities.Manager" />
      <Item Value="devguide.advanced.inheritance.entities.Technician" />
   </AttrArray>
   <!-- etc. -->
</ViewObject>

27.6.4 What You May Need to Know

{para}?>

27.6.4.1 Your Query Must Limit Rows to Expected Entity Subtypes

If your view object expects to work with only a subset of the available entity subtypes in a hierarchy, you need to include an appropriate WHERE clause that limits the query to only return rows whose discriminator column matches the expected entity types.

27.6.4.2 Exposing Selected Entity Methods in View Rows Using Delegation

By design, clients do not work directly with entity objects. Instead, they work indirectly with entity objects through the view rows of an appropriate view object that presents a relevant set of information related to the task as hand. Just as a view object can expose a particular set of the underlying attributes of one or more entity objects related to the task at hand, it can also expose a selected set of methods from those entities. You accomplish this by enabling a custom view row Java class and writing a method in the view row class that:

  • Accesses the appropriate underlying entity row using the generated entity accessor in the view row, and

  • Invokes a method on it

For example, assume that the User entity object contains a performUserFeature() method in its UserImpl class. To expose this method to clients on the UserList view row, you can enable a custom view row Java class and write the method shown in Example 27-13. JDeveloper generates an entity accessor method in the view row class for each participating entity usage based on the entity usage alias name. Since the alias for the User entity in the UserList view object is "TheUser", it generates a getTheUser() method to return the entity row part related to that entity usage.

Example 27-13 Exposing Selected Entity Object Methods on View Rows Through Delegation

// In UserListRowImpl.java
public void performUserFeature() {
  getTheUser().performUserFeature();
}

The code in the view row's performUserFeature() method uses this getTheUser() method to access the underlying UserImpl entity row class and then invokes its performUserFeature() method. This style of coding is known as delegation, where a view row method delegates the implementation of one of its methods to a corresponding method on an underlying entity object. When delegation is used in a view row with a polymorphic entity usage, the delegated method call is handled by appropriate underlying entity row subtype. This means that if the UserImpl, ManagerImpl, and TechnicianImpl classes implement the performUserFeature() method in a different way, the appropriate implementation is used depending on the entity subtype for the current row.

After exposing this method on the client row interface, client programs can use the custom row interface to invoke custom business functionality on a particular view row. Example 27-14 shows the interesting lines of code from a TestEntityPolymorphism class. It iterates over all the rows in the UserList view object instance, casts each one to the custom UserListRow interface, and invokes the performUserFeature() method.

Example 27-14 Invoking a View Row Method That Delegates to an Entity Object

UserList userlist = (UserList)am.findViewObject("UserList");
userlist.executeQuery();
while (userlist.hasNext()) {
  UserListRow user = (UserListRow)userlist.next();
  System.out.print(user.getEmail()+"->");
  user.performUserFeature();
}

Running the client code in Example 27-14 produces the following output:

austin->## performUserFeature as Technician
hbaer->## performUserFeature as User
:
sking->## performUserFeature as Manager
:

Rows related to User entities display a message confirming that the performUserFeature() method in the UserImpl class was used. Rows related to Technician and Manager entities display a different message, highlighting the different implementations that the respective TechnicianImpl and ManagerImpl classes have for the inherited performUserFeature() method.

27.6.4.3 Creating New Rows With the Desired Entity Subtype

In a view object with a polymorphic entity usage, when you create a new view row it contains a new entity row part whose type matches the base entity usage. To create a new view row with one of the entity subtypes instead, use the createAndInitRow() method. Example 27-15 shows two custom methods in the UserList view object's Java class that use createAndInitRow() to allow a client to create new rows having entity rows either of Manager or Technician subtypes. To use the createAndInitRow(), as shown in the example, create an instance of the NameValuePairs object and set it to have an appropriate value for the discriminator attribute. Then, pass that NameValuePairs to the createAndInitRow() method to create a new view row with the appropriate entity row subtype, based on the value of the discriminator attribute you passed in.

Example 27-15 Exposing Custom Methods to Create New Rows with Entity Subtypes

// In UserListImpl.java
public UserListRow createManagerRow() {
  NameValuePairs nvp = new NameValuePairs();
  nvp.setAttribute("UserRole","manager");
  return (UserListRow)createAndInitRow(nvp);
}
public UserListRow createTechnicianRow() {
  NameValuePairs nvp = new NameValuePairs();
  nvp.setAttribute("UserRole","technician");
  return (UserListRow)createAndInitRow(nvp);
} 

If you expose methods like this on the view object's custom interface, then at runtime, a client can call them to create new view rows with appropriate entity subtypes. Example 27-16 shows the interesting lines relevant to this functionality from a TestEntityPolymorphism class. First, it uses the createRow(), createManagerRow(), and createTechnicianRow() methods to create three new view rows. Then, it invokes the performUserFeature() method from the UserListRow custom interface on each of the new rows.

As expected, each row handles the method in a way that is specific to the subtype of entity row related to it, producing the results:

## performUserFeature as User
## performUserFeature as Manager
## performUserFeature as Technician

Example 27-16 Creating New View Rows with Different Entity Subtypes

// In TestEntityPolymorphism.java
UserListRow newUser = (UserListRow)userlist.createRow();
UserListRow newMgr  = userlist.createManagerRow();
UserListRow newTech = userlist.createTechnicianRow();
newUser.performUserFeature();
newMgr.performUserFeature();
newTech.performUserFeature();

27.6.5 What are Polymorphic View Rows?

In the example shown in Section 27.6, "Using View Objects to Work with Multiple Row Types", the polymorphism occurs "behind the scenes" at the entity object level. Since the client code works with all view rows using the same UserListRow interface, it cannot distinguish between rows based on a Manager entity object from those based on a User entity object. The code works with all view rows using the same set of view row attributes and methods common to all types of underlying entity subtypes.

If you configure a view object to support polymorphic view rows, then the client can work with different types of view rows using a view row interface specific to the type of row it is. By doing this, the client can access view attributes or invoke view row methods that are specific to a given subtype as needed. Figure 27-5 illustrates the hierarchy of view objects that enables this feature for the UserList example considered above. TechnicianList and ManagerList are view objects that extend the base UserList view object. Notice that each one includes an additional attribute specific to the subtype of User they have as their entity usage. TechnicianList includes an additional Certified attribute, while ManagerList includes the additional NextReview attribute. When configured for view row polymorphism as described in the next section, a client can work with the results of the UserList view object using:

  • UserListRow interface for view rows related to

  • TechnicianListRow interface for view rows related to technicians

  • ManagerListRow interface for view rows related to managers

As you'll see, this allows the client to access the additional attributes and view row methods that are specific to a given subtype of view row.

Figure 27-5 Hierarchy of View Object Subtypes Enables View Row Polymorphism

Image shows subtype hierarchy enabling row polymorphism

27.6.6 How to Create a View Object with Polymorphic View Rows

To create a view object with polymorphic view rows, follow these steps:

  1. Identify an existing view object to be the base

    In the example above, the UserList view object is the base.

  2. Identify a discriminator attribute for the view row, and give it a default value.

    Check the Discriminator checkbox on the attribute panel to mark the attribute as the one that distinguishes which view row interface to use. You must supply a value for the Default field that matches the attribute value for which you expect the base view object's view row interface to be used. For example, in the UserList view object, you would mark the UserRole attribute as the discriminator attribute and supply a default value of "user".

  3. Enable a custom view row class for the base view object, and expose at least one method on the client row interface. This can be one or all of the view row attribute accessor methods, as well as any custom view row methods.

  4. Create a new view object that extends the base view object

    In the example above, TechnicianList extends the base UserList view object.

  5. Enable a custom view row class for the extended view object.

    If appropriate, add additional custom view row methods or override custom view row methods inherited from the parent view object's row class.

  6. Supply a distinct value for the discriminator attribute in the extended view object.

    The TechnicianList view object provides the value of "technician" for the UserRole discriminator attribute.

  7. Repeat steps 4-6 to add additional extended view objects as needed.

    For example, the ManagerList view object is a second one that extends UserList. It supplies the value "manager" for the UserRole discriminator attribute.

After setting up the view object hierarchy, you need to define the list of view object subtypes that participate in the view row polymorphism. To accomplish this, do the following:

  1. Add an instance of each type of view object in the hierarchy to the data model of an application module.

    For example, the UserModule application module in the example has instances of UserList, TechnicianList, and ManagerList view objects.

  2. In the Data Model panel of the Application Module Editor, click Subtypes...

  3. In the Subtypes dialog that appears, shuttle the desired view object subtypes that you want to participate in view row polymorphism from the Available to the Selected list, and click OK

27.6.7 What You May Need to Know

{para}?>

27.6.7.1 Selecting Subtype-Specific Attributes in Extended View Objects

When you create an extended view object, it inherits the entity usage of its parent. If the parent view object's entity usage is based on an entity object with subtypes in your domain layer, you may want your extended view object to work with one of these subtypes instead of the inherited parent entity usage type. Two reasons you might want to do this are:

  1. To select attributes that are specific to the entity subtype

  2. To be able to write view row methods that delegate to methods specific to the entity subtype

In order to do this, you need to override the inherited entity usage to refer to the desired entity subtype. To do this, perform these steps in the View Object Editor for your extended view object:

  1. On the Entity Objects panel, verify that you are working with an extended entity usage.

    For example, when creating the TechnicianList view object that extends the UserList view object, the entity usage with the alias TheUser will initially display in the Selected list as: TheUser (User): extended. The type of the entity usage is in parenthesis, and the "extended" label confirms that the entity usage is currently inherited from its parent.

  2. Select the desired entity subtype in the Available list that you want to override the inherited one. It must be a subtype entity of the existing entity usage's type.

    For example, you would select the Technician entity object in the Available list to overridden the inherited entity usage based on the User entity type.

  3. Click > to shuttle it to the Selected list

  4. Acknowledge the alert that appears, confirming that you want to override the existing, inherited entity usage.

When you have performed these steps, the Selected list updates to reflect the overridden entity usage. For example, for the TechnicianList view object, after overriding the User-based entity usage with the Technician entity subtype, it updates to show: TheUser (Technician): overridden.

After overriding the entity usage to be related to an entity subtype, you can then use the Attributes tab of the editor to select additional attributes that are specific to the subtype. For example, the TechnicianList view object includes the additional attribute named Certified that is specific to the Technician entity object.

27.6.7.2 Delegating to Subtype-Specific Methods After Overriding the Entity Usage

After overriding the entity usage in an extended view object to reference a subtype entity, you can write view row methods that delegate to methods specific to the subtype entity class. Example 27-17 shows the code for a performTechnicianFeature() method in the custom view row class for the TechnicianList view object. It casts the return value from the getTheUser() entity row accessor to the subtype TechnicianImpl, and then invokes the performTechnicianFeature() method that is specific to Technician entity objects.

Example 27-17 View Row Method Delegating to Method in Subtype Entity

// In TechnicianListRowImpl.java
public void performTechnicianFeature() {
   TechnicianImpl tech = (TechnicianImpl)getTheUser();
   tech.performTechnicianFeature();
}

Note:

You need to perform the explicit cast to the entity subtype here because JDeveloper does not yet take advantage of the new JDK 5.0 feature called covariant return types that would allow a subclass like TechnicianListRowImpl to override a method like getTheUser() and change its return type.

27.6.7.3 Working with Different View Row Interface Types in Client Code

Example 27-18 shows the interesting lines of code from a TestViewRowPolymorphism class that performs the following steps:

  1. Iterates over the rows in the UserList view object.

    For each row in the loop, it uses Java's instanceof operator to test whether the current row is an instance of the ManagerListRow or the TechnicianListRow.

  2. If the row is a ManagerListRow, then cast it to this more specific type and:

    • Call the performManagerFeature() method specific to the ManagerListRow interface, and

    • Access the value of the NextReview attribute that is specific to the ManagerList view object.

  3. If the row is a TechnicianListRow, then cast it to this more specific type and:

    • Call the performTechnicianFeature() method specific to the TechnicianListRow interface, and

    • Access the value of the Certified attribute that is specific to the TechnicianList view object.

  4. Otherwise, just call a method on the UserListRow

Example 27-18 Using View Row Polymorphism in Client Code

// In TestViewRowPolymorphism.java
ViewObject vo = am.findViewObject("UserList");
vo.executeQuery();
// 1. Iterate over the rows in the UserList view object
while (vo.hasNext()) {
  UserListRow user = (UserListRow)vo.next();
  System.out.print(user.getEmail()+"->");
  if (user instanceof ManagerListRow) {
    // 2. If the row is a ManagerListRow, cast it
    ManagerListRow mgr = (ManagerListRow)user;
    mgr.performManagerFeature();       
    System.out.println("Next Review:"+mgr.getNextReview());
  }
  else if (user instanceof TechnicianListRow) {
    // 3. If the row is a ManagerListRow, cast it
    TechnicianListRow tech = (TechnicianListRow)user;
    tech.performTechnicianFeature();       
    System.out.println("Certified:"+tech.getCertified());        
  }
  else {
    // 4. Otherwise, just call a method on the UserListRow 
    user.performUserFeature();
  }
}

Running the code in Example 27-18 produces the following output:

daustin->## performTechnicianFeature called
Certified:Y
hbaer->## performUserFeature as User
:
sking->## performManagerFeature called
Next Review:2006-05-09
:

This illustrates that by using the view row polymorphism feature the client was able to distinguish between view rows of different types and access methods and attributes specific to each subtype of view row.

27.6.7.4 View Row Polymorphism and Polymorphic Entity Usage are Orthogonal

While often even more useful when used together, the view row polymorphism and the polymorphic entity usage features are distinct and can be used separately. In particular, the view row polymorphism feature can be used for read-only view objects, as well as for entity-based view objects. When you combine both mechanisms, you can have both the entity row part being polymorphic, as well as the view row type.

Note to use view row polymorphism with either view objects or entity objects, you must configure the discriminator attribute property separately for each. This is necessary because read-only view objects contain no related entity usages from which to infer the discriminator information.

In summary, to use view row polymorphism:

  1. Configure an attribute to be the discriminator at the view object level in the root view object in an inheritance hierarchy.

  2. Have a hierarchy of inherited view objects each of which provides a distinct value for the "Default Value" property of that view object level discriminator attribute.

  3. List the subclassed view objects in this hierarchy in the application module's list of Subtypes.

Whereas, to create a view object with a polymorphic entity usage:

  1. Configure an attribute to be the discriminator at the entity object level in the root entity object in an inheritance hierarchy.

  2. Have a hierarchy of inherited entity objects, each of which overrides and provides a distinct value for the "Default Value" property of that entity object level discriminator attribute.

  3. List the subclassed entity objects in a view object's list of Subtypes.