Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 698
Knu8 Avatar asked Sep 09 '16 13:09

Knu8


People also ask

How we can enable second level cache in Hibernate?

cache. use_second_level_cache is used to enable the second level cache.

Which 2nd level cache is better in Hibernate?

Hibernate second level cache uses a common cache for all the session object of a session factory. It is useful if you have multiple session objects from a session factory. SessionFactory holds the second level cache data. It is global for all the session objects and not enabled by default.

How EHCache works in Hibernate?

Ehcache as a plug-in second-level cache for Hibernate – Automatically cache common queries in memory to substantially lower latency. BigMemory for an in-memory store – Leverage off-heap physical memory to keep more of the data set close to your application and out of reach of Java garbage collection.


1 Answers

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.

like image 160
SEY_91 Avatar answered Oct 02 '22 23:10

SEY_91