[Redis] Redis (3) Redis JDBC Framework

本文主要聚焦于 Redis 的 JAVA 客户端连接框架,针对Redis本身的特性将不做过多介绍。
内容较长,请做好心理准备~

1 Redis JDBC 框架概述

1.1 Redis 简述

  • Redis(Remote Dictionary Server(远程字典服务))是一款开源的、由 Salvatore Sanfilippo 写的、遵守 BSD 协议的、使用ANSI C语言编写的、支持多种语言API的(跨平台的)、支持网络的、分布式的、高性能的、基于内存运行亦可持久化的日志型、(Key-Value)键值对NoSQL/非关系型数据库。

    • NOSQL(not only sql)不仅仅是sql,对所有非关系型数据库的一种通称。
    • 读写速度非常快,每秒可以处理超过10万次读写操作。
    • Redis被广泛应用于缓存分布式锁等场景
    • 此外,Redis支持事务持久化LUA 脚本LRU 驱动事件多种集群方案
    • 同类的产品还有memcache 、memcached 等。
  • Redis 通常也被称为数据结构服务器

  • 因为值(value)可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等数据结构类型
  • 还支持基于字符串的bitmap扩展数据结构

1.2 Redis JDBC框架

1.2.1 Jedis

  • 官网
  • Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。
    • 可以在Redis官网下载,当然还有一些开源爱好者提供的客户端,如Jredis、SRP、lettuce等等,推荐使用Jedis。
  • 依赖引用
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>${version}</version>
</dependency>
  • 示例代码
// 单连接方式
Jedis connection = new Jedis(host, port);

// 连接池方式
JedisPool pool = new JedisPool("localhost", 6379);

// 集群方式
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7380));
JedisCluster jedis = new JedisCluster(jedisClusterNodes);

1.2.2 Lettuce

  • 官网: https://github.com/lettuce-io/lettuce-core
  • Lettuce 是一个可伸缩线程安全Redis 客户端
    • 多个线程可以共享同一个 RedisConnection。它利用优秀 netty NIO 框架来高效地管理多个连接
    • 大并发下比 jedis 效率更高
  • 示例代码
RedisClient client = new RedisClient("localhost")
RedisConnection<String, String> connection = client.connect()
String value = connection.get("key")
  • 异步 API
StatefulRedisConnection<String, String> connection = client.connect();
RedisStringAsyncCommands<String, String> async = connection.async();
RedisFuture<String> set = async.set("key", "value")
RedisFuture<String> get = async.get("key")

async.awaitAll(set, get) == true

set.get() == "OK"
get.get() == "value"

1.2.3 Redission

  • Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。

【注】redisson 是 redis 官方的分布式锁组件

  • 它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中包括:

BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service

  • Redisson提供了使用Redis的最简单和最便捷的方法。

  • Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

  • 官网: https://github.com/redisson/redisson

  • 引入依赖

<dependency>
	<groupId>org.redisson</groupId>
	<artifactId>redisson</artifactId>
	<version>3.15.0</version>
</dependency>
  • 简单demo
package com.xm.ggn.test.redisson;

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import java.util.concurrent.TimeUnit;

@Slf4j
public class RedissonLockDemo {

    private static final Long TIME_LOCKED = 50 * 1000l;
    private static final String KEY_LOCKED = "myLock";
    private static RedissonClient redissonClient = null;

    public static void main(String[] args) {
        initRedissonClient();
        lock();
    }

    private static void initRedissonClient() {
        // 1. Create config object
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        // 2. Create Redisson instance
        Client.redissonClient = Redisson.create(config);
    }

    private static void lock() {
        RLock lock1 = redissonClient.getLock(KEY_LOCKED);
        log.error("lock1 clas: {}", lock1.getClass());
        lock1.lock();
        log.info("lock, ThreadName: {} id: {} locked, 重入次数: {}", Thread.currentThread().getName(), Thread.currentThread().getId(), lock1.getHoldCount());

        // 处理业务逻辑
        try {
            Thread.sleep(TIME_LOCKED);
            reLock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock1.unlock();
            log.info("lock, ThreadName: {} id: {} unlock, 重入次数: {}", Thread.currentThread().getName(), Thread.currentThread().getId(), lock1.getHoldCount());
        }
    }

    /**
     * 测试锁的重入
     */
    private static void reLock() {
        RLock lock1 = redissonClient.getLock(KEY_LOCKED);
        lock1.lock();
        log.info("reLock, ThreadName: {} id: {} locked, 重入次数: {}", Thread.currentThread().getName(), Thread.currentThread().getId(), lock1.getHoldCount());

        // 处理业务逻辑
        try {
            Thread.sleep(TIME_LOCKED);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock1.unlock();
            log.info("reLock, ThreadName: {} id: {} unlock, 重入次数: {}", Thread.currentThread().getName(), Thread.currentThread().getId(), lock1.getHoldCount());
        }
    }
}

output:

2021-02-01 16:20:23.013 | cmdb - ERROR | main | com.xm.ggn.test.redisson.Client | line:35 - lock1 clas: class org.redisson.RedissonLock
2021-02-01 16:20:23.050 | cmdb - INFO | main | com.xm.ggn.test.redisson.Client | line:37 - lock, ThreadName: main id: 1 locked, 重入次数: 1
2021-02-01 16:21:13.056 | cmdb - INFO | main | com.xm.ggn.test.redisson.Client | line:57 - reLock, ThreadName: main id: 1 locked, 重入次数: 2
2021-02-01 16:22:03.059 | cmdb - INFO | main | com.xm.ggn.test.redisson.Client | line:66 - reLock, ThreadName: main id: 1 unlock, 重入次数: 1
2021-02-01 16:22:03.061 | cmdb - INFO | main | com.xm.ggn.test.redisson.Client | line:47 - lock, ThreadName: main id: 1 unlock, 重入次数: 0

1.2.4 Spring Data Redis

  • 官网

https://spring.io/projects/spring-data-redis

  • Spring-Data 简介

    • Spring Data是一个用于简化数据库访问的开源框架。其主要目标是使得对数据的访问变得方便快捷,包含多个子项目:
      • Spring Data JDBC- 对 JDBC 的Spring Data存储库支持。
      • Spring Data JPA - 对 JPA 的Spring Data存储库支持。
      • Spring Data MongoDB - 对 MongoDB 的基于Spring对象文档的存储库支持。
      • Spring Data Redis - 从Spring应用程序轻松配置和访问 Redis
  • Features/主要特色

1. Connection package as low-level abstraction across multiple Redis drivers(Lettuce and Jedis).
连接包作为跨多个Redis驱动程序(Lettuse和Jedis)的低级抽象。

2. Exception translation to Spring’s portable Data Access exception hierarchy for Redis driver exceptions.
为Redis驱动程序异常转换为Spring的可移植数据访问异常层次结构。

3. RedisTemplate that provides a high-level abstraction for performing various Redis operations, exception translation and serialization support.
RedisTemplate提供了一个高级抽象,用于执行各种Redis操作、异常转换和序列化支持。

4. Pubsub support (such as a MessageListenerContainer for message-driven POJOs).
推送订阅支持(例如用于消息驱动的POJO的MessageListenerContainer)。

5.Redis Sentinel and Redis Cluster support.
Redis哨兵和Redis集群支持

6. Reactive API using the Lettuce driver.
使用Lettuce驱动程序的反应式API

7. JDK, String, JSON and Spring Object/XML mapping serializers.
JDK、String、JSON和Spring Object/XML映射序列化程序

8. JDK Collection implementations on top of Redis.
JDK Collection在Redis之上的实现。

9. Atomic counter support classes.
原子计数器支持类。

10. Sorting and Pipelining functionality.
分拣和流水线功能

11. Dedicated support for SORT, SORT/GET pattern and returned bulk values.
专门支持SORT、SORT/GET模式和返回的批量值。

12. Redis implementation for Spring 3.1 cache abstraction.
Spring 3.1缓存抽象的Redis实现

13. Automatic implementation of Repository interfaces including support for custom query methods using @EnableRedisRepositories.
存储库接口的自动实现,包括使用@EnableRedisStoreries支持自定义查询方法。

14. CDI support for repositories.
CDI对存储库的支持
  • spring-data-redis是spring、spring-data大家族的一部分。
    • SpringBoot 2.x 后RedisTemplate采用是lettuce(基于netty采用异步非阻塞式lO)进行通信,大并发下比 jedis 效率更高。
  • 提供了在srping应用中通过简单的配置访问redis服务
    • 对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装
    • RedisTemplate 提供了redis各种操作、异常处理及序列化
  • spring-data-redis针对jedis提供了如下功能:
    • 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
    • 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
      • ValueOperations:简单K-V操作
      • SetOperations:set类型数据操作
      • ZSetOperations:zset类型数据操作
      • HashOperations:针对map类型的数据操作
      • ListOperations:针对list类型的数据操作
    • 提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,即 BoundKeyOperations
      • BoundValueOperations
      • BoundSetOperations
      • BoundListOperations
      • BoundSetOperations
      • BoundHashOperations
    • 事务操作封装,有容器控制
    • 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)

2 Jedis(Redis for Java)

2.1 简介

  • jedis是官方首选的java客户端开发包
  • Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。
  • 在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。
  • 在企业中用的最多的就是Jedis,Jedis同样也是托管在github上
  • 项目地址:https://github.com/xetorthio/jedis
  • 下载jedis解压后得到jar包如下:java操作redis数据库API(Jedis)

2.2 基本用法

//连接redis服务器,192.168.0.100:6379
String ip="192.168.0.100";
jedis = new Jedis(ip, 6379);
//权限认证
jedis.auth("password");

2.3 Jedis API

【推荐】Jedis常用方法API - CSDN

(1).键的操作
jedis.flushDB();//清空数据
jedis.set("key1","value1");//设置数据
jedis.exists("key1");//判断键是否存在
Set<String> keys= jedis.keys("*");//获取所有键
jedis.del("key1");//删除键
jedis.expire("key1",10);//设置键过期/秒
jedis.ttl("key1");//获取键的剩余生存时间
jedis.type("key1");//查看键对应值的数据类型
 
(2).字符串的操作
jedis.set("key","value");//增加或覆盖数据项
jedis.setnx("key","value");//非覆盖增加
jedis.setex("key",3,"value");//增加数据项并设置过期时间
jedis.get("key");//获取key的数据项
jedis.append("key","1");//在key的value后面追加1,如果key不存在就创建
jedis.mset("key1","value1","key2","value2");//增加多个键值对
List<String> strs=jedis.mget("key1","key2");//获取多个key对应value
jedis.del(new String[]{"key1","key2"});//删除多个key
jedis.getSet("key","123");//获取value并更新value
 
 
(3).整数和浮点数操作
jedis.set("key","1");//value为可转化的数字
jedis.incr("key");//value加1
jedis.incrBy("key",2);//value加n
jedis.decr("key");//value减1
jedis.decrBy("key",2);//value减n
 
(4).List操作
jedis.lpush("key","1","2","3");//添加一个list
jedis.rpush("key","1","2","3");//添加一个list
jedis.lpop("key");//对应list左出栈一个元素
jedis.rpop("key");//对应list右出栈一个元素
jedis.llen("key");//获取key对应list长度
jedis.lset("key",1,"f");//修改key对应list下标n对应的元素
jedis.lindex("key",1);//获取key对应list下标n对应元素
jedis.sort("key");//key对应list的元素从小到大排序
 
(5).set操作
jedis.sadd("key","1","2","3","4");//添加一个set
jedis.sadd("key1","12","2","33","4");//添加一个set
Set<String> strings= jedis.smembers("key");//获取set对应元素
jedis.srem("key","1");//删除set下值为1的元素
jedis.spop("key");//随机出栈set里的某个元素
jedis.scard("key");//获取set里元素个数
strings=jedis.sinter("key","key1");//获取key和key1下对应元素的交集
strings=jedis.sunion("key","key1");//获取key和key1下对应元素的并集
strings=jedis.sdiff("key","key1");//获取key和key1下对应元素的差集
 
(6).hash操作
jedis.hmset("key",map);//添加hashMap
jedis.hset("key","a","2");//修改hashMap中key=a的元素
Map<String,String> map2=jedis.hgetAll("key");//获取hashMap下所有key-value元素
Set<String> keys =jedis.hkeys("key");//获取hashMap下的所有key
List<String> hvals =jedis.hvals("key");//获取hashMap下的所有value
jedis.hexists("key","a");//判断hashMap中是否存在key=a
jedis.hlen("key");//获取key下的元素个数
 
 
(7).有序集合操作
Map<String,Double> map=new HashMap<>();//key=member,value=score
map.put("1",1.51);
map.put("2",8.51);
map.put("3",3.51);
map.put("4",4.51);
map.put("5",2.71);
jedis.zadd("key",map);//添加zset
jedis.zadd("key",1.18,"6");//插入zset
Set<Tuple>strings= jedis.zrangeWithScores("key",0,2);//获取区间内score-member
long n=jedis.zcard("key");//获取元素个数
double score=jedis.zscore("key","1");//获取map下member=1的score
n=jedis.zrank("key","6");//获取map下member=6在所有score中的排名
jedis.zrem("key","1");//移除map下member=1的元素
 
(8).排序操作
jedis.lpush("key","5","3","10","1","2");
List<String> list= jedis.sort("key",new SortingParams().desc());
for (int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
public class RedisTest {
    @Test
    public void jedisPropertiesTest(){
        Jedis jedis = JedisUtil.getJedis();
        Print.print("ss: " + jedis.get("ss"));//output: ss:2423235
        JedisUtil.close(jedis);
    }

    @Test// 基于字符串存储的基本测试
    public void baseTest(){
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.auth("123456");
        //设置数据
        jedis.set("zengtai","1125418540");
        jedis.set("wangchen","2153253568");
        jedis.close();//释放资源
        Print.print("保存数据完成");

        //获取数据
        Print.print("ss: " + jedis.get("ss"));//output: ss:2423235
        Print.print("xx: " + jedis.get("xx"));//output: xx:bhdjfsahsf
        jedis.close();//释放资源
        Print.print("获取数据完成");

        //删除数据
        jedis.del("xx");
        jedis.close();//释放资源
        Print.print("删除数据完成");
        Print.print("xx: " + jedis.get("wangchen"));//output: xx:null
    }

    @Test//数据结构测试(栈/队列)
    public void dataStructureTest(){
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.auth("123456");

        //设置数据
        jedis.lpush("charsList","E","D","C","B");//添加List:B(0) C(1) D(2) E(3)
        jedis.lpush("charsList","A");//往key对应list左侧插入一个元素
        jedis.rpush("charsList","F");//往key对应list左侧插入一个元素
        Print.print("rpop:"+jedis.rpop("charsList"));//output: rpop:F
        Print.print("lpop:"+jedis.lpop("charsList"));//output: lpop:A

        Print.print("charsList[0]:"+jedis.lindex("charsList",0));//output: charsList[0]:B
        Print.print("charsList[1]:"+jedis.lindex("charsList",1));//output: charsList[1]:C
        jedis.lset("charsList",0,"<X>");//修改key对应list指定下标index的元素
        Print.print("charsList[0]:"+jedis.lindex("charsList",0));//output: charsList[0]:<X>

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

2.4 JedisPool(Jedis数据库连接池)

【推荐(JedisUtil/连接池JedisPool)】Jedis连接池 - CSDN

//1 获得连接池配置对象,设置配置项
JedisPoolConfig config = new JedisPoolConfig();
// 1.1 最大连接数
config.setMaxTotal(30);
//1.2 最大空闲连接数
config.setMaxIdle(10);
//获得连接池
JedisPool jedisPool = new JedisPool(config, "localhost", 6379);

Jedis jedis = null;
try {
    //3.获得核心对象
    jedis = jedisPool.getResource();
    //4.设置数据
    jedis.set("name", "xinruyi");
    //5.获得数据
    String name = jedis.get("name");
    System.out.println(name);
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (jedis != null) {
        jedis.close();
    }
}

//虚拟机关闭时,释放pool资源
if(jedisPool!=null){
    jedisPool.close();
}

2.5 JedisUtil(Redis简易封装的操纵工具)

实现Redis操纵(JedisUtil类)所依赖的工具有:

  • Redis数据库服务
  • Jedis(Redis for Java - API)
    • jedis是官方首选的java客户端开发包
    • Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持
      • 比如 java、C、C#、C++、php、Node.js、Go等
    • 在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis等
      • 其中,官方推荐使用Jedis和Redisson
    • 在企业中用的最多的就是Jedis,Jedis同样也是托管在github上
    • 地址:https://github.com/xetorthio/jedis
    • 下载jedis解压后得到jar包如下:java操作redis数据库API(Jedis)
  • JedisPool(Jedis连接池)
  • ResourceBundle(读取属性配置文件jedis.properties)
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ResourceBundle;

public final class JedisUtil {
    private JedisUtil() {
    }

    private static JedisPool jedisPool;
    private static int maxtotal;
    private static int maxwaitmillis;
    private static String host;
    private static int port;
    private static int timeout;
    private static String auth;//密码

    /*读取 jedis.properties 配置文件*/
    static {
        ResourceBundle rb = ResourceBundle.getBundle("jedis");
        maxtotal = Integer.parseInt(rb.getString("maxtotal"));
        maxwaitmillis = Integer.parseInt(rb.getString("maxwaitmillis"));
        host = rb.getString("host");
        port = Integer.parseInt(rb.getString("port"));
        auth = rb.getString("auth");
        timeout = Integer.parseInt(rb.getString("timeout"));
    }

    /*创建连接池*/
    static {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxtotal);
        jedisPoolConfig.setMaxWaitMillis(maxwaitmillis);
        jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, auth);
    }

    /*获取jedis*/
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * 根据指定的分数据库索引[dabaseIndex],获取Redis分数据库
     * + 参考文献
     *   + [Redis 分库](https://www.cnblogs.com/DillGao/p/8494710.html)
     * + Redis 可以分库,相当于 MySQL 中的 database。
     * + 控制数据库总数在 redis配置文件中设置,默认是 16 个。
     * + 数据库名称是整数索引标识,而不是由一个数据库名称字符串。
     * + 选择数据库用 select 命令: redis>select 2
     * + reidis 中的操作,默认是 数据库 0;
     * + 每个数据库都有属于自己的空间,不必担心数据库之间的key冲突。
     * @param dabaseIndex
     * @return
     */
    public static Jedis getJedis(int dabaseIndex){
        Jedis jedis = jedisPool.getResource();
        jedis.select(dabaseIndex);
        return jedis;
    }

    /*关闭Jedis*/
    public static void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}

jedis.properties [jedis(redis for java)连接池配置信息]

maxtotal=100
maxwaitmillis=3000
host=127.0.0.1
port=6379
auth=123456
timeout=1000

测试与示例

    @Test
    public void jedisPropertiesTest(){
        Jedis jedis = JedisUtil.getJedis();
        Print.print("aiqing: " + jedis.get("aiqing"));//output: aiqing:sdvwtrguyw32
        JedisUtil.close(jedis);//释放资源
    }

3 Spring-Data-Redis

3.1 引入依赖

方式1: spring-data-redis 【step1】

方式2: spring-boot-starter-data-redis

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

3.2 配置文件(application.x) 【step2】

propertie

# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器地址
spring.redis.host=127.0.0.1

# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接超时时间(毫秒)
spring.redis.timeout=5000ms

## 方式1 : jedis
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0

yaml

spring:
  redis:
    cluster:
      nodes:
        - 192.168.57.135:7001
        - 192.168.57.135:7002
        - 192.168.57.135:7003
        - 192.168.57.135:7004
        - 192.168.57.135:7005
        - 192.168.57.135:7006
    jedis:
      pool:
        max-active: 20 #连接池最大连接数
        max-idle: 10 #连接池中的最大空闲连接
        min-idle: 5 # 连接池中的最小空闲连接

3.3 核心API: RedisTemplate

RedisTemplate 注册为 Spring Bean 【step3】

Jackson2JsonRedisSerializer (推荐)

本序列化器于 202304 的项目中有实际应用

/**  
 * redisTemplate bean  
 * @param factory  
 * @return  
 */
@Bean  
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {  
    RedisTemplate<String, Object> template = new RedisTemplate<>();  
    // 配置连接工厂  
    template.setConnectionFactory(factory);  
  
    //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)  
    Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);  
   
    ObjectMapper om = new ObjectMapper();  
    // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public  
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);  
    // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常  
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  
    jacksonSeial.setObjectMapper(om);  
  
    // 使用StringRedisSerializer来序列化和反序列化redis的key值  
    template.setKeySerializer(new StringRedisSerializer());  
    // 值采用json序列化  
    template.setValueSerializer(jacksonSeial);  // new GenericJackson2JsonRedisSerializer()

    // 设置hash key 和value序列化模式  
    template.setHashKeySerializer(new StringRedisSerializer());  
    template.setHashValueSerializer(jacksonSeial); // new GenericJackson2JsonRedisSerializer() 
    template.afterPropertiesSet();  
  
    return template;  
}

GenericToStringSerializer

import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class ApplicationRedisConfiguration {

    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 1.创建 redisTemplate 模版
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        // 2.关联 redisConnectionFactory
        template.setConnectionFactory(redisConnectionFactory);
        // 3.创建 序列化类
        GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class);
        // 6.序列化类,对象映射设置
        // 7.设置 value 的转化格式和 key 的转化格式
        template.setValueSerializer(genericToStringSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

使用方法

直接操作

// 使用 @Autowired 自动装配/注入 RedisTemplate 对象
@Autowired 
private RedisTemplate redisTemplate;

// 删除 单个 key
public void delete(String key){
    redisTemplate.delete(key);
}

// 删除 多个 key
public void deleteKey (String ...keys){
    redisTemplate.delete(keys);
}

// 指定key的失效时间
public void expire(String key,long time){
    redisTemplate.expire(key,time,TimeUnit.MINUTES);
}

// 根据key获取过期时间
public long getExpire(String key){
    Long expire = redisTemplate.getExpire(key);
    return expire;
}

//  判断 key 是否存在
public boolean hasKey(String key){
    return redisTemplate.hasKey(key);
}

// ...

String类型操作

  • 添加缓存
//1、通过 redisTemplate 设置值
redisTemplate.boundValueOps("StringKey").set("StringValue");
redisTemplate.boundValueOps("StringKey").set("StringValue",1, TimeUnit.MINUTES);

//2、通过 BoundValueOperations 设置值
BoundValueOperations stringKey = redisTemplate.boundValueOps("StringKey");
stringKey.set("StringVaule");
stringKey.set("StringValue",1, TimeUnit.MINUTES);

//3、通过 ValueOperations 设置值
ValueOperations ops = redisTemplate.opsForValue();
//ops.set(cacheAccessToken, loginSessionCache, expireTime, TimeUnit.SECONDS);
ops.set("StringKey", "StringVaule");
ops.set("StringValue","StringVaule",1, TimeUnit.MINUTES);
  • 设置过期时间(单独设置)
redisTemplate.boundValueOps("StringKey").expire(1,TimeUnit.MINUTES);
redisTemplate.expire("StringKey",1,TimeUnit.MINUTES);
  • 获取缓存值(2/3是1的递进值)
//1、通过redisTemplate设置值
String str1 = (String) redisTemplate.boundValueOps("StringKey").get();

//2、通过BoundValueOperations获取值
BoundValueOperations stringKey = redisTemplate.boundValueOps("StringKey");
String str2 = (String) stringKey.get();

//3、通过ValueOperations获取值
ValueOperations ops = redisTemplate.opsForValue();
String str3 = (String) ops.get("StringKey");
  • 删除 key
Boolean result = redisTemplate.delete("StringKey");
  • 顺序递增
redisTemplate.boundValueOps("StringKey").increment(3L);
  • 顺序递减
redisTemplate.boundValueOps("StringKey").increment(-3L);

Hash类型操作

  • 添加缓存
//1、通过 redisTemplate 设置值
redisTemplate.boundHashOps("HashKey").put("SmallKey", "HashVaue");

//2、通过 BoundValueOperations 设置值
BoundHashOperations hashKey = redisTemplate.boundHashOps("HashKey");
hashKey.put("SmallKey", "HashVaue");

//3、通过 ValueOperations 设置值
HashOperations hashOps = redisTemplate.opsForHash();
hashOps.put("HashKey", "SmallKey", "HashVaue");
  • 设置过期时间(单独设置)
redisTemplate.boundValueOps("HashKey").expire(1,TimeUnit.MINUTES);
redisTemplate.expire("HashKey",1,TimeUnit.MINUTES);
  • 添加一个Map集合
HashMap<String, String> hashMap = new HashMap<>();
redisTemplate.boundHashOps("HashKey").putAll(hashMap );
  • 设置过期时间
redisTemplate.boundValueOps("HashKey").expire(1,TimeUnit.MINUTES);
redisTemplate.expire("HashKey",1,TimeUnit.MINUTES);
  • 提取所有的小key
//1、通过redisTemplate获取值
Set keys1 = redisTemplate.boundHashOps("HashKey").keys();

//2、通过BoundValueOperations获取值
BoundHashOperations hashKey = redisTemplate.boundHashOps("HashKey");
Set keys2 = hashKey.keys();

//3、通过ValueOperations获取值
HashOperations hashOps = redisTemplate.opsForHash();
Set keys3 = hashOps.keys("HashKey");
  • 提取所有的value值
//1、通过redisTemplate获取值
List values1 = redisTemplate.boundHashOps("HashKey").values();

//2、通过BoundValueOperations获取值
BoundHashOperations hashKey = redisTemplate.boundHashOps("HashKey");
List values2 = hashKey.values();

//3、通过ValueOperations获取值
HashOperations hashOps = redisTemplate.opsForHash();
List values3 = hashOps.values("HashKey");
  • 根据key提取value值
//1、通过redisTemplate获取
String value1 = (String) redisTemplate.boundHashOps("HashKey").get("SmallKey");

//2、通过BoundValueOperations获取值
BoundHashOperations hashKey = redisTemplate.boundHashOps("HashKey");
String value2 = (String) hashKey.get("SmallKey");

//3、通过ValueOperations获取值
HashOperations hashOps = redisTemplate.opsForHash();
String value3 = (String) hashOps.get("HashKey", "SmallKey");
  • 获取所有的键值对集合
//1、通过redisTemplate获取
Map entries = redisTemplate.boundHashOps("HashKey").entries();

//2、通过BoundValueOperations获取值
BoundHashOperations hashKey = redisTemplate.boundHashOps("HashKey");
Map entries1 = hashKey.entries();

//3、通过ValueOperations获取值
HashOperations hashOps = redisTemplate.opsForHash();
Map entries2 = hashOps.entries("HashKey");
  • 删除
//删除小key
redisTemplate.boundHashOps("HashKey").delete("SmallKey");
//删除大key
redisTemplate.delete("HashKey");
  • 判断Hash中是否含有该值
Boolean isEmpty = redisTemplate.boundHashOps("HashKey").hasKey("SmallKey");

Set类型操作

  • 添加Set缓存(值可以是一个,也可是多个)
//1、通过 redisTemplate 设置值
redisTemplate.boundSetOps("setKey").add("setValue1", "setValue2", "setValue3");

//2、通过 BoundValueOperations 设置值
BoundSetOperations setKey = redisTemplate.boundSetOps("setKey");
setKey.add("setValue1", "setValue2", "setValue3");

//3、通过 ValueOperations 设置值
SetOperations setOps = redisTemplate.opsForSet();
setOps.add("setKey", "SetValue1", "setValue2", "setValue3");

  • 设置过期时间(单独设置)
redisTemplate.boundValueOps("setKey").expire(1,TimeUnit.MINUTES);
redisTemplate.expire("setKey",1,TimeUnit.MINUTES);
  • 根据key获取Set中的所有值
//1、通过redisTemplate获取值
Set set1 = redisTemplate.boundSetOps("setKey").members();

//2、通过BoundValueOperations获取值
BoundSetOperations setKey = redisTemplate.boundSetOps("setKey");
Set set2 = setKey.members();

//3、通过ValueOperations获取值
SetOperations setOps = redisTemplate.opsForSet();
Set set3 = setOps.members("setKey");
  • 根据value从一个set中查询,是否存在
Boolean isEmpty = redisTemplate.boundSetOps("setKey").isMember("setValue2");
  • 获取Set缓存的长度
Long size = redisTemplate.boundSetOps("setKey").size();
  • 移除指定的元素
Long result1 = redisTemplate.boundSetOps("setKey").remove("setValue1");
  • 移除指定的key
Boolean result2 = redisTemplate.delete("setKey");

LIST类型操作

  • 添加缓存
//1、通过 redisTemplate 设置值
redisTemplate.boundListOps("listKey").leftPush("listLeftValue1");
redisTemplate.boundListOps("listKey").rightPush("listRightValue2");

//2、通过 BoundValueOperations 设置值
BoundListOperations listKey = redisTemplate.boundListOps("listKey");
listKey.leftPush("listLeftValue3");
listKey.rightPush("listRightValue4");

//3、通过 ValueOperations 设置值
ListOperations opsList = redisTemplate.opsForList();
opsList.leftPush("listKey", "listLeftValue5");
opsList.rightPush("listKey", "listRightValue6");
  • 将List放入缓存
ArrayList<String> list = new ArrayList<>();
redisTemplate.boundListOps("listKey").rightPushAll(list);
redisTemplate.boundListOps("listKey").leftPushAll(list);
  • 设置过期时间(单独设置)
redisTemplate.boundValueOps("listKey").expire(1,TimeUnit.MINUTES);
redisTemplate.expire("listKey",1,TimeUnit.MINUTES);
  • 获取List缓存全部内容(起始索引,结束索引)
List listKey1 = redisTemplate.boundListOps("listKey").range(0, 10); 
  • 从左或从右弹出一个元素
String listKey2 = (String) redisTemplate.boundListOps("listKey").leftPop();  //从左侧弹出一个元素
String listKey3 = (String) redisTemplate.boundListOps("listKey").rightPop(); //从右侧弹出一个元素
  • 根据索引查询元素
String listKey4 = (String) redisTemplate.boundListOps("listKey").index(1);
  • 根据索引修改List中的某条数据(key,索引,值)
redisTemplate.boundListOps("listKey").set(3L,"listLeftValue3");
  • 移除N个值为value(key,移除个数,值)
redisTemplate.boundListOps("listKey").remove(3L,"value");

Zset类型操作

  • 向集合中插入元素,并设置分数
//1、通过 redisTemplate 设置值
redisTemplate.boundZSetOps("zSetKey").add("zSetVaule", 100D);

//2、通过 BoundValueOperations 设置值
BoundZSetOperations zSetKey = redisTemplate.boundZSetOps("zSetKey");
zSetKey.add("zSetVaule", 100D);

//3、通过 ValueOperations设置值
ZSetOperations zSetOps = redisTemplate.opsForZSet();
zSetOps.add("zSetKey", "zSetVaule", 100D);
  • 向集合中插入多个元素,并设置分数
DefaultTypedTuple<String> p1 = new DefaultTypedTuple<>("zSetVaule1", 2.1D);
DefaultTypedTuple<String> p2 = new DefaultTypedTuple<>("zSetVaule2", 3.3D);
redisTemplate.boundZSetOps("zSetKey").add(new HashSet<>(Arrays.asList(p1,p2)));
  • 按照排名先后(从小到大)打印指定区间内的元素, -1为打印全部
Set<String> range = redisTemplate.boundZSetOps("zSetKey").range(0, -1);
  • 获得指定元素的分数
Double score = redisTemplate.boundZSetOps("zSetKey").score("zSetVaule");
  • 返回集合内的成员个数
Long size = redisTemplate.boundZSetOps("zSetKey").size();
  • 返回集合内指定分数范围的成员个数(Double类型)
Long COUNT = redisTemplate.boundZSetOps("zSetKey").count(0D, 2.2D);
  • 返回集合内元素在指定分数范围内的排名(从小到大)
Set byScore = redisTemplate.boundZSetOps("zSetKey").rangeByScore(0D, 2.2D);
  • 带偏移量和个数,(key,起始分数,最大分数,偏移量,个数)
Set<String> ranking2 = redisTemplate.opsForZSet().rangeByScore("zSetKey", 0D, 2.2D 1, 3);
  • 返回集合内元素的排名,以及分数(从小到大)
Set<TypedTuple<String>> tuples = redisTemplate.boundZSetOps("zSetKey").rangeWithScores(0L, 3L);
  for (TypedTuple<String> tuple : tuples) {
      System.out.println(tuple.getValue() + " : " + tuple.getScore());
  }ss
  • 返回指定成员的排名
//从小到大
Long startRank = redisTemplate.boundZSetOps("zSetKey").rank("zSetVaule");
//从大到小
	Long endRank = redisTemplate.boundZSetOps("zSetKey").reverseRank("zSetVaule");
  • 从集合中删除指定元素
redisTemplate.boundZSetOps("zSetKey").remove("zSetVaule");
  • 删除指定索引范围的元素(Long类型)
redisTemplate.boundZSetOps("zSetKey").removeRange(0L,3L);
  • 删除指定分数范围内的元素(Double类型)
redisTemplate.boundZSetOps("zSetKey").removeRangeByScorssse(0D,2.2D);
  • 为指定元素加分(Double类型)
Double score = redisTemplate.boundZSetOps("zSetKey").incrementScore("zSetVaule",1.1D);

3.4 核心API: StringRedistemplate

源码

package org.springframework.data.redis.core;

import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializer;

public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}

从上面创建StringRedisTemplate的无参构造方法可以看出:
此时将keySerializer、valueSerializer、hashKeySerializer、hashValueSerializer的序列化方式为 stringSerializer,也就是 StringRedisSerializer 序列化方式;
此时执行完整个方法后,还需要接着执行setConnectionFactory()方法,然后转向他的父类RedisTemplate中的afterPropertiesSet方法,此时上述四个序列化方式已经设置;

3.5 核心API: 序列化器

RedisTemplate模板使用序列化器操作redis数据,预定义的序列化方案有:

序列化器 说明
JdkSerializationRedisSerializer
org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
POJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputstream/ObjectOutputstream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。
StringRedisSerializer
org.springframework.data.redis.serializer.StringRedisSerializer
Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是"new String(bytes,charset)"和“string.getBytes(charset)"的直接封装。是最轻量级和高效的策略。
GenericToStringSerializer
org.springframework.data.redis.serializer.GenericToStringSerializer
...
Jackson2JsonRedisSerializer
org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
...
GenericJackson2JsonRedisSerializer
org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。

示例 : 参见 "RedisTemplate 注册为 Spring Bean"

3.x 辨析

3.x.1 StringRedisTemplate和RedisTemplate的区别

RedisTemplate和StringRedisTemplate的区别:

  • 两者的关系是StringRedisTemplate继承RedisTemplate。
  • 两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。
  • spring-data-redis 默认采用的序列化策略有两种
    一种是JDK的序列化策略
    RedisTemplate:但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象。
    RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。(JdkSerializationRedisSerializer)
    RedisTemplate使用的序列类在在操作数据的时候,比如说存入数据会将数据先序列化成字节数组然后在存入Redis数据库,这个时候打开Redis查看的时候,你会看到你的数据不是以可读的形式展现的,而是以字节数组显示,类似下面(RedisTemplate)

	当Redis当中的数据值是以**数组**形式显示出来的时候,只能使用**RedisTemplate**才能获取到里面的数据。故:当你使用RedisTemplate获取不到数据为**NULL**时,一般是获取的方式错误。检查一下数据是否可读即可。
一种是**String的序列化策略**
	StringRedisTemplate:当你的redis数据库里面本来存的是**字符串数据**或者你要存取的数据就是字符串类型数据的时候。
	StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的(StringRedisSerializer)。
		当然从Redis获取数据的时候也会默认将数据当做字节数组转化,这样就会导致一个问题,当需要获取的数据不是以字节数组存在redis当中而是正常的可读的字符串的时候,比如说下面这种形式的数据(StringRedisTemplate)

	当Redis当中的数据值是以**可读**的形式显示出来的时候,只能使用**StringRedisTemplate**才能获取到里面的数据。

X 参考文献

posted @ 2023-08-07 17:36  千千寰宇  阅读(132)  评论(0编辑  收藏  举报