Thursday, May 9, 2013

Sharing Data Between VO Instances in ADF BC

Probably you already noticed that by default data is shared between VO instances (or even separate VO's) based on the same EO. Newly inserted row into one instance becomes instantly visible from another VO instance. There is a way to control this behavior in ADF, this post is based on excellent Steve Muench source of information - Using the RowQualifies() Method to Fine Tune View Link Consistency Behavior. As Steve says, by default data is shared and you can turn it off either by calling setAssociationConsistent(false) or globally per AM with jbo.viewlink.consistent=false configuration property:


Idea of this post is to go a bit deeper and to show how you control data sharing between VO instances programmatically. For example how to delay new row sharing between VO instances until this row is commited.

Sample application - VOSynchEOCacheApp.zip, contains one EO and one VO for Employees:


There are two VO instances defined in AM out of Employees:


Two fragments are implemented in ADF Controller. First displays data from EmployeesEditView and allows to insert new row. While second displays a table with data from EmployeesListView:


We create now new row in the first fragment:


Navigate to the second fragment with the table. Keep in mind - new row is not commited yet, it is just inserted into EmployeesEditView VO instance. However, as per default behavior data is shared between VO's and this new row is appearing in the list from EmployeesListView VO instance:


In order to control when newly inserted row is shared between VO instances, we can override rowQualifies method from VO Implementation as per Steve Muench suggestion above. We have access to the row and can check row status. In this example row is qualified to be added into rowset if its status is unmodified:


Test now again the same scenario - try to insert new row, but don't commit yet:


Navigate to the second fragment with list data - new row is not added yet as expected:


Go back and save new row:


You can check it will be added to the rowset now and displayed in the list from second VO instance:

17 comments:

  1. Concept explained with very nice example.

    ReplyDelete
  2. Do you know how I can share the VO data in application level? I add a non-LOV VO to a shared application module and find that ADF always get the data from database when I start a new session.

    ReplyDelete
  3. Hi,

    I will need to test it for non-LOV VO's

    Thanks,
    Andrejus

    ReplyDelete
  4. Thanks for your help in advance.
    Maybe I can elaborate the case in more detail.
    1. I add a non-LOV Entity-based VO to a shared application module (ShareAM)
    2. I add the shared application module (ShareAM) to a non-shared application module (AM)
    3. I try to call this.getSharedAM().getVO().executeQuery() to get the data in a non-shared application module and return the result to front end JPX page.

    In the first run, it takes around 2 seconds. I close the browser and run again.
    I expect the time will be greatly reduced because the data is retrieved from memory instead of database. I find that ADF will fire the SQL to database again in ADF debug console.

    I think the best practice is to add all LOV VO and STATIC data non-LOV VO to shared application module so that both memory consumption and performance can be improved. (Correct me if I am wrong)

    Thanks again.

    ReplyDelete
  5. Hi,

    I think you are mistaken. ADF BC Shared data works *only* for LOV's, you can't access regular VO from Bindings through Shared AM. Only LOV: http://andrejusb.blogspot.com/2011/07/adf-code-corner-article-087-how-to.html

    You can use different solution with Coherence to cache VO's: http://andrejusb.blogspot.com/2013/02/oracle-coherence-integration-adf-bc.html

    Andrejus

    ReplyDelete
  6. Thanks for your clarification.

    According to your example, EO data will be shared to all the associated VO.

    If I insert a row in the entity object EO through a view object VO1, will all of the following 3 VOs be updated? Or only the VO using the EO as primary entity will be updated?
    (Both VO1 and VO2 uses the EO as the primary entity object.)

    1. The primary entity object of VO2 is EO and is updatable.
    2. The secondary entity object of VO3 is EO and is updatable.
    3. The secondary entity object of VO4 is EO and is not updatable.

    Moreover, I find some strange behaviour of view object.
    I create 2 different VOs referring to the same EO, say for example VO1 and VO2. The EO has 3 fields, say ID, description, case number. I create the following 2 criteria in VO1.

    1. Criteria 1 : description contains ? (bind variable :pDesc)
    2. Criteria 2 : case number contains ? (bind variable :pCase)

    VO2 does not have any criteria. The table the EO referring to have 6 records.
    ID : 1, Description : Default, Case Number : Case 1
    ID : 2, Description : ---, Case Number : Case 2
    ID : 3, Description : Default, Case Number : Unknown
    ID : 4, Description : ---, Case Number : Unknown
    ID : 5, Description : Default, Case Number : Case 5
    ID : 6, Description : ---, Case Number : Case 6

    My program logic is as follow
    1. Add the following 2 rows in VO1
    a. ID : 7, Description : Default, Case Number : Case 7
    b. ID : 8, Description : Unknown, Case Number : Unknown
    2. Add the following 2 rows in VO2
    a. ID : 9, Description : Default, Case Number : Case 9
    b. ID : 10, Description : Unknown, Case Number : Unknown
    3. Set the query mode of VO1 to “SCAN_DATABASE_TABLES”
    4. Apply Criteria1 with Criteria Mode QUERY and set the pDesc variable to “Default”
    5. Execute Query of VO1
    6. Add the following 2 rows in VO1
    a. ID : 11, Description : Default, Case Number : Case 11
    b. ID : 12, Description : Unknown, Case Number : Unknown
    7. Add the following 2 rows in VO2
    a. ID : 13, Description : Default, Case Number : Case 13
    b. ID : 14, Description : Unknown, Case Number : Unknown
    8. Remove apply criteria of VO1
    9. Set query mode of VO1 to “SCAN_VIEW_ROWS”
    10. Execute query of VO1
    In the first run, the execution result of step 5 shows ID 1, 3 and 5 only. I think the rows with ID 7-10 are not shown because the query mode is SCAN_DATABASE_TABLES and the criteria mode is QUERY. System will only consider the rows in database, but not the unposted record. Is it correct?

    The execution result of step 10 shows ID 1, 3, 5, 11 and 12 only.
    Do you have any suggestion why rows added from VO2 cannot be retrieved in the execution result of step 10 even the criteria is removed?

    In the second run, I change the criteria mode of step 4 to CACHE, the execution result of step 5 and 10 will be ID 1,3,5, 7, 9 and ID 1,3,5, 7-14. The result is the same as I expected.

    I have read some books about Oracle ADF and search information about query mode and criteria mode, but I cannot find a depth explanation on query and criteria mode and how it affect the sharing of EO data.

    Thanks for your help in advance.

    ReplyDelete
  7. I will need to check this with sample app. I will get back.

    Andrejus

    ReplyDelete
  8. Besides VO update problem, I find that some developers create a new EO to represent the same table, say for example, multiple EO with different packages to represent the same table in different projects of the same system. Is it a bad practice ? I guess only one EO object should be created for one table and the model project consisting all the tables of the system should be created as a shared library and used by all other projects. Developers should create VO based on their need. Is it correct?

    ReplyDelete
  9. You are correct, I agree with you 100%.

    Andrejus

    ReplyDelete
  10. Thanks for your prompt reply.

    Since the developers have already created multiple EOs to represent a table, any other bad effect besides the maintainability ? say poor performance ? or memory consumption ?

    Moreover, some developers use VO just like SQL query tools. Instead of creating VO links and manipulate the VOs, they create a VO every time they want to find a value and so many VOs will be created in one application module AM.

    I guess the size of application module AM will be very large, so it will use up much memory and the time of passivation will take longer. Am I correct? and any other consequence ? (over 50 VO associated to one AM). Any other solution? say close the rowset after use?

    ReplyDelete
  11. In this case maintainability is the main concern - when duplicating EO's. It may grow into huge effort of fixing everything in the future.

    Instead of having many VO's, is better to reuse VO's with different View Criterias.

    I think around 200 VO's is still OK to have in AM, if you set LazyLoading=true property in AM tuning.

    Andrejus

    ReplyDelete
  12. For my question posted on 25 May, what's your expected behavior ? Do you think it is a bug of version 11.1.1.5 ?

    ReplyDelete
  13. I didnt had time really to test it yet, but I promise to look into your question by the end of this week.

    Andrejus

    ReplyDelete
  14. Dear Andrejus,

    Thanks for your help in advance.

    In additional, I have some conceptual questions on view object. Do you have any idea?

    Question 1 :
    A view object VO has 3 entities (EO1, EO2 and EO3) joined together and the relationship of these 3 entities are 1-to-1 and 1-to-many.
    If I insert a record into EO1 through the VO1 which is default view object of EO1.
    I run executeQuery to get the result of VO.
    I find that the EO1 records in database will be duplicated many times because the relationship between EO2 and EO3 is 1-to-many. But the newly created record (not yet saved) of EO1 will be displayed only once. Is it correct?

    Question 2 :
    VO is the default view object of EO. EO is primary entity of VO1 and secondary entity of VO2.
    I add a EO record through VO.
    I find that the executeQuery result of VO2 does not show the newly created record of EO. Is it correct?

    Regards,
    Tony

    ReplyDelete
  15. I think you are mistaken. ADF BC Shared data works *only* for LOV's, you can't access regular VO from Bindings through Shared AM.

    ReplyDelete
  16. Yes, Shared AM works only for LOV's

    Andrejus

    ReplyDelete
  17. Hi Andrejus

    In reference to the use of Shared AM for LOVs I have a question.

    We have a shared AM to implement LOVs with tipical values of countries, gender, etc but now we have the need to access a VO of this shared AM to obtain a value.

    With view accessors I think there is no way and methods like getRootApplicationModule.findApplicationModule or createRootApplicationModule doesn't work or is a bad idea I think.

    Another option I think that it is to put the same AM as a nested AM also, but I don't know if it will be a good idea to have the same AM as shared and nested simultaneously.

    Any suggestion? What is the way in these cases?

    Thanks in advance.

    ReplyDelete