Ehcache Hibernate 2nd-level cache - Element for key is Null and N+1 query issue


#1

Hello, I am running a hibernate criteria query and setting the query cache using QueryHints. After the first execution, I see the error as below.

Please advise what this error is indicating. During first time execution, I dont see any N+1 queries as below, however, all subsequent requests are triggering these queries.

14:44:27,003 - DEBUG - org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - key: EMPLOYEE 
14:44:27,003 - DEBUG - org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - Element for key EMPLOYEE  is null
14:44:27,003 - DEBUG - org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - key: DEPARTMENT
14:44:27,003 - DEBUG - org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - Element for key DEPARTMENT is null
14:44:27,003 - DEBUG - org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - key: ADDRESS
14:44:27,003 - DEBUG - org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - Element for key ADDRESS is null
14:44:27,003 - DEBUG - org.hibernate.cache.internal.StandardQueryCache - Returning cached query results
Hibernate: 
    /* load com.test.Employee */ select
        emp0_.ID as EMP17_0_    
    from
        EMPLOYEE emp0_ 
    where
        emp0_.NAME=?
Hibernate: 
    /* load com.test.Department */ select
        dept0_.deptid as dept1_12_3_
    from
        DEPARTMENT dept0_     
    where
        dept0_.ID=?
Hibernate: 
    /* load com.test.Employee */ select
        emp0_.ID as EMP17_0_    
    from
        EMPLOYEE emp0_ 
    where
        emp0_.NAME=?
Hibernate: 
    /* load com.test.Department */ select
        dept0_.deptid as dept1_12_3_
    from
        DEPARTMENT dept0_     
    where
        dept0_.ID=?

#2

Most likely this happens because, by default, @ManyToOne and @OneToOne associations are using FetchType.EAGER.

So, in your case, you have an entity that was cached. But when you fetch it, it tries to load all EAGER associations as well, which are not cached, like:

  • Employee
  • Departement
  • Address

To fix it, you have to options:

  1. Either you set all to FetchType.LAZY for all these @ManyToOne and @OneToOne associations

     @MnyToOne(fetch = FetchType.LAZY)
     private Employee employee;
    
  2. Or, you cache all these entities as well, meaning that you need to provide the Hibernate @Cache annotation which provides the cache concurrency strategy:

     @Entity(name = "Employee")
     @Cacheable
     @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
     public class Employee {
         ....
     }

#3

Thank you Vlad. Could you also please clarify below queries in this regards.

  1. I’m using ehcache and want to implement distribution caching using ehcache. After referring your book, I learned hazelcast is an option. Please let me know if there are any code implementation challenges going with ehcache here other than the infra constraints you mentioned.

  2. I know you mentioned eager initialization smells. I have @OneToMany Mapping as well. Do you suggest me to go LAZY initialize on this as well considering the best practice.

Thank you.


#4
  1. You should check and see if EhCache or Hazelcast suits you better. There’s also Infinispan as well, which is very well integrated with Hibernate.
  2. Using LAZY by default is a very good practice. You can always swicth from LAZY to EAGER on a query-basis, but not the other way around.