Closed peteryuanpan closed 3 years ago
参考
小结
本例子是基于 https://github.com/peteryuanpan/notebook/issues/171#issuecomment-738669987 之上的,因此只贴出有修改或新增的代码
pom.xml
<?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">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-mybatis-redis</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 8082
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
redis:
database: 0
host: 127.0.0.1
port: 6379
password: # 为空
pool:
max-active: 8 # 连接池最大连接数
max-wait: -1 # 连接池最大阻塞等待时间
max-idle: 8 # 连接池最大空闲连接
min-idle: 0 # 连接池最小空闲连接
timeout: 300ms # 连接超时时间(毫秒)
mybatis:
mapper-locations:
- classpath:mapper/*.xml
logging:
level:
com:
peter:
mapper: debug
DaoController
package com.peter.controller;
import com.peter.mapper.EmployeeMapper;
import com.peter.model.Employee;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/dao")
public class DaoController {
private static final Logger logger = LoggerFactory.getLogger(DaoController.class);
@Resource
private RedisTemplate<String, Employee> redisTemplate;
@Autowired
private EmployeeMapper employeeMapper;
@GetMapping("/test1")
public void test1(HttpServletRequest request, HttpServletResponse response) {
List<Employee> employee = employeeMapper.getEmpById(1);
logger.info(employee.toString());
}
@GetMapping("/test2")
public void test2(HttpServletRequest request, HttpServletResponse response) {
logger.info(redisTemplate.hasKey("1").toString());
Employee employee = redisTemplate.opsForValue().get("1");
if (employee == null) {
employee = employeeMapper.getEmpById(1).get(0);
redisTemplate.opsForValue().set("1", employee);
}
logger.info(employee.toString());
}
}
RedisConfig
package com.peter.config;
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.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
setSerializer(template);// 设置序列化工具
template.afterPropertiesSet();
return template;
}
private void setSerializer(StringRedisTemplate template) {
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);
template.setValueSerializer(jackson2JsonRedisSerializer);
}
}
Employee(加了implements Serializable)
package com.peter.model;
import java.io.Serializable;
public class Employee implements Serializable {
private Integer id;
private String lastName;
private String email;
private String gender;
@Override
public String toString() {
return "Emplyee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", gender='" + gender + '\'' +
'}';
}
}
请求两次 curl http://localhost:8082/dao/test2 ,第一次从数据库中获取,第二次直接从redis缓存中获取
输出结果
2020-12-07 21:16:57.110 INFO 13340 --- [nio-8082-exec-1] com.peter.controller.DaoController : false
2020-12-07 21:16:57.594 DEBUG 13340 --- [nio-8082-exec-1] c.p.mapper.EmployeeMapper.getEmpById : ==> Preparing: select * from tbl_employee where id = ?
2020-12-07 21:16:57.615 DEBUG 13340 --- [nio-8082-exec-1] c.p.mapper.EmployeeMapper.getEmpById : ==> Parameters: 1(Integer)
2020-12-07 21:16:57.636 DEBUG 13340 --- [nio-8082-exec-1] c.p.mapper.EmployeeMapper.getEmpById : <== Total: 1
2020-12-07 21:16:57.642 INFO 13340 --- [nio-8082-exec-1] com.peter.controller.DaoController : Emplyee{id=1, lastName='null', email='peter@qq.com', gender='0'}
2020-12-07 21:17:09.301 INFO 13340 --- [nio-8082-exec-2] com.peter.controller.DaoController : true
2020-12-07 21:17:09.303 INFO 13340 --- [nio-8082-exec-2] com.peter.controller.DaoController : Emplyee{id=1, lastName='null', email='peter@qq.com', gender='0'}
上面代码涉及到了几个知识点
一,注解Resource和Autowired
参考 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-redis
If you add your own @Bean of any of the auto-configured types, it replaces the default (except in the case of RedisTemplate, when the exclusion is based on the bean name, redisTemplate, not its type). By default, if commons-pool2 is on the classpath, you get a pooled connection factory.
在项目中声明一个RedisTemplate < String, Object > 的Bean,应该用 Resource,默认按照名称获取bean 而要用 Autowired 的话,则声明为 RedisTemplate 类型,不带 K V
@RestController
@RequestMapping("/dao")
public class DaoController {
private static final Logger logger = LoggerFactory.getLogger(DaoController.class);
@Resource
private RedisTemplate<String, Object> redisTemplate;
二,序列化与反序列化
参考
Redis使用中,V 需要序列化,即 implements Serializable,如果不实现这个接口,会报错:java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.peter.model.Employee]
import java.io.Serializable;
public class Employee implements Serializable {
redis默认使用JdkSerializationRedisSerializer来进行序列化,会造成key是乱码,解决办法是设置 RedisSerializer,参考RedisConfig
参考
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zset(sorted set:有序集合)
string 和 hash 的区别:string 是 string -> string 的键值对结构,hash 是 string -> map 的键值对结构
简单举个实例来描述下Hash的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息
id,name,age,sex
1、1,张三,16,1
2、2,李四,22,1
3、3,王五,28,0
4、4,赵六,32,1
用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:
第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的
那么Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,也就是说,Key仍然是用户ID,value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field),也就是通过 key(用户ID) + field(属性标签)就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题
使用hash结构可以如下存储上面的数据 hset user_1 id 1 name 张三 age 16 sex 1 hset user_2 id 2 name 李四 age 22 sex 1 hset user_3 id 3 name 王五 age 28 sex 0 hset user_4 id 4 name 赵六 age 32 sex 1
因此,大部分情况下,尤其是存储对象包含多个字段,且只要修改某个字段时,用hash合适;而需要将整个对象都序列化/反序列化时,或者对象包含字段不多时(数据量小),可以用string
参考(优秀)
参考(动手)
参考(一般)
鲁班学院课程
总结