Java和SpringBoot操作redis

2. java操作Redis

2.1 环境准备

  1. 引入依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

jedis这个名字是java和redis名字的结合,意为java操作redis

  1. 创建 Jedis 对象
 public static void main(String[] args) {
   //1. 创建jedis对象
   //1.redis服务必须关闭防火墙  2.redis服务必须开启远程连接
   Jedis jedis = new Jedis("192.168.72.129", 7000);
   //选择操作的库
   // 如果不传参数的话表示默认连接的是本机的Redis,端口号为6379
   jedis.select(0);
   
   //2. 执行相关操作
   //....
   
   //3. 释放资源
   jedis.close();
 }
  1. 其他配置

如果想要操作使用java操作redis,我们还需要做如下配置:

  • redis服务器是否开放了指定端口
  • 修改配置文件(关闭保护模式,注释掉 bind)
  • 关闭防火墙

查看redis服务器是否开放了这个端口,这里以 7000 端口为例

- 查看是否打开端口号“7000”
firewall-cmd --query-port=7000/tcp  

- 如果返回值为“no”,则输入此命令,开启该“7000”端口
firewall-cmd --add-port=7000/tcp     

修改配置文件:关闭保护模式,注释掉 bind 127.0.0.1

在这里插入图片描述

查看防火墙状态,如果防火墙开启要关闭防火墙

Centos7默认安装了firewalld,如果没有安装的话,可以使用 yum install firewalld firewalld-config进行安装。

yum install firewalld firewalld-config  # 默认安装了防火墙,如果没有安装,使用这个命令安装
firewall-cmd --state  	# 查看防火墙的状态
systemctl stop firewalld  # 关闭防火墙
systemctl start firewalld.service  # 开启防火墙

之后重启redis就可以了

2.2 相关操作

java操作redis的api很多都是和redis的指令一一对应的,很方便,所以下面的api没有写的太详细,很多api基本都是和redis指令是一样的格式

1. 操作库相关的

// 操作库相关的
public class TestRedis {

    public static void main(String[] args) {
        // 创建Jedis对象连接Redis,传入两个参数,分别是Redis服务器的主机地址以及端口号
        // 如果不传参数的话表示默认连接的是本机的Redis,端口号为6379
        Jedis jedis = new Jedis("192.168.72.129", 7000);

        // 选择使用的库,如果不写的话默认使用的是0号库
        jedis.select(0);

        // 获取redis中当前库所有key信息
        Set<String> keys = jedis.keys("*");

        keys.forEach(key -> System.out.println("key = " + key));

        // 操作库相关的
        jedis.flushDB();    // 清空当前库
        jedis.flushAll();   // 清空所有库



        // 释放资源
        jedis.close();
    }
}

在这里插入图片描述

2. 操作key相关的

// 一下api没有写,其实java操作redis中的api和redis都是一一对应的,很好写的
public class TestKey {

    private Jedis jedis;
    @Before
    public void before(){
        jedis = new Jedis("192.168.72.129", 7000);
        jedis.select(0);
    }

    // 测试key相关的
    @Test
    public void testKeys(){
        // 删除一个key
        jedis.del("name");
        // 删除多个key
        jedis.del("name", "age");
        
        // 判断一个key是否存在
        Boolean age = jedis.exists("age");
        System.out.println(age);

        // 设置一个key的超时时间
        Long expire = jedis.expire("name", 100);
        System.out.println(expire);

        // 获取一个key的超时时间
        Long ttl = jedis.ttl("name");
        System.out.println(ttl);

        // 随机获取一个key
        String s = jedis.randomKey();
        System.out.println(s);

        // 修改一个key的名字
        jedis.rename("name", "newName");

        // 查看key对应的value的类型
        String s1 = jedis.type("maps");
        System.out.println(s1);

    }

    @After
    public void after(){
        jedis.close();
    }
}

在这里插入图片描述

3. 操作String相关的

// java操作redis的api很多都是和redis的指令一一对应的,很方便,所以这里不过多赘述
public class TestString {

    private Jedis jedis;
    @Before
    public void before(){
        jedis = new Jedis("192.168.72.129", 7000);
        jedis.select(0);
    }

    // 测试String相关的
    @Test
    public void testString(){
        // set
        jedis.set("name", "小陈");
        // get
        String s = jedis.get("name");
        System.out.println(s);
        // mset
        jedis.mset("content", "好人", "address", "海淀区");
        // mget
        List<String> mget = jedis.mget("content", "address");
        mget.forEach(v -> System.out.println("v = " + v));
        // getset
        String set = jedis.getSet("name", "小明");
        System.out.println(set);
        
        // ....
    }

    @After
    public void after(){
        jedis.close();
    }
}

在这里插入图片描述

4. 操作List相关的

// java操作redis的api很多都是和redis的指令一一对应的,很方便,所以这里不过多赘述
public class TestList {

    private Jedis jedis;
    @Before
    public void before(){
        jedis = new Jedis("192.168.72.129", 7000);
        jedis.select(0);
    }

    // 测试List相关的
    @Test
    public void testList(){
        // lpush
        jedis.lpush("names", "张三", "李四", "王五", "赵六", "win7");

        // rpush
        jedis.rpush("names", "xiaomingming");

        // lrange
        List<String> names = jedis.lrange("names", 0, -1);
        names.forEach(name -> System.out.println("name = " + name));

        // lpop rpop
        String names1 = jedis.lpop("names");
        System.out.println(names1);

        // llen
        jedis.llen("names");

        // linsert 在一个集合中某个元素的之前或之后插入元素
        // BinaryClient.LIST_POSITION.BEFORE 之前插入元素
        // BinaryClient.LIST_POSITION.AFTER 之后插入元素
        jedis.linsert("names", BinaryClient.LIST_POSITION.AFTER, "xiaohei", "xiaobai");

        // ...

    }

    @After
    public void after(){
        jedis.close();
    }
}

在这里插入图片描述

5. 测试Set相关的

// java操作redis的api很多都是和redis的指令一一对应的,很方便,所以这里不过多赘述
public class TestSet {

    private Jedis jedis;
    @Before
    public void before(){
        jedis = new Jedis("192.168.72.129", 7000);
        jedis.select(0);
    }

    // 测试Set相关的
    @Test
    public void testSet(){
        // sadd
        jedis.sadd("sets", "张三", "李四", "王五", "赵六");

        // smembers
        jedis.smembers("sets");

        // sismember
        jedis.sismember("names", "张三");

        // ...

    }

    @After
    public void after(){
        jedis.close();
    }
}

在这里插入图片描述

6. 测试ZSet相关的

// java操作redis的api很多都是和redis的指令一一对应的,很方便,所以这里不过多赘述
public class TestZSet {

    private Jedis jedis;
    @Before
    public void before(){
        jedis = new Jedis("192.168.72.129", 7000);
        jedis.select(0);
    }

    // 测试ZSet相关的
    @Test
    public void testZSet(){

        // zadd
        jedis.zadd("zsets", 10, "张三");

        // zrange
        jedis.zrange("zsets", 0, -1);

        // zcard
        jedis.zcard("zsets");

        // zrangeByScore
       jedis.zrangeByScore("name", "0", "100", 0, 5);

       // ...
    }

    @After
    public void after(){
        jedis.close();
    }
}

在这里插入图片描述

7. 测试Hash相关的

// java操作redis的api很多都是和redis的指令一一对应的,很方便,所以这里不过多赘述
public class TestHash {

    private Jedis jedis;
    @Before
    public void before(){
        jedis = new Jedis("192.168.72.129", 7000);
        jedis.select(0);
    }

    // 测试Hash相关的
    @Test
    public void testHash(){
        // hget
        jedis.hget("maps", "name");
        // hgetall
        jedis.hgetAll("maps");
        // hkeys
        jedis.hkeys("maps");
        // hvals
        jedis.hvals("maps");
        //...
    }

    @After
    public void after(){
        jedis.close();
    }
}

在这里插入图片描述

3. SpringBoot整合Redis

Spring Data(数据) 为Redis 提供了RedisTemplate和StringRedisTemplate,其中StringRedisTemplate是RedisTemplate的子类,两个方法基本一致,不同之处主要体现在操作的数据类型不同,**RedisTemplate中的两个泛型都是Object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的key和value都只能是字符串。**接下来我们来看看在SpringBoot中如何使用StringRedisTemplate和RedisTemplate这两个对象吧。

StringRedisTemplate在操作的时候key和value都是String类型,当然我们在使用的时候是不用写泛型的
StringRedisTemplate<String,String>

RedisTemplate在操作的时候key和value都是Object类型,当然我们在使用的时候是不用写泛型的
RedisTemplate<Object,Object>

注意: 使用RedisTemplate默认是将对象序列化到Redis中,所以放入的对象必须实现对象序列化接口

3.1 环境准备

使用快速构建方式构建一个SpringBoot模块

在这里插入图片描述

在这里插入图片描述

注:使用SpringBoot版本为2.2.5.RELEASE,模块构建完后之后记得修改一下

在这里插入图片描述

1. 引入依赖

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

2. 编写配置文件application.properties

# SpringBoot应用默认端口是8989,这里修改一下SpringBoot应用的端口为8989
server.port=8989

# 配置操作redis相关的(redis服务器的主机ip和端口和使用哪个库)
spring.redis.host=192.168.72.129
spring.redis.port=7000
spring.redis.database=0

在redis中还要开启远程连接

在这里插入图片描述

因为redis服务器上redis使用的是7000端口,所以我们还要检查 7000 端口是否开启,如果没有开启,我们要开启 7000端口

firewall-cmd --query-port=7000/tcp  # 查看是否打开端口号“7000”,如果返回值为“no”,则输入下面命令
firewall-cmd --add-port=7000/tcp    # 开启“7000”端口

3.2 使用StringRedisTemplate和RedisTemplate

因为很多都是和redis指令是对应的,所以下面测试的时候并没有写太多的api,在使用的时候根据提示望文生义使用或者查找即可,都是很好理解的。

使用 StringRedisTemplate 操作redis

StringRedisTemplate 在操作redis时传入的参数必须是String类型的,不能传入对象

import java.util.*;
import java.util.concurrent.TimeUnit;

// 下面两个注解相当于启动SpringBoot应用
@SpringBootTest(classes = RedisDay2Application.class)   // 让SpringBoot启动起来,并指向入口类
@RunWith(SpringRunner.class)
public class TestStringRedisTemplate {

    // 注入StringRedisTemplate
    @Autowired
    private StringRedisTemplate stringRedisTemplate;  // key和value都是字符串
    // StringRedisTemplate 只是传入的参数key和value都是字符串,并不代表它只能操作Redis中的String类型,它还可以操作List、Set等类型

    // 操作redis中key相关的
    @Test
    public void testKey(){
        //stringRedisTemplate.delete("name"); // 删除指定key
        Boolean hasKey = stringRedisTemplate.hasKey("name");// 判断指定key是否存在
        System.out.println(hasKey);
        DataType dataType = stringRedisTemplate.type("name");// 判断key所对应value的类型
        System.out.println(dataType);
        Set<String> keys = stringRedisTemplate.keys("*");   // 获取redis中所有key
        keys.forEach(key -> System.out.println("key = " + key));
        Long expire = stringRedisTemplate.getExpire("name");    // 获取key的超时时间 -1 永不超时 -2 key不存在
        System.out.println(expire);
        String s = stringRedisTemplate.randomKey(); // 在redis中随机获取一个key
        System.out.println(s);
        //stringRedisTemplate.rename("age", "age1");  // 修改key的名字,要求key必须存在,不存在会报错
        stringRedisTemplate.renameIfAbsent("name", "name1"); // 判断key是否存在,修改key的名字
        stringRedisTemplate.move("name1", 1);   // 移动key到指定库
    }

    // 操作redis中的字符串  opsForValue  实际就是操作redis中的String
    @Test
    public void testString(){
        stringRedisTemplate.opsForValue().set("name", "小陈");  // 代表操作的类型是String类型,用来设置一个key-value对
        String value = stringRedisTemplate.opsForValue().get("name"); // 代表操作的类型是String类型,用来获取一个key对应的value
        System.out.println("value = " + value);
        stringRedisTemplate.opsForValue().set("code", "2357", 120, TimeUnit.SECONDS);   // 设置一个key的超时时间
        stringRedisTemplate.opsForValue().append("name", "他是一个好人,单纯的少年");
        //stringRedisTemplate.opsForList();   // 代表操作的类型是List类型,要操作List的话,在之后写.相应的方法即可
        //stringRedisTemplate.opsForSet();    // 代表操作的是Set类型
        //stringRedisTemplate.opsForZSet();   // 代表操作的是ZSet类型
        //stringRedisTemplate.opsForHash();   // 代表操作的是Hash类型
    }

    // 操作redis中List类型  opsForList  实际就是操作redis中的List类型
    @Test
    public void testList(){
        stringRedisTemplate.opsForList().leftPush("names", "小陈");   // 创建一个列表,并放入一个元素
        stringRedisTemplate.opsForList().leftPushAll("names", "小张", "小王", "小明"); // 创建一个列表,放入多个元素
        List<String> names = new ArrayList<>();
        names.add("小明");
        names.add("小王");
        stringRedisTemplate.opsForList().leftPushAll("names", names);   // 创建一个列表并放入一个String类型的集合

        List<String> stringList = stringRedisTemplate.opsForList().range("names", 0, -1);   // 遍历List
        stringList.forEach(value -> System.out.println("value = " + value));
        stringRedisTemplate.opsForList().trim("names", 0, 2); // 使这个key所对应的value只保留这个区间内的元素,其他元素截断
    }
    // 操作redis中Set类型  opsForSet 实际就是操作redis中的Set类型
    @Test
    public void testSet(){
        stringRedisTemplate.opsForSet().add("sets", "小明", "小明", "小陈", "小王");

        Set<String> sets = stringRedisTemplate.opsForSet().members("sets");
        sets.forEach(value -> System.out.println("value = " + value));

        Long size = stringRedisTemplate.opsForSet().size("sets");   // 获取set集合的元素个数
        System.out.println("size = " + size);
    }
    // 操作redis中ZSet类型  opsForZSet 实际就是操作redis中的ZSet类型
    @Test
    public void testZSet(){
        stringRedisTemplate.opsForZSet().add("zsets", "小陈", 100); // 创建一个ZSet并放入一个元素和相应的得分
        stringRedisTemplate.opsForZSet().add("zsets", "小黑", 20);

        Set<String> zsets = stringRedisTemplate.opsForZSet().range("zsets", 0, -1); // 遍历ZSet
        zsets.forEach(value -> System.out.println("value = " + value));

        // 获取指定Zset集合中指定范围的元素以及分数
        Set<ZSetOperations.TypedTuple<String>> zsets1 = stringRedisTemplate.opsForZSet().rangeByScoreWithScores("zsets", 0, 1000);
        zsets1.forEach(typedTuple -> {
            System.out.println(typedTuple.getScore());
            System.out.println(typedTuple.getValue());
        });
    }
    // 操作redis中Hash类型  opsForHash 实际就是操作redis中的Hash类型
    @Test
    public void testHash(){
        stringRedisTemplate.opsForHash().put("maps", "name", "张三"); // 创建一个Hash类型,并放入一个value为键值对类型的键值对
        Map<String, String> map = new HashMap<>();
        map.put("age", "18");
        map.put("bit", "2012-12-12");
        // 一次放入一个map集合,但要注意这个map集合的key和value都是String类型的
        stringRedisTemplate.opsForHash().putAll("maps", map);
        stringRedisTemplate.opsForHash().multiGet("maps", Arrays.asList("name", "age", "bir")); // 获取多个key,但注意要将多个key封装成Collection
        String values = (String) stringRedisTemplate.opsForHash().get("maps", "name");// 获取value中某个key的值
        Set<Object> maps = stringRedisTemplate.opsForHash().keys("maps");// 获取key对应的value中所有的键
        List<Object> maps1 = stringRedisTemplate.opsForHash().values("maps");// 获取key对应的value中所有的值


    }
}

在这里插入图片描述

使用 RedisTemplate 操作redis

RedisTemplate 在操作redis时传入的参数可以传入对象其实RedisTemplate 操作redis和StringRedisTemplate操作redis的api很像,只是传入的参数类型不同,RedisTemplate 可以传入对象,StringRedisTemplate只能传入字符串

在使用RedisTemplate 传入参数为对象的时候,必须将对象序列化,对象存储到redis之中的应该是对象序列化的结果,取出来的时候要展现也要进行反序列化。(对象没有办法存储到redis,但是对象序列化之后是可以存储的redis的,所以默认存储到redis中的key和value都是进行序列化的,如果是自定义的类,还要实现Serializable接口)

使用RedisTemplate 往redis里面存值的时候key和value都应该序列化之后再存进去,之后取value的时候应该根据key序列化之后从redis取,相同的key序列化的结果相同,存key的时候存的是key序列化的,如果取key的时候用的是key没有序列化去取,是取不到的。

对于String、Integet、Double、等等类型是默认进行了序列化的,对于自定义的类,我们要实现Serializable接口。

注意:由于在redis中的key都是String类型,所以 RedisTemplate 在实际操作的时候key都是String类型

# 关于 RedisTemplate  中的序列化机制

在RedisTemplate中默认 key 和 value默认都是 JdkSerializationRedisSerializer 序列化方案
但是我们说,这种方式存在局限性:我们在终端无法操作JdkSerializationRedisSerializerxuliehuadekey
在业务操作中,一般都是:
      key   为 String
      value 为 Object
所以我们要修改key的序列化方案为:StringRedisSerializer 即字符串的方式, value的序列化方案不用修改
# 修改key的序列化机制和Hash类型中value内部key的序列化机制为String序列化机制

// 修改key序列化方案  为String序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 修改Hash类型中value中key的序列化为String序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());

在这里插入图片描述

用到的User类

// 下面这两个注解的作用是创建get、set方法,使用了下面的这两个注解就不需要写get、set方法了,会自动创建
@Data
@Accessors(chain = true)
public class User implements Serializable { // 在使用RedisTemplate操作redis的时候对象必须序列化
    private String id;
    private String name;
    private Integer age;
    private Date bir;
}

使用RedisTemplate

// 下面两个注解相当于启动SpringBoot应用
@SpringBootTest(classes = RedisDay2Application.class)   // 让SpringBoot启动起来,并指向入口类
@RunWith(SpringRunner.class)
public class TestRedisTemplate {

    @Autowired
    private RedisTemplate redisTemplate;

    // String、List、Set、ZSet、Hash

    @Test
    public void testRedisTemplate(){
        /*
          在RedisTemplate中默认 key 和 value默认都是 JdkSerializationRedisSerializer 序列化方案
          但是我们说,这种方式存在局限性:我们在中断无法操作JdkSerializationRedisSerializerxuliehuadekey
          在业务操作中,一般都是:
                   key   为 String
                   value 为 Object
          所以我们要修改key的序列化方案为:StringRedisSerializer 即字符串的方式
          value的序列化方案不用修改
         */
        // 如果我们把key也序列化存进去,那我们在终端操作的时候就无法操作key了,因为redis
        // 中存储的是key序列化的结果,我们在终端操作的时候有没有序列化、反序列化的操作,这种方式存储使得我们只可以
        // 使用RedisTemplate操作,存在局限性
        // 修改key序列化方案  为String序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 修改Hash类型中value中key的序列化为String序列化
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        User user = new User();
        user.setId(UUID.randomUUID().toString()).setName("小陈").setAge(23).setBir(new Date());
        // String默认实现了序列化反序列化而对于自定义的对象我们必须实现Serializable接口
        redisTemplate.opsForValue().set("user", user);

        // 在使用redisTemplate取的时候虽然我们直接传入了key,但是实际是先把key序列化之后再从redis中取的(默认)
        // 在取值的时候虽然我们存value的时候也是序列化之后存的,会默认把value反序列化回来
        System.out.println(redisTemplate.opsForValue().get("user"));

        redisTemplate.opsForList().leftPush("list", user);
        redisTemplate.opsForSet().add("set", user);
        redisTemplate.opsForZSet().add("zset", user, 100);
        redisTemplate.opsForHash().put("map", "name", user);



    }
}

在这里插入图片描述

补充:关于 Bound API 的使用

当我们对于同一个key多次操作时,为了简便,提供了bound api,即对key进行绑定操作,StringRedisTemplate和RedisTemplate都可以使用Bound API

// 下面两个注解相当于启动SpringBoot应用
@SpringBootTest(classes = RedisDay2Application.class)   // 让SpringBoot启动起来,并指向入口类
@RunWith(SpringRunner.class)
public class TestBoundAPI {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // spring data 为了方便我们对redis进行更友好的操作 因此又提供了 bound api 简化操作
    @Test
    public void testBound(){
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        // 对同一个key的多次操作进行绑定, 对key绑定
        stringRedisTemplate.opsForValue().set("name", "zhangsan");
        stringRedisTemplate.opsForValue().set("name", "是一个好人");
        String s = stringRedisTemplate.opsForValue().get("name");
        System.out.println(s);

        // 对字符串类型key进行绑定 后续所有操作都是基于这个key的操作
        BoundValueOperations<String, String> nameValueOperations = stringRedisTemplate.boundValueOps("name");
        nameValueOperations.set("zhangsan");
        nameValueOperations.set("是一个好人");
        String s1 = nameValueOperations.get();
        System.out.println(s1);

        // 对List的绑定操作
        BoundListOperations<String, String> listOperations = stringRedisTemplate.boundListOps("lists");
        listOperations.leftPushAll("张三", "李四", "小陈");
        List<String> list = listOperations.range(0, -1);
        list.forEach(value -> System.out.println("value = " + value));

        // 对Set的绑定操作
        BoundSetOperations<String, String> setOperations = stringRedisTemplate.boundSetOps("sets");

        // 对ZSet的绑定操作
        BoundZSetOperations<String, String> zsetOperations = stringRedisTemplate.boundZSetOps("zsets");

        // 对Hash的绑定操作
        BoundHashOperations<String, Object, Object> hashOperations = stringRedisTemplate.boundHashOps("map");


        BoundValueOperations name = redisTemplate.boundValueOps("name");
        BoundListOperations lists = redisTemplate.boundListOps("lists");
        BoundSetOperations sets = redisTemplate.boundSetOps("sets");
        BoundZSetOperations zsets = redisTemplate.boundZSetOps("zsets");
        BoundHashOperations map = redisTemplate.boundHashOps("map");
    }
}

在这里插入图片描述

总结

  1. 针对于日后处理key value 都是String 使用StringRedi sTemplate
  2. 针对于日后的key value 存在对象使用Redi sTemplate
  3. 针对于同一个key多次操作可以使用boundXXxOps ()的api简化书写(bound api)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值