SpringBoot在使用@Cacheable缓存对象为空时遇到的坑

今天生产环境遇到@Cacheable的一个问题,记录一下

1、发现问题

接口突然请求失败,查询日志发现有如下报错

Cache ‘cache:getCustRange’ does not allow ‘null’ values. Avoid storing null via ‘@Cacheable(unless="#result == null")’ or configure RedisCache to allow ‘null’ via RedisCacheConfiguration.
java.lang.IllegalArgumentException: Cache ‘cache:getCustRange’ does not allow ‘null’ values. Avoid storing null via ‘@Cacheable(unless="#result == null")’ or configure RedisCache to allow ‘null’ via RedisCacheConfiguration

日志提示信息说的很清楚,缓存中不允许存储null值,在@Cacheable中加上(unless="#result == null")

或者在RedisCacheConfiguration中配置成允许缓存中存储null值

下面来看一下有问题的代码

@Cacheable(cacheNames = "cache:getCustRange", key = "#root.args[0]['custId'] + ''")
  public CustRangeVo getCustRange(Map<String, Object> map) {
    return custMapper.getCustRange(map);
  }

很明显,是这里的CustRangeVo 对象为空了,保存到缓存时就报错了

2、解决问题

两种方法来解决这个问题

  • 设置结果为空时不缓存
  • 设置允许缓存为null值

设置结果为空时不缓存,直接加上unless="#result == null"就好了

@Cacheable(cacheNames = "cache:getCustRange", key = "#root.args[0]['custId'] + ''", unless="#result == null")
  public CustRangeVo getCustRange(Map<String, Object> map) {
    return custMapper.getCustRange(map);
  }

注意,我这里的场景是99.9%会查询到数据,极端情况下对象才会为空

假如你的大部分场景查询都为空,你不缓存空的话,会导致大部分请求命中数据库,你的缓存加的就没有意义了

设置允许缓存为null值
需要配置RedisCacheConfiguration,我们看一下源码

public void put(Object key, @Nullable Object value) {

		Object cacheValue = preProcessCacheValue(value);
		//可以看到报这个错的地方有个条件判断,不允许value为空的情况下value为空值才会报这个错误
		if (!isAllowNullValues() && cacheValue == null) {

			throw new IllegalArgumentException(String.format(
					"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
					name));
		}

		cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
	}

那我们继续来看一下这个isAllowNullValues()

protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {

		//可以看到此处allowCacheNummValues最终取的是RedisCacheConfiguration的cacheNullValues
		super(cacheConfig.getAllowCacheNullValues());

		Assert.notNull(name, "Name must not be null!");
		Assert.notNull(cacheWriter, "CacheWriter must not be null!");
		Assert.notNull(cacheConfig, "CacheConfig must not be null!");

		this.name = name;
		this.cacheWriter = cacheWriter;
		this.cacheConfig = cacheConfig;
		this.conversionService = cacheConfig.getConversionService();
	}

所以你只要把RedisCacheConfiguration 的cacheNullValues设置为true,就可以缓存null值了

当然,我使用的方案是设置结果为空时不缓存,RedisCacheConfiguration 的方案我没有测试过

从文档上看应该是可行的,有兴趣的同学可以自己测试一下

如果感觉对你有些帮忙,想跟我一起学习,坚信技术改变世界,请关注Java天堂公众号,我会定期分享自己的学习成果,第一时间推送给你

在这里插入图片描述

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot是一个开发框架,它简化了使用Spring框架进行Java应用程序开发的过程。Redis是一个内存数据结构存储系统,它可以用作缓存和数据库。@Cacheable是Spring框架的注解之一,它可以用于缓存方法的返回值。 要在Spring Boot中使用Redis和@Cacheable来实现缓存,首先需要配置Redis连接。可以通过在`application.properties`或`application.yml`文件中添加以下配置来完成: ```yaml spring.redis.host=127.0.0.1 spring.redis.port=6379 ``` 接下来,在需要缓存的方法上使用`@Cacheable`注解。例如,假设我们有一个名为`getUserById`的方法,用于根据用户ID获取用户信息: ```java @Service public class UserService { @Cacheable(value = "users", key = "#id") public User getUserById(Long id) { // 从数据库或其他数据源获取用户信息 return userRepository.findById(id); } } ``` 在上述示例中,`@Cacheable`注解用于将方法的返回值缓存起来。其中,`value`属性指定了缓存的名称,`key`属性指定了缓存的键。在这个例子中,缓存的名称为"users",缓存的键为方法的参数id。 最后,需要在Spring Boot应用程序的启动类上添加`@EnableCaching`注解来启用缓存功能: ```java @SpringBootApplication @EnableCaching public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 以上就是使用Spring Boot、Redis和@Cacheable实现缓存的基本步骤。通过配置Redis连接,使用`@Cacheable`注解来标记需要缓存的方法,并在启动类上添加`@EnableCaching`注解来启用缓存功能,可以轻松地实现缓存功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值