从零搭建SpringBoot后台框架(十三)——集成Redis

121 阅读4分钟

一、安装Redis

  1. Windowss电脑可以从github.com/ServiceStac… 下载兼容Windows系统的redis
  2. 下载后解压,cd到对应目录执行redis-server.exe redis.windows.conf即可启动redis
  3. 也可以直接到对应目录下双击执行redis-server.exe

image.png

二、添加依赖

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

三、添加配置

spring:
  redis:
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器端口号
    port: 6379
    # 使用的数据库索引,默认是0
    database: 0
    # 连接超时时间(毫秒)30*60*1000=30分钟=1800000
    timeout: 1800000
    lettuce:
      pool:
        # 最大阻塞等待时间,负数表示没有限制
        max-wait: -1
        # 连接池中的最大空闲连接
        max-idle: 5
        # 连接池中的最小空闲连接
        min-idle: 0
        # 连接池中最大连接数,负数表示没有限制
        max-active: 20

四、添加配置类

  1. 设置使用json序列化对象和hash的key、value
  2. 不序列话final字段
  3. 设置日期json转换方式objectMapper.registerModule(new JavaTimeModule())
  4. 设置仅序列化非空字段objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL),节省存储空间
package com.example.demo.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

@Configuration
public class RedisConfig {
    /**
     * retemplate相关配置
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);

        // 使用 JSON 序列化方式
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //不序列化final字段
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        //解决LocalDateTime的转换问题
        objectMapper.registerModule(new JavaTimeModule());
        //仅序列化非空字段
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置 key 和 value 的序列化方式
        template.setKeySerializer(jackson2JsonRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置 hash key 和 hash value 的序列化方式
        template.setHashKeySerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }
}

五、封装redis操作常用方法

  1. 封装get、set(含设置超时时间)、del方法
package com.example.demo.service;

public interface RedisService {
    Object get(String key);

    boolean set(String key, Object value);

    /**
     *
     * @param key
     * @param value
     * @param seconds 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return
     */
    boolean set(String key, Object value, long seconds);

    void del(String... keys);
}
package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.example.demo.service.RedisService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author chenzl105
 */
@Service
public class RedisServiceImpl implements RedisService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    @Override
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    @Override
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param seconds  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    @Override
    public boolean set(String key, Object value, long seconds) {
        try {
            if (seconds > 0) {
                redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param keys 可以传一个值 或多个
     */
    @Override
    public void del(String... keys) {
        if (ObjectUtils.isNotEmpty(keys)) {
            if (keys.length == 1) {
                redisTemplate.delete(keys[0]);
            } else {
                redisTemplate.delete(Arrays.asList(keys));
            }
        }
    }
}

六、功能验证

  1. getById方法默认从redis获取缓存,获取不到再从数据库读取
  2. 读取完成后,把缓存更新到redis中

@Resource
private RedisService redisService;

//...
//省略部分代码
//...

@PostMapping("/getById")
@ApiOperation(value = "根据id查询系统用户表")
@ApiImplicitParams(
        @ApiImplicitParam(name = "id", value = "主键id", required = true, dataType = "String")
)
public RestResult<UserInfoDetailResp> getById(String id) {
    UserInfo userInfo = (UserInfo) redisService.get("userInfo:" + id);
    if (null == userInfo) {
        userInfo = userInfoService.getById(Long.parseLong(id));
    }
    if (null == userInfo) {
        throw new BusinessException("id不存在");
    }
    redisService.set("userInfo:" + id, userInfo,10);
    UserInfoDetailResp resp = UserInfoConverter.INSTANCE.convert2DetailResp(userInfo);
    return RestResult.getSuccessResult(resp);
}
  1. UserInfo类的创建和更新时间,设置一下转json的格式为yyyy-MM-dd HH:mm:ss
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty("创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

@ApiModelProperty("创建人")
@TableField(fill = FieldFill.INSERT)
private String createdBy;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty("最后修改时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
  1. Redis中存储的内容如下
[
	"com.example.demo.model.UserInfo",
	{
		"id": 2,
		"userName": "2",
		"vip": false,
		"deleteFlag": 0,
		"createTime": "2023-10-10 14:49:57",
		"createdBy": "",
		"updateTime": "2023-10-10 14:49:57",
		"updatedBy": "",
		"version": 0
	}
]
  1. 测试可以发现,当redis中没有缓存时,会查询数据库,并且序列化到Redis中
  2. 10s内再次请求此接口,直接从Redis读取,不再查询数据库(没有打印SQL)
  3. 相同ID,10s内没有再次请求时,redis中缓存会自动删除

七、项目地址

gitee

PS:可以通过tag下载本文对应的代码版本

八、结尾

集成Redis已完成,有问题可以联系chenzhenlindx@qq.com

九、参考文章

  1. 从零搭建自己的SpringBoot后台框架(十二)
  2. springboot整合redis