Spring Boot + JPA2 + Hibernate - 启用二级缓存

64

我正在使用Spring Boot 1.2.5和JPA2来注释实体(并将Hibernate作为底层JPA实现)。

我想在这个设置中使用二级缓存,所以实体被注释了@javax.persistence.Cacheable

我还在application.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.EhCacheRegionFactory

在启动时,Hibernate 抱怨缺少 EhCacheRegionFactory,所以我也将其添加到了 pom 文件中:

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

但是像 entityManager.find(Clazz.class, pk) 这样的查询仍会触发数据库查询,而不是使用缓存的数据。

有任何想法是哪里出了问题吗?


1
希望您在Configuration类中使用@EnableCaching或在xml文件中使用<cache:annotation-driven />启用了缓存管理。 - Arpit Aggarwal
虽然它只用于Spring缓存,但我想在类级别上使用JPA2缓存(更新问题以指示我正在使用@javax.persistence.Cacheable)。 - Daimon
你好,我按照你和其他人提到的所有步骤进行了操作,但仍然无法在Hibernate中启用二级缓存。我正在使用Spring Boot和Hibernate 5.4.15 final jar,在Spring Boot中它给我提供了ehcache 2.10.6 jar。我收到以下警告:“HHH020100:Hibernate的Ehcache二级缓存提供程序已过时。” - Piyush Yawalkar
6个回答

111

总结一下(包括L2缓存和查询缓存):

第一件事是将缓存提供程序(我建议使用EhCache)添加到您的类路径中。

Hibernate < 5.3

添加hibernate-ehcache依赖项。该库包含已停止支持的EhCache 2。

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

Hibernate >=5.3

新版的Hibernate应该使用实现JSR-107(JCache)API的缓存。因此需要两个依赖项,一个是JSR-107 API,另一个是实际的JCache实现(EhCache 3)。

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

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.6.3</version>
    <scope>runtime</scope>
</dependency>

现在让我们转到application.properties/yml文件:

spring:
  jpa:
    #optional - show SQL statements in console. 
    show-sql: true 
    properties:
      javax:
        persistence:
          sharedCache: 
            #required - enable selective caching mode - only entities with @Cacheable annotation will use L2 cache.
            mode: ENABLE_SELECTIVE 
      hibernate:
        #optional - enable SQL statements formatting.
        format_sql: true 
        #optional - generate statistics to check if L2/query cache is actually being used.
        generate_statistics: true
        cache:
          #required - turn on L2 cache.
          use_second_level_cache: true
          #optional - turn on query cache.
          use_query_cache: true 
          region:
            #required - classpath to cache region factory.
            factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory 

对于EhCache 3(或Hibernate >=5.3),应该使用此区域工厂:

factory_class: org.hibernate.cache.jcache.JCacheRegionFactory

你还可以启用Hibernate的TRACE级别日志记录来验证你的代码和配置:

logging:
  level:
    org:
      hibernate:
        type: trace

现在让我们进入代码。要在您的实体上启用L2缓存,您需要添加这两个注释:

@javax.persistence.Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) //Provide cache strategy.
public class MyEntity {
  ...
}

注意 - 如果你想缓存你的@OneToMany@ManyToOne关系 - 还需要在这个字段上添加@Cache注释。

并且要启用Spring Data JPA仓库中的查询缓存,你需要添加正确的QueryHint

public class MyEntityRepository implements JpaRepository<MyEntity, Long> {

  @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
  List<MyEntity> findBySomething(String something);

}

现在通过日志验证您的查询是否仅执行一次,并记得关闭所有调试内容 - 现在您已完成。

注2 - 您还可以将缺失的缓存策略定义为create,如果您想保持默认设置而不在日志中收到警告:

spring:
  jpa:
    properties:
      hibernate:
        javax:
          cache:
            missing_cache_strategy: create

1
如何为缓存实体添加"生存时间(time to live)"? 另外,二级缓存默认情况下是自动清除还是删除自身,还是通过应用程序的运行时间保持可用性? - greperror
4
@LeO,您混淆了两件事情。有hibernate-echache-2hibernate-ehcache-3。第一个(2)是独立的缓存实现,现在已经过时。而第二个(3)是JSR-107 API的实现(也称为jcache)。如果您使用的是ehcache版本3,则需要这两个依赖项(hibernate-jcache和hibernate-ehcache-3)。 - Michał Stochmal
2
@greperror 第二级缓存在每次实体更改时都会自动清除。如果要更改“生存时间”,您需要通过@Bean public CacheManager cacheManager()提供自定义cacheManager bean。有关缓存过期配置的Ehcache文档:https://www.ehcache.org/documentation/3.8/expiry - Michał Stochmal
2
@LeO技术上讲,Hibernate-ehcache(3)在提供范围内使用'javax.cache:cache-api'构件,因此您必须在'compile'范围内手动添加该构件。实际上,'hibernate-jcache'在'compiled'范围内具有此依赖项以及一些额外的记录器和'hibernate-core'依赖项。只需查看这些Maven构件:https://mvnrepository.com/artifact/org.ehcache/ehcache/3.8.1, https://mvnrepository.com/artifact/org.hibernate/hibernate-jcache/5.4.12.Final - Michał Stochmal
最新的Spring Boot和Hibernate默认版本大于5.3,需要将<b>org.hibernate.cache.jcache.JCacheRegionFactory</b>设置为"factory_class"。这解决了我在使用过程中遇到的问题。 - Indrajit Kanjilal
显示剩余5条评论

41

经过进一步探索,以下是我在application.properties中漏掉的内容:

spring.jpa.properties.javax.persistence.sharedCache.mode=ALL

希望能对某人有所帮助 :)


1
谢谢Daimon,对于任何已经来到这里的人,值得注意的是,您需要问题中的配置以及此答案的配置。 - Xiangyu
34
最好设置 spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE,因为只有这样才能遵循您的 @javax.persistence.Cacheable 注解。 - Michael Piefel
我通过设置以下属性解决了这个问题:hibernate.cache.region.factory_class。 - Pasha
我在使用Spring Boot 1.5.9.RELEASE时遇到了错误:运行时发生异常。null: InvocationTargetException: 在类路径资源[org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]中定义的'entityManagerFactory' bean创建失败:初始化方法调用失败;嵌套异常是java.lang.IllegalArgumentException: No enum constant javax.persistence.SharedCacheMode.javax.persistence.SharedCacheMode.ALL。 - Nitul
据我所见,没有必要显式设置hibernate.cache.region.factory_classhibernate.cache.region.use_second_level_cache,因为如果只有一个RegionFactory实现,org.hibernate.cache.internal.RegionFactoryInitiator会自动执行此操作。 - Yevhenii Smyrnov

9

@Daimon 我不太确定,是否

spring.jpa.properties.javax.persistence.sharedCache.mode=ALL

是最好的决定。

引用自Hibernate 20.2.1. 缓存映射文档部分

默认情况下,实体不属于第二级缓存,我们建议您坚持使用此设置。但是,您可以通过在persistence.xml文件中设置shared-cache-mode元素或使用配置中的javax.persistence.sharedCache.mode属性来覆盖此设置。

ENABLE_SELECTIVE(默认和推荐值):仅当明确标记为可缓存时,才会缓存实体。

所以,你是否没有使用@javax.persistence.Cacheable或@org.hibernate.annotations.Cache注释所有受影响的实体?这可能导致查询缓存尝试在第二级缓存中查找受影响的实体失败,然后开始通过单个select获取每个实体。


不,情况并非如此。必须明确设置spring.jpa.properties.javax.persistence.sharedCache.mode。无论是全部设置还是其他设置都是另一回事,与此问题本身无关。 - Daimon
1
补充一下我的意见:使用Spring Boot 1.4以及Ehcache和Hibernate 5.1,你真的至少需要设置区域工厂和共享缓存模式。即使‘ENABLE_SELECITVE’被记录为默认选项,我也需要显式地设置它。 - Michael Piefel

2
你是否添加了 <\p>?
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY) 

你想要缓存哪个类?

0

你的类路径中应该有一个 ehcache.xml 文件。该文件至少应包含默认缓存策略。为了更容易地进行调试,请将其设置为永久以确保实体不会从缓存中清除:

ehcache.xml:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:noNamespaceSchemaLocation="ehcache.xsd"
  Name="CacheManager" 
  maxBytesLocalHeap="250m">

<defaultCache eternal="true"
...
/>

<cache name="org.hibernate.cache.internal.StandardQueryCache"
       eternal="true"
...
/>

为确保一切正常,在应用程序启动期间,您应该拥有以下日志记录:
Could not find a specific ehcache configuration for cache named [com.yourcompany.YourClass]; Using defaults.

这意味着您的实体缓存注释已被正确读取,并将使用默认缓存。

如果您使用entityManager.find(Clazz.class, pk)进行测试,那么它不涉及查询缓存,而仅涉及实体缓存。查询缓存用于查询(em.createQuery(...)和关系)

此外,我使用org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory,但我不知道哪个更好。


1
虽然建议拥有 ehcache.xml,但这并非必需。Ehcache 将使用默认的缓存配置,其中包含 10,000 个元素和 120 秒的 TTL - 这并没有进行调整,但对于许多情况来说是一个足够好的起点。还要注意的是,仅拥有 ehcache.xml 是不够的,您还必须定义正确的缓存以消除所有警告。 - Michael Piefel

-2

您可以使用第三方缓存提供程序,包括JCache、Ehcache、Gvava Cache、Hazelcast Cache和Caffeine Cache。

请参考Quora上的答案,了解如何在Spring Boot中启用和配置二级缓存。


请将下列与编程有关的内容从英语翻译成中文。仅返回翻译的文本:请至少在此处提供链接的一部分,因为链接可能会在未来失效。 - Jef
提供的链接描述了如何配置Spring Boot缓存,而不是Hibernate。它们是不同的东西。 - Rod

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