如何在Payara+Eclipselink中为方法周期缓存查询实体?
解决EclipseLink方法级临时本地缓存问题(Payara 5.201 + EclipseLink 2.6.4)
针对你遇到的PermitType实体重复查询数据库、@Cache注解未生效,且需要方法执行期间临时缓存、方法结束后自动释放的需求,以下是几种可行的解决方案:
一、先排查@Cache注解未生效的常见原因
如果只是想让EclipseLink的默认缓存生效,先检查以下几点:
- 确认
@Cache的配置参数:避免设置cacheType=CacheType.NONE或expiry=0,推荐配置示例:@Entity @Cache(cacheType = CacheType.SOFT_WEAK, cacheSize = 100, expiry = 3600000) // 1小时过期,软引用缓存 public class PermitType { // 实体字段 } - 避免使用原生SQL查询:原生SQL默认绕过EclipseLink缓存,需添加查询提示
QueryHints.CACHE_USAGE强制走缓存。 - 检查EntityManager生命周期:确保同一方法内的查询使用同一个EntityManager实例,缓存是绑定到Session/EntityManager的。
二、方法级临时缓存实现方案
方案1:手动控制EclipseLink Session缓存生命周期
利用EclipseLink的Session API,在方法开始时启用缓存,方法结束后清除指定实体的缓存,完全符合你的需求:
public void myProcessingMethod() { // 从容器注入或EntityManagerFactory获取EntityManager EntityManager em = entityManagerFactory.createEntityManager(); try { // 转换为EclipseLink原生Session,以便操作缓存 Session session = em.unwrap(Session.class); // 确保PermitType的缓存配置生效(覆盖可能的错误注解配置) ClassDescriptor descriptor = session.getDescriptor(PermitType.class); descriptor.setCacheType(CacheType.SOFT_WEAK); descriptor.setCacheSize(50); // 第一次查询:从数据库加载并写入缓存 List<PermitType> allPermits = em.createQuery("SELECT p FROM PermitType p", PermitType.class) .setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheThenDatabase) .getResultList(); // 后续方法内的所有查询都会直接从缓存读取 PermitType typeById = em.find(PermitType.class, 1L); PermitType typeByCode = em.createQuery("SELECT p FROM PermitType p WHERE p.code = :code", PermitType.class) .setParameter("code", "BUILDING") .setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheThenDatabase) .getSingleResult(); } finally { // 方法结束后,清除PermitType的缓存,释放内存 if (em.isOpen()) { Session session = em.unwrap(Session.class); session.getIdentityMapAccessor().clearIdentityMap(PermitType.class); } // 关闭EntityManager if (em != null && em.isOpen()) { em.close(); } } }
方案2:使用本地集合实现轻量临时缓存
如果不需要依赖EclipseLink的缓存机制,直接在方法内用Java集合实现缓存,方法结束后集合会被GC自动回收,简单高效:
public void myProcessingMethod() { EntityManager em = entityManagerFactory.createEntityManager(); // 方法内本地缓存:按ID和业务代码分别存储,方便快速查询 Map<Long, PermitType> permitCacheById = new HashMap<>(); Map<String, PermitType> permitCacheByCode = new HashMap<>(); try { // 预加载所有PermitType到本地缓存(如果数据量小) List<PermitType> allPermits = em.createQuery("SELECT p FROM PermitType p", PermitType.class).getResultList(); for (PermitType permit : allPermits) { permitCacheById.put(permit.getId(), permit); permitCacheByCode.put(permit.getCode(), permit); } // 后续查询直接从本地缓存取,无需访问数据库 PermitType type1 = permitCacheById.get(1L); PermitType type2 = permitCacheByCode.get("TRAFFIC"); // 按需加载场景:如果未预加载,先查缓存再查数据库 Long targetId = 3L; PermitType type3 = permitCacheById.computeIfAbsent(targetId, id -> em.find(PermitType.class, id)); } finally { if (em != null && em.isOpen()) { em.close(); } // 方法结束后,本地集合自动被GC回收,无需手动清理 } }
内容的提问来源于stack exchange,提问作者Joe McCay
相关产品推荐
相关产品推荐

