Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g (10.1.3.1.0) Part Number B25947-01 |
|
|
View PDF |
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 sameInheritanceAndPolymorphicQueries project in the AdvancedEntityExamples workspace used in Section 26.6, "Using Inheritance in Your Business Domain Layer". |
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.
To create a view object with a polymorphic entity usage, follow these steps:
Identify the entity object that represents the base type in the entity inheritance hierarchy you want to work with.
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). |
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.
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>
{para}?>
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.
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.
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();
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.
To create a view object with polymorphic view rows, follow these steps:
Identify an existing view object to be the base
In the example above, the UserList
view object is the base.
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
".
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.
Create a new view object that extends the base view object
In the example above, TechnicianList
extends the base UserList
view object.
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.
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.
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:
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.
In the Data Model panel of the Application Module Editor, click Subtypes...
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
{para}?>
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:
To select attributes that are specific to the entity subtype
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:
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.
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.
Click > to shuttle it to the Selected list
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.
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 likeTechnicianListRowImpl to override a method like getTheUser() and change its return type. |
Example 27-18 shows the interesting lines of code from a TestViewRowPolymorphism
class that performs the following steps:
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
.
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.
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.
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.
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:
Configure an attribute to be the discriminator at the view object level in the root view object in an inheritance hierarchy.
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.
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:
Configure an attribute to be the discriminator at the entity object level in the root entity object in an inheritance hierarchy.
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.
List the subclassed entity objects in a view object's list of Subtypes.