SpringBoot 整合 SpringCache 使用Redis(使用 lettuce)实现查询缓存

36 篇文章 2 订阅
8 篇文章 0 订阅

第一步:创建SpringBoot项目

maven依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

有的文章说需要再加入依赖spring-boot-starter-cache,实际测试spring-boot-starter-web中已经包含了spring cache相关的依赖。

application.yml

spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    #    password: 1234
    timeout: 10000ms  # 超时时间
    lettuce:
      pool:
        max-idle: 8 # 最大空闲连接数,默认值为8
        max-wait: -1ms # 最大连接阻塞等待时间,默认值-1
        min-idle: 2 # 最小空闲连接数
        max-active: 20 #最大连接数
  cache:
    type: redis #缓存类型
    redis:
      cache-null-values: false #不缓存null数据
      time-to-live: 50000ms #超时时间
      use-key-prefix: false #不使用前缀

第二步:Cache配置文件

//启用Redis缓存,这个注释必须加上,即使单元测试也得加上
@EnableCaching
@Configuration
public class CacheRedisConfig {
    /**
     * 过期时间7天
     */
    private final Duration timeToLive = Duration.ofDays(7);

    private final StringRedisSerializer keySerializer = new StringRedisSerializer();

    /**
     * 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
     */
    private final Jackson2JsonRedisSerializer valueSerializer = new Jackson2JsonRedisSerializer(Object.class);

    /**
     * 代码块,会优先执行
     * 用来设置Jackson2JsonRedisSerializer
     */ {
        ObjectMapper objectMapper = new ObjectMapper();
        //设置所有访问权限以及所有的实际类型都可序列化和反序列化
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        //下面两行解决Java8新日期API序列化问题
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());

        valueSerializer.setObjectMapper(objectMapper);
    }

    /**
     * 由于原生的redis自动装配,在存储key和value时,没有设置序列化方式,故自己创建redisTemplate实例
     *
     * @param factory
     * @return
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // key采用String的序列化方式
        template.setKeySerializer(keySerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(keySerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(valueSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(valueSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean(name = "cacheManager")
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        // 配置序列化(解决乱码的问题),通过config对象对缓存进行自定义配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                // 设置缓存的默认过期时间
                .entryTtl(timeToLive)
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer))
                // 不缓存空值
                .disableCachingNullValues();

        //根据redis缓存配置和reid连接工厂生成redis缓存管理器
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
        return redisCacheManager;
    }

    /**
     * 配置事务管理器
     *
     * @param dataSource
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

}

在SpringBoot2.0之后,spring容器自动的生成了StringRedisTemplate和RedisTemplate<Object,Object>,可以直接注入。但是在实际使用中,大多情况下不会直接使用RedisTemplate<Object,Object>,而是会对key和value进行序列化,所以我们还需要新增一个配置类。

第三步:Redis工具类

参看博客:Redis工具类:RedisTemplate

第四步:实体类:

@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Province implements Serializable {
    private static final long serialVersionUID = -7311767494745575164L;
    /**
     * 编号
     */
    @ApiModelProperty(value = "编号")
    private Integer id;
    /**
     * 省名
     */
    @ApiModelProperty(value = "省名")
    private String name;
    /**
     * 所属地区
     */
    @ApiModelProperty(value = "所属地区")
    private String area;
    /**
     * 显示优先级
     */
    @ApiModelProperty(value = "显示优先级")
    private Integer priority;
    /**
     * 状态 1正常 0删除
     */
    @ApiModelProperty(value = "状态 1正常 0删除")
    private Integer status;
}

第五步:Service 接口及实现类:

使用MyBatisPlus实现

DeptService.java

public interface ProvinceService extends IService<Province> {
    /**
     * 添加省份
     *
     * @param province
     * @return 返回修改后的省份
     */
    Province insertProvince(Province province, Boolean flag);

    /**
     * 根据id查找省份
     *
     * @param id
     * @return
     */
    Province findProvinceById(Integer id, Boolean flag);

    /**
     * 从Redis中查找所有的省份
     *
     * @return
     */
    List<Province> findAllProvince(String key, Boolean flag);

    /**
     * 修改省份
     *
     * @param province
     * @return 返回修改后的省份
     */
    Province updateProvince(Province province, Boolean flag);


    /**
     * 删除id指定的省份
     *
     * @param id
     * @return
     */
    Province deleteProvinceById(Integer id, Boolean flag);
}

DeptServiceImpl.java

@Service
@CacheConfig(cacheManager = "cacheManager")
public class ProvinceServiceImpl extends ServiceImpl<ProvinceMapper, Province> implements ProvinceService {
    @Resource
    private RedisUtil<List<Province>> provinceListRedisUtil;

    @Override
    @Cacheable(value = RedisConst.PROVINCE, key = "#key", condition = "#flag==true")
    public List<Province> findAllProvince(String key, Boolean flag) {
        List<Province> provinceList = baseMapper.selectList(null);
        return provinceList;
    }

    @Override
    @CachePut(value = RedisConst.PROVINCE, key = "#province.id", condition = "#flag==true")
    public Province insertProvince(Province province, Boolean flag) {
        int insertRes = baseMapper.insert(province);
        if (insertRes == 1) {
            //更新Province列表
            List<Province> provinceList = provinceListRedisUtil.get(RedisConst.PROVINCE + "::all");
            provinceList.add(province);
            //排序
            provinceList.sort((item1, item2) -> {
                int t1 = item1.getPriority() == null ? 0 : item1.getPriority();
                int t2 = item2.getPriority() == null ? 0 : item2.getPriority();
                return t2 - t1;
            });
            provinceListRedisUtil.set(RedisConst.PROVINCE + "::all", provinceList);
        }
        return province;
    }

    @Override
    @Cacheable(value = RedisConst.PROVINCE, key = "#id", condition = "#flag==true")
    public Province findProvinceById(Integer id, Boolean flag) {
        Province province = baseMapper.selectById(id);
        return province;
    }

    @Override
    @CachePut(value = RedisConst.PROVINCE, key = "#province.id", condition = "#flag==true")
    public Province updateProvince(Province province, Boolean flag) {
        int updateRes = baseMapper.updateById(province);
        if (updateRes == 1) {
            province = baseMapper.selectById(province.getId());
            //目标城市下的所有Province
            List<Province> provinceList = provinceListRedisUtil.get(RedisConst.PROVINCE + "::all");
            for (int i = 0; i < provinceList.size(); i++) {
                Province item = provinceList.get(i);
                //如果参数country在目标城市中,即没有换城市
                if (item.getId().equals(province.getId())) {
                    provinceList.set(i, province);
                    break;
                }
            }
            provinceListRedisUtil.set(RedisConst.PROVINCE + "::all", provinceList);
        }
        return province;
    }

    @Override
    @CacheEvict(value = RedisConst.PROVINCE, key = "#id", condition = "#flag==true")
    public Province deleteProvinceById(Integer id, Boolean flag) {
        int deleteRes = baseMapper.deleteById(id);
        if (deleteRes == 1) {
            //目标城市下的所有Province
            List<Province> provinceList = provinceListRedisUtil.get(RedisConst.PROVINCE + "::all");
            for (int i = 0; i < provinceList.size(); i++) {
                Province item = provinceList.get(i);
                //如果参数country在目标城市中,即没有换城市
                if (item.getId().equals(id)) {
                    provinceList.remove(i);
                    break;
                }
            }
            provinceListRedisUtil.set(RedisConst.PROVINCE + "::all", provinceList);
        }
        return new Province();
    }
}

第六步:测试代码

@SpringBootTest
class ProvinceServiceImplTest {
    @Resource
    private ProvinceService provinceService;

    @Test
    void findAllProvince() {
        List<Province> provinceList = provinceService.findAllProvince("all", true);
        provinceList.forEach(System.out::println);
    }

    @Test
    void insertProvince() {
        Province province = Province.builder()
                .name("haha")
                .priority(79)
                .build();
        provinceService.insertProvince(province, true);
    }

    @Test
    void findProvinceById() {
        Province province = provinceService.findProvinceById(22, true);
        System.out.println(province);
    }

    @Test
    void updateProvince() {
        Province province = Province.builder().id(47).name("america").build();
        provinceService.updateProvince(province, true);
    }

    @Test
    void deleteProvinceById() {
        Province province = provinceService.deleteProvinceById(47, true);
        System.out.println(province);
    }
}
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梁云亮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值