对Redis数据类型的操作

对数据类型的操作才是 Redis 需要关心的部分,下面正式介绍 Redis 数据类型的操作部分。主要有三个部分:StringRedisTemplate 的使用、模板 template 以及其他数据类型的操作。

StringRedisTemplate的使用

在上文,添加与获取都需要使用 byte 数组进行操作,这样显得比较烦琐。其实,Spring Boot 有可以使用模板,可简化代码书写。

package com.springBoot.redis.config;

@Configuration
public class RedisConfig {
   @Bean
   public JedisConnectionFactory getFactory(){
      RedisStandaloneConfiguration redisStandaloneConfiguration=new RedisStandaloneConfiguration();
      redisStandaloneConfiguration.setDatabase(0);
      redisStandaloneConfiguration.setHostName("127.0.0.1");
      redisStandaloneConfiguration.setPort(6379);redisStandaloneConfiguration.setPassword(RedisPassword.of("123456"));
      JedisConnectionFactory factory = new JedisConnectionFactory (redisStandaloneConfiguration);
      return factory;
   }
   @Bean
   public StringRedisTemplate redisTemplate(JedisConnectionFactory
jedisConnectionFactory) {
      return new StringRedisTemplate(jedisConnectionFactory);
   }
}

RedisConfig 类在8.1节中有不使用连接池返回工厂类和使用连接池返回工厂类两种方式。在这个实例中,我们只使用其中一种进行演示,效果相同。我们讲解了工厂类,那么如何使用?

在 Spring 中有模板,在建立模板的时候则需要传入工厂类。因此,我们把刚生成的工厂类带入模板,并返回。在 Spring Boot 中有多个模板,先使用 StringRedisTemplate,至于其他的模板将会在后面进行介绍。然后,我们写测试类程序,进行测试,代码如下所示。

package com.springBoot.redis;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisApplicationTest {
   @Autowired
   StringRedisTemplate stringRedisTemplate;
   @Test
   public void testRedis(){
      stringRedisTemplate.opsForValue().set("myKey2","myValue2");
      String myVal=stringRedisTemplate.opsForValue().get("myKey2");
      System.out.println("myVal: "+myVal);
   }
}

上面程序中,将 StringRedisTemplate 模板注入,然后使用模板的方法。首先,使用 SET 方法把数据存放进 Redis。为了验证方法正确性,可以从 Redis 的命令行上观察,不过这里还是使用取出的方式进行验证,执行结果如图8.4所示。

image 2024 03 31 22 14 21 722
Figure 1. 图8.4 执行结果

Spring Data Redis 提供了两个模板,一个为 RedisTemplate,另一个为 StringRedisTemplate。RedisTemplate 使用的序列化类是 JdkSerializationRedisSerializer,StringRedisTemplate 使用的序列化类是 StringRedisSerializer。

首先,来看 StringRedisTemplate 的类关系,如图8.5所示。

image 2024 03 31 22 17 58 930
Figure 2. 图8.5 StringRedisTemplate类关系

通过图8.5可以看到 StringRedisTemplate 继承了 RedisTemplate。当要存入、读取 Redis 数据库中的数据是字符串或者数据库中的数据是字符串,那么进行操作时使用 StringRedisTemplate 模板非常方便,从上面的实例中也可以发现的确方便很多。

只有对数据的复杂类型进行操作,才会使用 RedisTemplate,所以这里先来看 StringRedisTemplate 常用的操作,代码如下所示。

stringRedisTemplate.opsForValue().set("test", "100",60*10,TimeUnit. SECONDS);//向redis里存入数据和设置缓存时间
stringRedisTemplate.boundValueOps("key").increment(-1); //val做-1操作
stringRedisTemplate.opsForValue().get("key") //根据key获取缓存中的val
stringRedisTemplate.boundValueOps("key").increment(1); //val +1
stringRedisTemplate.getExpire("key") //根据key获取超时时间
stringRedisTemplate.getExpire("key",TimeUnit.SECONDS) //获取超时时间并换算成指定单位
stringRedisTemplate.delete("key "); //根据key删除缓存
stringRedisTemplate.hasKey("aaaaa"); //检查key是否存在,返回boolean值
stringRedisTemplate.opsForSet().add("red_123","2","3"); //向指定key中存放Set集合
stringRedisTemplate.expire("red_123",1000 , TimeUnit.MILLISECONDS);//设置超时时间
stringRedisTemplate.opsForSet().members("rrr"); //根据key获取Set集合

模板template

Redis 支持五种数据类型,分别是字符串(String)、列表(List)、散列(Hash)、集合(Set)、有序集合(Zset)。对于字符串,我们建议使用 StringRedisTemplate 模板进行操作,那么对其他几种数据类型如何操作?本节将会介绍。

Redis的数据类型

首先,把这五种数据类型做一个归纳总结,让不熟悉 Redis 的读者有一个了解,方便后续的学习。

  • String:在 Redis 中存取的是字符串,也可以是整数与浮点数,需要注意的是学习过 Java 编程的读者,不要把整数与浮点数当成字符串。其实 StringRedisTemplate 的用法有 “val+1” 的操作。

  • List:Redis 中的链表。可以在链表的开头与结尾,或者说链表左侧与链表右侧进行添加与删除数据的操作,同样可以对其中的指定位置进行操作,即在每一个节点上都是一个字符串。

  • Hash:包含键值的无序散列表。

  • Set:集合的意思。与 Java 中的语法不同,在集合中是字符串,其特点是无序,而且唯一,即不允许重复。

  • Zset:是字符串成员与数值的有序映射,然后根据数值的大小进行排序。

RedisTemplate的使用

在8.2.1小节中,讲到了 StringRedisTemplate 与 RedisTemplate 的关系,也说明了对于字符串的操作,使用 StringRedisTemplate 即可,但操作其他的数据类型,就需要 RedisTemplate 的配合。

很多时候,我们不仅要知道操作字符串需要与该模板打交道,也要知道该模板是 Redis 的核心,是 Redis 交互的高级抽象,有着更丰富的功能。

在最开始时,我们使用 RedisConnect 进行操作,这种方式存取都需要 byte 数组,而 RedisTemplate 则不需要这么费事,因为在模板中就可以处理序列化与连接,使得开发人员只需要关注业务开发,而不需要太关注 Redis 的细节。

首先,Spring 对数据类型的操作都提供了接口,在具体操作前,我们先看有哪些接口,这也被称为 Key 类型操作,接口清单如下所示。

ValueOperations       //字符串类型的操作
ListOperations       //列表类型的操作
SetOperations        //集合类型的操作
ZsetOperations       //有序集合类型的操作
HashOperations       //散列类型的操作
GeoOperations        //地理位置的操作
HyperLogLogOperations    //基数类型的操作

上面定义了几种接口,这些定义的接口如何使用?RedisTemplate 定义了对数据类型的操作,我们可以通过如下的几种方式获取接口,接口清单如下。

redisTemplate.opsForValue();      //操作字符串
redisTemplate.opsForHash();       //操作Hash
redisTemplate.opsForList();       //操作List
redisTemplate.opsForSet();        //操作Set
redisTemplate.opsForZSet();       //操作有序Set
redisTemplate.opsForGeo();        //操作地理位置
redisTemplate.opsForHyperLogLog();   //操作基数

然后,除了在前面提过的 Key 类型操作,其实还有一种 Key 绑定操作。例如有这样的场景,如果想对某一个键值对做一系列操作,这时 Spring Data Redis 也存在相应接口,接口的清单如下所示。

BoundValueOperations    //绑定一个字符串键操作
BoundListOperations     //列表键绑定
BoundSetOperations     //集合键绑定
BoundZSetOperations     //有序集合键绑定
BoundHashOperations     //散列键绑定
BoundGeoOperations     //地理位置绑定

同理,定义了接口,就可以使用 RedisTemplate 获取接口,接口清单如下所示。

redisTemplate.boundValueOps("string");
redisTemplate.boundSetOps("set");
redisTemplate. boundZSetOps ("zset");
redisTemplate.boundHashOps("hash");
redisTemplate.boundListOps("list");
redisTemplate.boundGeoOps("geo");

数据类型的操作

在 Redis 中存在多种数据类型,现在对 String、List、Set、Hash 数据类型进行操作。下面是测试类程序,以及运行的结果。

String操作

首先,进行 String 数据类型的操作,代码继续写在测试类 RedisApplicationTest中。

package com.springBoot.redis;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisApplicationTest {
   @Autowired
   RedisTemplate redisTemplate;
   @Test
   public void testString(){
      ValueOperations<String,String> valueOperations=redisTemplate. opsForValue();
      //不设置超时时间
      valueOperations.set("testString1","testStringValue1");
      //设置超时时间,设置的时间单位为秒,切位30秒
      valueOperations.set("testString2","testStringValue2",30, TimeUnit.SECONDS);
      //判断key是否存在,不存在则存取
      valueOperations.setIfAbsent("testString3","testStringValue3");
      //获取数据类型
      String val1=valueOperations.get("testString1");
      String val2=valueOperations.get("testString2");
      String val3=valueOperations.get("testString3");
      System.out.println("val1: "+val1);
      System.out.println("val2: "+val2);
      System.out.println("val3: "+val3);
   }
}

运行测试类程序,观察执行结果如图8.6所示。

image 2024 03 31 22 30 59 042
Figure 3. 图8.6 String执行结果

List操作

在这里,放入一个对象。首先在 redis 的包下新建 entity 包,在 entity 包下新建 Employ 类,代码如下所示。

package com.springBoot.redis.entity;

public class Employ implements Serializable {
   private static final long serialVersionUID = 366444554774130L;
   private String id;
   private String name;
   //GET和SET方法
   @Override
   public String toString() {
      return "Employ{" +
             "id='" + id + '\" +
             ", name='" + name + '\" +
             '}';
   }
}

在这段代码中,需要注意的是要让这个类可以序列化,因为我们在测试时没有特别指定序列器,则会使用 JDK 序列化,所以需要实现 Serializable 接口。然后,写测试类程序,测试代码如下所示。

package com.springBoot.redis;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisApplicationTest {
   @Autowired
   RedisTemplate redisTemplate;
   @Test
   public void testList(){
      ListOperations<String,Object> listOperations=redisTemplate. opsForList();
      for (int i=0;i<3;i++){
          Employ employ=new Employ();
          employ.setId("\""+i+"\"");
          employ.setName("name"+i);
          listOperations.rightPush("list1",employ);
          listOperations.rightPush("list2",employ);
      }
      Employ employ=(Employ)listOperations.rightPop("list1");
      System.out.println(employ.toString());
   }
}

在这段代码中,通过 RedisTemplate 获取操作 List 的接口,然后模拟数据在链表的右侧插入数据,最后通过在右侧弹出的方式验证程序的执行结果,如图8.7所示。

image 2024 03 31 22 36 59 248
Figure 4. 图8.7 List执行结果

分析一下结果,我们在右侧执行弹出是后放入的,所以 id 为 2。

Set操作

继续在测试类程序中添加测试方法,代码如下所示。

@Test
public void testSet(){
   SetOperations<String,Object> setOperations=redisTemplate. opsForSet();
   Employ employ=new Employ();
   employ.setId("1");
   employ.setName("a");
   setOperations.add("set",employ);
   Employ employ1=(Employ)setOperations.pop("set");
   System.out.println(employ1.toString());
}

执行结果如图8.8所示。

image 2024 03 31 22 38 15 515
Figure 5. 图8.8 Set执行结果

Hash操作

继续在测试类中程序添加测试方法,代码如下所示。

@Test
public void testHash() {
   HashOperations<String,Object,Object> hashOperations=redisTemplate. opsForHash();
   Map<String,String> newMap=new HashMap<>();
   newMap.put("testMap1","test1");
   newMap.put("testMap2","test2");
   hashOperations.putAll("hash",newMap);
   System.out.println(hashOperations.entries("hash"));
}

执行结果如图8.9所示。

image 2024 03 31 22 40 34 855
Figure 6. 图8.9 Hash执行结果