Spring-Data-Redis 入门学习_diaopitian0181的博客-程序员宝宝

技术标签: java  数据结构与算法  数据库  

Spring-Data-Redis 入门学习

参考文章:

https://www.jianshu.com/p/4a9c2fec1079

  1. 导入 redis 相关依赖
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.7.2.RELEASE</version>
</dependency>
  1. 配置3个 bean ,连接池配置类 连接工厂类 模板类

JedisPoolConfig JedisConnectionFactory RedisTemplate RedisSerializer

  1. 提供 redis.properties 文件 (可选)

​ 将 redis 的相关参数从 spring 配置文件中挪到 redis.properties 文件中。

spring 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        ">

    <!-- 配置连接池 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="10"></property>
        <property name="maxIdle" value="10"></property>
        <property name="minIdle" value="2"></property>
        <property name="maxWaitMillis" value="15000"></property>
        <property name="minEvictableIdleTimeMillis" value="300000"></property>
        <property name="numTestsPerEvictionRun" value="3"></property>
        <property name="timeBetweenEvictionRunsMillis" value="60000"></property>
        <property name="testOnBorrow" value="true"></property>
        <property name="testOnReturn" value="true"></property>
        <property name="testWhileIdle" value="true"></property>
    </bean>

    <!-- 连接工厂 -->
    <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="127.0.0.1"/>
        <property name="port" value="6379"/>
        <!--<property name="password" value="123456"/>-->
        <property name="usePool" value="true"/>
        <property name="poolConfig" ref="jedisPoolConfig"/>
    </bean>

    <!-- 用于数据交互 -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnFactory"/>
    </bean>
</beans>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory"/>

    <!-- 序列化策略 推荐使用StringRedisSerializer -->
    <property name="keySerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    </property>
    <property name="valueSerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    </property>
    <property name="hashKeySerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    </property>
    <property name="hashValueSerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    </property>
</bean>
SDR序列化方式有多种,如:StringRedisSerializer JdkSerializationRedisSerializer Jackson2JsonRedisSerializer OxmSerializer等等

pom 文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mozq.redis</groupId>
    <artifactId>spring-redis-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>5.0.2.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!-- spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- redis 相关 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.7.2.RELEASE</version>
        </dependency>

        <!-- 测试相关 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>

</project>

hash 结构测试类 存储简单值

package com.mozq.redis;

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

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

//hash结构,即 map 的测试。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-redis.xml")
public class HashTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testSetEntry(){
        BoundHashOperations book = redisTemplate.boundHashOps("book");
        book.put("b1", "仙逆");
        book.put("b2", "凡人修仙传");
    }

    @Test
    public void testGetEntryKeys(){
        Set book = redisTemplate.boundHashOps("book").keys();
        System.out.println(book);
    }

    @Test
    public void testGetEntryValues(){
        List book = redisTemplate.boundHashOps("book").values();
        System.out.println(book);
    }

    @Test
    public void testGetEntries(){
        Map book = redisTemplate.boundHashOps("book").entries();
        System.out.println(book);
    }

    @Test
    public void testGetEntryValueByEntryKey(){
        String book1 = (String) redisTemplate.boundHashOps("book").get("b1");
        System.out.println(book1);
    }

    @Test
    public void testRemoveEntry(){
        Long row = redisTemplate.boundHashOps("book").delete("b1");
        System.out.println(row);
    }

    @Test
    public void testRemoveKey(){
        redisTemplate.delete("book");
    }
}
hash 结构

EntryValue = List<Map>

@Test
public void testSetEntry(){
    BoundHashOperations users = redisTemplate.boundHashOps("users");
    //用户列表
    List<Map> userList = new ArrayList<>();
    //创建用户
    Map map1 = new HashMap();
    map1.put("name", "刘备");
    map1.put("age", 22);
    Map map2 = new HashMap();
    map2.put("name", "刘备2");
    map2.put("age", 23);
    //添加用户到列表
    userList.add(map1);
    userList.add(map2);

    users.put("userList", userList);
}

@Test
public void testGetEntryValueByEntryKey(){
    List<Map> userList = (List<Map>) redisTemplate.boundHashOps("users").get("userList");
    System.out.println(userList);//[{name=刘备, age=22}, {name=刘备2, age=23}]
}
String 类型

RedisSerializer<T> 将一个对象序列化字节数组,存入 redis 。将 redis 得到的字节数组反序列化成对象。

public interface RedisSerializer<T> {
    byte[] serialize(T t) throws SerializationException;
    T deserialize(byte[] bytes) throws SerializationException;
}
@Test
public void addObj(){
    User user = new User();
    user.setName("刘备");
    user.setAge(20);
    redisTemplate.boundValueOps("moStr").set(user);
}//java.lang.ClassCastException: com.mozq.domain.User cannot be cast to java.lang.String


@Test
public void addObj(){
    User user = new User();
    user.setName("刘备");
    user.setAge(20);
    redisTemplate.boundValueOps(user).set("孙权");
}//java.lang.ClassCastException: com.mozq.domain.User cannot be cast to java.lang.String
@Test
public void testStrCR(){
    redisTemplate.boundValueOps("用户名").set("刘备");
    String username = (String) redisTemplate.boundValueOps("用户名").get();
    System.out.println(username);//刘备
}

@Test
public void testStrD(){
    redisTemplate.delete("用户名");
}

127.0.0.1:6379> keys *
1) "\xe7\x94\xa8\xe6\x88\xb7\xe5\x90\x8d" UTF-8 用户名
2) "\xd3\xc3\xbb\xa7\xc3\xfb" GBK 用户名
127.0.0.1:6379> get "\xd3\xc3\xbb\xa7\xc3\xfb" GBK 用户名
"\xe5\x88\x98\xe5\xa4\x87" UTF-8 刘备
127.0.0.1:6379> get "\xe7\x94\xa8\xe6\x88\xb7\xe5\x90\x8d"  UTF-8 用户名
"\xe5\x88\x98\xe5\xa4\x87" UTF-8 刘备

/*
    可见 redis 命令行打印就是字节数组的16进制形式。中文字符串 + 编码 = 字节数组。客户端发送给 redis 的是字节数组。
*/
@Test
public void x3() throws UnsupportedEncodingException {
    byte[] bytesUTF8 = "用户名".getBytes("UTF-8");
    System.out.println(bytesToHexString(bytesUTF8));//e794a8e688b7e5908d
    byte[] bytesGBK = "用户名".getBytes("GBK");
    System.out.println(bytesToHexString(bytesGBK));//d3c3bba7c3fb
    byte[] bytes= "刘备".getBytes("UTF-8");
    System.out.println(bytesToHexString(bytes));//e58898e5a487
}

常用字节转换(字符串转16进制,16进制转字符串)https://blog.csdn.net/yyz_1987/article/details/80634224

package org.springframework.data.redis.serializer;

public class StringRedisSerializer implements RedisSerializer<String> {

    private final Charset charset;

    public StringRedisSerializer() {
        this(Charset.forName("UTF8"));
    }

    public StringRedisSerializer(Charset charset) {
        Assert.notNull(charset);
        this.charset = charset;
    }

    public String deserialize(byte[] bytes) {
        return (bytes == null ? null : new String(bytes, charset));
    }

    public byte[] serialize(String string) {
        return (string == null ? null : string.getBytes(charset));
    }
}

redisTemplate 键值的序列化策略

<!-- 配置RedisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory"/>

    <!-- 序列化策略 推荐使用StringRedisSerializer ,可以通过构造参数指定字符集,默认为 UTF-8 -->
    <property name="keySerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer">
            <constructor-arg ref="gbkCharSet" />
        </bean>
    </property>
    <property name="valueSerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    </property>

    <property name="hashKeySerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    </property>
    <property name="hashValueSerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    </property>
</bean>
package com.mozq.charset;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;

//@Component
@Configuration
public class CharSetUtil {

    @Bean("gbkCharSet")
    public Charset gbkCharSet(){
        return Charset.forName("GBK");
    }

}

通用操作

// redisTemplate 删除全部的键
@Test
public void deleteAll(){
    Set keys = redisTemplate.keys("*");
    redisTemplate.delete(keys);
    
    System.out.println(keys);
    System.out.println(keys.size());
}

// redis 删除全部键 Redis Flushall 命令用于清空整个 Redis 服务器的数据 ( 删除所有数据库的所有 key )。
127.0.0.1:6379> flushall
OK
实战 hash 的 EntryValue 的值为 List<Map>

hash 结构 EntryValue = List<Map>

存入
@Service
public class TypeTemplateServiceImpl implements TypeTemplateService {

    @Autowired
    private TbTypeTemplateMapper typeTemplateMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TbSpecificationOptionMapper specificationOptionMapper;

    private void saveToRedis(){
        List<TbTypeTemplate> typeTemplateList = typeTemplateMapper.selectByExample(null);
        for (TbTypeTemplate typeTemplate : typeTemplateList) {
            //缓存品牌列表
            List<Map> brandList = JSON.parseArray(typeTemplate.getBrandIds(), Map.class);
            redisTemplate.boundHashOps("searchBrandList").put(typeTemplate.getId(), brandList);
            //缓存规格列表
            List<Map> specList = findSpecList(typeTemplate.getId());
            redisTemplate.boundHashOps("searchSpecList").put(typeTemplate.getId(), specList);
        }
        System.out.println("缓存品牌和规格列表");
    }
}
取出
private Map searchBrandAndSpecList(String categoryName){
    HashMap map = new HashMap();
    Long typeId = (Long) redisTemplate.boundHashOps("searchCategoryList").get(categoryName);
    if(typeId != null){
        //品牌列表
        List<Map> brandList = (List<Map>) redisTemplate.boundHashOps("searchBrandList").get(typeId);
        //规格列表
        List<Map> specList = (List<Map>) redisTemplate.boundHashOps("searchSpecList").get(typeId);

        map.put("brandList", brandList);
        map.put("specList", specList);
    }
    return map;
}

问题 插入 redis 的键,不是程序中设置的键。

redisTemplate模板类在操作 redis 会使用默认的 jdk 方式序列化。我们要手动配置序列化方式为:StringRedisSerializer

127.0.0.1:6379> keys *
1) "categoryList"
2) "heima01"
3) "hema01"
4) "\xac\xed\x00\x05t\x00\bhashkey1"
5) "user"
127.0.0.1:6379> keys *
1) "categoryList"
2) "heima01"
3) "hema01"
4) "book" // 已经好了
5) "\xac\xed\x00\x05t\x00\bhashkey1"
6) "user"
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory"/>

    <!-- 序列化策略 推荐使用StringRedisSerializer -->
    <property name="keySerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    </property>
    <property name="valueSerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    </property>
    <property name="hashKeySerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    </property>
    <property name="hashValueSerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    </property>
</bean>
# 如果存储的 value 是对象,则必须实现序列化。
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: com.mozq.domain.User
nested exception is redis.clients.jedis.exceptions.JedisDataException: WRONGTYPE Operation against a key holding the wrong kind of value
原因:
    造成这个问题,肯定是程序在多处使用了同一个 key ,并且是以不同的类型,有的以 key-value 类型,有的以 key-map ,有的以 key-object 。我这里 categoryList 被多次使用分别以不同的数据结构存储,报错。
# 因为验证失败,获取不到 redis 连接
redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool
Caused by: java.util.NoSuchElementException: Unable to validate object
java.lang.IllegalArgumentException: non null hash key required
原因:
    代码逻辑错误,导致 categoryName 变量的值为 null 。报错。  
redisTemplate.boundHashOps("searchCategoryList").get(categoryName);
/*
    searchMap.get("category") 的值为 null 时,会进入 if ,而实际的逻辑应该进入 else 
*/
if(!"".equals(searchMap.get("category"))){
    //搜索条件有分类,直接使用这个分类查品牌和规格
    resultMap.putAll(searchBrandAndSpecList((String) searchMap.get("category")));
}else{
    if(categoryList.size() > 0){
        resultMap.putAll(searchBrandAndSpecList(categoryList.get(0)));
    }
}

转载于:https://www.cnblogs.com/mozq/p/11306835.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/diaopitian0181/article/details/102014268

智能推荐

VOS3000如何呼入送到OKCC_vos呼入设置_ai智能@kelaile520的博客-程序员宝宝

1,vos通过落地网关送号到OKCC,SIP端口用2080。2,运营商登录添加did绑定企业,业务->did。3,跳转到企业,did绑定坐席,业务->did。

Java HttpClient保持会话信息并跟踪重定向_allway2的博客-程序员宝宝

import java.net.CookieManager;import java.net.ProxySelector;import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.time.Duration;import java.util.concurrent.Executors;imp.

java 循环删除ftp_java连接FTP、删除、下载文件的方法_青山依旧笑的博客-程序员宝宝

本文共例举了二个连接FTP的方法,可以下载删除FTP上的文件,代码有点凌乱JAVA自带的方法importjava.io.BufferedInputStream;importjava.io.DataInputStream;importjava.io.File;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava...

VSCode+Python, conda激活环境错误的解决方案: CommandNotFoundError_没有python: create environment_weixin_42108016的博客-程序员宝宝

之前VSCode运行Python总是会在终端报错:CommandNotFoundError:Your shell has not been properly configured to use 'conda activate'

选择香港主机遇到的几个常见问题_LuHai3005151872的博客-程序员宝宝

互联网时代,网站访问就讲究一个“速”字。同时,随着国家对互联网络监管严格,现在国内站长建站都需要备案,而且手续比较繁琐。因此,随之而来的是,众多企业及个人站长开始寻找新的服务器,而香港主机也便成了大家的焦点。香港离中国大陆地区最近,拥有访问速度最快服务质量最好的香港机房香港。目前,为了更好地服务于大客户,很多国内外主机商都推出免备案高速稳定的香港主机,首先香港主机对比国内主机无需备案,速度相比国...

分享一些网站及系统功能截图_暖枫无敌的博客-程序员宝宝

1、小型网站访问地址: http://www.bntsj.com2、利用ArcGIS API for Silverlight 调用GP模型 实现的等值线和等值面

随便推点

夜间模式(模仿喜马拉雅FM)_android 仿喜玛拉雅广播_SunnyRivers的博客-程序员宝宝

经常玩喜马拉雅FM的app,看到它的夜间模式的实现和其他的都不一样,仅仅是通过改变屏幕的亮度来实现。感觉这种方式比起换肤来说更加简洁。实现思路:1.在程序入口后的第一个Activity中:(1)获取系统当前的亮度(2)保存系统当前的亮度(3)如果系统打开了自动调节亮度则关闭,并记录2.在有夜间模式开关的界面中:(1)初始化开关后,先判断之前开关的状态,修改对应的UI

如何在Django 1.8.3中引用添加js,css,fonts等静态文件(管理静态文件)_GOD1116的博客-程序员宝宝

Django建设的网站所需要的额外的文件such as images,js,css 都会被看作静态文件。Django提供django.contrib.staticfiles来方便管理。 1.确保installed_apps中有django.contrib.staticfiles;​ 2.确保settings.py中有声明STATIC_URL='/st...

NOIP模拟赛4总结_XSC062的博客-程序员宝宝

我 自 闭 了这场比赛……不总结一下我良心上都过不去……1.营救题目描述铁塔尼号遇险了!他发出了求救信号。距离最近的哥伦比亚号收到了讯息,时间就是生命,必须尽快赶到那里。通过侦测,哥伦比亚号获取了一张海洋图。这张图将海洋部分分化成 n∗nn*nn∗n 个比较小的单位,其中用 111 标明的是陆地,用 000 标明是海洋。船只能从一个格子,移到相邻的四个格子。为了尽快赶到出事地点,哥伦比亚号最少需要走多远的距离。输入格式第一行为 nnn,下面是一个 n∗nn*nn∗n 的 0、10、10、1

HOJ 2979 Escape from Pyramids --------BFS求最小的步数_码蹄疾的博客-程序员宝宝

HOJ 2979 Escape from Pyramids//题意:按照题中给的金字塔的位置 ,s代表起点 ,*代表障碍,@代表此路可走// D代表出口(注意出口不止一个),在规定的时间内判断是否能走出金字塔//思路:BFS求最短的路径上面所走的步数,注意搜索失败的时候队列是空的,判断一下子//调试注意:// 1.注意步数是1这种特殊情况(对应题目中所给样例的case3

win7下mysql的安装_jojo52013145的博客-程序员宝宝

一 , 当前mysql的最新版本是5.5.25a。到http://dev.mysql.com/downloads/mysql/下载mysql安装文件 。我们这里下载mysql-5.5.25a-win32.msi就可以了,下载完,直接点击安装。mysql有好几个版本,稍微了解下各个版本之间的区别:  MySQL Community Server :社区版本 不提供官方技术支持,是免费的

ClassNotFoundException: org.apache.ws.commons.schema.resolver.URIResolver_fire2bug的博客-程序员宝宝

在使用Axis2+eclipse开发web service时,在启动tomcat时,一直报错:java. lang.ClassNotFoundException: org.apache.ws.commons.schema.resolver.URIResolver解决的有效方法是:1. 在Axis2的安装目录下的lib文件夹下找到xmlschema-core-2.2.1.ja

推荐文章

热门文章

相关标签