Embracing ExtendsBy Steve Muench
Simplifying development via inheritance
Many enterprise applications include at least one table that stores different logical kinds of business information in its rows. For example, in the EMP table you’ll work with in this column’s application, the rows represent employees with different job types. Although all job types share some attributes (such as hire date and salary), other attributes and logic are specific to certain types of jobs. This column illustrates how to use inheritance in the entity object and view object layers to simplify building applications in which the business logic and attribute list vary by type.
To begin, download the starter workspace at otn.oracle.com/oramag/oracle/10-mar/o20frame.zip and ensure that you’re using the studio edition of the Oracle JDeveloper 220.127.116.11 production release, available as a free download on Oracle Technology Network (OTN) at otn.oracle.com/software/products/jdev. Start by extracting the contents of the o20frame.zip file and opening the FrameworksMarApr2010.jws workspace in Oracle JDeveloper. The Model project in the workspace defines a base set of Oracle Application Development Framework (Oracle ADF) 11g components for working with the data in the EMP table in the SCOTT schema.
Before proceeding, adjust the properties of the connection named scott in the Application Resources zone of the Application Navigator until you can successfully test a connection to a SCOTT schema. If you need to create the tables in SCOTT, use the provided CreateDeptEmpTables.sql script.
Defining Entity Subtypes
Entity object subtypes inherit all the attributes and logic that belong to a base entity type. A subtype entity can then override the base entity’s values to define business logic (such as validations or optional custom code) or to add attributes that are specific to only that subtype. To use entity object subtypes, you must provide Oracle ADF with the information it needs in order to identify the type of a given row. To do this, you nominate one attribute in the base entity object to be the discriminator attribute. Then, when defining the subtypes of that base entity, you override the discriminator attribute by assigning a unique value for that attribute for every entity subtype. At runtime, Oracle ADF uses the discriminator attribute’s value to automatically select the appropriate entity type.
In this exercise, you’ll use the EMP table’s Emp entity as the base type for multiple more-specific types of employees (clerks and salespeople). Double-click the Emp entity object in the Application Navigator to open the editor. On the Attributes page, select the Job attribute in the table. If the Property Inspector is not visible, choose View -> Property Inspector from the main menu. In the Property Inspector, expand the Type section and check the Discriminator box. This enables the Subtype Value field, which you should leave blank for this base entity. Right-click Emp in the Application Navigator, and choose New Extended Object . When the Create Extended Object dialog box appears, click Tab to retain the default package name, oramag.model; change the Name field’s value to Clerk; and then click OK . In the editor for the new Clerk entity object, on the General page, note that Clerk extends Emp . On the Attributes page, select the Job attribute. Click the Override button to override the Job attribute’s subtype value so that you can provide a clerk-specific value. In the Property Inspector, note that the Discriminator box is already checked and enter CLERK in the Job attribute’s Subtype Value field. This value represents a clerk-type row in the EMP table. Note that the value is case-sensitive and must correspond to a value of the JOB column in the EMP table, so type it in all uppercase letters. Repeat the process to create another subtype of Emp to represent a salesperson. Right-click Emp in the Application Navigator, and choose New Extended Object . Keep the default package name, enter Salesperson as the object name, and then click OK . In the editor for the new Salesperson entity object, on the Attributes page, select the Job attribute. As you did for the Clerk subtype, click the Override button to enable overriding of properties inherited from the Emp supertype object. In the Property Inspector, enter SALESMAN in the Subtype Value field. This value represents a salesperson-type row in the EMP table.
On a subtype entity, you can add attributes that are not relevant to the supertype. For example, a commission attribute may apply only to salespeople. On the Attributes page of the entity editor for Emp , note that Emp does not have a Comm attribute. To add the Comm attribute to the Salesperson entity only, go back to its editor and click the Add from Table button on the Attributes page. When the Create New Entity Attributes for Columns dialog box appears, select the COMM column in the Available list, click > to shuttle it into the Selected list, and click OK.
Next you’ll define a simple validation rule on both the Clerk and Salesperson subtypes. At runtime, Oracle ADF will automatically handle the validation, based on the appropriate entity subtype, as you’ll observe later. In the entity editor for Clerk , on the Business Rules page, select the Ename attribute in the tree. Note that the toolbar icon for creating a new validator (the plus sign) is disabled. To add an attribute-level validation rule on a subtype’s attribute, you must first override the attribute. On the Attributes page, select the Ename attribute in the table and click the Override button. Then, back on the Business Rules page, ensure that Ename is still selected and click the plus-sign button, which is now enabled. When the Add Validation Rule for: Ename dialog box appears, select Compare for Rule Type , set Operator to NotEquals , and enter ABC in the Enter Literal Value text box. Click the Failure Handling tab, and in the Message Text box, enter the validation failure message A clerk cannot be named ABC , and click OK . Repeat the same steps to add a simple compare validator to the Ename attribute of the Salesperson entity object that prevents a salesperson from having the name XYZ. Remember that you must override the Ename attribute before you can add the attribute-level validator in the Salesperson entity. Enter A salesperson cannot be named XYZ as the validation failure message.
Using a Polymorphic View Object
When a view object retrieves rows that have different values for its entity usage’s discriminator attribute, it is known as polymorphic —a word of Greek origin meaning “having many forms”—because the results represent entities of multiple types and the entity usage type changes at runtime, based on the discriminator value. To configure EmpView to be a polymorphic view object that handles two distinct subtypes ( Clerk and Salesperson ), start by double-clicking the EmpView view object in the Application Navigator to open it in the editor. On the Entity Objects page, note that the view object is based on the Emp entity, which is the supertype of both the Clerk and Salesperson entity objects. Select this Emp entity usage in the Selected list, and click the Subtypes button. In the Select Subtypes dialog box, select Clerk in the Available list and click > to shuttle it into the Selected list. Repeat to add Salesperson to the Selected list, and then click OK . Next, on the Query page, scroll down to see the View Criteria section. Double-click the existing ClerksOrSalespeople view criteria to inspect its filter predicates. In the Edit View Criteria dialog box, you can see that the view criteria identifies rows whose Job attribute equals CLERK or SALESMAN. Click Cancel to dismiss the dialog box without making any changes. Next you’ll apply this view criteria to the instance of the EmpView view object in the HRModule application module so that it retrieves only employees of these two types. Double-click HRModule in the Application Navigator to open it in the editor. On the Data Model page, select the EmpView1 view object instance in the Data Model tree and click the Edit button to edit its instance-level properties. When the Edit View Instance: EmpView1 dialog box appears, on the View Criteria page, select the ClerksOrSalespeople view criteria in the Available list, click > to shuttle it into the Selected list, and then click OK . This declaratively configures the ClerksOrSalespeople view criteria to be applied at runtime to the EmpView1 instance.
To test your polymorphic view object, right-click HRModule in the Application Navigator and choose Run . When the Oracle Business Component Browser appears, double-click the EmpView1 view instance to browse through its query results. The first row that appears is ADAMS—a clerk. Change the Ename field to the value ABC and tab out of the field to verify that the clerk-specific validation rule is applied.
Choose the Database -> Rollback menu option to roll back your edits. Next, click the > toolbar button to navigate to the second row, ALLEN—a salesperson. Change the value of the Ename field to ABC , and tab out of the field to observe that the previous clerk-specific error is not raised for a salesperson. In contrast, if you change the Ename value to XYZ and leave the field, you’ll see the salesperson-specific exception. Again, roll back your changes. You’ve verified that the queried rows exhibit subtype-specific behavior at runtime, depending on the value of their Job discriminator attribute.
Under the covers, for each row fetched from the database, Oracle ADF compares the value of the Job discriminator attribute with the subtype values for the available Emp entity object subtypes and creates an appropriate entity instance to match. To observe a behavior that the Clerk and Salesperson entities inherit from their Emp supertype, try to modify the Hiredate value in both kinds of rows to a future value. In both cases, you’ll get a validation error: “The hiredate must be before today.” If you return to the editor for the Emp entity object, you’ll be able to see on the Business Rules page that an expression validator on Hiredate enforces the condition that newValue < adf.currentDate.
Back in the Oracle Business Component Browser, if you try to insert a row by clicking the plus sign, you’ll be prompted to enter a value for the new row’s Job discriminator attribute. Enter either CLERK or SALESMAN to create a row with the appropriate entity subtype under the covers, and then verify that the correct entity type has been used by trying to enter either ABC or XYZ for the value of the Ename field in the new row.
Defining View Object Subtypes
As you navigate through the rows in the Oracle Business Component Browser, note that the Comm attribute specific to the Salesperson entity doesn’t appear in the user interface. By default, each row in a view object with polymorphic entity usages has the same “shape,” which corresponds to that of the supertype Emp entity usage. Different entity object types are used on your behalf behind the scenes, but the client layer working with the view rows sees only the set of attributes that are common to all rows. If you want the attributes available for client display and/or data entry to vary automatically by discriminator type in the client layer, you’ll need to choose that behavior explicitly by exploiting a feature called view row polymorphism . The steps parallel what you did for the earlier entity object exercise: you define a discriminator attribute at the view object level, define new view objects that extend a base component and override the discriminator attribute to assign a subtype-specific value to it, and configure the application module to know which view row subtypes you want the client to see.
First you’ll mark the Job attribute as a discriminator attribute at the view object level for EmpView . In EmpView ’s editor, on the Attributes page, select the Job attribute in the table. In the Property Inspector’s Type section, check the Discriminator box, leaving the View button selected and Subtype Value blank. Next you’ll create two subtype view objects, one for clerks and one for salespeople. Start by right-clicking the EmpView view object in the Application Navigator and choosing New Extended Object . When the Create Extended Object dialog box appears, keep the default package name, oramag.model; enter ClerkView as the View Object name; and click OK . On the ClerkView editor’s Attributes page, select the Job attribute. Click the Override button above the attributes table to enable the Job attribute to be customized in the new subtype view. Select the Job attribute again. If you don’t see the Subtype Value field in the Property Inspector, close the Property Inspector and choose View -> Property Inspector to remedy the problem. In the Property Inspector, enter CLERK as the Subtype Value for the Job attribute. Finally, you’ll define a default WHERE clause that limits the rows queried by this view object to ones whose Job attribute equals CLERK . On the Query page, click the pencil/edit icon to the right of the Query section. When the Edit Query: ClerkView dialog box appears, go to the Query page, enter JOB=’CLERK’ in the Where text box in the Query Clauses section, and click OK.
Now define a second subtype of EmpView by right-clicking the EmpView view object in the Application Navigator and choosing New Extended Object. Keep the default package name, enter SalespersonView as the View Object name, and click OK . On the Attributes page, click the small downward-pointing arrow on the right side of the green plus-sign icon and select Add Attribute from Entity from the menu. In the Attributes dialog box, note that the Comm attribute does not appear in the list. The reason is that SalespersonView has inherited the Emp entity usage from the EmpView supertype view object, which has no Comm attribute. To access the salesperson-specific attributes, you must replace this inherited entity usage with the Salesperson subtype of Emp . Start by clicking Cancel to dismiss the Attributes dialog box. Then, on the Entity Objects page, select the Emp: extended entry in the Selected list. This is the entity usage you want to replace. In the Available list, select the Salesperson subtype entity object and then click the > button to replace the Emp entity usage with this more specific Emp subtype. When asked to confirm that you want to override the existing entity usage, click Yes . The Selected list then updates to show Emp(Salesperson): overridden. Now, back on the Attributes page, choose Add Attribute from Entity again. This time Comm appears in alphabetical order at the top of the Available list, so select it and click > to include this salesperson-specific attribute in the view object’s Selected list and then click OK . Next, select the Job attribute in the table, and in the Property Inspector, enter SALESMAN for Subtype Value . As you did before, you’ll define a default WHERE clause that limits the rows queried by this view object to ones whose Job attribute equals SALESMAN. On the Query page, click the pencil/edit icon to the right of the Query section. When the Edit Query: SalespersonView dialog box appears, enter JOB=’SALESMAN’ in the Where text box in the Query page’s Query Clauses section and click OK.
Using Polymorphic View Rows
The last step is to configure which view object subtypes the HRModule application module should allow the client layer to “see.” On the Data Model page of the application module editor for HRModule , click the Subtypes button. Select ClerkView in the Available list, and click > to shuttle it into the Selected list. Repeat to add SalespersonView , and then click OK . To test your work, right-click HRModule and choose Run . This time as you scroll back and forth among the clerks and salespeople, note that each salesperson row displays the additional Comm attribute, illustrating that the client layer can now address the unique structure of each type of employee row you’ve configured.
The concepts you’ve learned in this column are key to leveraging the productivity of inheritance in your own applications. For more information, see section 38.7, “Using Inheritance in Your Business Domain Layer,” and section 39.6, “Using View Objects to Work with Multiple Row Types,” in Oracle Fusion Middleware Fusion Developer’s Guide for Oracle Application Development Framework 11g.
Steve Muench is a consulting product manager for Oracle JDeveloper. Since 1990 he has developed and supported Oracle tools and XML technologies and continues to evangelize them. Muench coauthored Oracle ADF Developer’s Guide for Forms/4GL Developers (Oracle, 2006), wrote Building Oracle XML Applications (O’Reilly Media, 2000), and shares tips and tricks on Oracle Technology Network and in his Dive into ADF blog.