[SpringBoot & Redis] SpringBoot integrates Redis

1, Introduction

After spring boot 2. X, jedis in the bottom layer of spring boot starter data redis is replaced with lettuce. The project is the difference between jedis and lettuce

Jedis: direct connection is adopted. If there are multiple threads, it will lead to security risks. Of course, you can use jedis pool (BIO) connection pool to avoid thread insecurity.

lettuce: adopt netty , instances can be shared among multiple threads. There is no thread insecurity. It is more like Nio pattern


2, Integration

Step 1: create a springboot project

Slightly

Step 2: import redis dependency

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

Step 3: configure redis profile

# Configure redis
spring.redis.host=192.168.174.128
spring.redis.port=6379

Step 4: Test

We will use RedisTemplate to operate. opsForValue() is used to operate strings. We will explain the operation of various data types below.

@Autowired
private RedisTemplate redisTemplate;

@Test
void contextLoads() {
    redisTemplate.opsForValue().set("name","studioustiger");
    String name = (String)redisTemplate.opsForValue().get("name");

    System.out.println(name);
}

3, Use of Redis data types in RedisTemplate

When we used Jedis as like as two peas, we found that the command of Jedis operation Redis is basically the same as that of Redis, so we are very good at it.

However, in the RedisTemplate in springboot, we need to determine the data type of Redis you want to operate through the opsforxx () method, and then call the methods supported by the data type.

// The operation of String type, xxx(), is the various methods supported by String we talked about earlier
redisTemplate.opsForValue().xxx()

// The operation Set type, xxx() is the various methods supported by Set as we mentioned earlier
redisTemplate.opsForSet().xxx()

// Operate the Hash type. xxx() is the various methods supported by Hash we talked about earlier
redisTemplate.opsForHash().xxx()

// Operate the List type, xxx() is the various methods supported by the List we talked about earlier
redisTemplate.opsForList().xxx()

// Operating Zset type, xxx() is the various methods supported by Zset we talked about earlier
redisTemplate.opsForZSet().xxx()

// Operating Geo type, xxx() is the various methods supported by Geo we talked about earlier
redisTemplate.opsForGeo().xxx()

// To operate the HyperLogLog type, xxx() is the various methods supported by HyperLogLog we talked about earlier
redisTemplate.opsForHyperLogLog().xxx()



4, Custom RestTemplate

1. Source code introduction

Let's first look at the source code to see why we can customize RestTemplate and how to customize it

Find spring.factories and open

RedisAutoConfiguration found

Enter RedisAutoConfiguration (see note)

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})

// We can see what redis can configure in RedisProperties and the default values of some parameters
@EnableConfigurationProperties({RedisProperties.class}) 
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    // ConditionalOnMissingBean: indicates that when there is a redisTemplate, the injected class will not take effect, which means that we can customize the redisTemplate to replace the default redisTemplate
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    	// We see that the generic type of its RedisTemplate is < object, Object >, but we prefer < string, Object >, so we will pay attention to this later when rewriting
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    // This is a redisTemplate that operates on strings. Because String is the most commonly used in redis, spring officially encapsulates a class.
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

Click RedisTemplate

We don't want to use jdk serialization. We think jkd serialization will cause garbled code in redis (even in English).

Through the above source code introduction, we know that if we want to store an object in redis, there are two ways. The first method: convert the string to be stored in redis into json, and then store it in redis. The second method is to serialize the entity classes used and store them in redis.

Let's customize the second method.

2. Write your own RedisTemplate

If you are impressed, we said earlier that in actual development, our entity classes need to be serialized. Function of serialization: a mechanism for managing object flow, which can easily save the state of java objects in memory and facilitate transmission. Therefore, the entity classes we use need to implement Serializable.

① Write a custom RedisTemplate

package com.tiger.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    // This is a custom serialization template that we can use directly in development
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // For the convenience of our own development, we generally directly use < string, Object >
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        
        
        // Json serialization configuration
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        
        
        // Serialization of String
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // The key is serialized by String
        template.setKeySerializer(stringRedisSerializer);
        // The key of hash is also serialized by String
        template.setHashKeySerializer(stringRedisSerializer);
        // value is serialized by jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // The value serialization method of hash is jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        
        return template;
    }
}

In "3", if we have redis data types, we need to determine the types and usage methods first. This method is really cumbersome, so we can customize a RedisUtil to simplify development

② RedisUtil tool class

package com.tiger.utils;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis Tool class*/
@Component
public final class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // =============================common============================
    /**
     * Specify cache expiration time
     * @param key  key
     * @param time Time (seconds)
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Get expiration time according to key
     * @param key Key cannot be null
     * @return Time (seconds) returns 0, which means it is permanently valid
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }


    /**
     * Determine whether the key exists
     * @param key key
     * @return true Exists false does not exist
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Delete cache
     * @param key One or more values can be passed
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
            }
        }
    }


    // ============================String=============================

    /**
     * Normal cache fetch
     * @param key key
     * @return value
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * Normal cache put
     * @param key   key
     * @param value value
     * @return true Success false failure
     */

    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Normal cache put and set time
     * @param key   key
     * @param value value
     * @param time  Time (seconds) time must be greater than 0. If time is less than or equal to 0, the infinite period will be set
     * @return true Success false failure
     */

    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Increasing
     * @param key   key
     * @param delta How many to add (greater than 0)
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("The increment factor must be greater than 0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }


    /**
     * Diminishing
     * @param key   key
     * @param delta How many to reduce (less than 0)
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("Decrement factor must be greater than 0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }


    // ================================Map=================================

    /**
     * HashGet
     * @param key  Key cannot be null
     * @param item Item cannot be null
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * Get all key values corresponding to hashKey
     * @param key key
     * @return Corresponding multiple key values
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key key
     * @param map Corresponding to multiple key values
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * HashSet And set the time
     * @param key  key
     * @param map  Corresponding to multiple key values
     * @param time Time (seconds)
     * @return true Success false failure
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Put data into a hash table. If it does not exist, it will be created
     *
     * @param key   key
     * @param item  term
     * @param value value
     * @return true Success false failure
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Put data into a hash table. If it does not exist, it will be created
     *
     * @param key   key
     * @param item  term
     * @param value value
     * @param time  Time (seconds): Note: if the existing hash table has time, the original time will be replaced here
     * @return true Success false failure
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Delete values from hash table
     *
     * @param key  Key cannot be null
     * @param item Item can make multiple non null able
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }


    /**
     * Judge whether there is a value of this item in the hash table
     *
     * @param key  Key cannot be null
     * @param item Item cannot be null
     * @return true Exists false does not exist
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }


    /**
     * hash If increment does not exist, it will create one and return the added value
     *
     * @param key  key
     * @param item term
     * @param by   How many to add (greater than 0)
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }


    /**
     * hash Diminishing
     *
     * @param key  key
     * @param item term
     * @param by   To reduce (less than 0)
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }


    // ============================set=============================

    /**
     * Get all the values in the Set according to the key
     * @param key key
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * Query from a set according to value whether it exists
     *
     * @param key   key
     * @param value value
     * @return true Exists false does not exist
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Put data into set cache
     *
     * @param key    key
     * @param values Values can be multiple
     * @return Number of successful
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * Put set data into cache
     *
     * @param key    key
     * @param time   Time (seconds)
     * @param values Values can be multiple
     * @return Number of successful
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * Gets the length of the set cache
     *
     * @param key key
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * Remove with value
     *
     * @param key    key
     * @param values Values can be multiple
     * @return Number of removed
     */

    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================list=================================

    /**
     * Get the contents of the list cache
     *
     * @param key   key
     * @param start start
     * @param end   End 0 to - 1 represent all values
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * Gets the length of the list cache
     *
     * @param key key
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * Get the value in the list through the index
     *
     * @param key   key
     * @param index When index index > = 0, 0 header, 1 second element, and so on; When index < 0, - 1, footer, - 2, the penultimate element, and so on
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * Put list into cache
     *
     * @param key   key
     * @param value value
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Put list into cache
     * @param key   key
     * @param value value
     * @param time  Time (seconds)
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * Put list into cache
     *
     * @param key   key
     * @param value value
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * Put list into cache
     *
     * @param key   key
     * @param value value
     * @param time  Time (seconds)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Modify a piece of data in the list according to the index
     *
     * @param key   key
     * @param index Indexes
     * @param value value
     * @return
     */

    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * Remove N values as value
     *
     * @param key   key
     * @param count How many are removed
     * @param value value
     * @return Number of removed
     */

    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }

    }

}

④ Testing

Note: the entity class we use implements the Serializable interface.

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisUtil redisUtil;

    @Test
    void contextLoads() throws JsonProcessingException {
        User user = new User("tiger", 18, "male");

        redisUtil.set("user",user);
        System.out.println(redisUtil.get("user").toString());
    }


3. Considerations when serializing and deserializing

Serialization:
When the entity class needs to be serialized, the serialized entity class must implement the Serializable interface.

Deserialization:
If the following error occurs in your program
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of com.tiger.pojo.User (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.tiger.pojo.User (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

Then it means that there is no parameterless constructor in your entity class.


May you live up to your youth and return as a teenager

Tags: Java Redis Spring Boot

Posted on Fri, 19 Nov 2021 09:34:47 -0500 by jamiet757