|
Constructing The Desired Entity in a Polymorphic View Object
The BC4J framework allows developers to make rich use of inheritance and it supports easily mapping hierarchies of inherited entity objects to a single table, based on a discriminator attribute indicated declaratively. It also supports the ability for a view object to issue a select over a table and deliver a result set with the appropriately related entity object subtypes underneath, based on automatic discriminator attribute detection. This article shows how you can use a new API introduced in BC4J 9.0.3 called createAndInitRow() to selectively construct a new row in the view object having the desired, underlying entity object instance.
As a simple example, let's say we create a entity object named Emp and a subtype entity object CommissionedEmp that extends from the Employee entity. The that the Emp entity has an attribute EmpType that we've flagged as being the discriminator attribute via the same-named attribute setting in the entity editor. We provide a default value like R for the regular, salaried employees.
When creating the subtyped entity CommissionedEmp we need to override the default value for the inherited discriminator attribute to be a value different than R, so we visit the attributes page and click (New From Table...) to reselect the discriminator column in the table to which the discriminator attribute is mapped. [This is a non-intuitive UI for which Bug# 2788087 has been filed to improve the discoverability of this step]. Once overridden, we can give the EmpType attribute a different default value in the subtype of C. These R and C values correspond to the values stored in the EMP_TYPE column in the EMP table to discriminate what kind of employee is stored in each row.
Next we create a view object AllEmpsView with an entity usage for the Emp entity, and indicate that CommissionedEmp is one of the subtypes we'd like to include (using the Subtypes... button in the view object editor).
To make the example a little more interesting, we add the following method to the EmpImpl.java class:
public String whatKindOfEmpAreYou() { return "Regular Employee"; }
and the overridden implementation of it to the CommissionedEmpImpl.java class:
public String whatKindOfEmpAreYou() { return "Commissioned Employee"; }
Then, we can promote the whatKindOfEmpAreYou() method to the view row level by enabling a view row class for the AllEmpsView view object, and implementing it as follows:
/* File: AllEmpsViewRowImpl.java */ public String whatKindOfEmpAreYou() { /* Delegate to the underlying Employee */ return getEmp().whatKindOfEmpAreYou(); }
Finally, we can publish this whatKindOfEmpAreYou() method on the custom view row interface for this view object (along with the getter and setter methods, if we choose to expose those, too) on the "Client Row Methods" panel in the VO editor.
If we add an instance of AllEmpsView to an application module named TestModule (with a VO instance name of AllEmpsView), then we can write a little test program to illustrate working with the custom method, as well show the trick for creating a row in the AllEmpsView having the appropriate polymorphic Emp subtype we want.
package test; import oracle.jbo.ApplicationModule; import oracle.jbo.NameValuePairs; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; import oracle.jbo.domain.Number; import test.common.AllEmpsViewRow; public class Test { public static void main(String[] args) { String _am = "test.TestModule", _cf = "TestModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(_am,_cf); ViewObject vo = am.findViewObject("AllEmpsView"); vo.executeQuery(); /* * Loop over the results and call the custom row method on each row */ while (vo.hasNext()) { AllEmpsViewRow employee = (AllEmpsViewRow)vo.next(); System.out.println(employee.getEmpno()+" is a " + employee.whatKindOfEmpAreYou()); } /* * Setup a NameValuePairs object to pass attribute values to the * createAndInitRow() method. If the attributes include a discriminator * attribute, then this will have the effect of constructing the * correct entity object implementation. It has to be done with * createAndInitRow(), and cannot be done subsequently via a setAttribute() */ NameValuePairs argsForNewEmp = new NameValuePairs(); argsForNewEmp.setAttribute("EmpType","C"); argsForNewEmp.setAttribute("Empno", new Number(1234)); AllEmpsViewRow newEmployee = (AllEmpsViewRow)vo.createAndInitRow(argsForNewEmp); newEmployee.setAttribute("Ename","Paul"); vo.insertRow(newEmployee); /* * Call the custom row method to make sure we are talking about a * commissioned employee. */ System.out.println(newEmployee.getEmpno()+" is a " + newEmployee.whatKindOfEmpAreYou()); am.getTransaction().commit(); /* * Clean up so I can rerun the test over and over. */ newEmployee.remove(); am.getTransaction().commit(); Configuration.releaseRootApplicationModule(am,true); } }
On my slightly altered EMP table (to which I've added an EMP_TYPE column with R and C values), when I run the above test program I see the following output.
7839 is a Regular Employee 7788 is a Regular Employee 7902 is a Regular Employee 7566 is a Regular Employee 7698 is a Regular Employee 7782 is a Regular Employee 7499 is a Commissioned Employee 7844 is a Commissioned Employee 7934 is a Regular Employee 7521 is a Commissioned Employee 7654 is a Commissioned Employee 7876 is a Regular Employee 7900 is a Regular Employee 7369 is a Regular Employee 1234 is a Commissioned Employee
Notice that the newly created employee reports itself to correctly be a "Commissioned Employee" after creation using the createAndInitRow() API, having initialized the EmpType attribute to "C" as part of that call. Of course the example of the whatKindOfEmpAreYou() method is just a silly example to illustrate that the correct underlying polymorphic entity is being used. In practice, this polymorphism is valuable since the business logic and validation rules for subtyped entities may be different than on their supertypes and though BC4J's easy-to-use mechanism, the correct entity object type (and hence the correct business logic) will get used automatically.
|