SpringBoot + JPA enable second level caching for entity not working
Asked Answered
A

0

6

I am using Springboot 2.1 and spring data-jpa for persistence using @RepositoryRestResource. I have enabled caching for my API calls and that works well with @Cacheable, But now I want to enable second-level cache for all my JPA entities and have below configurations but still any query on these entities are firing hibernate queries and not using cache. Please let me know what I am missing for this Entity caching.

Gradle Dependency:

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
implementation 'org.springframework.boot:spring-boot-starter-jersey'

implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation  'org.ehcache:ehcache:3.7.1'
implementation 'javax.cache:cache-api'
compile group: 'org.hibernate', name: 'hibernate-jcache', version: '5.3.10.Final'

runtimeOnly 'mysql:mysql-connector-java'

}

application.properties

    spring.datasource.url=jdbc:mysql://localhost:3306/mar_db
spring.datasource.username=root
spring.datasource.password=

spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true 
spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory
spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE

spring.cache.jcache.config=classpath:ehcache.xml

ehcache.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.ehcache.org/v3"
        xmlns:jsr107="http://www.ehcache.org/v3/jsr107">

    <service>
        <jsr107:defaults enable-statistics="true" />
    </service>
    <cache alias="readOnlyEntityData">
        <key-type>java.lang.Object</key-type>
        <expiry>
            <ttl unit="minutes">360</ttl>
        </expiry>
        <listeners>
            <listener>
                <class>com.tfsc.ilabs.selfservice.common.utils.CacheLogger</class>
                <event-firing-mode>ASYNCHRONOUS</event-firing-mode>
                <event-ordering-mode>UNORDERED</event-ordering-mode>
                <events-to-fire-on>CREATED</events-to-fire-on>
                <events-to-fire-on>UPDATED</events-to-fire-on>
                <events-to-fire-on>EXPIRED</events-to-fire-on>
                <events-to-fire-on>REMOVED</events-to-fire-on>
                <events-to-fire-on>EVICTED</events-to-fire-on>
            </listener>
        </listeners>
        <resources>
            <heap unit="entries">1000</heap>
            <offheap unit="MB">256</offheap>
        </resources>
    </cache>
</config>

Config.java

import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import java.util.Objects;

@Entity
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "readOnlyEntityData")
public class Config {

    @Id
    @NotNull
    @Column(unique = true)
    private String code;
    @NotNull
    private String value;
    @NotNull
    @Column(columnDefinition = "boolean default true")
    private boolean status;
    @NotNull
    private String type;

   ......
}

Service class:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class DBConfigServiceImpl implements DBConfigService {

    @Autowired
    private DBConfigRepository dbConfigRepository;

    @Override
    public List<ConfigDTO> findAll() {
        return dbConfigRepository.findAll().stream().map(Config::toDTO).collect(Collectors.toList());
    }

    @Cacheable(value = "readOnlyEntityData", keyGenerator = "cacheKeyGenerator")
    @Override
    public ConfigDTO findByCode(String code) {
        Optional<Config> config =  dbConfigRepository.findById(code);
        if(config.isPresent()){
            return config.get().toDTO();
        }else {
            throw new NoSuchResourceException(new ErrorObject("Config not found {0}", code));
        }
    }
}

JpaRepository:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource
public interface DBConfigRepository extends JpaRepository<Config, String> {
}

Only Methods marked with @Cacheable responses are cached and works well, But when It comes to Entity level caching of data, always db query is fired to pull the data. Please let me know what I am missing here.

Edit: Found that only JPA's findById() returns from the cache as hibernate cache is stored in hydrated form as key value pair, where id is the key. Whereas findAll() and findByType() etc methods always fires DB queries to get data. How to make them store and return from cache.?

Aletheaalethia answered 19/5, 2020 at 8:13 Comment(2)
could it be that you will need to add findAll(), findByType() (any other methods where you want to use second level cache) mehtods to DBConfigRepository and annotate them with @QueryHints(value = { @QueryHint(name = org.hibernate.jpa.QueryHints.HINT_CACHEABLE, value = "true")}) (since this annotation can not be applied to the whole repository)?Seale
@Seale Thanks after adding QueryHints it started caching queries. Any Idea how it will expire and get removed from cache.? And how to control as these QueryHints are not controlled by ehcache.xml which I have declared.Aletheaalethia

© 2022 - 2024 — McMap. All rights reserved.