SpringBoot integrated with Redis

1.1 Jedis and Lettuce

In SpringBoot 1.x, Jedis is used as the default client to connect to the Redis Server. In SpringBoot 2.x, the default Jedis is used as the default client to connect to the Redis Server.

Jedis and Lettuce differ:

  • Jedis: directly connected to Redis Server on the underlying implementation. It is not thread-safe in multi-threaded environment and needs to be used together with connection pool, for example:commons-pool2
  • Lettuce: The underlying implementation is based on Netty, multiple instances can reuse a connection, thread safety, excellent performance.

1.2 Adding a Dependency

Since SpringBoot 2.x defaults to the Redis client, there is no need to introduce additional dependencies, just import the Redis starter in the POP. XML file, as shown below:

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

1.3 Configuration File

Add Redis related configuration items to the application.properties configuration file as follows:

Redis base configuration
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=redis
spring.redis.timeout=3000
Redis connection pool configuration
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=- 1
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
Copy the code

Connection pool configuration description:

  • Max-active: indicates the maximum number of connections in the connection pool. The default value is 8.
  • Max-wait: maximum waiting time for connection pool blocking. Default value: -1 (negative value indicates no limit)
  • Max-idle: indicates the maximum number of idle connections in the connection pool. The default value is 8
  • Min-idle: indicates the minimum number of idle connections in the connection pool. The default value is 0

Second, the RedisTemplate

2.1 Customize RedisTemplate

When the project starts, SpringBoot automatically creates two beans in the IOC container, RedisTemplate and StringRedisTemplate, as shown below:

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        returntemplate; }}Copy the code

By RedisAutoConfiguration. Class source code, the SpringBoot automatically create StringRedisTemplate applies to the value as a string of scenes, and automatically create RedisTemplate is generic, It requires various types of conversion and is extremely inconvenient to use.

To do this, you can implement a RedisTemplate with a

generic to replace the RedisTemplate automatically generated by SpringBoot. The Redis Template defines serializers for key, value, hashKey, and hashValue. Therefore, if you need to implement a RedisTemplate of generic type

yourself, you need to set up the appropriate serializer, as follows:
,>
,>

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> getRedisTemplate(JedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(connectionFactory);
        
        // Set the serializer for key
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // Set the serializer for the hashKey
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        // Set the serializer for value
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        // Set the serializer for hashValue
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        
        returnredisTemplate; }}Copy the code

2.2 Serializer

StringRedisTemplate default StringRedisSerializer as serializer, RedisTemplate default JdkSerializationRedisSerializer as serial number, At this point the serialized object must implement the Serializable interface. Besides, there are Jackson2JsonRedisSerializer and GenericJackson2JsonRedisSerializer, both objects can be serialized as Json, however, the latter will be added in the serialized results a @ class attribute, Property value is the full class name of the object, which is used to facilitate deserialization.

3. Encapsulate tool classes

The following is a simple encapsulation of redIS-related operations, as follows:

package com.hannibal.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

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

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

    /** * Sets the expiration time **@paramThe key key *@paramTime time *@return* /
    public boolean expire(final String key, final long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
                return true;
            }
            return false;
        } catch (Exception e) {
            return false; }}/** * Retrieves the expiration time (in seconds)@paramThe key key *@return* /
    public long ttl(final String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /** * Check whether key exists **@paramThe key key *@return* /
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /** * Delete key **@paramThe key key * /
    public void  del(String... key) {
        if(key ! =null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else{ redisTemplate.delete(CollectionUtils.arrayToList(key)); }}}/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * the String data type * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
    /** * get cache **@paramThe key key *@return* /
    public Object get(final String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /** * Get cache ** in batches@paramKeys key *@return* /
    public List<Object> mget(final List<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /** * Add cache **@paramThe key key *@paramThe value value *@return* /
    public boolean set(final String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            return false; }}/** * Add cache and set expiration time **@paramThe key key *@paramThe value value *@paramTime Expiration time *@return* /
    public boolean setex(final String key, Object value, final long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
                return true;
            } else {
                throw new RuntimeException("Expiration time must be greater than 0"); }}catch (Exception e) {
            return false; }}/** * If key does not exist, add cache **@paramThe key key *@paramThe value value *@return* /
    public boolean setnx(final String key, Object value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /** * Add cache ** in batches@paramMap key value pair *@return* /
    public boolean mset(final Map<String, Object> map) {
        return redisTemplate.opsForValue().multiSet(map);
    }

    /** * increment operation **@paramThe key key *@paramNumber of numerical *@return* /
    public long incr(final String key, final long number) {
        if (number > 0) {
            return redisTemplate.opsForValue().increment(key, number);
        } else {
            throw new RuntimeException("Parameter number must be greater than 0"); }}/** * decrement operation **@paramThe key key *@paramNumber of numerical *@return* /
    public long decr(final String key, final long number) {
        if (number > 0) {
            return redisTemplate.opsForValue().increment(key, -number);
        } else {
            throw new RuntimeException("Parameter number must be greater than 0"); }}/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Hash data type * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
    /** * hget to get the specified value **@paramThe key key *@paramThe field field *@return* /
    public Object hget(final String key, final String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /** * hmget to get multiple values **@paramThe key key *@paramFields Field list *@return* /
    public List<Object> hmget(final String key, List<String> fields) {
        return redisTemplate.opsForHash().multiGet(key, fields);
    }

    /** * hkeys to obtain all fields ** in the hash@paramThe key key *@return* /
    public Set<Object> hkeys(final String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /** * hvalues to obtain all the hash values **@paramThe key key *@return* /
    public List<Object> hvalues(final String key) {
        return redisTemplate.opsForHash().values(key);
    }

    /** * hgetall to getall key pairs **@paramThe key key *@return* /
    public Map<Object, Object> hgetall(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /** * hset operation **@paramThe key key *@paramThe field field *@paramThe value value *@return* /
    public boolean hset(final String key, final String field, Object value) {
        try {
            redisTemplate.opsForHash().put(key, field, value);
            return true;
        } catch (Exception e) {
            return false; }}/** * hset operation, and set expiration time **@paramThe key key *@paramThe field field *@paramThe value value *@paramTime Expiration time *@return* /
    public boolean hset(final String key, final String field, Object value, final long time) {
        try {
            redisTemplate.opsForHash().put(key, field, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            return false; }}/** * hsetnx operation, if the field does not exist, add cache *@param key
     * @param field
     * @param value
     * @return* /
    public boolean hsetnx(final String key, final String field, Object value) {
        return redisTemplate.opsForHash().putIfAbsent(key, field, value);
    }

    /** * hmset operation **@paramThe key key *@paramMap key value pair *@return* /
    public boolean hmset(final String key, final Map<Object, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            return false; }}/** * hmset operation, and set expiration time **@paramThe key key *@paramMap key value pair *@paramTime Expiration time *@return* /
    public boolean hmset(final String key, final Map<Object, Object> map, final long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            return false; }}/** * hexists to check whether the hash field * exists@paramThe key key *@paramThe field field *@return* /
    public boolean hexists(final String key, final String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /** * Delete key-value pairs in hash. Multiple key-value pairs * can be deleted simultaneously@paramThe key key *@paramFields Field list *@return* /
    public boolean hdel(final String key, final String... fields) {
        return redisTemplate.opsForHash().delete(key, fields);
    }

    /** * increment operation ** in hash@paramThe key key *@paramThe field field *@paramNumber Specifies the increasing length *@return* /
    public long hincrby(final String key, final String field, final long number) {
        return redisTemplate.opsForHash().increment(key, field, number);
    }

    /** * decrement operation ** in hash@paramThe key key *@paramThe field field *@paramNumber specifies the decreasing length *@return* /
    public long hdecrby(final String key, final String field, final long number) {
        return redisTemplate.opsForHash().increment(key, field, -number);
    }

    / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * the Set data type * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
    /** * sadd adds the element ** to the collection@paramThe key key *@paramElement values *@return* /
    public long sadd(final String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /** * srem removes the specified element **@paramThe key key *@paramElement values *@return* /
    public long srem(final String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    /** * scard operation to get the number of elements in the collection **@paramThe key key *@return* /
    public long scard(final String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /** * sismember to check whether the specified value is an element ** in the collection@paramThe key key *@paramThe value value *@return* /
    public boolean sismember(final String key, Object value) {
        return redisTemplate.opsForSet(key, value);
    }

    / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * the List data type * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
    /** * lpush returns the length of the list after adding elements **@paramThe key key *@paramThe value value *@return* /
    public long lpush(final String key, Object value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /** * rpush returns the length of the list after adding elements **@paramThe key key *@paramThe value value *@return* /
    public long rpush(final String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /** * lpop returns the removed element **@paramThe key key *@return* /
    public Object lpop(final String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /** * rpop returns the removed element **@paramThe key key *@return* /
    public Object rpop(final String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /** * lindex returns the element ** at the specified index position@paramThe key key *@paramThe index index *@return* /
    public Object lIndex(final String key, final long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /** * gets the first occurrence of the element in the list **@paramThe key key *@paramThe value value *@return* /
    public long lIndecOf(final String key, Object value) {
        return redisTemplate.opsForList().indexOf(key, value);
    }

    /** * gets the last occurrence of the element in the list **@paramThe key key *@paramThe value value *@return* /
    public long lLastIngdexOf(final String key, Object value) {
        return redisTemplate.opsForList().lastIndexOf(key, value);
    }

    /** * llen to obtain the length of list **@paramThe key key *@return* /
    public long lLen(final String key) {
        return redisTemplate.opsForList().size(key);
    }

    /** * lrange to obtain the specified range of elements **@paramThe key key *@paramStart Start position *@paramEnd Indicates the end position *@return* /
    public List<Object> lRange(final String key, final long start, final long end) {
        returnredisTemplate.opsForList().range(key, start, end); }}Copy the code

Reference Documents:

  • SpringBoot 2.0 integrates Redis
  • GenericJackson2JsonRedisSerializer serializer
  • RedisTemplate API documentation