> 文档中心 > Java和SpringBoot操作redis

Java和SpringBoot操作redis


2. java操作Redis

2.1 环境准备

  1. 引入依赖
<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  # 查看防火墙的状态sytemctl disable 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"); // zrangeByScorejedis.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类型,当然我们在使用的时候是不用写泛型的StringRedisTemplateRedisTemplate在操作的时候key和value都是Object类型,当然我们在使用的时候是不用写泛型的RedisTemplate

注意: 使用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应用的端口为8989server.port=8989# 配置操作redis相关的(redis服务器的主机ip和端口和使用哪个库)spring.redis.host=192.168.72.129spring.redis.port=7000spring.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)