SpringBoot + JPA 为实体不工作启用二级缓存

Sus*_*hil 6 java jpa second-level-cache spring-boot ehcache-3

我使用 Springboot 2.1 和 spring data-jpa 使用@RepositoryRestResource进行持久化。我已经为我的 API 调用启用了缓存,并且它与@Cacheable配合得很好,但是现在我想为我的所有 JPA 实体启用二级缓存并且具有以下配置,但仍然对这些实体的任何查询都在触发休眠查询而不使用缓存. 请让我知道我缺少此实体缓存的内容。

Gradle依赖:

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'
Run Code Online (Sandbox Code Playgroud)

}

应用程序属性

    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
Run Code Online (Sandbox Code Playgroud)

缓存文件

<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>
Run Code Online (Sandbox Code Playgroud)

配置文件

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;

   ......
}
Run Code Online (Sandbox Code Playgroud)

服务等级:

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Jpa存储库:

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

@RepositoryRestResource
public interface DBConfigRepository extends JpaRepository<Config, String> {
}
Run Code Online (Sandbox Code Playgroud)

只有标有@Cacheable响应的方法会被缓存并且运行良好,但是当涉及到实体级别的数据缓存时,总是会触发 db 查询以提取数据。请让我知道我在这里缺少什么。

编辑:发现只有 JPA 的findById()从缓存返回,因为休眠缓存以水合形式存储为键值对,其中 id 是键。而findAll()findByType()等方法总是触发数据库查询来获取数据。如何让它们存储并从缓存中返回。?