Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring data JPA with Hibernate and Ehcache not working

I'm working on an application using Spring Data JPA with Hibernate and I'm trying to enable second level cache using ehcache. I broke up my application into two projects:

  • CoreDataFacade: where I define data access operations using QueryDSL, Spring Data JPA with Hibernate and ehcache.
  • QueryComponent: is a spring boot project that uses CoreDataFacade project to access data.

CoreDataFacade's configuration is as follows:

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>1.7.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.3.6.Final</version>
    </dependency>

    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache-core</artifactId>
        <version>2.4.7</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-ehcache</artifactId>
        <version>4.3.6.Final</version>  
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>4.3.6.Final</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.33</version>
    </dependency>
    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>
    <dependency>
        <groupId>com.mysema.querydsl</groupId>
        <artifactId>querydsl-apt</artifactId>
        <version>3.6.0</version>
    </dependency>
    <dependency>
        <groupId>com.mysema.querydsl</groupId>
        <artifactId>querydsl-jpa</artifactId>
        <version>3.6.0</version>
    </dependency>

application-context.xml

<jpa:repositories
    base-package="com.coredata.services.impl.sql.mysql.repositories" />

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close" p:driverClass="com.mysql.jdbc.Driver"
    p:jdbcUrl="jdbc:mysql://localhost/FOO" p:user="****" p:password="****"
    p:acquireIncrement="5" p:minPoolSize="10" p:maxPoolSize="100"
    p:maxIdleTime="1200" p:unreturnedConnectionTimeout="120" />

<bean id="jpaVendorAdapter"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
    p:database="MYSQL" p:showSql="true" p:generateDdl="true" />


<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaVendorAdapter"
    p:packagesToScan="com.coredata.services.impl.sql.mysql.model">
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            <prop key="hibernate.cache.use_query_cache">true</prop>
            <prop key="hibernate.cache.use_second_level_cache">true</prop>
            <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
            <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory
            </prop>
            <prop key="javax.persistence.sharedCache.mode">ENABLE_SELECTIVE</prop>
            <prop key="hibernate.generate_statistics">true</prop>
        </props>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" />

Entity Cache annotations

@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region="cache_states")
@Table(name="states")
public class State implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id_state")
    private int idState;
    ...

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">

    <defaultCache maxElementsInMemory="10000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120"
        overflowToDisk="false" diskPersistent="false" />

    <cache name="cache_states" maxElementsInMemory="300" eternal="false"
        timeToIdleSeconds="5000" timeToLiveSeconds="5000" overflowToDisk="false">
    </cache>
</ehcache>

QueryComponent's configuration imports above configuration and exclude JPA:

@Configuration
@PropertySource("classpath:/component.properties")
@ImportResource({ "classpath:/application-context.xml"})
@EnableAutoConfiguration(exclude = { JpaRepositoriesAutoConfiguration.class })
public class Application {

    public void run(String... args) {   }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

When ComponentQuery starts everything is OK. When I execute a query to find all states in database, hibernate statistics are as follows:

Hibernate: select count(state0_.id_state) as col_0_0_ from states state0_
Hibernate: select state0_.id_state as id_stat1_5_, state0_.name_state as  name_e2_5_ from states state0_ limit ?
[2015-08-31 18:52:21.402] boot - 1946  INFO [SimpleAsyncTaskExecutor-1]    --- StatisticalLoggingSessionEventListener:    Session Metrics {
    32992 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    238285 nanoseconds spent preparing 2 JDBC statements;
    935976 nanoseconds spent executing 2 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    269717 nanoseconds spent performing 4 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    68790 nanoseconds spent executing 2 partial-flushes (flushing a total of 0 entities and 0 collections)
  }

When I repeat the same query I got this statistics:

Hibernate: select count(state0_.id_state) as col_0_0_ from states state0_
Hibernate: select state0_.id_state as id_stat1_5_, state0_.name_state as  name_e2_5_ from states state0_ limit ?
[2015-08-31 19:26:48.479] boot - 1946  INFO [SimpleAsyncTaskExecutor-1]    --- StatisticalLoggingSessionEventListener:     Session Metrics {
    314930 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    356832 nanoseconds spent preparing 2 JDBC statements;
    681615 nanoseconds spent executing 2 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    209324 nanoseconds spent performing 4 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    12368 nanoseconds spent executing 2 partial-flushes (flushing a total of 0 entities and 0 collections)
}

It seems that every query is putting results (4 states in database) in cache:

    269717 nanoseconds spent performing 4 L2C puts;
    209324 nanoseconds spent performing 4 L2C puts;

I expect second query to retrive data from cache but it statistics hits and misses are zero:

    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;

My question is: why L2C hits and L2C misses are zero when I execute second query?

UPDATE

This is how I'm running my query

Reposiory

I'm working with QueryDSL with spring data jpa. I followed this tutorial to integrate my JpaRepository using QueryDslPredicateExecutor

public interface StateRepository extends JpaRepository<State, Integer>, QueryDslPredicateExecutor<State> {
 }

Service

In my service I execute my query using queryDLS predicates and PathBuilder as shown in this great article so that, I can find States or any other entity by any field. E.g "StateName=Texas", "StatePopulation=26448193".

@Autowired
StateRepository repo;

public List<State> getStatesByFields(String options, Integer page, Integer pageSize,String order) {
    PredicateBuilder predicateBuilder = new PredicateBuilder().onEntity("State")
    Pattern pattern = Pattern.compile(OPERATION_PATTERN);
    Matcher matcher = pattern.matcher(options + ",");
    while (matcher.find()) {
        predicateBuilder.with(matcher.group(1), matcher.group(2), matcher.group(3));
    }
    PageRequest pag = new PageRequest(page, page_size)
    BooleanExpression predicate = predicateBuilder.build();
    //findAll is provided by QueryDslPredicateExecutor interface
    Page<State> result = repo.findAll(predicate, pag);
}

Queries runs like charm, but data seems not be cached.

like image 916
CharlieG. Avatar asked Sep 01 '15 00:09

CharlieG.


People also ask

Can we use Spring data JPA and Hibernate together?

You cant actually use both of them in the same application. For backwards compatibility. We are expanding our application and want to start using Spring Data JPA, but still keep the old hibernate implementation. Its better you develop your new application as a separate microservice and use spring data jpa ..

Does Hibernate use EhCache?

Hibernate Second Level cache providers include EHCache and Infinispan, but EHCache is more popular and we will use it for our example project. However before we move to our project, we should know different strategies for caching an object.

How do I enable EhCache?

Ehcache can be configured in two ways: The first way is through Java POJO where all configuration parameters are configured through Ehcache API. The second way is configuration through XML file where we can configure Ehcache according to provided schema definition.


2 Answers

Entity cache only works if the entity is retrieved using its id e.g. load(), get(). It doesn't work if you use query.

To enable caching the query you have to use Query cache. e.g.

List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
    .setEntity("blogger", blogger)
    .setMaxResults(15)
    .setCacheable(true)
    .setCacheRegion("frontpages")
    .list();

or using jpa

query.setHint(“org.hibernate.cacheable”, true);

I'm not sure how to implement this with QueryDslPredicateExecutor, but hopes this will help to understand hibernate 2nd lvl cache

L2C hits and L2C misses are equal to zero means that hibernate never search the data from the cache because you are retrieving records using query without enabling the query cache

L2C puts not zero because hibernate cache the records to be used later if you retrieve the entity by its id(this is different than caching the query result)

like image 60
KSTN Avatar answered Oct 22 '22 11:10

KSTN


Your cache region configuration has a typo. In the entity class, your configuration (@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region="cache_states")) points to a different region than one defined in ehcache.xml (cache name="cache_estados")

like image 22
Rafal G. Avatar answered Oct 22 '22 10:10

Rafal G.