Yesterday I was investigateing a very strange behavior in Hibernate. Yesterday morning I added EhCache as a second level cache to the hibernate and I made some tests to be sure everything is alright. I was very surprised when I saw that my entities are not cached so I started investigating the bug.
Firstly I tried different setups but nothing changed. Because I used this configuration many times without Spring, I suspected it can cause the bug. In the next step I tried get rid of all the Spring related stuff, e.g. removed the spring transaction management. I used the Hibernate as pure as it was possible and the cache was still not working.
Today morning (after I fixed an other bug) I leaned again to the problem, having downloaded the hibernate and ehcache source code I was debugging the code. It did't take a long time to find the reason. The second level cache is NOT enabled for my entities because those are extended classes, inherited some properties from our GenericEntity class.
Environment
- EhCache 2.5.1 - http://ehcache.org/
- Hibernate 3.3.2 GA - http://hibernate.org (Docs)
- Spring
- Tomcat 7
- MySql
Domain model
Invesigation
Shortly After I fired up the my debugger and I was running a query on the database, I found the code which should have stored the entities in the second level cache. It lays in the TwoPhaseLoad class initializeEntity method.
org.hibernate.engine.TwoPhaseLoad#initializeEntity(Object, boolean, SessionImplementor, PreLoadEvent,PostLoadEvent)
But if the code is here, why it is not invoked?
When the entity is being loaded by the TwoPhaseLoad, the persister hasCache() method returns false. This method checks the AbstractEntityPersister's cacheAccessStrategy property which is null.
org.hibernate.persister.entity.AbstractEntityPersister
The AbstractEntityPersister gets the cacheAccessStrategy instance through its constructor. Therefore I investigated where the constructor is invoked.
The creation of AbstractEntityPersister happends in the SessionFactoryImpl and the second parameter will be the cacheAccessStrategy, here called simply accessStrategy.
org.hibernate.impl.SessionFactoryImpl
The accessStrategy (although there is a pretty complex code) is always null, because the settings.isSecondLevelCacheEnabled() is always false and the body of the if statement never runs. But why? The secret is in the model classes. As I mentioned before the cache behaviour is defined on our subclasses, therefore hibernate will invoke SubClass's getCacheConcurrencyStrategy() method, which forwards the request to the root class.
org.hibernate.mapping.RootClass
org.hibernate.mapping.SubClass
At the moment I have no solution, but I'll update this post when I'll have found it although I wrote a test application to test Hibernate 4.
Nice post.
ReplyDeleteI'm having a similar problem. Have you found any solutions?
Hi Mehdi,
ReplyDeleteSorry, I can't remember the solution. But I guess we worked it around. I'll try to take a look at the code today or tomorrow afternoon and if I find anything, I'll let you know.