Skip to content
标签
spring
数据库
字数
2487 字
阅读时间
13 分钟

一、概述

SpringData Redis的作用是通过一段简单的配置即可访问redis服务,它的底层是对java提供的redis开发包(比如jedis等)进行了高度封装,主要提供了如下功能:

  • 连接池自动管理,提供了一个高度封装的RedisTemplate类,基于这个类的对象可以对redis进行各种操作
  • 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
    • ValueOperations:简单字符串类型数据操作
    • SetOperations:set类型数据操作
    • ZSetOperations:zset类型数据操作
    • HashOperations:map类型的数据操作
    • ListOperations:list类型的数据操作

1.1 序列化器

SpringData Redis在保存数据的时候,底层有一个序列化器在工作,它会将要保存的数据(键和值)按照一定的规则进行序列化操作后再进行存储。spring-data-redis提供如下几种序列化器:

  • StringRedisSerializer: 简单的字符串序列化
  • GenericToStringSerializer: 可以将任何对象泛化为字符串并序列化
  • Jackson2JsonRedisSerializer: 序列化对象为json字符串
  • GenericJackson2JsonRedisSerializer:功能同上,但是更容易反序列化
  • OxmSerializer: 序列化对象为xml字符串
  • JdkSerializationRedisSerializer: 序列化对象为二进制数据

RedisTemplate默认使用的是JdkSerializationRedisSerializer对数据进行序列化。

1.2 运行原理分析

  1. 通过JedisConnectionFactory连接工程获取jedis连接
  2. 通过JedisConnectionFactory产生JedisConnetion

SpringData提供redisTemplate就是在原生的Jedis或者其他操作redis的技术上做的一层封装,它屏蔽掉了这些原生技术的实现细节,统一了调用接口,使得我们的操作更加简单明了。

二、使用示例

2.1 入门Demo

依赖

xml
<dependency> 
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.3</version>
</dependency> 

<dependency>
    <groupId>org.springframework.data</groupId> 
    <artifactId>spring-data-redis</artifactId>
    <version>2.1.8.RELEASE</version> 
</dependency>

配置

xml
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" 
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd 
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd 
                           http://www.springframework.org/schema/aop 
                           http://www.springframework.org/schema/aop/spring-aop.xsd 
                           http://www.springframework.org/schema/tx 
                           http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 配置读取 properties 文件的工具类 --> 
    <context:property-placeholder location="classpath:redis.properties"/> 
    <!-- Jedis 连接池 --> 
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="${redis.pool.maxTotal}"/>
        <property name="maxIdle" value="${redis.pool.maxIdle}"/> 
        <property name="minIdle" value="${redis.pool.minIdle}"/>
    </bean> 
    <!-- Jedis 的连接工厂 --> 
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionF actory">
        <property name="hostName" value="${redis.conn.hostName}"/>
        <property name="port" value="${redis.conn.port}"/> 
        <property name="poolConfig" ref="poolConfig"/> 
    </bean> 
    <!-- Redis 模板对象 --> 
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> 
        <property name="connectionFactory" ref="jedisConnectionFactory"/>
        <!-- 序列化器:能够把我们储存的 key 与 value 做序列化处理的对象 --> 
        <!-- 配置默认的序列化器 --> 
        <!-- keySerializer、valueSerializer 配置 Redis 中的 String 类型 key 与 value 的序列化器 -->
        <!-- HashKeySerializer、HashValueSerializer 配置 Redis 中的 Hash 类型 key 与 value 的序列化器 --> 
        <property name="keySerializer"> 
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> 
        </property> 
        <property name="valueSerializer"> 
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> 
        </property> 
    </bean>
</beans>

使用demo

java
@Autowired 
private RedisTemplate<String, Object> redisTemplate;

/**
* 添加键值对 
*/ 
public void test1(){
    this.redisTemplate.opsForValue().set("key", "test"); 
}
/**
* 获取 redis 中的数据 
*/ 
public void test2(){ 
    String str = (String)this.redisTemplate.opsForValue().get("key"); 
    System.out.println(str); 
}

//Spring Data Redisd 存储实体对象
//更换序列化器 jdk序列化器
this.redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); 
this.redisTemplate.opsForValue().set("users", users);


//json格式
this.redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Users.class)); 
this.redisTemplate.opsForValue().set("usersjson", users);

三、核心知识

3.1 修改使用的序列化器

通过配置文件选择

xml
<!--redisTemplate--> 
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory"/>
    <!--指定非hash类型的数据序列化器--> 
    <property name="keySerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    </property> 
    <property name="valueSerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> 
    </property>
</bean>

通过RedisTemplate设定

java
@Test
public void test1() { 
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new StringRedisSerializer());
    //存入数据
    redisTemplate.opsForValue().set("name", "heima"); 
    //查询数据 
    String name = (String) redisTemplate.opsForValue().get("name");
    System.out.println(name);
}

3.2 操作不同类型数据示例

String类型

java
package com.itheima.test;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-redis.xml")
public class RedisStringTest {

    @Autowired
    private RedisTemplate redisTemplate;

    ValueOperations<String, String> operations = null;

    @Before
    public void init() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());

        operations = redisTemplate.opsForValue();
    }


    @Test
    public void testSet() {
        //向数据库中保存name--heima
        //operations.set("name","heima");

        //相关数据库保存name1--heima1  有效时间为10s
        //operations.set("name1", "heima1", 10, TimeUnit.SECONDS);

        //替换 heima    --->   heXXa   offset 索引位置是从0开始
        //operations.set("name", "XX", 2);

        //当key不存在的时候,执行保存操作;当key存在的时候,什么都不做
        //operations.setIfAbsent("name1","heima");

        //批量保存
//        Map map = new HashMap();
//        map.put("name2", "heima2");
//        map.put("name3", "heima3");
//        map.put("name4", "heima4");
//
//        operations.multiSet(map);


        //追加 当key存在时,会执行追加操作;当key不存在时,会执行保存操作
        operations.append("name5", "Heima");

    }

    @Test
    public void testGet() {
        //根据key获取value
/*        String value = operations.get("name");
        System.out.println(value);//heXXaHeima*/

        //首先根据key获取value,然后再根据value进行截取,从start位置截取到end位置[包含start和end]
/*        String value2 = operations.get("name", 5, 7);
        System.out.println(value2);//heXXaHeima-->Hei*/

        //批量获取
//        List<String> keys = new ArrayList<>();
//        keys.add("name2");
//        keys.add("name3");
//        keys.add("name4");
//        List<String> values = operations.multiGet(keys);
//        for (String s : values) {
//            System.out.println(s);
//        }

        //根据key获取value的长度
        Long size = operations.size("name");
        System.out.println(size);

    }

    //自增
    @Test
    public void testIncrement() {
        operations.set("age", "18");
        operations.increment("age");//自增1--->19
        System.out.println(operations.get("age"));
        operations.increment("age", 5);//自增5
        System.out.println(operations.get("age"));//---->24

        //自减
        //operations.decrement("age")

    }

    //删除
    @Test
    public void testDelete() {
        //单个删除
        redisTemplate.delete("name");

        List<String> keys = new ArrayList<>();
        keys.add("name2");
        keys.add("name3");
        keys.add("name4");

        //批量删除
        redisTemplate.delete(keys);
    }


}

Hash类型

java
package com.itheima.test;

import com.itheima.domain.Article;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-redis.xml")
public class RedisHashTest {

    @Autowired
    private RedisTemplate redisTemplate;

    HashOperations<String, String, Article> operations = null;

    @Before
    public void init() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());

        operations = redisTemplate.opsForHash();
    }

    //保存
    @Test
    public void testPut() {
        Article article = new Article();
        article.setTitle("黑马");
        article.setAuthor("黑马程序员");
        article.setCreateTime(new Date());

        operations.put("article", "3", article);
    }


    //获取
    @Test
    public void testGet() {
        //判断hashkey是否存在
        Boolean flag = operations.hasKey("article", "3");
        System.out.println(flag);

        //根据key和hashkay获取操作
        Article article = operations.get("article", "2");
        System.out.println(article);


        //根据key获取所有的hashkey
        Set<String> set = operations.keys("article");
        for (String s : set) {
            System.out.println(s);
        }

        List<Article> articles = operations.values("article");
        for (Article art : articles) {
            System.out.println(art);
        }

        Map<String, Article> map = operations.entries("article");
        for (Map.Entry<String, Article> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }

    //删除
    @Test
    public void testDelete() {
        //当hash中的数据全部被删除后,整个hash就没了
        operations.delete("article", "2", "3");
    }
}

List类型

java
package com.itheima.test;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.ArrayList;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-redis.xml")
public class RedisListTest {

    @Autowired
    private RedisTemplate redisTemplate;

    ListOperations<String, String> operations = null;

    @Before
    public void init() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());

        operations = redisTemplate.opsForList();
    }

    //增加
    @Test
    public void testAdd() {
        //从左边添加一个元素
        operations.leftPush("students", "zhangsan");
        //从左边添加多个元素
        operations.leftPushAll("students", "lisi", "wangwu", "zhaoliu");


        //从右边添加一个元素
        operations.rightPush("students", "zhangsan1");
        //从右边添加多个元素
        operations.rightPushAll("students", "lisi", "wangwu", "zhaoliu");
    }

    //查询
    @Test
    public void testFind() {
        //根据key和索引进行查询
        //0和正数代表从左边开始    0  1  2
        //负数代表从右边开始       -1   -2  -3
        String student = operations.index("students", 1);
        System.out.println(student);

        String student1 = operations.index("students", -2);
        System.out.println(student1);

        //范围查询
        //根据key  [start,end]  包括首尾
        List<String> students = operations.range("students", 0, 2);
        for (String s : students) {
            System.out.println(s);
        }
    }

    //删除
    @Test
    public void testRemove(){
        //从左边删除第一个元素
        //String s = operations.leftPop("students");

        //从右边删除第一个元素
        //operations.rightPop("students");


        // count > 0:删除左边起第几个等于指定值的元素
        // count < 0:删除右边起第几个等于指定值的元素
        // count = 0:删除所有等于value的元素。
        //删除左边起第二个wangwu
        operations.remove("students",2,"wangwu");
    }


}

Set类型

java
package com.itheima.test;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;
import java.util.Set;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-redis.xml")
public class RedisSetTest {

    @Autowired
    private RedisTemplate redisTemplate;

    SetOperations<String, String> operations = null;

    @Before
    public void init() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());

        operations = redisTemplate.opsForSet();
    }

    //增加
    @Test
    public void testAdd() {
        operations.add("students","zhangsan","lisi","wangwu","zhangsan");
    }

    //查询
    @Test
    public void testFind() {
        //查询所有元素
/*        Set<String> students = operations.members("students");
        for (String student : students) {
            System.out.println(student);
        }*/

        //随机获取一个元素
/*        String student = operations.randomMember("students");
        System.out.println(student);*/

        //随机多个元素[可能会重复]
        List<String> stus = operations.randomMembers("students", 2);
        for (String stu : stus) {
            System.out.println(stu);
        }

    }

    //删除
    @Test
    public void testRemove() {
        //移除元素,并返回移除成功个数
        Long count = operations.remove("students", "zhangsan", "wangwu","sunliu");
        System.out.println(count);

        //随机移除指定集合中的多少个元素
        List<String> students = operations.pop("students", 2);
        for (String student : students) {
            System.out.println(student);
        }
    }

    //多集合操作
    @Test
    public void testMoreSet(){
        operations.add("names1", "zhangsan", "li", "wangwu");
        operations.add("names2", "zhangsan", "li", "zhaoliu");

        //取交集
/*        Set<String> sets1 = operations.intersect("names1", "names2");
        for (String s : sets1) {
            System.out.println(s);
        }*/

        //取并集
/*        Set<String> sets2 = operations.union("names1", "names2");
        for (String s : sets2) {
            System.out.println(s);
        }*/

        //取差集[第一个集合中存在,但是在第二个集合中不存在的元素]
        Set<String> sets3 = operations.difference("names2", "names1");
        for (String s : sets3) {
            System.out.println(s);
        }
    }



}

ZSet类型

java
package com.itheima.test;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;
import java.util.Set;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-redis.xml")
public class RedisZSetTest {

    @Autowired
    private RedisTemplate redisTemplate;

    ZSetOperations<String, String> operations = null;

    @Before
    public void init() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());

        operations = redisTemplate.opsForZSet();
    }

    //增加
    @Test
    public void testAdd() {
        operations.add("students", "zhangsan", 100);
        operations.add("students", "lisi", 60);
        operations.add("students", "wangwu", 80);
    }

    //分数的增减
    @Test
    public void testScore() {
        //incrementScore 可以用来增减分数  增加就用正数    减少用负数
        //增加分数
        operations.incrementScore("students", "wangwu", 30);
        //减少分数
        operations.incrementScore("students", "wangwu", -70);
    }

    //查询一个元素的信息
    @Test
    public void testFindOne() {
        //查询一个元素的分数
        Double score = operations.score("students", "wangwu");
        System.out.println(score);

        //查询一个元素在集合中的排名   排名从0开始
        Long rank = operations.rank("students", "zhangsan");
        System.out.println(rank);
    }

    //根据一个区间获得一个列表
    @Test
    public void testFindList() {
        //根据排名区间来获取元素列表
        Set<String> students = operations.range("students", 0, 2);
        for (String student : students) {
            System.out.println(student);
        }
        System.out.println("=============");
        Set<ZSetOperations.TypedTuple<String>> set = operations.rangeWithScores("students", 0, 2);
        for (ZSetOperations.TypedTuple<String> tuple : set) {
            System.out.println(tuple.getValue() + "同学,得了" + tuple.getScore() + "分");
        }
        System.out.println("---------------------------------");


        //根据分数区间来获取列表
        Set<String> students2 = operations.rangeByScore("students", 60, 90);
        for (String student : students2) {
            System.out.println(student);
        }
        System.out.println("=============");
        Set<ZSetOperations.TypedTuple<String>> set2 = operations.rangeByScoreWithScores("students", 60, 90);
        for (ZSetOperations.TypedTuple<String> tuple : set2) {
            System.out.println(tuple.getValue() + "同学,得了" + tuple.getScore() + "分");
        }
    }

    //统计
    @Test
    public void testCount() {
        //统计一个集合中元素
        Long zCard = operations.zCard("students");
        System.out.println(zCard);

        //根据一个分数区间统计元素数量
        Long count = operations.count("students", 50, 100);
        System.out.println(count);
    }


    //删除
    @Test
    public void testRemove() {
        //根据key-value删除  value允许传入多个
        //operations.remove("students","zhangsan","lisi");

        //根据排名区间删除
        //operations.removeRange("students", 0, 1);

        //根据分数区间删除
        operations.removeRangeByScore("students", 70, 100);
    }
}