Sunday, April 26, 2009

Inheritance Feature in Oracle ADF BC - Part 1

When building large applications with Oracle ADF, you can consider to use inheritance in Oracle ADF BC layer. This can reduce Model complexity in terms of size and relationship. I will split inheritance topic into two parts - today I will describe inheritance with ADF BC Entities, and in my next post with ADF BC View Objects. I recommend to read Oracle ADF Developer guide related topic - 34.7 Using Inheritance in Your Business Domain Layer. In ADF BC you can implement inheritance layer for Entities and create View Objects with polymorphic Entity usages. This means Oracle ADF supports abstraction on both - components and data.

You can download sample application - ADFBCInheritance.zip where I have implemented inheritance between Entities and created View Object based on polymorphic Entity usage.

I have generated Countries Entity in order to use it as Base Entity in an inheritance hierarchy:


Four Entities - CountriesAfrica, CountriesAmerica, CountriesAsia and CountriesEurope are created as Subtype Entities from Countries Entity in inheritance hierarchy.

While creating Base Entity, I have declared discriminator attribute - RegionId. This attribute will allow us to perform polymorphic queries:


I have left discriminator value as empty by default, this means if View Object will not include any subtype, empty rowset will be returned:


Subtype Entities are created by extending Base Entity and overriding its attributes:


In Subtype Entity we need to set corresponding value for overriden discriminator attribute - RegionId:


For example, in CountriesAfrica Entity I have set this attribute discriminator value to 4:


In CountriesAmerica, CountriesAsia and CountriesEurope it is set to 3, 2 and 1 accordingly. Of course, there is no point to create inheritance hierarchy just for fun, in most of the cases its useful to have it when implementing different validation rules for example. In my sample, I have implemented different validation rules for transient Profit attribute. In CountriesAfrica Entity validation rule is implemented as range between 100 and 1000:


In CountriesAsia Entity, with discriminator value set to 2:


Same type validation rule is implemented with different constraints set to 300 and 1200:


Finally, when View Object is created based on Base Entity - Countries:


You need to include Subtypes of Base Entity you wish to use in your View Object. If you will not include any of Subtypes, View Object will return empty rowset (remember we have set empty default value for discriminator in Base Entity). In my sample, View Object will work with all four Subtypes, so I'm declaring this:


Even my View Object is based on all four Subtypes, I will want to limit rows to expected Enity Subtypes. We can do this, simply by adding discriminator into WHERE clause and declaring Bind Variable, however I have noticed if to implement it in such way - View Accessors from any Detail View Objects will not work. However, everything works fine, if to declare and use View Criteria:


This View Criteria is really simple, it just includes discriminator attribute filtering:


At this point, inheritance in ADF BC Entities is finished. Let's add some more complexity and define association relationship. I will define it between Countries entity with discriminator attribute and Locations:


Based on this association its possible to define Master-Detail relationship through View Link:


Its cool that, you can use Detail View Object - Locations in relationship with Master View Object - Countries just in standard way, even we know that Countries is based on Entity Subtypes. In order to test and prove this, I have created transient attribute in Locations View Object - CountryName. This attribute gets value through accessor from its Master - Countries:


We have following Master-Detail relationship exposed to View-Controller layer:


In View-Controller I have created navigation flow to pass discriminator value into Bounded ADF Task Flow through a call:


In Bounded ADF Task Flow I'm doing ExecuteWithParams in order to set Bind Variable value and opening main page with already filtered values by provided discriminator:


Rowset is pre-filtered by activating View Criteria on page load. View Criteria is activated by referencing Query Model, read more in my previous post - ADF Query Component Usage with ADF Panel Stretch Layout.

On runtime, select any of regions, this will set discriminator value:


Let's select Asia and set Profit value to 350. Remember, CountriesAsia Entity implements validation 300 - 1200, so no error is shown and value is accepted:


Now let's select Europe and set Profit value to the same 350. Remember, CountriesEurope Entity implements validation 400 - 1300, validation error will be shown:


Spanish Summary:

En este post, Andrejus nos entrega un ejemplo de como aplicar mecanismos de herencia entre entidades de Business Components. En una entrega posterior nos mostrarĂ¡ la manera de implementar herencia entre View Objects.

16 comments:

HusainD said...

Great Article!!
I was trying to experiment with Discriminator myself but this article explains things in a neat way.
Thanks for posting this.

Andrejus Baranovskis said...

Thanks ;)

Andrejus

fakintoye said...

Hi,

The tables in my application share about 10 common columns, I'd like to use inheritance to code some common business logic for these columns and have the tables inherit from this. I've tried using this process but a can't do it without adding an extra column for the discriminator, which I don't want to have to do. Is there a work around that you are aware of?

Thanks

tullio0106 said...

Too simple.
In real life is not possible to know "a priori" every implementation and implemetations should be applied in different customizations.
The "slave" entity should know it's master but the masted can't know every slave.
It should be possible to add columns/relationships in the master entity and "inerhit" them in slaves.
Could You apply the solutionin such real situation ?
Tks

Andrejus Baranovskis said...

Yes, its what is described in this blog.

tullio0106 said...

Sorry but I don't understand how can You manage the situation I described.
I don't know "a priori" every subtype and I don't have any discriminatory column.
Subtypes are not in different tables but in the same table : it's just a different, customized implementation of the same table.
I also don't want keep them in the same project because I want to deploy in different situations (sometimes I want customization and sometime I don't).
Can I still use inheritance ?
Tks

Andrejus Baranovskis said...

Hi,

If you want to change inheritance structure on runtime, its not possible. In ADF BC inheritance is used mainly during development, in order to design your Model more effectively.

Regards,
Andrejus

Abhishek Kumar said...

Hi Andrejus,
I have create a polymorphic View Hierarchy EmpVO,ClerkVO,SalesPersonVO based on discriminator colums JOB_TYPE. SalesPerson has additional column Comission. In App Module , I have EmpVO1 and subtypes ClerkVO and SalePersonVO declared.
This is as per Steve http://www.oracle.com/technology/oramag/oracle/10-mar/o20frame.html
Now when I run the App Module using Business Component Browser and Navigate through EmpVO1, it brings up the screen properly, meaning when SalesPerson record comes commission is displayed.
Now my question is how the same display functionality could be achieved on the web page

Eduardo said...

After reading you excellent article and the ADF guide, still have no solution for my inheritance problem.
Your solution and the guide’s are both based on having a single table containing all attributes. No problem with that at all. But “my”schema implements one table with attributes common to all types, and a table for each subtype.
In JPA 2 there is a solution for this scenario, but couldn’t find one using ADF Entity Objects.
I’m from Brasil and not so good in English, so maybe I’m not very clear in my explanation.

Thanks in advance.

Andrejus Baranovskis said...

May be you can join these tables using database View, only that it will be not updatable.

Andrejus

Eduardo said...

In fact we’ve already done that. But I’d like to be sure this is a EO limitation and not just mine.

Thanks for spending your time.

Andrejus Baranovskis said...

I would not say its limitation, its just very clear design - 1 EO = 1 DB Entity :)

Andrejus

Arav said...

Hi Andrejus,

This blog is very helpful. We have a scenario where we do not have control over the base Entity Object code. For eg, a ProductEO (on which we dont have control of) consumed in our VO SupportProductsVO. Now, we would like to implement a specific validation method for entity records created specifically for ProductEO. Is there a way we can extend ProductEO as SupportProductEO and code this validation on SupportProductEO and further use this extended EO in the SupportProductsVO. Note, we can not introduce a descriminator value in ProductEO.

BradW said...

Hi Andrejus.

I have been tinkering with this today and was wondering whether we could have different tables on the subtype? I've tried doing this with the entities and they seem to work fine. However, as soon as I try to create a view object on top of the entity with a different table, jdeveloper complains.

My use case is for code tables with the same definition. I'd rather not have to recreate all the same business rules etc for all of the code table entities as there are a lot of them.

I'm wondering if it is just a jdeveloper limitation or an actual runtime limitation.

Any thoughts on the topic?

Thanks,


BradW

Andrejus Baranovskis said...

Hi,

I think you can use it for subtypes coming from the same physical DB table only. May be you could create DB view, joining multiple tables and then use ADF inheritance with discriminator for that DB view.

Regards,
Andrejus

BradW said...

Thanks for the feedback Andrejus. I think this would then just push all the logic back into the database.

If I did do this, I might be able to just do a SP call to do the DML operations using dynamic SQL. I don't normally like dynamic SQL, but this would just be for some admin tables.

I'll tinker and get back to you later.