-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: Just how does the session cache work?
PostPosted: Tue Apr 17, 2007 3:56 pm 
Newbie

Joined: Tue Aug 23, 2005 10:02 am
Posts: 7
I thought I understood how Hibernate's first level cache worked, but after reading some more of the Reference Manual, and analyzing some debug output from my code, I realize that I really don't know how the cache works. Here's my problem in a nutshell: when I issue an saveOrUpdate command for an object after the system has been idle for a few minutes, Hibernate re-reads the object from the database (including reading all of the collections that make up the object) before the update of the object is performed. So, the question is, why is this happening? I thought it was because the idle time caused the data in the first level cache to expire, so Hibernate first wanted to refresh the cache to make it current. But, after reading some of the reference manual, it appeared that the cache was in the session and was only available while the session was open. In my case, I get the session, update the object, and then immediately close the session. I put some debug statements before and after the update statement to see if the object was in the session's cache, and found out that the object was never in the session before the update, but always was after the update.

So I guess I have three questions:
1) How does the session cache actually work?
2) Why is my object being re-read from the database before the update after the system has been idle for awhile?
3) Can I keep Hibernate from re-reading the object before the update?

Thanks.

Here's the code that updates the object:
Code:
    public static void update(Object updateObject) throws Exception
    {
        Session session = null;
        Transaction transaction = null;
        Exception exception = null;
   
        try
       {
          session = HibernateUtils.currentSession();
          transaction = session.beginTransaction();
          session.saveOrUpdate(updateObject);
          session.flush();
          transaction.commit();
       }
       catch (Exception e)
       {
           e.printStackTrace();
           exception = e;
           try
           {
               transaction.rollback();         
           }
           catch (Exception ex)
           {
               //do nothing
           }
       }
       finally
       {
           try
           {
               if (session != null)
               {
                HibernateUtils.closeSession();
               }
           }
           catch (Exception e)
           {
               //do nothing
           }
       }
      //rethrow any DB2 exception encountered.  the exception will result in an error message being displayed on the UI.
      if (exception != null)
      {
          throw exception;
      }
    }


Here's my object definition:
Code:
   <class name="com.ibm.caes.wcs.db.cache.beans.UserPreferences" table="user">
      <cache usage="read-write"/>
       <id name="webId" column="webId"/> 
      <property name="rssCategory"/>
      <property name="rssTitle"/>                  
      <set name="selectedProducts" inverse="true" cascade="all, delete-orphan" lazy="false" sort="com.ibm.caes.wcs.db.cache.beans.UserProduct">
         <cache usage="read-write"/>
         <key column="webId"/>
         <one-to-many class="com.ibm.caes.wcs.db.cache.beans.UserProduct"/>
      </set>
      <set name="settings" inverse="true" cascade="all, delete-orphan" lazy="false">
         <cache usage="read-write"/>
         <key column="webId"/>
         <one-to-many class="com.ibm.caes.wcs.db.cache.beans.UserSetting"/>
      </set>      
      <set name="tabs" lazy="false" inverse="true" cascade="all, delete-orphan" sort="com.ibm.caes.wcs.db.cache.beans.Tab">
         <cache usage="read-write"/>
         <key column="webid" not-null="true"/>
         <one-to-many class="com.ibm.caes.wcs.db.cache.beans.Tab"/>
      </set>
      <array name="searches" inverse="true" cascade="all, delete-orphan">
         <cache usage="read-write"/>
         <key column="userid" not-null="true"/>
         <list-index column="index"/>
         <one-to-many class="com.ibm.caes.wcs.db.cache.beans.SavedSearch"/>
      </array>
   </class>


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 18, 2007 9:59 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
1) Session cache holds item loaded or updated as long as the Session exists. Once the Session is closed, the cache is cleared. Since you are using detached objects, the nonexistence of the object in the newly opened Session's first level cache is an expected result.

2 & 3) It is the optimistic locking mechanism that is forcing the query before the update. When updating a detached object, Hibernate forces a lock on the row (equivalent to Session.lock() with LockMode.UPGRADE) in order to ensure that the row has not been modified since it was detached. If you can modify the tables, I highly suggest using a version field. Then Hibernate can issue the update as "update my_table set field1="a" where primary_key=152 and version_number=12", thus bypassing the need to check all fields for modifications before updating.

References:
Concurrency and optimistic locking
Version column mapping


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 18, 2007 11:38 am 
Newbie

Joined: Tue Aug 23, 2005 10:02 am
Posts: 7
Is there any way to bypass the version checking altogether? The data can't be modified concurrently; it is only applicable to a single user.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 18, 2007 12:38 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
I'm wondering what would happen if you removed the flush(), since it is unnecessary (you already called saveOrUpdate for the detached object and the transaction commit does a flush). Lemme know what the effect is, if anything.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 18, 2007 12:44 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Your class mappings do not have select-before-update set to true hopefully. Otherwise, you can try changing the optimistic-lock attribute of the class mapping to none, dirty or all and see what the effect is. I'm guessing that 'all' will result in an update statement with all unmodified parameters included in the where clause.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 18, 2007 4:02 pm 
Newbie

Joined: Tue Aug 23, 2005 10:02 am
Posts: 7
Removing the flush() call didn't change the behavior. Adding optimistic-lock="none" caused the selects to be made every time (kind of the opposite of what I expected). Adding optimistic-lock="all" broke the application; it got a ClassDefNotFoundException when trying to find the HibernateUtils class, even though all that I had done was add the optimistic-lock="all" statements (just replaced "none" with "all"). If I wanted to use versioning, is this what I would need to do and how it would work?
    - add a version column to the tables and to the class definitions in the Hibernate xml files
    - the where clause on the update statement would automatically be added by Hibernate, and would know what field to look at for the version value based on the class definition
    - the value that it would be matching on would be whatever the object currently had, and if what's in the database didn't match what was in the object, a StaleObjectStateException would be thrown.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 18, 2007 4:15 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Dave D. wrote:
If I wanted to use versioning, is this what I would need to do and how it would work?
    - add a version column to the tables and to the class definitions in the Hibernate xml files
    - the where clause on the update statement would automatically be added by Hibernate, and would know what field to look at for the version value based on the class definition
    - the value that it would be matching on would be whatever the object currently had, and if what's in the database didn't match what was in the object, a StaleObjectStateException would be thrown.

Yep, that's exactly how it works/how to do it. It introduces the least overhead of any other optimisitc locking procedures that I have analyzed. You can do it with timestamps as well, if that would give you more helpful information (essentially, it becomes a "last modified date"). Of course, that does introduce the possibility (however slim) of concurrent modification if both modifications are made within one millisecond of each other. I actually prefer using timestamps because the data actually has a semantic value. There is no value in a strict version field. The likelihood of millisecond-concurrent modifications is so slim, that I can live with the possibility. Of course, it really depends on the criticality of the data integrity for the application.

Odd that the optimistic-lock="all" broke the application. There's got to be more behind that, and perhaps it is indicitive of another (maybe unrelated) problem. I can't imagine that Hibernate itself is looking for HibernateUtils.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 26, 2007 9:59 am 
Newbie

Joined: Tue Aug 23, 2005 10:02 am
Posts: 7
For what it's worth, the reason the app broke when I added optimistic-lock="all" is that I needed to add dynamic-update="true" as well.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.