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.  [ 5 posts ] 
Author Message
 Post subject: Hibernate second level cache and lazy assocations question
PostPosted: Thu Mar 17, 2011 8:42 am 
Newbie

Joined: Thu Mar 17, 2011 7:48 am
Posts: 3
Can anyone provide any assistance with the second level cache in hibernate. I am relatively new to this area and I am experiencing some unexpected behaviour.
I am using JBoss AS 5.01 with JBoss cache as follows:

Code:
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory"/>
<property name="hibernate.cache.region.jbc2.cfg.entity" value="mvcc-entity"/>
<property name="hibernate.cache.region.jbc2.cfg.query" value="local-query"/>
...



Before I go any further I know the second level cache and query cache is working as I have viewed the cache contents and regions via the JMX console and also monitor the SQL output from hibernate by configuring log4j as follows:
Code:
log4j.logger.org.hibernate.cache=TRACE
log4j.logger.org.hibernate.SQL=DEBUG

I can see hibernate starting regions and returning results from cache and not executing the SQL.


I have a scenario involving three entities and a query which does not behave the way I thought it would.
Here is a simplified representation of the problem I am having:


Code:
@Entity
@Cache(usage = TRANSACTIONAL, region = "entityA")
EntityA
{

...
@NamedQuery(name = "EntityA.listWithAssociation", query = "  SELECT DISTINCT a FROM EntityA a "
                                                            + "LEFT JOIN FETCH a.association", hints =
    {
        @QueryHint(name = "org.hibernate.cacheable", value = "true"),
        @QueryHint(name = "org.hibernate.cacheRegion", value = "entityA")
    })
...

@OneToMany(cascade = CascadeType.ALL)
@MapKey(...)
@JoinColumn(...)
@Cache(usage = TRANSACTIONAL, region = "entityA")
private Map<EntityC, EntityB> association;

}


@Entity
@Cache(usage = TRANSACTIONAL, region = "entityB")
EntityB
{
...
}


@Entity
@Cache(usage = TRANSACTIONAL, region = "entityC")
EntityC
{
...
}



As you can see I have three entities (all of which are cachable), a named query (which is cached) and an association from EntityA which is a Map of EntityC's to EntityB's (one to many lazy association which is also marked for caching).

The first time I execute the query everything works as expected.
Here is a log extract:


Code:
INFO  org.hibernate.cache.StandardQueryCache - starting query cache at region: appName.entityA
DEBUG org.hibernate.cache.jbc2.query.QueryResultsRegionImpl - Created Region for appName.entityA -- regionPrefix is appName
DEBUG org.hibernate.cache.StandardQueryCache - checking cached query results in region: appName.entityA
DEBUG org.hibernate.cache.StandardQueryCache - query results were not found in cache
DEBUG org.hibernate.SQL - <The named query SQL>
DEBUG org.hibernate.cache.StandardQueryCache - caching query results in region: appName.entityA; timestamp=....


If I now open the JMX console and look at the query cache and entity cache the query results are indeed cached ( i can see the correct id's) and the entity cache has regions for each entity and I can see the state in dehydrated form including the association value mapping.

Now if I re-execute the same query I get this in the logs:

Code:
DEBUG org.hibernate.cache.StandardQueryCache - checking cached query results in region: appName.entityA
DEBUG org.hibernate.cache.StandardQueryCache - Checking query spaces for up-to-dateness: [EntityB, EntityA, EntityC]
DEBUG org.hibernate.cache.StandardQueryCache - returning cached query results


No SQL executes
So far so good. Then...
LazyInitializationException occurs attempting to access entityA.association.

This is where I am confused. The entities are all cached, the query is cached and the association is also cached. I can see all the relevant data in the query cache and entity cache and no SQL is executing (as expected). Why does hibernate not populate the association in entityA when it re hydrates it?

Perhaps rather interestingly if i mark the association as EAGER fetch OR if I navigate the association in my EJB directly after running the named query the LazyInitializationException no longer occurs AND NO SQL is run. It appears when we tell it to eager fetch or navigate it realises it is in the cache but when we don't it does not bother to look?
Is this expected behaviour or have I missed something?


Top
 Profile  
 
 Post subject: Re: Hibernate second level cache and lazy assocations question
PostPosted: Fri Mar 18, 2011 9:01 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Looking at the Java-Doc of LazyInitializationException
Quote:
* Indicates access to unfetched data outside of a session context.
* For example, when an uninitialized proxy or collection is accessed
* after the session was closed


I guess that Hiberante does always check the session state before populating the association,
regardless if the then association can be fetched from 2L-cache or not.


Top
 Profile  
 
 Post subject: Re: Hibernate second level cache and lazy assocations question
PostPosted: Fri Mar 18, 2011 9:14 am 
Newbie

Joined: Thu Mar 17, 2011 7:48 am
Posts: 3
Thank you for your reply.

I appreciate what you are saying, I just find it slightly odd considering the following points;
1) The query which originally put the entity in the cache was fetching the association.
2) The collection and associated entities are visible in the entity cache.
3) The second time we run the cached query if we navigate the collection or apply an eager fetch strategy it starts to work without hitting the database whatsoever.

It just seems a little odd/unexpected that everything it needs is there and if we give it a nudge it does pick up the required data without a DB access.
Perhaps this is more of a misunderstanding on my part about how this is meant to work rather than a "problem" with the 2L-cache.

Perhaps someone with a deeper knowledge of the caching than me can provide some incite into this behavior?


Top
 Profile  
 
 Post subject: Re: Hibernate second level cache and lazy assocations question
PostPosted: Fri Mar 18, 2011 10:21 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Hi David,

I just take a look at the code of Hibernate and in fact it is like I guessed:
see lines 368 and 369 below:
-first hibernate checks the state of the session
-if the check goes well, hibernate begins to initialize the collection

According the documentation this is expected behavior.

And my question to you:
Why for gods sake do you close the session before accessing entityA.association. ?


class AbstractPersistentCollection:
Code:
/**
    * Initialize the collection, if possible, wrapping any exceptions
    * in a runtime exception
    * @param writing currently obsolete
    * @throws LazyInitializationException if we cannot initialize
    */
   protected final void initialize(boolean writing) {
      if (!initialized) {
         if (initializing) {
            throw new LazyInitializationException("illegal access to loading collection");
         }
         throwLazyInitializationExceptionIfNotConnected();   //line 368
         session.initializeCollection(this, writing);                 //line 369
      }
   }
   
   private void throwLazyInitializationExceptionIfNotConnected() {
      if ( !isConnectedToSession() )  {
         throwLazyInitializationException("no session or session was closed");
      }
      if ( !session.isConnected() ) {
            throwLazyInitializationException("session is disconnected");
      }      
   }


Top
 Profile  
 
 Post subject: Re: Hibernate second level cache and lazy assocations question
PostPosted: Fri Mar 18, 2011 10:40 am 
Newbie

Joined: Thu Mar 17, 2011 7:48 am
Posts: 3
Hi,
Thanks once again for replying so quickly. OK, what you are saying makes more sense to me now.

Quote:
throwLazyInitializationExceptionIfNotConnected(); //line 368


I see what you are saying here. I guess I was perhaps hopeful in this case that somewhere within this method call (or else where for that matter) hibernate might realise that even though we are not attached to an active session we don't actually need to be in order to fetch the required data as it is all readily available in the 2L cache. As I said I am not knowledgeable in this area and it is most likely that I have overlooked the way in which it is supposed to work. It just came as a surprise to me, that is all.

The reason the session is closed is because my EJB method which runs the named query and returns the list of entities is being called from the web tier of my application (a Struts 2 Action). My Stateless Session Bean is completely container managed in regards to transactions and so forth so the session is closed once the method call finishes. It is my Struts 2 action which is attempting to access the key set of the map association in order to display some information on a JSP.

Am I using this in a way that it was not designed to be used?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 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.