如何在insert抛异常时失效@Cacheable缓存或确保缓存正确性?
解决方案
问题根源
@CachePut默认仅在方法正常返回时更新缓存,但如果存在事务或异常处理的特殊场景,可能导致未正确持久化的实例被缓存。结合你的情况,序列化异常发生在saveAndFlush阶段,若缓存操作先于异常触发,就会缓存data字段异常的GlobalData实例,后续get方法读取缓存时返回null。
方案1:给@CachePut增加生效条件
修改DataModelRepository的insert方法,通过condition确保仅在方法成功返回有效实例时才更新缓存:
@CachePut( cacheNames = CACHE_NAME, key = "#root.target.class.getInterfaces()[0].getSimpleName() + '@' + #entity.getPK()", condition = "#result != null" // 仅返回非null时缓存 ) default T insert(T entity) { return saveAndFlush(entity); }
同时在get方法的异常捕获块中,手动清除可能生成的无效缓存条目:
try { data = new GlobalData<>(); data.setType(datatype); data.setData(clazz.newInstance()); data = globalDataRepository.insert(data); } catch (Exception e) { log.error("初始化GlobalData失败,类型:{}", datatype, e); // 清除潜在的无效缓存 String cacheKey = globalDataRepository.getClass().getInterfaces()[0].getSimpleName() + '@' + datatype; Cache cache = cacheManager.getCache(DataModelRepository.CACHE_NAME); if (cache != null) { cache.evict(cacheKey); } return null; }
注意:需要在包含get方法的服务类中注入CacheManager。
方案2:手动控制缓存时机,替代注解
放弃@CachePut,改为在insert方法中手动处理缓存,确保数据库操作成功后再写入缓存:
// 移除insert方法上的@CachePut注解 default T insert(T entity) { // 先执行数据库持久化,确认无异常 T savedEntity = saveAndFlush(entity); // 持久化成功后再写入缓存 String cacheKey = this.getClass().getInterfaces()[0].getSimpleName() + '@' + entity.getPK(); Cache cache = cacheManager.getCache(CACHE_NAME); if (cache != null) { cache.put(cacheKey, savedEntity); } return savedEntity; }
这种方式完全由代码控制缓存时机,从根源避免异常场景下的错误缓存。
方案3:优化双重检查锁的异常处理
当前get方法的双重检查锁中,若insert失败,后续请求会重复进入同步块尝试初始化。可以在异常捕获后清除缓存,同时可选抛出异常让调用方感知错误:
try { data = new GlobalData<>(); data.setType(datatype); data.setData(clazz.newInstance()); data = globalDataRepository.insert(data); } catch (Exception e) { log.error("初始化GlobalData失败,类型:{}", datatype, e); // 清除可能存在的无效缓存 String cacheKey = globalDataRepository.getClass().getInterfaces()[0].getSimpleName() + '@' + datatype; Cache cache = cacheManager.getCache(DataModelRepository.CACHE_NAME); if (cache != null) { cache.evict(cacheKey); } // 可选:抛出异常替代返回null,让调用方明确感知错误 // throw new RuntimeException("获取GlobalData失败", e); return null; }
额外建议:修复序列化异常根源
解决缓存问题的同时,优先排查PersistableConverter的序列化错误:
- 检查
GlobalPersistable实现类是否满足序列化要求(如有无无参构造、字段是否支持序列化) - 验证
PersistableConverter#convertToDatabaseColumn方法的逻辑,确保对象序列化过程无异常
内容的提问来源于stack exchange,提问作者Criwran
相关产品推荐
相关产品推荐

