Updated: 5/24/2006; 10:57:43 AM.
Sung Im's Radio Weblog
        

Wednesday, May 24, 2006

How Does View Link/Entity Association Consistency Exactly Work?

 

I get asked the above question a lot.  So, I'm going to try to give as much detailed info as possible.  Steve's following blog page has a lot of functional description of view link consistency

 

http://radio.weblogs.com/0118231/2003/12/02.html

 

Please take a look.

 

I will focus my discussions on technical details.

 

 

What Are View Link/Entity Association Consistency for?

 

Let me first discuss view link consistency and leave entity association consistency for later.

 

This flag controls whether unposted rows show up in the query collections of this VO of not.

 

For example, suppose we have 2 VOs: VO1 and VO2 both based on the same entity.  If you create a new row through VO1, a question comes up as to whether VO2 will see this new row.

 

If the view link consistency flag is on for VO2, it can see the new row (more discussion on this below).  If the view link consistency flag is off for VO2, VO2 will not see any unposted new row.

 

 

How View Link Consistency Works

 

Suppose the user creates a new row through VO1.  The act of creating a new view row means that a new entity row is created for the corresponding entity.  This row is by default blank (if the user overrode create() to initialize some attr, it won¡¯t be blank).  And, a blank row is not yet added to the entity cache.

 

Only when some (I say 'some' because the EO's PK may be multi-part) of the PK attributes are set (most likely through the viewRow.setAttribute(..)), will the entity row be added to the entity cache.  And, in response to the set attribute call, the

oracle.jbo.server.EntityCache's following method is invoked:

 

   protected void deliverEntityEvent(EntityEvent event)

 

 

This will then turn around looking for ViewObject's that are listening in on the EO events.  Going back to our VO1 and VO2 example, as they are based on this EO, both of them will be listeners to the entity cache, regardless of its view link consistent flag value.  For each entity listener, the following method is invoked (that of the oracle.jbo.server.EntityListener interface, which ViewObjectImpl implements):

 

   public void sourceChanged(EntityEvent event);

 

 

So, VO1's sourceChanged() is invoked first, followed by VO2.  Let's assume now that VO1's view link consistency is false, but VO2's consistency is true.  Then, VO1's sourceChanged() will not do anything with this new row.  VO2, on the other hand, will walk through all its query collections (oracle.jbo.server.QueryCollection) and process the new row for each one of these QCs.

 

Each QC in a VO has a data structure called the row filter.  Let me back up a bit and explain this.  A VO may have a where-clause with a bind parameter, e.g.,

 

   DEPTNO = :myDeptno

 

for EmpView (myDeptno is the bind variable).  Typically, setWhereClauseParam() or setNamedWhereClauseParam() is invoked to specify the bind variable value.  Or, if the where-clause was one generated from a view link, the where-clause parameters are automatically supplied by BC4J.

 

Suppose the user or (in the case of master-detail) the system, creates a row set from this VO for myDeptno = 10.  This creates a row set and the underlying QC, where the QC's row filter is {10} (an Object[] with 10 as its only element).  If you or the system creates another row set for myDeptno = 20, or if you navigate the master VO to the row whose Deptno is 20, this creates another QC whose row filter is {20}.  So, the VO now has 2 QCs with row filters {10} and {20}, respectively.

 

Going back to the view link consistency discussion, while processing the new row for the QC, the VO checks to see if the new row's row filter value matches that of the QC's.  If so, then we proceed further.

 

Now, a check is made to see if the QC in its collection already has a view row, which is based on this entity row (a data structure named ViewRowCache makes this look-up fast).  If the QC already has one, we just return and not bother with the new entity row.  If the QC does not have any view row, which is based on the new entity row, a new view row is created on the QC and the new view rows points to this new entity row.

 

The VO's RowQualifier/RowMatch is now applied to the new view row to see if the row qualifies to be in this QC.  If true, the row is finally inserted into the QC and the QC is now able to see the new entity row.

 

 

Why Are They Called Link/Assoc Consistency?

 

An observant BC4J user asked me why we call this flag view link/entity association when it seems deal with the question of unposted rows.

 

Part of the reason is historical.  This feature was originally requested for entity associations where the user wanted to see news rows through the association accessor before they are posted to database.

 

Another reason is that this feature simply works more logically for master-detail situation.  More specifically, it works better for the detail VO.  Let's go back to the above discussion on row filter.  I mentioned earlier that "The VO checks to see if the new row's row filter value matches that of the QC's".  For master-detail, we're able to build the row filter value from the new row because the view link/entity association definition let's us know which attribute is involved in the QC's row filter.

 

For example, if the view link links Dept's Deptno to Emp's Deptno, when we deal with a new Emp row, we take the Emp's Deptno value to build the row filter value to compare to that of the QC.

 

But, if no view link is involved, BC4J doesn't have that information; in the above example, suppose the where-clause was specified through setWhereClause() and not by a view link.  When sourceChanged() is invoked, BC4J does not know that the where-clause parameter has any relationship to any of the attributes of Emp.  So, when view link consistency is being applied to a non-detail VO, we do not perform the row filter check (because the bottom line is that we don't know how to build the row filter from the row).  The row qualifier (oracle.jbo.server.RowQualifier) or the row match (oracle.jbo.RowMatch) check is still performed.

 

 

What about Entity Association Consistency?

 

Keep in mind the following important facts re: entity assocation.  When you call an entity association accessor, an oracle.jbo.server.EntityRowSetImpl is returned (assuming that the accessor is for the many side of the association).  EntityRowSetImpl is a facade in front of ViewRowSetImpl and its main job is to peel off the entity row from the view row.  Behind this EntityRowSetImpl is a ViewObjectImpl which is created through the following ApplicationModule API:

 

   public ViewObject createViewObjectFromQueryClauses(String voName,

                                               String eoName,

                                               String selectClause,

                                               String fromClause,

                                               String whereClause,

                                               String orderByClause);

 

Hence, all of the above discussions apply to entity association in that EntityRowSetImpl is really a ViewRowSetImpl with the added functionality of peeling the view row off.

 

 

Why Are the APIs Called set/isAssociationConsistent Even for View Links?

 

Someone asked me why the APIs are named AssocationConsistent even for view links.  As I mentioned early, these APIs first went in for entity associations.  So, we originally named them set/isAssociationConsistent.  Later, users asked the same functionality to be supported for view links.  These APIs were elevated to oracle.jbo.RowSet and the names stuck.

 

 

What Are the Rules for Computing This Flag?

 

Steve's page (link included at the beginning) has detailed description of various ways through which this flag can be specified.  Take a look.

 

 

Why Do These New View Rows Sometimes Appear at Different Locations?

 

The above description re: how new view rows appear in a QC is only half the story.  It explains how new view rows appear in an existing QC.  The other half is what happens if a QC is being formed new (how the new QC picks up unposted new rows).  But, before I discussed that, let me say that for an existing QC, the new view rows will appear at the end of the current collection.  So, suppose the database table contains 100 rows that would go into this QC.  The QC has fetched 20 rows.  If the user causes a new row to be inserted through view link consistency, the new row will appear in the 21-st slot.  If the user then continues to fetch, the 21-st row from the table will go into the 22-nd slot and so on.

 

Back to the new QC case (which is the same case as if executeQuery() is invoked on the view object or the row set)...  As the QC is created and executeQuery'ed, it will check the view link consistency flag.  If the flag is true, the appropriate entity cache will be examined to see if it has unposted new rows.  Depending on whether the QC is for a detail VO or not, the row filter check is made to pre-filter the new entity rows.  New view rows are created from these and put into a "temporary holding station."

 

As the user retrieves rows from this QC, new rows are moved from this temporary holding station to the real collection and returned to the caller.  So, these new rows will appear at the beginning of the collection.

 

Do View Link Consistency Make Sense for EO-less VOs?

 

No.  View link consistency is not really supported for VOs that are not EO based.


10:57:42 AM    comment []

Saturday, May 20, 2006

BC4J 10.1.3 supports a new feature called "query mode."  Using this, the user can decide how the VO draws its data.
 
The following constants are defined in oracle.jbo.ViewObject:

QUERY_MODE_SCAN_VIEW_ROWS: To refine further View rows currently in the QC (using the RowQualifier--for tier transparency, oracle.jbo.RowMatch is created and RowQualifier subclasses this).  For example, your RS may have Emps whose SAL is > 3000.  From this, you can further qualify (i.e., remove from the QC unqualifying rows) rows that have Commission > 10.

QUERY_MODE_SCAN_ENTITY_ROWS: To draw rows from Entity Cache.  This is good for performing in-memory filtering of rows that are already in Entity Cache.  In addition, this enables the user to produce View rows on Entity rows that were created and not yet posted.  Again, you use RowMatch to qualify rows.

QUERY_MODE_SCAN_DATABASE_TABLES: To draw rows frmo query result as before.


The default is QUERY_MODE_SCAN_DATABASE_TABLES.  You can OR these flags together.  If OR them, BC4J handles skipping of duplicate rows.

Here are relevant JavaDocs:

     /**
      * These <code>QUERY_MODE_SCAN_...</code> constants are used to specify
      * the query mode for the View Object.  Query mode controls how qualifying
      * formulated for each Row Set in the View Object.  These constants can be
      * OR'ed together.  If more than one query mode is specified in this
      * manner, rows for the collection will be produced from more than
      * one source.
      * <p>
      *
      * If you call {@link #setQueryMode(int)} or {@link #addQueryMode(int)} to
      * change the query mode, the new query mode does not go into effect
      * until you call {@link RowSet#executeQuery()} on the Row Set.
      * <p>
      *
      * Rows can come from three different sources.  If the Row Set currently
      * has a collection of rows (View rows), <code>QUERY_MODE_SCAN_VIEW_ROWS</code>
      * will indicate that the row match (see {@link #setRowMatch(RowMatch)}) should
      * be applied to further filter View rows.  Unqualifying rows are removed
      * from the collection.
      * <p>
      *
      * If the query mode includes <code>QUERY_MODE_SCAN_ENTITY_ROWS</code>,
      * the Entity cache is scanned to see if qualifying rows can be produced.
      * If so, these rows are added to the collection.
      * <p>
      *
      * If the query mode includes <code>QUERY_MODE_SCAN_DATABASE_TABLES</code>,
      * a database query is issued to produce rows from the query result.
      * The default query mode is <code>QUERY_MODE_SCAN_DATABASE_TABLES</code>.
      * <p>
      *
      * If the query mode specifies multiple sources, then care if taken
      * to prevent duplicate rows from appearing.
      */
     static final int QUERY_MODE_SCAN_VIEW_ROWS = 0x0001;

     /**
      * Specifies that rows should be produced from Entity cache.
      * See above for details.
      */
     static final int QUERY_MODE_SCAN_ENTITY_ROWS = 0x0002;

     /**
      * Specifies that rows should be produced from database query result.
      * See above for details.
      */
     static final int QUERY_MODE_SCAN_DATABASE_TABLES = 0x0004;

     /**
      * Sets an in-memory filter (<code>RowMatch</code>) for the View Object.
      * While the WHERE clause is used when a database query is issued,
      * the row match is used for qualifying rows in memory.
      * Calling this method does not cause filtering of rows by the
      * row match.  To filter, {@link RowSet#executeQuery()}
      * must be called.
      *
      * @param rowMatch the new row match.
      */
     void setRowMatch(RowMatch rowMatch);

     /**
      * Gets the in-memory filter (<code>RowMatch</code>) for the View Object.
      * While the WHERE clause is used when a database query is issued,
      * the row match is used for qualifying rows in memory.
      *
      * @return the current row match.
      */
     RowMatch getRowMatch();

     /**
      * Sets query mode for the View Object.
      * Query mode controls how qualifying rows are formulated for each
      * Row Set in the View Object.
      * <p>
      *
      * The <code>queryMode</code> parameter may be an OR'ed flag of
      * <code>QUERY_MODE_SCAN_...</code> constants.  See these constants
      * for further details.
      * <p>
      *
      * Calling this method does not cause automatically cause the new
      * query mode to go into effect.  Call
      * {@link RowSet#executeQuery()} to apply the new query
      * mode.
      * <p>
      *
      * The default query mode is <code>QUERY_MODE_SCAN_DATABASE_TABLES</code>.
      *
      * @param queryMode the new query mode.
      */
     void setQueryMode(int queryMode);

     /**
      * Adds query mode for the View Object.
      * Query mode controls how qualifying rows are formulated for each
      * Row Set in the View Object.
      * <p>
      *
      * The <code>queryMode</code> parameter may be an OR'ed flag of
      * <code>QUERY_MODE_SCAN_...</code> constants.  This method
      * OR'es in the incoming <code>queryMode</code> to the View Object's
      * current query mode.  See these constants for further details.
      * <p>
      *
      * Calling this method does not cause automatically cause the new
      * query mode to go into effect.  Call
      * {@link RowSet#executeQuery()} to apply the new query
      * mode.
      *
      * @param queryMode the new query mode to be added (OR'ed).
      */
     void addQueryMode(int queryMode);

     /**
      * Gets the current query mode of this View Object.
      * Query mode controls how qualifying rows are formulated for each
      * Row Set in the View Object.
      * <p>
      *
      * The returning value may be an OR'ed flag of
      * <code>QUERY_MODE_SCAN_...</code> constants.  See these constants
      * for further details.
      * <p>
      *
      * The default query mode is <code>QUERY_MODE_SCAN_DATABASE_TABLES</code>.
      *
      * @return query mode.
      */
     int getQueryMode();

1:24:10 PM    comment []

BC4J 10.1.3 supports 'named where-clause params' and 'variables' on various BC4J objects.  The memo below explains tech details:


This memo will describe the named where-clause parameter feature.

Underlying the named where-clause parameter is a general feature of Variable support (aka, name space).  Each app component object (specifically, AM, VO, RS and eventually binding container) is a variable manager (name space).

Each def object (AM def, VO def, EO def) is a variable manager.

A variable manager may be nested in the sense of scope nesting. (Interface name is oracle.jbo.VariableManager).  Name lookup goes from the current var mgr and searches outward.  Hence, a variable mgr has the concept of parent var mgr.

For a RowSet, the parent var mgr is the VO's var mgr.

For a VO, its parents are the AM instance's var mgr and view def's var mgr.

For an AM, its parents are the parent AM's (in terms of AM nesting) var mgr and the AM def's var mgr.

For a VO def its parents are the entity defs of the base EOs.

~~~~~

A var mgr contains variables (interface Variable).

A variable is typed, has a name, and has a default value.  I also copied AttributeHint support into it so that people can to attr-hinting with these vars (just like Shailesh's JUCtrlParameterDef).

A var mgr is further extended into VariableValueManager, which knows how to store and manage current values (above and beyond the default value).

Strictly speaking, an app component object will have a VariableValueManager.  AM def, VO def, EO def has VariableManager.

A variable can be a named where-clause parameter or a method parameter (or other things to be included as requirements arise).  A "variable kind" indicates what kind of variable it is.

A where-clause parameter will have an additional list of integer ids. These ids will be used to bind the variable values into where-clause when the query is executed (will translate into setWhereClauseParam(int, Object) calls).

~~~~~

For named where-clause parameter implementation, I added the following APIs to oracle.jbo.RowSet.

   void defineNamedWhereClauseParam(String name, Object defaultValue, int[] indices);
   void removeNamedWhereClauseParam(String name);
   void setNamedWhereClauseParam(String name, Object value);
   void skipNamedWhereClauseParam(String name);


Also, use the following link for further info

http://www.oracle.com/technology/oramag/oracle/06-mar/o26frame.html?msgid=4658272


11:54:34 AM    comment []

To allow customization of data consistency check (between the data in the EO cache and the database), the following overriddable methods are provide:

oracle.jbo.server.EntityImpl:

   protected boolean checkConsistency (SparseArray target, boolean lock)

The default implementation of this method calls EntityImpl.compare() which is also overriddable:

   protected boolean compare(SparseArray target)

The default implementation of compare() will check to see if this entity has a change indicator attr.  If so, it uses that.  If not, is compares all attr values (non-LOB attrs).

 


11:41:57 AM    comment []

Background:  RowInconsistentException is an exception thrown under the following scenario.  The user reads a row in with value aaa for an attribute.  He performs some operation that is about to lock the row.  A check is made to see if someone else has modified the row since this user read it.  If so, this exception is thrown.

Question:  When RowInconsistentException is thrown, why does BC4J compare values of attributes that are not modified by this user?  Isn't that a bit excessive?  In other words, shouldn't BC4J only compare 'modified' attribute's original value with the db value to raise RowInconsistentException?

Answer: RowInconsistentException is usually thrown when the row is about to be locked.  Under pessimistic, RIE will be thrown during lock request ==> No attr has been modified.

Even under optimistic, since data updates were performed based on the old data, if some data were changed by another transaction, we want the user notified that their updates may have been performed based on stale data.

For example, suppose in a banking app (under opt), the user sees remaining balance of $300 in his app.  The user makes a request to withdraw $250 from it.  Before this change is posted, another user comes into the same row and withdraws $200 from it.  If we compare only the modified attr, the req to w/d $250 will go through.

This is why we want to compare all attrs regardless of whether they were modified or not.


11:37:12 AM    comment []

After long silence on this Weblog, I decided to post some useful articles and Q&A's.  Of course, when it comes to BC4J resources, you must first look at Steve's (Muench) site:

http://radio.weblogs.com/0118231/

Stay tuned for more article.s

 


11:33:00 AM    comment []

Monday, May 01, 2006

Link to old articles:

http://radio.weblogs.com/0123729/2004/05/25.html

 


2:08:09 PM    comment []

© Copyright 2006 Sung Im.
 
May 2006
Sun Mon Tue Wed Thu Fri Sat
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
Apr   Jun

Home

Click here to visit the Radio UserLand website.

Subscribe to "Sung Im's Radio Weblog" in Radio UserLand.

Click to see the XML version of this web page.

Click here to send an email to the editor of this weblog.