Introduction
In an ADF application a view accessor is considered as an approach to access a view object from another view object or entity object. The view accessors are mostly used to provide result sets for the lists of values. In this post I am going to show how actually the view accessors work, to reveal what's going on behind the scene, to clarify how many internal view object instances are created by the framework and how many row sets are used.
Internal View Object instances
Let's start with easy things. I've got a view object definition VForexTrade representing forex operations on the currency market. It has an attribute Buycurrencyid with a list of values LOV_Buycurrencyid:
It also has an attribute Sellcurrencyid with a list of values LOV_Sellcurrencyid:
There is a view accessor VCurrency1 which is based on VCurrency view definition.
So there are two LOVs referring to the same view accessor.
And there is an application module TestAppModule with an instance of VForexTrade view object definition. The name of this instance is VTodTrade and it represents today trade operations:
This simple structure can be represented in the following table:
Since VCurrency1 view accessor doesn't use any shared VO instance, the framework, at run time, is going to create an internal view object instance basing on VCurrency view definition. In this case its name will be like _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1. Note that the name is constructed as a concatenation of view definition names. The VO instance name doesn't matter.
Since the RowLevelBinds property of the view accessor is set to true, the framework, at run time, is going to create a separate non-default row set for each row of the VTodTrade instance. So, the internal view object instance will have a number of row sets like _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1_0, _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1_1,
_LOCAL_VIEW_USAGE_VForexTrade_VCurrency1_2, ...
So, each row in the VTodTrade refers to its own row set providing a list of currencies. But since this list of currencies doesn't really depend on a VTodTrade's row, and VCurrency VO doesn't have any bind variables, all these row sets are backed up by the same query collection. By the first one _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1_0. That means that the VCurrency's SQL query will be executed only once.
Let's change this structure a little bit defining a separate view accessor VCurrency2 for the LOV_Sellcurrencyid list of values.
In this case, at run time, the framework will create another internal VO instance _LOCAL_VIEW_USAGE_VForexTrade_VCurrency2 for the VCurrency2 view accessor. This instance is going to have its own bunch of row sets _LOCAL_VIEW_USAGE_VForexTrade_VCurrency2_0,
_LOCAL_VIEW_USAGE_VForexTrade_VCurrency2_1,
_LOCAL_VIEW_USAGE_VForexTrade_VCurrency2_2, ... .
All of them are going to be backed up the _LOCAL_VIEW_USAGE_VForexTrade_VCurrency2_0 query collection.
So, here we'll have two executions of the VCurrency's SQL query for both query collections.
Let's switch off the RowLevelBinds property of our view accessors:
In this case the framework will also create two internal VO instances _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1 and _LOCAL_VIEW_USAGE_VForexTrade_VCurrency2. But each of them will have only one default row set to be shared across all rows of the VTodTrade view object instance. However, the VCurrency's SQL query will be executed twice, since each internal VO is going to be backed up by its own query collection.
Alrighty, let's add one more instance of the VForexTrade to the application module.
Our BC model is represented in the following table:
The interesting thing is that nothing is going to be changed, actually. The framework will create the same two internal view object instances with default row sets for each VA definition, and they will be shared by both VTodTrade and VForwardTrade VO instances.
Shared View Object instances
And now let's redefine our view accessors and get them referring to a shared VO instance.
Since the view accessors refers to a real VO instance from some shared AM, the framework won't create any additional internal view objects. Instead of that, it will create a non-default row set within VCurrency shared VO for each view accessor usage with RowLevelBind set to true. And again, there is no any bind variables in the VCurrency and its result set doesn't depend on a row from which it is used. So, all these created row sets are going to be backed up by a single query collection instance. The behavior is pretty similar for both session and application shared AMs. The difference is that in the case of the session shared AM there will be one VCurrency instance per user session, and in the case of the application shared AM the framework will create only one single instance of VCurreny to be shared across the entire application. Besides that, the query collection of a session shared VO can be destroyed due to the AM recycling process or because of the garbage collection process. That will not happen with an application shared VO instance. There is a different mechanism to clean up query collections of the application shared view objects.
If we set the RowLevelBind property to false
The framework will create a single default row set for the VCurrency view object, and this row set is
going to be shared for each view accessor usage across all rows.
That's it!
God Save Ukraine!
In an ADF application a view accessor is considered as an approach to access a view object from another view object or entity object. The view accessors are mostly used to provide result sets for the lists of values. In this post I am going to show how actually the view accessors work, to reveal what's going on behind the scene, to clarify how many internal view object instances are created by the framework and how many row sets are used.
Internal View Object instances
Let's start with easy things. I've got a view object definition VForexTrade representing forex operations on the currency market. It has an attribute Buycurrencyid with a list of values LOV_Buycurrencyid:
There is a view accessor VCurrency1 which is based on VCurrency view definition.
So there are two LOVs referring to the same view accessor.
And there is an application module TestAppModule with an instance of VForexTrade view object definition. The name of this instance is VTodTrade and it represents today trade operations:
VO instance | LOV attribute | VA | VA View definition | Shared VO instance | Row Level Binds |
VTodTrade | Buycurrencyid | VCurrency1 | VCurrency | - | true |
VTodTrade | Sellcurrencyid | VCurrency1 | VCurrency | - | true |
Since VCurrency1 view accessor doesn't use any shared VO instance, the framework, at run time, is going to create an internal view object instance basing on VCurrency view definition. In this case its name will be like _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1. Note that the name is constructed as a concatenation of view definition names. The VO instance name doesn't matter.
Since the RowLevelBinds property of the view accessor is set to true, the framework, at run time, is going to create a separate non-default row set for each row of the VTodTrade instance. So, the internal view object instance will have a number of row sets like _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1_0, _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1_1,
_LOCAL_VIEW_USAGE_VForexTrade_VCurrency1_2, ...
So, each row in the VTodTrade refers to its own row set providing a list of currencies. But since this list of currencies doesn't really depend on a VTodTrade's row, and VCurrency VO doesn't have any bind variables, all these row sets are backed up by the same query collection. By the first one _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1_0. That means that the VCurrency's SQL query will be executed only once.
Let's change this structure a little bit defining a separate view accessor VCurrency2 for the LOV_Sellcurrencyid list of values.
VO instance | LOV attribute | VA | VA View definition | Shared VO instance | Row Level Binds |
VTodTrade | Buycurrencyid | VCurrency1 | VCurrency | - | true |
VTodTrade | Sellcurrencyid | VCurrency2 | VCurrency | - | true |
In this case, at run time, the framework will create another internal VO instance _LOCAL_VIEW_USAGE_VForexTrade_VCurrency2 for the VCurrency2 view accessor. This instance is going to have its own bunch of row sets _LOCAL_VIEW_USAGE_VForexTrade_VCurrency2_0,
_LOCAL_VIEW_USAGE_VForexTrade_VCurrency2_1,
_LOCAL_VIEW_USAGE_VForexTrade_VCurrency2_2, ... .
All of them are going to be backed up the _LOCAL_VIEW_USAGE_VForexTrade_VCurrency2_0 query collection.
So, here we'll have two executions of the VCurrency's SQL query for both query collections.
Let's switch off the RowLevelBinds property of our view accessors:
VO instance | LOV attribute | VA | VA View definition | Shared VO instance | Row Level Binds |
VTodTrade | Buycurrencyid | VCurrency1 | VCurrency | - | false |
VTodTrade | Sellcurrencyid | VCurrency2 | VCurrency | - | false |
In this case the framework will also create two internal VO instances _LOCAL_VIEW_USAGE_VForexTrade_VCurrency1 and _LOCAL_VIEW_USAGE_VForexTrade_VCurrency2. But each of them will have only one default row set to be shared across all rows of the VTodTrade view object instance. However, the VCurrency's SQL query will be executed twice, since each internal VO is going to be backed up by its own query collection.
Alrighty, let's add one more instance of the VForexTrade to the application module.
Our BC model is represented in the following table:
VO instance | LOV attribute | VA | VA View definition | Shared VO instance | Row Level Binds |
VTodTrade | Buycurrencyid | VCurrency1 | VCurrency | - | false |
VTodTrade | Sellcurrencyid | VCurrency2 | VCurrency | - | false |
VForwardTrade | Buycurrencyid | VCurrency1 | VCurrency | - | false |
VForwardTrade | Sellcurrencyid | VCurrency2 | VCurrency | - | false |
The interesting thing is that nothing is going to be changed, actually. The framework will create the same two internal view object instances with default row sets for each VA definition, and they will be shared by both VTodTrade and VForwardTrade VO instances.
Shared View Object instances
And now let's redefine our view accessors and get them referring to a shared VO instance.
VO instance | LOV attribute | VA | VA View definition | Shared VO instance | Row Level Binds |
VTodTrade | Buycurrencyid | SharedModule_VCurrency1 | VCurrency | VCurrency | true |
VTodTrade | Sellcurrencyid | SharedModule_VCurrency2 | VCurrency | VCurrency | true |
Since the view accessors refers to a real VO instance from some shared AM, the framework won't create any additional internal view objects. Instead of that, it will create a non-default row set within VCurrency shared VO for each view accessor usage with RowLevelBind set to true. And again, there is no any bind variables in the VCurrency and its result set doesn't depend on a row from which it is used. So, all these created row sets are going to be backed up by a single query collection instance. The behavior is pretty similar for both session and application shared AMs. The difference is that in the case of the session shared AM there will be one VCurrency instance per user session, and in the case of the application shared AM the framework will create only one single instance of VCurreny to be shared across the entire application. Besides that, the query collection of a session shared VO can be destroyed due to the AM recycling process or because of the garbage collection process. That will not happen with an application shared VO instance. There is a different mechanism to clean up query collections of the application shared view objects.
If we set the RowLevelBind property to false
VO instance | LOV attribute | VA | VA View definition | Shared VO instance | Row Level Binds |
VTodTrade | Buycurrencyid | SharedModule_VCurrency1 | VCurrency | VCurrency | false |
VTodTrade | Sellcurrencyid | SharedModule_VCurrency2 | VCurrency | VCurrency | false |
The framework will create a single default row set for the VCurrency view object, and this row set is
going to be shared for each view accessor usage across all rows.
That's it!
God Save Ukraine!