如何在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.ItemNONSTRICT_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
相关产品推荐
相关产品推荐

