Spring Boot Redis

Spring Boot Redis 入门实战

Posted by leone on 2019-01-12

Spring Boot Redis

开发环境

开发工具: Intellij IDEA 2018.2.6

springboot: 2.0.7.RELEASE

jdk: 1.8.0_192

maven: 3.6.0

redis: 4.0.12

redis 简介

什么是 redis ?

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

搭建项目

  • pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?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">

<groupId>com.andy</groupId>
<artifactId>spring-boot-redis</artifactId>
<version>1.0.7.RELEASE</version>
<modelVersion>4.0.0</modelVersion>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Cairo-SR6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>

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

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

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.6.RELEASE</version>
<configuration>
<!--<mainClass>${start-class}</mainClass>-->
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
  • application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server:
port: 8081
servlet:
context-path: /

spring:
application:
name: spring-redis
redis:
# 数据库索引
database: 3
# 连接超时时间
timeout: 10000ms
# ip地址
host: 127.0.0.1
# 端口
port: 6379
# 密码
# password:
jedis:
pool:
# 最大连接数
max-active: 8
# 最大阻塞等待时间(负数表示没限制)
max-wait: -1ms
# 最大空闲
max-idle: 8
# 最小空闲
min-idle: 0
  • 启动类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

/**
* @author Leone
* @since 2018-05-11
**/
@EnableCaching
@SpringBootApplication
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class, args);
}
}
  • RedisConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
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.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* <p> redis配置
*
* @author Leone
* @since 2018-07-19
**/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {


@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认过期时间,也是使用Duration设置
config = config.entryTtl(Duration.ofMinutes(1))
// 不缓存空值
.disableCachingNullValues();

// 设置一个初始化的缓存空间set集合
Set<String> cacheNames = new HashSet<>();
cacheNames.add("my-redis-cache1");
cacheNames.add("my-redis-cache2");

// 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("my-redis-cache1", config);
configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120)));

// 使用自定义的缓存配置初始化一个cacheManager
return RedisCacheManager.builder(factory)
// 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap)
.build();
}

/**
* 设置 redis 入库 各种类型 k v 序列化
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// RedisSerializer<String> stringSerializer = new StringRedisSerializer();

Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

ObjectMapper objectMapper = new ObjectMapper();

objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}

}
  • User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import java.io.Serializable;
import java.util.Date;

/**
* <p>
*
* @author Leone
* @since 2018-03-02
**/
public class User implements Serializable {

private Long userId;

private String account;

private String password;

private String description;

private Integer age;

private Date createTime;

private boolean deleted;

public User() {
}

public User(Long userId, String account, String password, String description, Integer age, Date createTime, Boolean deleted) {
this.userId = userId;
this.account = account;
this.password = password;
this.description = description;
this.age = age;
this.createTime = createTime;
this.deleted = deleted;
}

public Long getUserId() {
return userId;
}

public void setUserId(Long userId) {
this.userId = userId;
}

public String getAccount() {
return account;
}

public void setAccount(String account) {
this.account = account;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}

public boolean isDeleted() {
return deleted;
}

public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
  • RedisPrefix.java
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* redis 前缀生成器
*
* @author Leone
* @since 2018-07-08
**/
public class RedisPrefix {

public static String userCatch(String uuid) {
return "app.user:" + uuid;
}

}
  • RedisController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

import com.andy.redis.service.RedisCacheService;
import com.andy.redis.service.RedisService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
* @author Leone
* @since 2018-07-08
**/
@RestController
@RequestMapping("/api/redis")
public class RedisController {

@Resource
private RedisService redisService;

@Resource
private RedisCacheService redisCacheService;

@GetMapping("/list")
public String list() {
return "list: " + redisService.list() + " !";
}

@GetMapping("/value")
public String value() {
return "value: " + redisService.value() + " !";
}

@GetMapping("/set")
public String set() {
return "set " + redisService.set() + " !";
}


@GetMapping("/zSet")
public String zSet() {
return "zSet " + redisService.zSet() + " !";
}

@GetMapping("/hash")
public String hash() {
return "hash " + redisService.hash() + " !";
}


@GetMapping("/catch")
public String userCatch() {
return redisCacheService.userCatch();
}

}
  • RedisService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

import com.andy.redis.config.RedisPrefix;
import com.andy.redis.util.EntityFactory;
import com.andy.redis.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
* <p>
*
* @author Leone
* @since 2018-08-11
**/
@Slf4j
@Service
public class RedisService {

@Resource
private RedisTemplate<String, Object> redisTemplate;

/**
* 相当于队列操作
*
* @return
*/
public long list() {
log.info("list:{}", 1);
for (int i = 0; i < 10; i++) {
String key = RedisPrefix.userCatch("list"), value = RandomUtil.getName();
Long push = redisTemplate.opsForList().leftPush(key, value);
log.info("leftPush key:[{}] -- value:[{}]", key, value);
}

try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}

for (int i = 0; i < 9; i++) {
Object result = redisTemplate.opsForList().rightPop(RedisPrefix.userCatch("list"));
log.info("leftPop key:[{}] -- value:[{}]", i, result);
}

return 1;
}

/**
* set 普通 字符串或对象 类型数据
*
* @return
*/
public long value() {
log.info("value:{}", 1);
redisTemplate.opsForValue().set(RedisPrefix.userCatch(RandomUtil.getNum(6)), EntityFactory.getUser(), 120, TimeUnit.SECONDS);
return 1;
}


/**
* set 操作
*
* @return
*/
public long set() {
log.info("set:{}", 1);
// set值
Long add = redisTemplate.opsForSet().add("key1", "dd", "bb", "ee", "aa");

// 获取值
Set<Object> resultSet = redisTemplate.opsForSet().members("key1");

// 获取变量中值的长度
Long size = redisTemplate.opsForSet().size("key1");

// 随机获取变量中的元素
Object randomVal = redisTemplate.opsForSet().randomMember("key1");

// 随机获取变量中指定个数的元素
List<Object> randomValues = redisTemplate.opsForSet().randomMembers("key1", 3);

// 检查给定的元素是否在变量中
Boolean isMember = redisTemplate.opsForSet().isMember("key1", "ff");

// 转移变量的元素值到目的变量
//Boolean move = redisTemplate.opsForSet().move("key1", "aa", "ee");

// 弹出变量中的元素
//List<Object> key1 = redisTemplate.opsForSet().pop("key1", 1);

// 批量移除变量中的元素。
//Long remove = redisTemplate.opsForSet().remove("key1", "aa", "bb");

// 匹配获取键值对,ScanOptions.NONE为获取全部键值对;ScanOptions.scanOptions().match("C").build()匹配获取键位map1的键值对,不能模糊匹配。
/*Cursor<Object> cursor = redisTemplate.opsForSet().scan("setValue", ScanOptions.scanOptions().match("c").build());
while (cursor.hasNext()){
Object object = cursor.next();
System.out.println("通过scan(K key, ScanOptions options)方法获取匹配的值:" + object);
}*/

log.info("size: {} randomVal: {} randomValues: {} resultSet: {} isMember: {}", size, randomVal, randomValues, resultSet, isMember);
return 1;
}


public long zSet() {
log.info("zSet:{}", 1);
redisTemplate.opsForZSet().add(RedisPrefix.userCatch(RandomUtil.getNum(6)), EntityFactory.getUser(), 3);

redisTemplate.opsForZSet().add("zSetValue", "A", 1);
redisTemplate.opsForZSet().add("zSetValue", "B", 3);
redisTemplate.opsForZSet().add("zSetValue", "C", 2);
redisTemplate.opsForZSet().add("zSetValue", "D", 5);

// 获取指定区间的元素
Set zSetValue = redisTemplate.opsForZSet().range("zSetValue", 0, -1);
log.info("range: {}", zSetValue);


// 用于获取满足非score的排序取值。这个排序只有在有相同分数的情况下才能使用,如果有不同的分数则返回值不确定。
RedisZSetCommands.Range range = new RedisZSetCommands.Range();
// range.gt("A");
range.lt("D");
zSetValue = redisTemplate.opsForZSet().rangeByLex("zSetValue", range);
log.info("rangeByLex: {}", zSetValue);

// 通过TypedTuple方式新增数据
ZSetOperations.TypedTuple<Object> typedTuple1 = new DefaultTypedTuple<>("E", 6.0);
ZSetOperations.TypedTuple<Object> typedTuple2 = new DefaultTypedTuple<>("F", 7.0);
ZSetOperations.TypedTuple<Object> typedTuple3 = new DefaultTypedTuple<>("G", 5.0);
Set<ZSetOperations.TypedTuple<Object>> typedTupleSet = new HashSet<>();

typedTupleSet.add(typedTuple1);
typedTupleSet.add(typedTuple2);
typedTupleSet.add(typedTuple3);
redisTemplate.opsForZSet().add("typedTupleSet", typedTupleSet);
zSetValue = redisTemplate.opsForZSet().range("typedTupleSet", 0, -1);
log.info("添加元素:{}", zSetValue);

long count = redisTemplate.opsForZSet().count("zSetValue", 1, 5);
log.info("获取区间值的个数:{}", count);

return 1;
}

/**
* @return
*/
public long hash() {
log.info("hash:{}", 1);
// put() 添加
redisTemplate.opsForHash().put("he1", "key1", EntityFactory.getUser());
redisTemplate.opsForHash().put("he1", "key2", EntityFactory.getUser());
redisTemplate.opsForHash().put("he1", "key3", EntityFactory.getUser());
Map<Object, Object> entries = redisTemplate.opsForHash().entries("he1");
log.info("put:{}", entries);

// putAll() 批量添加
Map<String, Object> param = new HashMap<>();
param.put("key1", EntityFactory.getUser());
param.put("key2", EntityFactory.getUser());
param.put("key3", EntityFactory.getUser());
redisTemplate.opsForHash().putAll("he2", param);
log.info("putAll:{}", redisTemplate.opsForHash().entries("he2"));

// delete() 删除指定key
redisTemplate.opsForHash().delete("he3", "key1");
log.info("delete:{}", redisTemplate.opsForHash().entries("he3"));

// hasKey() 判断某个key是否存在
log.info("hasKey:{}", redisTemplate.opsForHash().hasKey("he2", "key2"));

// get() 获取值
log.info("get:{}", redisTemplate.opsForHash().get("he2", "key1"));

// multiGet() 批量获取
log.info("multiGet:{}", redisTemplate.opsForHash().multiGet("he2", Arrays.asList("key1", "key2")));

// 获取所有key
log.info("keys:{}", redisTemplate.opsForHash().keys("he2"));

// 获取所有key size
log.info("size:{}", redisTemplate.opsForHash().size("he2"));

// 获取所有key values
log.info("values:{}", redisTemplate.opsForHash().values("he2"));

Cursor<Map.Entry<Object, Object>> scan = redisTemplate.opsForHash().scan("he2", ScanOptions.NONE);
while (scan.hasNext()) {
Map.Entry<Object, Object> entry = scan.next();
log.info("key:{} -- value:{}", entry.getKey(), entry.getValue());
}

return 1;
}
}

传送门