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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 03:14:59