Second level cache not working in Hibernate + Spring + JPA and EhCache
Asked Answered
L

2

6

Let me make my understanding clear for second level cache. There is a query in base class of my web application. This query is called for almost every action (I am using Struts and this is how application is designed so can't really mess with it), for example loading my home page calls three separate Struts actions and this query is executed for each action. The query in QueryDsl form looks like Iterable<Event> eventsFromDb2 = eventRepository.findAll(EventExpressions.queryAllEvents()); and in simplified form it looks Select e from Event e where e.deleted = false

This query takes it own sweet time of ~10sec so it makes the application quite slow because its called for every action(CRUD) of the web application. As per my understanding, on enabling second level cache, hibernate+ Spring orm should fetch the result from cache and avoid database request. However, it is not working. The persistence.xml looks like following

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc
                       http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
                       http://www.springframework.org/schema/beans
                       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                       http://www.springframework.org/schema/tx
                       http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">



<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" depends-on="flyway">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="de.mm.moreevent.type" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="false" />
            <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
            <property name="generateDdl" value="true" />
        </bean>
    </property>
<property name="jpaPropertyMap">
    <map>
        <entry key="hibernate.cache.use_query_cache" value="true" />
        <entry key="hibernate.cache.use_second_level_cache" value="true" />
        <entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
        <entry key="hibernate.cache.default_cache_concurrency_strategy" value="read-write" />
        <entry key="javax.persistence.sharedCache.mode" value="ALL" />
        <entry key="hibernate.generate_statistics" value="false" />
    </map>
</property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- see: http://springcert.sourceforge.net/2.5/4-study-transaction-management.html -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="makeBooking" read-only="false" propagation="NESTED" rollback-for="de.mm.moreevent.exception.ManagerException" />
        <tx:method name="sendConfirmationAndInvoice" read-only="true" propagation="NESTED" rollback-for="de.mm.moreevent.exception.ManagerException" />
        <tx:method name="sendConfirmationOnly" read-only="true" propagation="NESTED" rollback-for="de.mm.moreevent.exception.ManagerException" />
        <tx:method name="saveOrUpdate" read-only="false" propagation="NESTED" rollback-for="de.mm.moreevent.exception.ManagerException" />
        <tx:method name="participantsImport" read-only="false" propagation="NESTED" rollback-for="de.mm.moreevent.exception.ManagerException" />
    </tx:attributes>

</tx:advice>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <qualifier value="transactionManager" />
</bean>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- class="org.springframework.jdbc.datasource.DriverManagerDataSource" -->
    <property name="driverClass">
        <value>org.postgresql.Driver</value>
    </property>
    <property name="jdbcUrl">
        <value>jdbc:postgresql://127.0.0.1:port/dbName</value>
    </property>
    <property name="user">
        <value>user1</value>
    </property>
    <property name="password">
        <value>******</value>
    </property>
    <property name="initialPoolSize" value="5" />
    <property name="minPoolSize" value="5" />
    <property name="maxPoolSize" value="30" />
    <property name="idleConnectionTestPeriod" value="200" />
    <property name="acquireIncrement" value="1" />
    <property name="maxStatements" value="0" />
    <property name="numHelperThreads" value="3" />
</bean>

<!-- see: http://flywaydb.org/documentation/api.html  -->
<bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate">
    <property name="dataSource" ref="dataSource" />
     <property name="table" value="schema_version"></property>
     <property name="locations" value="customer/db-scripts/migration"/>
</bean>


<bean id="bouncyCastleProviderInitialisation" class="de.mm.moreevent.util.BouncyCastleProviderInitialisation" init-method="init" />

<bean id="strongEncryptorBC" class="org.jasypt.encryption.pbe.PooledPBEStringEncryptor">
    <property name="providerName">
        <value>BC</value>
    </property>
    <property name="algorithm">
        <value>algo</value>
    </property>
    <property name="password">
        <value>******</value>
    </property>
    <property name="poolSize">
        <value>4</value>
    </property>
</bean>

<bean id="hibernateStringEncryptor" class="org.jasypt.hibernate4.encryptor.HibernatePBEStringEncryptor">
    <property name="registeredName">
        <value>strongHibernateStringEncryptor</value>
    </property>
    <property name="encryptor">
        <ref bean="strongEncryptorBC" />
    </property>
</bean>

Following is Ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
     name="cacheManager"
     updateCheck="false"
     maxBytesLocalHeap="100M"
     statistics="true">

<!--
 | Please see http://ehcache.sourceforge.net/documentation    /configuration.html for
 | detailed information on how to configurigure caches in this file
 +-->
<!-- Location of persistent caches on disk -->
<diskStore path="java.io.tmpdir/moreEventObjCache" />

<defaultCache eternal="false" maxElementsInMemory="100000"
    overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="600"
    timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" statistics="true"/>

<cache name="bookingTransaktions" eternal="true"
    overflowToDisk="false" diskPersistent="false"
    timeToIdleSeconds="0" timeToLiveSeconds="0" statistics="true"/>

<cache name="mailingBean" eternal="true" maxElementsInMemory="10000"
    overflowToDisk="false" diskPersistent="false"
    />

Following is my Entity class

import javax.persistence.Cacheable;
...    
@Entity 
@Table(name = "EVENT")
@Cacheable
@Configurable(dependencyCheck = true)
public class Event extends MoreEventDataBaseEntity implements CloneChangeEventI {
...

I am testing for time taken to execute the query, following is the code(I am calling same query two times consecutively )

    timer.mark();
    Iterable<Event> eventsFromDb = eventRepository.findAll(EventExpressions.queryAllEvents(), EventExpressions.orderByOnlineStartDate(true));
    timer.mark();
    Iterable<Event> eventsFromDb2 = eventRepository.findAll(EventExpressions.queryAllEvents(), EventExpressions.orderByOnlineStartDate(true));
    eventsFromDb2.getClass();
    timer.mark();

Now, in the result, this code snippet is called three times from web page and following is the result in the console

init Struts page load: 
EventManager.java:130: +0ms

// Query fired first time, it took 8 seconds as expected  
EventManager.java:132: +8103ms
// Query fired second time, it took 15 ms due to so caching
EventManager.java:135: +15ms

init (Ajax1):
EventManager.java:130: +0ms
// Query fired and it took 9.5 sec, However I am expecting it to be few milliseconds ???? second level cache not working I suppose ????   
EventManager.java:132: +9501ms
EventManager.java:135: +21ms
Before timer 2016-09-09T14:21:41.853+02:00

init (Ajax2): 
EventManager.java:130: +1ms
???? took 9.5 seconds again. second level cache not working I suppose same as Ajax1????
EventManager.java:132: +9506ms
EventManager.java:135: +22ms

The same thing happens throughout the application. The second level cache is not working at all. This will be of great help for me if I can save this query execution time by caching. I am using Spring ORM 3.2.1, Hibernate EhCache 4.1.9

Lam answered 9/9, 2016 at 13:10 Comment(7)
where is the ehcache config?Pilfer
Added ehcache configLam
#10601198 how this link can help you about the query cache.Arrow
@Huang Not much helpfulLam
At first I just want to know if my understanding of second level caching is correct.Lam
Can you get the EntityManagerFactory in your codes, then you can get the Cache via EntityManagerFactory#getCache method, then you can check is there any object in the second cache by Cache#contains method.Arrow
There is no getCache method in my EntityManagerFactory LocalContainerEntityManagerFactoryBeanLam
B
1

The second level cache is not working because you are not fetching data by ID (see this link When and how to use hibernate second level cache?).

In your case you can use query cache.

Bitten answered 19/9, 2016 at 15:40 Comment(0)
S
0

I think you should use a different region factory class:

net.sf.ehcache.hibernate.EhCacheRegionFactory

http://www.ehcache.org/documentation/2.7/integrations/hibernate

On the other hand if you use @Cacheable annotations you must enable them using @EnableCaching or

<cache:annotation-driven />
Shew answered 13/9, 2016 at 8:35 Comment(1)
Unfortunately I am using older versions of hibernate and Ehcache. Therefore, some of the attributes mentioned in tutorial are not applicable for me. I think I have to upgrade my Ehcache and Hibernate first before trying that tutorial. Moreover the SAX parser is giving error for <cache name="com.somecompany.someproject.domain.Country" maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" <persistence strategy="localTempSwap"/> />Lam

© 2022 - 2024 — McMap. All rights reserved.