Mario B
Mario B

Reputation: 2355

Spring-Boot - Activating Hibernate 2nd Level Cache

i am trying to activate Hibernate 2nd Level Cache on a Spring-Boot Application that uses spring-boot-starter-data-jpa. I use Ehcache 2 and have hibernate-ehcache in my classpath for that

I used the following properties

spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
spring.jpa.properties.hibernate.generate_statistics=true

Also i created ehcache.xml in src/main/resources like this (just a test with a cache that never expires)

<ehcache updateCheck="false" monitoring="autodetect"
     dynamicConfig="true">

  <defaultCache
        maxElementsInMemory="100000"
        maxElementsOnDisk="10000000"
        eternal="true"
        overflowToDisk="false">
  </defaultCache>
</ehcache>

Also the Entity is Annotated with @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) and in the logfile I can clearly see that the default cache is used for the Entity, so it seems to recognize the Annotation and initialize the cache as usual.

WARN .h.c.e.AbstractEhcacheRegionFactory : HHH020003: Could not find a specific ehcache configuration for cache named [at.test.demo.persistence.entity.Employee]; using defaults.

Now to test this I wrote a simple Test that inserts 3 Employees and loads them using usual JPA-Entitymanager. After that I am trying to verify that the loaded Employee actually really got into the Cache, by calling this:

Assert.assertTrue(em.getEntityManagerFactory().getCache().contains(Employee.class, employeeId));

But that always fails. Also the SessionFactory-Staticstics shows zeros for everything, that cant be right.

Any ideas?

EDIT I stripped down the project and added it to public gitlab repo for you to reproduce it: https://gitlab.com/matrium00/reproduce-cache-issue

There is a single unit test for this that fails right now because of the problem.

EDIT2 Here is an example of a working XML configuration of em-factory without Spring-Boot. I know I could create the necessary beans in my Configuration-Class manually, but there has to be a better way:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
      autowire="byName" depends-on="flyway">
    <property name="packagesToScan" value="at.my.package.demo.persistence.entity"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.default_schema">${jdbc.schema}</prop>

            <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
            <prop key="hibernate.cache.use_second_level_cache">true</prop>
            <prop key="hibernate.cache.use_query_cache">true</prop>
            <prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
        </props>
    </property>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
</bean>

Upvotes: 4

Views: 5607

Answers (1)

Anatoly Shamov
Anatoly Shamov

Reputation: 2686

You have the @Transactional annotation on your test method and @DataJpaTest (which means @Transactional too) on your test class.

As there is a single transaction, there will be a single session. When you are accessing the same persistent object inside of the same transaction, Hibernate uses first level cache (session cache). To take a hit in the second level cache, you should have different sessions (different transactions).

Simple replace of @DataJpaTest with @SpringBootTest and remove of @Transactional from test method makes test to work.

See also:

Upvotes: 3

Related Questions