Redis+SpringBoot的简单使用

Redis

Redis基础知识

一共五种类型

String

就像我们下面在springboot中的例子一样,单个USer在redis存储为String,如果多个用户的话,可能会存储为其他的类型

Hash

Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。

List

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

Set

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

Zset

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。

有序集合的成员是唯一的,但分数(score)却可以重复。

List和Set的区别

List方法与set方法的区别

(1)重复对象

list方法可以允许重复的对象,而set方法不允许重复对象

(2)null元素

list可以插入多个null元素,而set只允许插入一个null元素

(3)容器是否有序

list是一个有序的容器,保持了每个元素的插入顺序。即输出顺序就是输入顺序,而set方法是无序容器,无法保证每个元素的存储顺序

引入Redis依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis  默认是lettuce客户端 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.4.5</version>
</dependency>
<!-- redis依赖commons-pool 这个依赖一定要添加 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

配置

spring:
  redis:
    database: 0
    host: r-xxxxx.redis.rds.aliyuncs.com
    port: 6379
    password: xxxx
    lettuce:
      pool:
        max-active: 8
        max-wait: -1
        # 连接池最大空闲连接数(默认是8)
        max-idle: 8
        min-idle: 0
    # 连接超时时间(毫秒)
    timeout: 3000

集群配置(与单机配置的区别???下次补充)

#集群连接示例
#spring.redis.cluster.nodes=192.168.157.135:7000,192.168.157.135:7001,192.168.157.135:7002,192.168.157.135:7003,192.168.157.135:7004,192.168.157.135:7005

自定义RedisTemplate对象

package com.crud.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * 1. 在Redis中,如存入的数据是中文,取出来可能会是乱码。
 * ⑴ 关于Redis的中文乱码问题,这就涉及到RedisTemplate底层的数据类型问题*
 * ⑵ 在RedisTemplate默认的序列化方式是JDK序列化,JDK序列化就会让我们的字符串
 * 转义(这就是造成乱码的原因)
 * ⑶ 所以就需要我们去自定义一个配置类,不使用Redis底层默认的JDK序列化配置方式。
 * - 因此Redis官方表示:如果我们自己写一个RedisTemplate类,Redis底层的默认的
 * RedisTemplate类就会失效!
 * 默认情况下的模板只能支持 RedisTemplate<String,String>,只能存入字符串,很多时候,我们需要自定义 RedisTemplate ,
 * 设置序列化器(需要实体对象实现Serializable),这样我们可以很方便的操作实例对象。如下所示:
 * **/
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
        RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        /**
         * 使用GenericJackson2JsonRedisSerializer序列化时,会保存序列化的对象的包名和类名,反序列化时以这个作为标示就可以反序列化成指定的对象。
         * **/
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        return redisTemplate;
    }
}

编写缓存配置类RedisConfig用于调优缓存默认配置,RedisTemplate<String, Object>的类型兼容性更高

spring-data-redis中序列化类有以下几个:

GenericToStringSerializer:可以将任何对象泛化为字符创并序列化
Jackson2JsonRedisSerializer:序列化Object对象为json字符创(与JacksonJsonRedisSerializer相同)
JdkSerializationRedisSerializer:序列化java对象
StringRedisSerializer:简单的字符串序列化

JdkSerializationRedisSerializer序列化被序列化对象必须实现Serializable接口,被序列化除属性内容还有其他内容,长度长且不易阅读,默认就是采用这种序列化方式

存储内容如下:

“\xac\xed\x00\x05sr\x00!com.oreilly.springdata.redis.User\xb1\x1c \n\xcd\xed%\xd8\x02\x00\x02I\x00\x03ageL\x00\buserNamet\x00\x12Ljava/lang/String;xp\x00\x00\x00\x14t\x00\x05user1”

JacksonJsonRedisSerializer序列化,被序列化对象不需要实现Serializable接口,被序列化的结果清晰,容易阅读,而且存储字节少,速度快

存储内容如下:

“{“userName”:“user1”,“age”:20}”

StringRedisSerializer序列化

一般如果key、value都是string字符串的话,就是用这个就可以了

测试

entity

其实我们应该在类上添加“implements Serializable”的,但是没有添加也没关系。why?

package com.crud.entity;

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String username;
    private String avatar;
    private String email;
    private String password;
    private Integer status;
    private String created;
    private String last_login;
}

测试类

package com.crud.controller;

import com.crud.config.RedisConfig;
import com.crud.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

@SpringBootTest
public class RedisOneTest extends AbstractTestNGSpringContextTests {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void testRedis(){
        String key="waiwai";
        User user =new User();
        user.setUsername("zhazha");
        user.setPassword("caicai");
        user.setEmail("zhangyin@newlink.com");
        user.setId(999);
        user.setStatus(0);
        user.setAvatar("www.baidu.com");
        user.setCreated("2021-06-18 10:36:42");
        user.setLast_login("2021-06-18 10:36:42");
        ValueOperations<String,Object> valueOperations=redisTemplate.opsForValue();
        valueOperations.set(key,user);
        User userone= (User) redisTemplate.opsForValue().get(key);
        System.out.println("userone"+":"+userone.toString());
    }

}

在上面这个例子中我们使用redisTemplate调用了opsForValue会得到一个ValueOperations操作。这个是专门操作String类型的数据,所以里面的键值对中键为String,而值是我们的User。当然redisTemplate还为我们提供了下面几个。

redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set

检查结果

进入redis数据库0(因为在yaml中我们配置的是0),根据key来查询,查询出的结果

{
@class:"com.crud.entity.User"
id:999
username:"zhazha"
avatar:"www.baidu.com"
email:"zhangyin@newlink.com"
password:"caicai"
status:0
created:"2021-06-18 10:36:42"
last_login:"2021-06-18 10:36:42"
}

添加util工具类

在企业开发中,要使用Redis操作各种类型,就将Redis里面各个指令的操作方法封装
到一个工具类中RedisUtil,直接调用这个RedisUtil工具类即可!

其实这个东西也可以不手写一遍,只是写一遍能加深印象,也可以直接复制我的,网上也有一大把,我也是照着写的,中间自己按照自己的理解改了一些。而且,自己写的工具类,自己用着舒服不是。

package com.crud.utils;

import com.sun.corba.se.spi.ior.ObjectKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    /**
     * 指定缓存失效时间
     * */
    public boolean expire(String key,long time){
        try{
            if(time>0){
                redisTemplate.expire(key,time, TimeUnit.SECONDS);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 获取key过期时间
     * */
    public long getExpire(String key){
        return redisTemplate.getExpire(key);
    }
    /**
     * 判断key是否存在
     * **/
    public boolean hasKey(String key){
        try{
            return redisTemplate.hasKey(key);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 删除缓存
     * 告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。
     * String... key :不定个数的String参数
     * key 可以传一个值 或多个
     * **/
//    @SuppressWarnings("unchecked")
//    public void del(String... key){
//        if (key !==null && key.length>0){
//            if (key.length==1){
//                redisTemplate.delete(key[0]);
//            }else {
//                redisTemplate.delete(CollectionUtils.arrayToList(key));
//            }
//        }
//    }
    //----------------------------------------字符串-----------------------------------------
    /**
     * 普通缓存获取
     * **/
    public Object get(String key){
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }
    /**
     * 普通缓存存入
     * **/
    public boolean set(String key,Object object){
        try{
            redisTemplate.opsForValue().set(key,object);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 普通缓存存入并设置失效时间
     * **/
    public boolean set(String key,Object object,long time){
        try{
            if (time>0){
                redisTemplate.opsForValue().set(key,object,time,TimeUnit.SECONDS);
            }else {
                set(key,object);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 需求是限制IP频繁访问某接口,用的方案是使用redis记录访问IP的值,先设定好初始值,每次访问自增,达到某限定值后,进行阻止
     * 递增
     * delta 要增加几(大于0)
     * **/
    public long incrementUp(String key,long delta){
        if (delta<=0){
            throw new RuntimeException("递增因子必须大于0");
        }else {
            return redisTemplate.opsForValue().increment(key,delta);
        }
    }
    /**
     * 递减
     *delta 要减少几(大于0)
     * **/
    public long deincrementDown(String key,long detla){
        if (detla<0){
            throw new RuntimeException("递减因子必须大于0");
        }else {
            return redisTemplate.opsForValue().increment(key,-detla);
        }
    }

    //================================================Hash======================================================
    /**
     *HashGet
     * 根据key和map的key(item),获取其中一个value
     */
    public Object hashMapGetValueByItem(String key,String item){
        return redisTemplate.opsForHash().get(key,item);
    }
    /**
     * 获取该key下所有的键值对
     * */
    public Map<Object,Object> hashMapGetAllByKey(String key){
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * HashSet
     * map 对应多个键值
     * */
    public boolean hashMapPutMap(String key, Map<String,Object> map){
        try{
            redisTemplate.opsForHash().putAll(key,map);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * HashSet,但是要加上过期时间
     * map 对应多个键值
     * **/
    public boolean hashSetMapAndTime(String key,Map<String,Object> map,long time){
        redisTemplate.opsForHash().putAll(key,map);
        try {
            if (time>0){
                expire(key,time);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * **/
    public boolean hashSetOne(String key,String item,Object value){
        try{
            redisTemplate.opsForHash().put(key,item,value);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * **/
    public boolean hashSetOneAndTime(String key,String item,Object value,long time){
        try{
            redisTemplate.opsForHash().put(key,item,value);
            if (time>0){
                expire(key,time);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 删除hash中的值
     * */
    public void hashDel(String key,String item){
        redisTemplate.opsForHash().delete(key,item);
    }
    /***
     * 判断hash表中是否有该项的值
     * */
    public boolean hashHasKey(String key,String value){
        return redisTemplate.opsForHash().hasKey(key,value);
    }
    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * 竟然没有value
     * **/
    public double hashIncrementUp(String key,String item,double dalte){
        return  redisTemplate.opsForHash().increment(key,item,dalte);
    }
    /**
     * hash递减,如果不存在,就会创建一个 并把新增后的值返回
     * 竟然没有value
     * **/
    public double hashIncrementDown(String key,String item,double dalte){
        return redisTemplate.opsForHash().increment(key,item,-dalte);
    }
    //==================================================Set===============================================
    /**
     * 根据key获取所有set中的值
     * **/
    public Set<Object> setGet(String key){
        try{
            return redisTemplate.opsForSet().members(key);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 根据value从一个Set中查询是否存在
     * **/
    public boolean setHasKey(String key,Object value){
        try{
            redisTemplate.opsForSet().isMember(key,value);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将数据放到Set中,可以放多个
     * 返回的是成功的个数
     * **/
    public long setSet(String key, Object... objects){
        try {
           return redisTemplate.opsForSet().add(key,objects);
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }
    /***
     *将数据放入Set,并且设置过期时间
     * **/
    public long setSetAndTime(String key,long time,Object... objects){
        try{
            Long count =redisTemplate.opsForSet().add(key,objects);
            if (time>0){
                expire(key,time);
            }
            return count;
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }
    /**
     *获取set的长度
     ***/
    public long setGetLength(String key){
        try{
            return redisTemplate.opsForSet().size(key);
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }
    /***
     * 从set中删除某一个或者多个value
     * 返回被删除的个数
     *
     * */
    public long setDelete(String key,Object... object){
        try{
            return    redisTemplate.opsForSet().remove(key,object);
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }
    //=============================list==================================
    /***
     * 获取list的内容
     * 从第start个到end个
     * **/
    public List<Object> listGet(String key,long start,long end){
        try {
            return redisTemplate.opsForList().range(key,start,end);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 获取list缓存的长度
     *
     * **/
    public long listGetLengh(String key){
        try{
            return redisTemplate.opsForList().size(key);
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }
    /**
     * 通过索引获取list中的值
     * index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * **/
    public Object listGetByIndex(String key,long index){
        try {
           return redisTemplate.opsForList().index(key,index);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 将单条记录放入到list缓存内,且不指定失效时间
     * **/
    public boolean listInsertOne(String key,Object value){
        try {
             redisTemplate.opsForList().rightPush(key,value);
             return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将单条记录放入到list缓存内,且指定失效时间
     * **/
    public boolean listInsertOneAndTime(String key,Object value,long time){
        try {
            redisTemplate.opsForList().rightPush(key,value);
            if (time>0){
                expire(key,time);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将List
     *记录放入到list缓存内,不指定失效时间
     * **/
    public boolean listInsertAll(String key,List<Object> list){
        try{
            redisTemplate.opsForList().rightPushAll(key,list);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将List
     *记录放入到list缓存内,且指定失效时间
     * **/
    public boolean listInsertAllAndTime(String key,List<Object> list,long time){
        try {
            redisTemplate.opsForList().rightPushAll(key,list);
            if (time>0){
                expire(key,time);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
                            }
            }
    /**
     * 根据索引,修改list中的某一条记录
     * Set可没这个功能
     * */
    public boolean listUpdateByIndex(String key,long index,Object value){
        try{
            redisTemplate.opsForList().set(key,index,value);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     *   从存储在键中的列表中删除等于值的元素的第一个计数事件。
     *   count> 0:删除等于从左到右移动的值的第一个元素;
     *   count< 0:删除等于从右到左移动的值的第一个元素;
     *   count = 0:删除等于value的所有元素
     * **/
    public long listRemove(String key, Object value,long count){
        try {
            return redisTemplate.opsForList().remove(key,count,value);
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }
    //=====================Zset===============================
    /***
     * 获取Zset中的所有元素,为什么没有对应的方法呢,set还有一个members方法呢
     * **/
//    public Set<Object> zsetGetAll(String key){
//        try {
//            return redisTemplate.opsForZSet().
//        }
//    }
    /**
     * ZSET有序集合添加内容,单个值,无有效期
     * **/
    public boolean zsetInsertOne(String key,Object value,double score){
        try {
            redisTemplate.opsForZSet().add(key,value,score);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 获取变量指定区间的元素
     * */
    public Set<Object> zsetRange(String key,long start,long end){
        try {
            return redisTemplate.opsForZSet().range(key,start,end);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 获取某一个元素的权重
     * **/
    public double zsetGetScore(String key,Object value){
        try{
            return redisTemplate.opsForZSet().score(key,value);
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }
    /**
     * 获取某一个ZSET中的个数
     * **/
    public double zsetGetSize(String key){
        try {
            return redisTemplate.opsForZSet().zCard(key);
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }
    /***
     * range,根据下标获取元素
     * **/
    public Object zsetGetByindex(String key,long start,long end){
        try {
            return redisTemplate.opsForZSet().range(key,start,end);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    /***
     * range,根据权重获取元素
     * **/
    public Object zsetGetByScore(String key,double start,double end){
        try {
            return redisTemplate.opsForZSet().rangeByScore(key,start,end);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

添加Controller层测试

package com.crud.controller;

import com.crud.entity.User;
import com.crud.service.impl.UserServiceImpl;
import com.crud.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/crud")
public class UserController {
    @Autowired
    private UserServiceImpl userService;
    @Autowired
    private RedisUtils redisUtils;
    @RequestMapping(value = "/redis",method = RequestMethod.POST)
    public String redis(@RequestParam(value = "id")Integer id){
        User user=userService.getUserById(id);
        String username=user.getUsername();
        boolean hasKey=redisUtils.hasKey(username);
        if (hasKey){
            Object value=redisUtils.get(username);
            String str = value.toString();
            System.out.println("从缓存中取出来的数据为:"+str);
            return "从缓存中取出来的数据为"+str;
        }else {
            System.out.println("从数据库中取数据");
            redisUtils.set(username,user,100000);
            return "从数据库中取数据"+user.toString();
        }
    }
}

第一次请求接口前

根据数据库查询知道了id=1对应的username=markerhub,用markerhub在redis中查询,查询无结果

第一次请求后

请求参数 id=1
出参

从数据库中取数据User(id=1, username=markerhub, avatar=https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg, email=xxx@xxx.com, password=96e79218965eb72c92a549dd5a330112, status=0, created=2021-06-02 17:52:01, last_login=null)

根据markerhub在redis中查询,有结果

{
@class:"com.crud.entity.User"
id:1
username:"markerhub"
avatar:"https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg"
email:"xxx@xxx.com"
password:"96e79218965eb72c92a549dd5a330112"
status:0
created:"2021-06-02 17:52:01"
last_login:null
}

第二次请求后

出参

从缓存中取出来的数据为User(id=1, username=markerhub, avatar=https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg, email=xxx@xxx.com, password=96e79218965eb72c92a549dd5a330112, status=0, created=2021-06-02 17:52:01, last_login=null)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis是一个开源的内存数据存储系统,它可以用作数据库、缓存和消息中间件。在Spring Boot中使用Redis可以通过添加相关依赖和配置来实现。 首先,你需要在Maven的pom.xml文件中添加Redis的依赖。可以使用以下代码片段: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> </dependency> ``` 这样就可以引入Spring Boot和Spring Data Redis的相关依赖。 接下来,你需要在application.yml(或application.properties)文件中配置Redis的连接信息。可以参考以下示例: ``` spring: redis: host: 192.168.2.9 port: 6379 password: CacheDB123 ``` 这里配置了Redis的主机地址、端口和密码。 在Spring Boot中,你可以使用RedisTemplate来操作Redis。例如,你可以使用redisTemplate.opsForValue()来操作字符串类型的数据,redisTemplate.opsForHash()来操作哈希类型的数据,redisTemplate.opsForList()来操作列表类型的数据,等等。你可以根据你的需求选择合适的操作方法。 总结起来,使用Redis和Spring Boot可以通过添加依赖、配置连接信息和使用RedisTemplate来实现对Redis的操作。 #### 引用[.reference_title] - *1* *2* [springboot + redis](https://blog.csdn.net/CaBCo/article/details/121993117)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Redis+SpringBoot简单使用](https://blog.csdn.net/qq13933506749/article/details/118193308)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值