You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring6+Hibernate6中通过代码可靠配置Ehcache二级缓存?

问题:Spring 6 + Hibernate 6.1.7 + Ehcache 3.10.x 二级缓存自定义代码配置异常

背景

  • 环境:Spring 6、Hibernate 6.1.7、Ehcache 3.10.x
  • Spring方法级缓存(@Cacheable/@CachePut/@CacheEvict)运行正常
  • 二级缓存依赖Hibernate自动创建默认缓存时可正常工作,但默认缓存无内存限制,引发生产环境堆内存溢出
  • 尝试手动创建自定义配置的二级缓存时,切换实体的CacheConcurrencyStrategy(如READ_WRITE→NONSTRICT_READ_WRITE)会触发ClassCastException;即使调整value类型,高负载下仍会出现类型不匹配异常

问题根源

手动创建缓存时指定了具体的value类型(如AbstractReadWriteAccess.Item或StandardCacheEntryImpl),但Hibernate不同缓存策略下会存储多种类型的对象:

  • READ_WRITE策略存储AbstractReadWriteAccess.Item
  • NONSTRICT_READ_WRITE策略存储StandardCacheEntryImpl
  • 高负载场景下会生成SoftLockImpl作为锁对象
    Ehcache的类型校验会因此触发ClassCastException

正确解决方案

1. 修改二级缓存代码配置

核心是将缓存的key和value类型统一设为Object.class,跳过类型校验;同时添加内存限制、过期时间等自定义规则:

@Configuration
@EnableCaching
public class MyCacheConfig {
    @Bean
    public CacheManager ehCacheManager() {
        CacheManager cacheManager = Caching.getCachingProvider().getCacheManager();

        // 原有Spring方法级缓存配置(保持不变)
        var springCacheConfig = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(SomeMethodArg.class, SomeMethodReturnType.class)
                .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMinutes(30)))
                .withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
                        .heap(1000, EntryUnit.ENTRIES)) // 可选:限制堆内存条目数
                .build();
        cacheManager.createCache("my_spr_cache", Eh107Configuration.fromEhcacheCacheConfiguration(springCacheConfig));

        // 通用Hibernate二级缓存配置模板
        var hibernateL2CacheTemplate = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(Object.class, Object.class) // 关键:key/value均为Object
                .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMinutes(30)))
                .withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
                        .heap(5000, EntryUnit.ENTRIES) // 限制堆内存使用
                        .offheap(50, MemoryUnit.MB)) // 可选:堆外内存扩展
                .build();
        // 为每个需要缓存的实体创建对应缓存(名称为实体全类名)
        cacheManager.createCache("com.mydomain.model.MyClass", Eh107Configuration.fromEhcacheCacheConfiguration(hibernateL2CacheTemplate));
        // 如有其他实体,重复上述createCache调用

        // 配置查询缓存及配套的更新时间戳缓存
        var queryCacheConfig = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(Object.class, Object.class)
                .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10)))
                .withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
                        .heap(2000, EntryUnit.ENTRIES))
                .build();
        cacheManager.createCache(org.hibernate.cache.spi.QueryCache.class.getName(), Eh107Configuration.fromEhcacheCacheConfiguration(queryCacheConfig));
        cacheManager.createCache(org.hibernate.cache.spi.UpdateTimestampsCache.class.getName(), Eh107Configuration.fromEhcacheCacheConfiguration(queryCacheConfig));

        return cacheManager;
    }
}

2. 调整application.yml配置

将缺失缓存策略改为fail,强制使用手动配置的缓存,避免Hibernate自动生成无限制的默认缓存:

spring:
  jpa:
    properties:
      jakarta:
        persistence:
          sharedCache:
            mode: ENABLE_SELECTIVE
      hibernate:
        cache:
          use_second_level_cache: true
          use_query_cache: true
          region:
            factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
        jakarta:
          cache:
            missing_cache_strategy: fail # 禁止自动创建默认缓存

关键说明

  • 用Object作为缓存类型:绕过Ehcache的类型校验,适配Hibernate不同策略下的多种存储对象
  • 内存限制:通过withResourcePools配置堆内存条目数或堆外内存,解决生产堆溢出问题
  • 查询缓存配套:必须手动创建QueryCache和UpdateTimestampsCache对应的缓存区域,否则查询缓存无法正常工作

内容的提问来源于stack exchange,提问作者RemasteredDruid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 00:48:10