Springboot整合Reids当作数据库使用

1:说明

本项目主要使用redis模拟数据库基本增、删、改、查、分页查询功能。如果涉及复杂SQL语句查询,请直接使用主流关系型数据库。

本项目基于Springboot工程,使用了ReidTemplate。


2:项目构建

Web工程和Redis以及Lombok相关依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>

Redis相关配置,使用了yml文件进行配置,其中配置根据需求自行修改即可。

spring:
  redis:
    database: 2         #redis提供了16个database 编号为0-15
    host: 127.0.0.1
    port: 6379            #端口号
    password:     #没有密码
    jedis:
      pool:
        max-active: 8     #最大连接数
        max-idle: 8       #最大空闲连接数
        max-wait: -1      #最大阻塞等待时间
        min-idle: 0       #最小空闲连接数

RedisTemplate相关配置,主要修改了Key,Value的序列化方式,便于直接查看。如果不配置则默认使用JDK进行序列化。

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<Object, Object> testTemplate(RedisConnectionFactory factory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
       Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);
        template.setValueSerializer(jacksonSeial);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(jacksonSeial);
        template.setHashValueSerializer(jacksonSeial);
        template.setDefaultSerializer(jacksonSeial);
        template.afterPropertiesSet();
        return template;
    }
}

RedisUtil工具类,主要使用了Hash和ZSet

@Component
@Slf4j
public class RedisUtil {
    @Autowired
    private RedisTemplate<Object,Object> testTemplate;

    /**
     * 存放单个hash缓存
     *
     * @param key   键
     * @param hkey  键
     * @param value 值
     * @return
     */
    public boolean hput(String key, Object hkey, Object value) {
        try {
            testTemplate.opsForHash().put(key, hkey, value);
            log.debug("hput {} = {}", key + hkey, value);
            return true;
        } catch (Exception e) {
            log.warn("hput {} = {}", key + hkey, value, e);
        }
        return false;
    }

    /**
     * 获取hash中所有数据
     * @param key hash存取的key
     * @return
     */
    public List<Object> getValues(String key){
        List<Object> values=null;
        try {
            values = testTemplate.opsForHash().values(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return values;
    }

    /**
     * 在hash集合中根据hashKey获取单个对象数据
     * @param key
     * @param hashKey
     * @return
     */
    public Object get(String key,Object hashKey){
        Object o=null;
        try {
            o= testTemplate.opsForHash().get(key,hashKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return o;
    }

    /**
     * 分页存取数据
     *
     * @param key   hash存取的key
     * @param hkey  hash存取的hkey
     * @param score 指定字段排序
     * @param value
     * @return
     */
    public boolean setPage(String key, Object hkey, double score, Object value) {
        boolean result = false;
        try {
            testTemplate.opsForZSet().add(key + ":page", hkey, score);
            result = hput(key, hkey, value);
            log.debug("setPage {}", key);
        } catch (Exception e) {
            log.warn("setPage {}", key, e);
        }
        return result;
    }

    /**
     * 根据页码和条数取出数据
     * @param key
     * @param page
     * @param limit
     * @return
     */
    public List<Object> getResultByPage(String key, int page, int limit){
        List<Object> result=new ArrayList<>();
        try {
            //1 100000代表score的排序氛围值,即从1-100000的范围
            Set<Object> pageResult=testTemplate.opsForZSet().rangeByScore(key + ":page", 1, 100000, (page - 1) * limit, limit);
            for (Object o : pageResult) {
                Object o1 = this.get(key, o);
                result.add(o1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 计算key值对应的数量
     *
     * @param key
     * @return
     */
    public Integer getSize(String key) {
        Integer num = 0;
        try {
            Long size = testTemplate.opsForZSet().zCard(key + ":page");
            log.debug("getSize {}", key);
            return size.intValue();
        } catch (Exception e) {
            log.warn("getSize {}", key, e);
        }
        return num;
    }

    public boolean removeHashKey(String key,Object hashKey){
        boolean result=false;
        try {
            testTemplate.opsForZSet().remove(key+":page",hashKey);
            testTemplate.opsForHash().delete(key,hashKey);
            result=true;
            log.info("删除数据成功!");
        } catch (Exception e) {
            log.info("出现异常:{}",e.getMessage());
        }
        return result;
    }

    /**
     * 获取最大值
     * @param key
     * @return
     */
    public Integer getMaxValue(String key){
        Integer result=null;
        try {
            Set<Object> set = testTemplate.opsForZSet().reverseRange(key+":page", 0, 0);
            for (Object o : set) {
                result= (Integer) o;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取最大分数
     * @param key
     * @param value
     * @return
     */
    public Double getMaxScore(String key,Object value){
        Double result=null;
        try {
            result = testTemplate.opsForZSet().score(key+":page", value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book implements Serializable {

    private Integer id;
    private String name;
    private String author;
    
}

测试Controller方法

@RestController
@Slf4j
public class RedisController {
    @Autowired
    private RedisUtil redisUtil;
    private static final String REDIS_KEY = "books";

    @GetMapping("/books")
    public ConcurrentMap<String, Object> fetchList(@RequestParam("page") Integer page, @RequestParam("limit") Integer limit) {
        ConcurrentMap<String, Object> concurrentMap = new ConcurrentHashMap<>(16);
        List<Object> resultByPage = redisUtil.getResultByPage(REDIS_KEY, page, limit);
        if (!CollectionUtils.isEmpty(resultByPage)) {
            concurrentMap.put("message", "success");
            concurrentMap.put("data", resultByPage);
            concurrentMap.put("code", 1000);
            return concurrentMap;
        }
        concurrentMap.put("message", "暂无数据!");
        concurrentMap.put("code", 1001);
        return concurrentMap;
    }

    @PostMapping("/book")
    public ConcurrentMap<String, Object> saveBook(@RequestBody Book book) {
        ConcurrentMap<String, Object> concurrentMap = new ConcurrentHashMap<>(16);
            Integer maxValue = redisUtil.getMaxValue(REDIS_KEY);
            if (maxValue == null) {
                book.setId(1);
                redisUtil.setPage(REDIS_KEY, book.getId(), 1, book);
            } else {
                Double maxScore = redisUtil.getMaxScore(REDIS_KEY, maxValue);
                book.setId(maxValue + 1);
                redisUtil.setPage(REDIS_KEY, book.getId(), maxScore + 1, book);
            }
            concurrentMap.put("message", "新增成功");
            concurrentMap.put("data", book);
            return concurrentMap;
    }

    @GetMapping("/book/{id}")
    public ConcurrentMap<String, Object> getBook(@PathVariable("id") Integer id) {
        ConcurrentMap<String, Object> concurrentMap = new ConcurrentHashMap<>(16);
        Object o = redisUtil.get(REDIS_KEY, id);
        if (o == null) {
            concurrentMap.put("message", "查询的数据不存在");
        } else {
            concurrentMap.put("message", "success");
            concurrentMap.put("data", o);
        }
        return concurrentMap;
    }

    @PutMapping("/book")
    public ConcurrentMap<String, Object> modifyBook(@RequestBody Book book) {
        ConcurrentMap<String, Object> concurrentMap = new ConcurrentHashMap<>(16);
        Object o = redisUtil.get(REDIS_KEY, book.getId());
        if (o == null) {
            concurrentMap.put("message", "书籍不存在,修改失败");
        } else {
            if (redisUtil.hput(REDIS_KEY, book.getId(), book)) {
                concurrentMap.put("message", "修改成功");
                concurrentMap.put("data", book);
            } else {
                concurrentMap.put("message", "修改失败");
            }
        }
        return concurrentMap;
    }
    @DeleteMapping("/book/{id}")
    public ConcurrentMap<String,Object> removeBook(@PathVariable("id") Integer id){
        ConcurrentMap<String, Object> concurrentMap = new ConcurrentHashMap<>(16);
        Object o = redisUtil.get(REDIS_KEY, id);
        if (o == null) {
            concurrentMap.put("message", "书籍不存在,删除失败");
            return  concurrentMap;
        }
        if (redisUtil.removeHashKey(REDIS_KEY,id)){
            concurrentMap.put("message", "删除成功");
        }else {
            concurrentMap.put("message", "删除失败");
        }
        return concurrentMap;
    }
}

3.测试

基于Postman测试

为了方便测试我先插入了10条记录

    @Test
    public void insertData(){
        for (int i=1;i<=10;i++){
            redisUtil.setPage("books",i,i,new Book(i,"书籍"+i,"作者"+i));
        }
    }
zset集合数据
hash集合数据
  • 测试查询 按一页5条记录进行测试


    第一页数据
    第二页数据

查询结果一切正常!

  • 新增数据

    新增数据测试

传入数据时并为给定id,为后台自增。

查询测试,由于一页五条,此时第11条记录为第三页 所有只有这一条记录。

第三页数据
  • 根据id查询测试

    根据编号查询
    不存在的数据
  • 修改测试

    修改数据测试

再次查询

查询修改之后的数据
  • 删除测试

    删除数据测试

再次查询

查询被删除的数据

4.结语

至此,redis模拟数据操作全部完成。可以根据自身需求调整RedisUtil类中的方法。此demo只为一个简单实例,Redis的用途也并非仅此而已。我只不过是用来模拟一下数据库使用而已。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。