30 Nov 2014

Understanding VO's method getCappedQueryHitCount

In one of my previous posts I explained the getQueryHitCount view object method. The method is typically invoked by the framework when some UI components, such as tables or tree tables, need to investigate the number of iterator rows in order to size their scroll bar. The method hits database with a SQL query calculating the number of rows. For example, if a view object query is "select * from Currency", then the getQueryHitCount will generate the following query "select count(1) from (select * from Currency)".  Off course it has a certain performance impact. Furthermore, in some cases, for example when it comes to programmatically populated view objects, it could be a pretty complicated task to calculate the number of view object rows. Therefore this method is often overridden in order to return -1 instead of performing an expensive query.
So, having done that developers can relax and feel happy since they have prevented the framework from executing needless expensive SQL queries. The application performance improved and everything seemed to be ok.
At some point they came up with an idea to fix one more performance issue. They decided to limit the number of rows to be fetched by any view object in the application. That can be done by specifying the Row Fetch Limit parameter in adf-config.xml:


But unfortunately, having done that, the application slowed down. The log analysis showed that the framework started performing "select count(1) ..." queries again.
If the Row Fetch  Limit is setup, the framework doesn't execute the getQueryHitCount when it needs to get an estimated row count. Instead of that, it invokes the getCappedQueryHitCount VO method. The method has the following signature:

public long getCappedQueryHitCount(ViewRowSetImpl viewRowSet,
           Row[] masterRows, long oldCap, long cap)

The viewRowSet is actually the row set containing the fetched rows, the masterRows is needed to get the parameters in a master-detail scenario, the oldCap  is not used, and the cap is actually the value that prevents the database from scanning all rows while performing a "select count ..." query.
So, in our case the cap is going to be 200 and the method is going to generate the following query:
"select count(1) from (select * from Currency where ROWNUM<=201)".
The framework demonstrates the same behavior and invokes getCappedQueryHitCount rather than
getQueryHitCount when the iterator binding property RowCountThreshold comes with some positive value:

    <iterator Binds="CurrencyView" RangeSize="25"
              DataControl="AppModuleDataControl" id="CurrencyViewIterator"
              RowCountThreshold="200"/>

By default RowCountThreshold = 0. 
So, if we need to avoid that sort of "select count ..." queries as well, we should either override the getCappedQueryHitCount method or set up RowCountThreshold = -1 at the iterator binding level.

That's it!











22 Nov 2014

Populating Dirty Entities while VO execution

As we know entity based view objects don't store the data themselves. The view object rows are backed up by the entity instances stored in the entity cache. When the VO execution process is being performed the framework runs over the fetched result set, creates view object rows, creates blank entity instances for each VO row, populates these entities with the fetched data and adds them to the entity cache.  Very roughly this process can be represented on the following diagram:



But what is going to happen if the entity cache already contains entities being fetched? Furthermore, how is the framework going to perform VO execution if those entities are modified by a user? On one hand users need to get recent data from the datasource, on the other hand the modified and uncommitted data shouldn't be lost. The following diagram shows how this magic works:



When the framework adds a new entity to the entity cache it checks whether there is already an entity with the same primary key in the entity cache. If such entity is found then the framework merges the old entity and the new entity. It is supposed that the new entity contains more recent data and this data should be applied to the old entity attributes. However, the framework doesn't change attributes of the old entity if they contain modified and uncommitted data. Note, in this example the user has changed the first name from John to Frank. On the other hand the last name has been changed in the data source from Stone to Gordon. So, we can see how John Stone turned into Frank Gordon.

One important thing should be mentioned here. When the framework merges modified entities it tries to verify the consistency. In other words it checks whether the dirty entity has not been modified by another user in the datasource. By default it compares values of all entity attributes and if it finds any differences it's going to raise an exception "Another user has changed the row with primary key ...". So, in this particular example we are likely to run into that exception, unless we have explicitly marked which attributes should by checked to verify the consistency. We can do that by setting up the  "Change Indicator" property of the entity attribute. If an entity contains any change indicator attributes then only those attributes participate in the consistency verification process. For sure, in our example we should mark as change indicators some other attributes, but not last name.



Since the old entity in the entity cache contains already updated data, there is no need in the new entity anymore. The view row is backed up now by the old entity and the new entity just becomes garbage which is going to be collected by the GC.

The situation can be more complicated when the user has modified entities, but they are not in the entity cache. This could be possible if the user's application module has been borrowed by another user and all the transactional data has been passivated to the passivation storage. Definitely before VO execution the framework is going to restore and activate all that stuff back. When the framework restores dirty entities from the passivation storage it is able to populate only modified attributes, since  values of those attributes only have been passivated. In order to get values of the rest of the attributes from the datasource the framework invokes findByPrimaryKey method on the entity definition instance. Once a restored entity instance has been prepared in this way it is going to be put in the entity cache. The rest of the process is exactly the same as we have considered already.

That's it!