Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g (10.1.3.1.0) Part Number B25947-01 |
|
|
View PDF |
Inheritance is a powerful feature of object-oriented development that can simplify development and maintenance when used appropriately. As you've seen in Section 25.9, "Creating Extended Components Using Inheritance", ADF Business Components supports using inheritance to create new components that extend existing ones in order to add additional properties or behavior or modify the behavior of the parent component. This section helps you understand when inheritance can be useful in modeling the different kinds of entities in your reusable business domain layer.
Note: The examples in this section refer to theInheritanceAndPolymorphicQueries project in the AdvancedEntityExamples workspace. See the note at the beginning of this chapter for download instructions. Run the AlterUsersTable.sql script in the Resources folder against the SRDemo connection to setup the additional database objects required for the project. |
Your application's database schema might contain tables where different logical kinds of business information are stored in rows of the same table. These tables will typically have one column whose value determines the kind of information stored in each row. For example, the SRDemo application's USERS
table stores information about end-users, technicians, and managers in the same table. It contains a USER_ROLE
column whose value — user
, technician
, or manager
— determines what kind of user the row represents.
While the simple SRDemo application implementation doesn't yet contain this differentiation in this release, it's reasonable to assume that a future release of the application might require:
Managing additional database-backed attributes that are specific to managers or specific to technicians
Implementing common behavior for all users that is different for managers or technicians
Implementing new functionality that is specific to only managers or only technicians
Figure 26-2 shows what the business domain layer would look like if you created distinct User
, Manager
, and Technician
entity objects to allow distinguishing the different kinds of business information in a more formal way inside your application. Since technicians and managers are special kinds of users, their corresponding entity objects would extend the base User
entity object. This base User
entity object contains all of the attributes and methods that are common to all types of users. The performUserFeature()
method in the figure represents one of these common methods.
Then, for the Manager
and Technician
entity objects you can add specific additional attributes and methods that are unique to that kind of user. For example, in the figure, Manager
has an additional NextReview
attribute of type Date
to track when the manager must next review his employees. There is also a performManagerFeature()
method that is specific to managers. Similarly, the Technician
entity object has an additional Certified
attribute to track whether the technician has completed training certification. The performTechnicianFeature()
is a method that is specific to technicians. Finally, also note that since expertise areas only are relevant for technicians, the association between "users" and expertise levels is defined between Technician
and ExpertiseArea
.
By modeling these different kinds of users as distinct entity objects in an inheritance hierarchy in your domain business layer, you can simplify having them share common data and behavior and implement the aspects of the application that make them distinct.
To create entity objects in an inheritance hierarchy, you use the Create Entity Object wizard to create each entity following the steps outlined in the sections below. The example described here assumes that you've altered the SRDemo application's USERS
table by executing the following DDL statement to add two new columns to it:
alter table users add ( certified varchar2(1), next_review date );
Before creating entity objects in an inheritance hierarchy based on table containing different kinds of information, you should first identify which column in the table is used to distinguish the kind of row it is. In the SRDemo application's USERS
table, this is the USER_ROLE
column. Since it helps partition or "discriminate" the rows in the table into separate groups, this column is known as the discriminator column.
Next, determine the valid values that the descriminator column takes on in your table. You might know this off the top of your head, or you could execute a simple SQL statement in the JDeveloper SQL Worksheet to determine the answer. To access the worksheet:
Choose View | Connection Navigator.
Expand the Database folder and select the SRDemo
connection.
Choose SQL Worksheet from the right-mouse context menu.
Figure 26-3 shows the results of performing a SELECT DISTINCT
query in the SQL Worksheet on the USER_ROLE
column in the USERS
table. It confirms that the rows are partitioned into three groups based on the USER_ROLE
discriminator values: user
, technician
, and manager
.
Once you know how many different kinds of business entities are stored in the table, you will also know how many entity objects to create to model these distinct items. You'll typically create one entity object per kind of item. Next, in order to help determine which entity should act as the base of the hierarchy, you need to determine which subset of attributes is relevant to each kind of item.
Using the example above, assume you determine that all of the attributes except Certified
and NextReview
are relevant to all users, that Certified
is specific to technicians, and that NextReview
is specific to managers. This information leads you to determine that the Users
entity object should be the base of the hierarchy, with Manager
and Technician
entity object each extending Users
to add their specific attributes.
To create the base entity object in an inheritance hierarchy, use the Create Entity Object wizard and following these steps:
In step 1 on the Name panel, provide a name and package for the entity, and select the schema object on which the entity will be based.
For example, name the entity object User
and base it on the USERS
table.
In step 2 on the Attributes panel, select the attributes in the Entity Attributes list that are not relevant to the base entity object (if any) and click Remove to remove them.
For example, remove the Certified
and NextReview
attributes from the list.
In step 3 on the Attribute Settings panel, use the Select Attribute dropdown list to choose the attribute that will act as the discriminator for the family of inherited entity objects and check the Discriminator checkbox to identify it as such. Importantly, you must also supply a Default Value for this discriminator attribute to identify rows of this base entity type.
For example, select the UserRole
attribute, mark it as a discriminator attribute, and set its Default Value to the value "user
".
Note: Leaving the Default Value blank for a discriminator attribute is legal. A blank default value means that a row whose discriminator column valueIS NULL will be treated as this base entity type. |
Then click Finish to create the entity object.
To create a subtype entity object in an inheritance hierarchy, first do the following:
Determine the entity object that will be the parent entity object from which your new entity object will extend.
For example, the parent entity for a new Manager
entity object will be the User
entity created above.
Ensure that the parent entity has a discriminator attribute already identified.
The base type must already have the discriminator attribute identified as described in the section above. If it does not, use the Entity Object editor to set the Discriminator property on the appropriate attribute of the parent entity before creating the inherited child.
Then, use the Create Entity Object wizard and follow these steps to create the new subtype entity object in the hierarchy:
In step 1 on the Name panel, provide a name and package for the entity, and click the Browse button next to the Extends field to select the parent entity from which the entity being created will extend.
For example, name the new entity Manager
and select the User
entity object in the Extends field.
In step 2 on the Attributes panel, use the New from Table button to add the attributes corresponding to the underlying table columns that are specific to this new entity subtype.
For example, select the NEXT_REVIEW
column to add a corresponding NextReview
attribute to the Manager
entity object
Still on step 2, use the Override button to override the discriminator attribute so that you can customize the attribute metadata to supply a distinct Default Value for the Manager
subtype.
For example, override the UserRole
NextReview
attribute.
In step 3 on the Attribute Settings panel, use the Select Attribute dropdown list to choose the discriminator attribute. Importantly, you must change the Default Value field to supply a distinct default value for the discriminator attribute that defines the entity subtype being created.
For example, select the UserRole
attribute and change its Default Value to the value "manager
".
Then click Finish to create the subtype entity object.
Note: You can repeat the same steps to define theTechnician entity that extends User to add the additional Certified attribute and overrides the Default Value of the UserRole discriminator attribute to have the value "technician ". |
To add methods to entity objects in an inheritance hierarchy, enable the custom Java class for the entity in question and visit the code editor to add the method.
To add a method that is common to all entity objects in the hierarchy, enable a custom Java class for the base entity object in the hierarchy and add the method in the code editor. For example, if you add the following method to the UserImpl
class for the base User
entity object, it will be inherited by all entity objects in the hierarchy:
// In UserImpl.java public void performUserFeature() { System.out.println("## performUserFeature as User"); }
To override a method in a subtype entity that is common to all entity objects in the hierarchy, enable a custom Java class for the subtype entity and choose Source | Override Methods to launch the Override Methods dialog. Select the method you want to override, and click OK. Then, customize the overridden method's implementation in the code editor. For example, imagine overriding the performUserFeature()
method in the ManagerImpl
class for the Manager
subtype entity object and change the implementation to look like this:
// In ManagerImpl.java public void performUserFeature() { System.out.println("## performUserFeature as Manager"); }
When working with instances of entity objects in a subtype hierarchy, sometimes you will process instances of multiple different subtypes. Since Manager
and Technician
entities are special kinds of User
, you can write code that works with all of them using the more generic UserImpl
type that they all have in common. When doing this generic kind of processing of classes that might be one of a family of subtypes in a hierarchy, Java will always invoke the most specific override of a method available.
This means that invoking the performUserFeature()
method on an instance of UserImpl
that happens to really be the more specific ManagerImpl
subtype, will the result in printing out the following:
## performUserFeature as Manager
instead of the default result that regular UserImpl
instances would get:
## performUserFeature as User
To add a method that is specific to a subtype entity object in the hierarchy, enable a custom Java class for that entity and add the method in the code editor. For example, you could add the following method to the ManagerImpl
class for the Manager
subtype entity object:
// In ManagerImpl.java public void performManagerFeature() { System.out.println("## performManagerFeature called"); }
{para}?>
In the example above, the User
entity object corresponded to a concrete kind of row in the USERS
table and it also played the role of the base entity in the hierarchy. In other words, all of its attributes were common to all entity objects in the hierarchy. You might wonder what would happen, however, if the User
entity required a property that was specific to users, but not common to managers or technicians. Imagine that end-users can participate in customer satisfaction surveys, but that managers and technicians do not. The User
entity would require a LastSurveyDate
attribute to handle this requirement, but it wouldn't make sense to have Manager
and Technician
entity objects inherit it.
In this case, you can introduce a new entity object called BaseUser
to act as the base entity in the hierarchy. It would have all of the attributes common to all User
, Manager
, and Technician
entity objects. Then each of the three entities the correspond to concrete rows that appear in the table could have some attributes that are inherited from BaseUser
and some that are specific to its subtype. In the BaseUser
type, so long as you mark the UserRole
attribute as a discriminator attribute, you can just leave the Default Value blank (or some other value that does not occur in the USER_ROLE
column in the table). Because at runtime you'll never be using instances of the BaseUser
entity, it doesn't really matter what its discriminator default value is.
When you use the findByPrimaryKey()
method on an entity definition, it only searches the entity cache for the entity object type on which you call it. In the example above, this means that if you call UserImpl.getDefinitionObject()
to access the entity definition for the User
entity object when you call findByPrimaryKey()
on it, you will only find entities in the cache that happen to be users. Sometimes this is exactly the behavior you want. However, if you want to find an entity object by primary key allowing the possibility that it might be a subtype in an inheritance hierarchy, then you can use the EntityDefImpl
class' findByPKExtended()
method instead. In the User
example described here, this alternative finder method would find an entity object by primary key whether it is a User
, Manager
, or Technician
. You can then use the Java instanceof
operator to test which type you found, and then cast the UserImpl
object to the more specific entity object type in order to work with features specific to that subtype.
When you create an entity-based view object with an entity usage corresponding to a base entity object in an inheritance hierarchy, you can configure the view object to query rows corresponding to multiple different subtypes in the base entity's subtype hierarchy. Each row in the view object will use the appropriate subtype entity object as the entity row part, based on matching the value of the discriminator attribute. See Section 27.6.2, "How To Create a View Object with a Polymorphic Entity Usage" for specific instructions on setting up and using these view objects.