• Spring-Data-Redis 入门学习


    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) "xe7x94xa8xe6x88xb7xe5x90x8d" UTF-8 用户名
    2) "xd3xc3xbbxa7xc3xfb" GBK 用户名
    127.0.0.1:6379> get "xd3xc3xbbxa7xc3xfb" GBK 用户名
    "xe5x88x98xe5xa4x87" UTF-8 刘备
    127.0.0.1:6379> get "xe7x94xa8xe6x88xb7xe5x90x8d"  UTF-8 用户名
    "xe5x88x98xe5xa4x87" 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) "xacxedx00x05tx00hashkey1"
    5) "user"
    
    127.0.0.1:6379> keys *
    1) "categoryList"
    2) "heima01"
    3) "hema01"
    4) "book" // 已经好了
    5) "xacxedx00x05tx00hashkey1"
    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)));
        }
    }
    
  • 相关阅读:
    2.4.4.1、Django新建APP(acounts)
    2.4.3、Django用户权限控制
    2.4.2、Django处理登录和注销
    2.4.1、Django使用内置的Admin管理用户
    2.4、使用Django自带的admin用户管理,权限管理
    2.3、django项目对数据库的操作
    2.2、创建第一个Django项目
    2、Django框架介绍
    github设置添加SSH(转)
    form表单回车提交问题
  • 原文地址:https://www.cnblogs.com/mozq/p/11306835.html
Copyright © 2020-2023  润新知