Spring Boot 2.0结合Hibernate 5和JCache实现EhCache 3

10

我正在尝试使用EhCache作为Hibernate的二级缓存,但TTL无法正常工作。

以下是我的依赖项:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-jcache</artifactId>
</dependency>

<dependency>
  <groupId>org.ehcache</groupId>
  <artifactId>ehcache</artifactId>
</dependency>

<dependency>
  <groupId>javax.cache</groupId>
  <artifactId>cache-api</artifactId>
</dependency>

这是我的YAML配置:

spring:
  jpa:
    show-sql: true
    properties:
      hibernate:
        dialect: Dialect
        cache:
          use_second_level_cache: true
          region.factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
          use_query_cache: true
  cache:
    jcache:
      config: classpath:ehcache.xml

这是我的实体类如何配置的:

@Entity
@javax.persistence.Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class PersonEntity {
  //
}

该实体的JpaRepository:

public interface PersonRepository extends JpaRepository<PersonEntity, Integer> {
  @org.springframework.data.jpa.repository.QueryHints({
      @javax.persistence.QueryHint(name = "org.hibernate.cacheable", value = "true")
  })
  List<PersonEntity> findByName(String name);
}

我已将缓存配置为在2秒钟后过期,但调用findByName仍然使用缓存(第一个SQL查询之后没有打印出任何SQL查询)。

这是ehcache.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://www.ehcache.org/v3">

  <cache-template name="simple">
    <expiry>
      <ttl>2</ttl>
    </expiry>
    <heap>100</heap>
  </cache-template>

  <cache alias="com.sample.PersonEntity" uses-template="simple"/>

</config>

编辑: 我已经进行了一些调试。我在org.ehcache.jsr107.ExpiryPolicyToEhcacheExpiry中添加了一个断点:

javax.cache.expiry.Duration duration = this.expiryPolicy.getExpiryForCreation();

出于某种原因,这个持续时间是无限的。那么可能是配置没有设置好?我知道xml被读取了,因为当我使其无效(例如删除堆标签)时,会出现错误。

在此输入图片描述


你有收到任何异常吗?你怎么知道它没有工作? - Vishrant
你可以尝试更新数据库并检查它是否仍在使用缓存数据或更新后的数据吗? - Vishrant
我已经更新了数值,但无论我等了多久,这些数值都没有刷新。 - George
我不知道为什么,但在我的演示项目中,一切都按预期工作。只需运行它并访问GET localhost:8080/users?name=smith。用户的TTL为1分钟。希望这可以帮助... - Cepr0
+1 - 那么在原问题中,cache.jcache.config=classpath:ehcache.xml 是关于什么的。请花点时间回复。我想知道上述内容与 hibernate.javax.cache.uri=classpath:ehcache.xml 之间的区别。 - samshers
显示剩余4条评论
2个回答

15

我认为我找到了问题的原因 - 您没有指定ehcache.xml文件的位置:

spring:
  jpa:
    properties:
      hibernate:
        javax.cache:
          provider: org.ehcache.jsr107.EhcacheCachingProvider
          uri: classpath:ehcache.xml
        cache:
          use_second_level_cache: true
          region.factory_class: jcache
          use_query_cache: true

在这种情况下,Hibernate使用默认配置创建缓存。以下是我的演示项目日志的一部分:

17:15:19 WARN [main] org.hibernate.orm.cache: HHH90001006: Missing cache[user] was created on-the-fly. The created cache will use a provider-specific default configuration: make sure you defined one. You can disable this warning by setting 'hibernate.javax.cache.missing_cache_strategy' to 'create'.

你知道这在哪里有记录吗?这解决了我的问题。 - George
@George 在这里:https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#caching-provider-jcache。 - Cepr0
感谢分享这些配置。我的配置有误,因为它们使用相同的名称创建了两个不同的缓存,因此ttl无法正常工作。 - SPS
+1 - 那么在原问题中,cache.jcache.config=classpath:ehcache.xml 是关于什么的。请花点时间回复。我想知道上述内容与 hibernate.javax.cache.uri=classpath:ehcache.xml 之间的区别。 - samshers

0

当您在实体顶部设置@Cacheable注释时,它会创建一个区域,其中KEY是实体的ID,而Value是实体本身。这意味着如果您通过ID访问键,则会命中缓存。如果您使用Spring Data和findById,则会命中缓存。如果您创建一个名为findByName的方法,则访问将不是通过键,因此不会命中由Cacheable注释定义的缓存区域。另一方面,它将命中查询缓存,但查询缓存位于完全不同的区域。根据您的配置,您根本没有配置查询缓存。为了使此方法命中任何缓存,您需要使用此属性添加它:

spring:jpa:properties:hibernate:cache:use_query_cache: true

或者您可以在存储库方法的顶部指定@Cacheable,以此定义一个新区域。

您可以配置默认缓存,这应该捕获StandardQueryCahache。

<defaultCache 
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="3600"
    timeToLiveSeconds="3600">
  </defaultCache>

在EhCache2中,您可以通过此元素配置标准查询缓存:
  <cache
name="org.hibernate.cache.internal.StandardQueryCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600">

不确定在 ehcache 3 中如何实现。我认为应该是相同的,因为 StandartQueryCache 类是 hibernate 包的一部分,而不是 ehcache 包的一部分。

我还认为你需要设置:
hibernate.javax.cache.provider = org.ehcache.jsr107.EhcacheCachingProvider


我遇到了这个错误:Error:(23, 33) java: 无法访问net.sf.ehcache.CacheManager 找不到net.sf.ehcache.CacheManager的类文件 - George
我认为这已经过时了,适用于Ehcache 2。 - George
是的,你说得对。这是Ehcache2。尝试添加hibernate.javax.cache.provider = org.ehcache.jsr107.EhcacheCachingProvider。还有另一个提供程序,非Hibernate的那个,不清楚Spring Boot自动配置默认选择哪个。值得设置一下。 - Alexander Petrov
我还看到您已将查询修改为findByName。缓存区域由ID定义,因此ID是关键。如果您按名称选择,则不会命中缓存区域,除非您使用Cacheable注释该方法,然后您将有效地创建一个与“com.sample.PersonEntity”不同的新缓存区域。将该方法更改为findById并查看是否命中缓存。 - Alexander Petrov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接