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

如何避免JPARepository count()调用时出现ClassCastException?

为什么会出现HashSet转Number的ClassCastException?

我帮你分析下这个问题——从你给出的代码和堆栈信息来看,这个异常的根源是Spring缓存中存储的对象类型和你的Service方法返回类型不匹配,而且因为Spring用了CGLIB动态代理包装你的Service类,所以异常栈会指向那个代理生成的类,看起来有点绕,但本质是缓存出了问题。

先回顾下你的核心代码:

Service层代码

@Cacheable(cacheResolver = CachingConfiguration.CACHE_RESOLVER_NAME)
public long countElement() { 
    return repository.count(); 
}

Controller层代码

@ApiOperation(value = "Returns total count of elements")
@GetMapping(value = "/element/count", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public long countElements() { 
    logger.info("Count elements"); 
    return service.countElement(); 
}

异常的核心原因

堆栈显示异常发生在CGLIB代理的countElement方法里,说明Spring从缓存里取出来的是一个HashSet对象,但你的方法声明要返回long,强转时自然就抛出了ClassCastException。可能的触发场景有这几个:

  1. 缓存Key冲突了
    你的缓存解析器CachingConfiguration.CACHE_RESOLVER_NAME大概率是多个方法共享的缓存空间,而且Key生成策略没有做区分。比如另一个返回HashSet的方法,它生成的缓存Key和countElement()的Key完全一样——当那个方法先被调用并把HashSet存入缓存后,countElement()再去取缓存时,拿到的就是错误类型的对象。

  2. 缓存序列化/反序列化出问题
    如果你的缓存用了序列化存储(比如Redis),可能在序列化或反序列化过程中出现了类型混淆,导致原本的Long被反序列化成了HashSet。不过这种情况相对少见,更多还是Key冲突的锅。

  3. 缓存配置错误
    检查你的CachingConfiguration,是不是给countElement()方法分配了错误的缓存实例,或者缓存的Value类型配置有误。比如有些缓存配置默认是为集合类型设计的,而你的方法返回单个数值,就会导致类型不匹配。

具体解决思路

针对这些原因,你可以按以下步骤排查和修复:

  • 给缓存Key加上唯一标识
    确保countElement()的缓存Key是独一无二的,比如用方法名作为Key的一部分:

    @Cacheable(cacheResolver = CachingConfiguration.CACHE_RESOLVER_NAME, key = "#root.methodName")
    public long countElement() { 
        return repository.count(); 
    }
    

    如果方法有参数,还要把参数也加入Key生成逻辑,避免不同参数的请求共用同一个Key。

  • 隔离缓存空间
    给统计类方法单独指定缓存名称,和其他返回集合的方法分开:

    @Cacheable(cacheNames = "elementCountCache", cacheResolver = CachingConfiguration.CACHE_RESOLVER_NAME)
    public long countElement() { 
        return repository.count(); 
    }
    

    这样即使Key相同,不同缓存空间也不会互相干扰。

  • 先清理现有缓存验证
    手动清空当前的缓存数据,然后单独调用/element/count接口。如果不再抛出异常,说明之前的缓存里确实存了错误类型的对象,清理后重新缓存正确的Long值就解决问题了。

  • 检查缓存序列化配置
    如果用的是Redis这类需要序列化的缓存,确认序列化器配置正确。比如使用GenericJackson2JsonRedisSerializer时,要保证它能正确处理Long类型,避免类型混淆。

  • 开启缓存日志排查细节
    打开Spring Cache的DEBUG日志,看看缓存的读写过程,就能知道什么时候存入了HashSet,以及对应的Key是什么。比如在日志配置里加:

    <logger name="org.springframework.cache" level="DEBUG"/>
    

    日志会清晰展示缓存的操作细节,帮你快速定位到冲突的方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:15:19