Contents
起步
首先,我们需要初始化我们的Spring-Boot工程,必要添加的pom
1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.springframework.boot</groupId>
7 <artifactId>spring-boot-starter-parent</artifactId>
8 <version>2.2.1.RELEASE</version>
9 <relativePath/> <!-- lookup parent from repository -->
10 </parent>
11 <groupId>online.keepon.mybatis</groupId>
12 <artifactId>mybatis-cache</artifactId>
13 <version>0.0.1-SNAPSHOT</version>
14 <name>mybatis-cache</name>
15 <description>Demo project for Spring Boot</description>
16
17 <properties>
18 <java.version>11</java.version>
19 </properties>
20
21 <dependencies>
22 <dependency>
23 <groupId>org.springframework.boot</groupId>
24 <artifactId>spring-boot-starter-web</artifactId>
25 </dependency>
26 <dependency>
27 <groupId>org.mybatis.spring.boot</groupId>
28 <artifactId>mybatis-spring-boot-starter</artifactId>
29 <version>2.1.1</version>
30 </dependency>
31 <dependency>
32 <groupId>org.springframework.boot</groupId>
33 <artifactId>spring-boot-starter-data-redis</artifactId>
34 </dependency>
35 <dependency>
36 <groupId>mysql</groupId>
37 <artifactId>mysql-connector-java</artifactId>
38 <scope>runtime</scope>
39 </dependency>
40 <dependency>
41 <groupId>org.projectlombok</groupId>
42 <artifactId>lombok</artifactId>
43 <optional>true</optional>
44 </dependency>
45 <dependency>
46 <groupId>com.alibaba</groupId>
47 <artifactId>fastjson</artifactId>
48 <version>1.2.61</version>
49 </dependency>
50 <dependency>
51 <groupId>org.springframework.boot</groupId>
52 <artifactId>spring-boot-starter-test</artifactId>
53 <scope>test</scope>
54 <exclusions>
55 <exclusion>
56 <groupId>org.junit.vintage</groupId>
57 <artifactId>junit-vintage-engine</artifactId>
58 </exclusion>
59 </exclusions>
60 </dependency>
61 </dependencies>
62
63 <build>
64 <plugins>
65 <plugin>
66 <groupId>org.springframework.boot</groupId>
67 <artifactId>spring-boot-maven-plugin</artifactId>
68 </plugin>
69 </plugins>
70 </build>
71
72</project>
73
配置文件
1server.port=8990
2spring.datasource.username=root
3spring.datasource.password=root
4spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
5spring.datasource.url=jdbc:mysql://localhost:3306/assistant?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
6#Redis配置## Redis数据库索引(默认为0)
7spring.redis.database=0
8# Redis服务器地址
9spring.redis.host=127.0.0.1
10# Redis服务器连接端口
11spring.redis.port=6379
12# Redis服务器连接密码
13spring.redis.password=
14# 连接池最大连接数(使用负值表示没有限制)
15spring.redis.jedis.pool.max-active=8
16# 连接池最大阻塞等待时间(使用负值表示没有限制)
17spring.redis.jedis.pool.max-wait=30000ms
18# 连接池中的最大空闲连接
19spring.redis.jedis.pool.max-idle=8
20# 连接池中的最小空闲连接
21spring.redis.jedis.pool.min-idle=1
22# 连接超时时间(毫秒)
23spring.redis.timeout=6000ms
24#mybatis
25mybatis.type-aliases-package=online.keepon.mybatis.model
26mybatis.mapper-locations=classpath:mapper/*
27mybatis.configuration.cache-enabled=true
28mybatis.configuration.map-underscore-to-camel-case=true
29spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
30spring.jackson.time-zone=GMT+8
31logging.level.online.keepon.mybatis.mapper=trace
一个简单的User
需要实现Serializable 接口,避免redis报错
1@Data
2public class User implements Serializable {
3 private Integer id;
4 private String username;
5}
6
UserMapper
1@Mapper
2@CacheNamespace(implementation = RedisCache.class)
3public interface UserMapper {
4
5 @Select("select * from user where id=#{id} ")
6 User selectById(@Param("id") Integer id);
7
8 /**
9 * flushCache = FlushCachePolicy.TRUE 更新的时候删除缓存
10 * @param user
11 * @return
12 */
13 @Options(flushCache = FlushCachePolicy.TRUE)
14 @Update("update user set username=#{username} where id=#{id} ")
15 int update(User user);
16}
17
新建一个RedisCache 实现mybatis 的缓存
1import lombok.extern.slf4j.Slf4j;
2import online.keepon.mybatis.utils.ApplicationContextHolder;
3import org.apache.ibatis.cache.Cache;
4import org.springframework.data.redis.core.RedisCallback;
5import org.springframework.data.redis.core.RedisTemplate;
6
7import java.util.Objects;
8import java.util.concurrent.TimeUnit;
9import java.util.concurrent.locks.ReadWriteLock;
10import java.util.concurrent.locks.ReentrantReadWriteLock;
11
12/**
13 * @author felix
14 * @ 日期 2019-12-06 15:04
15 */
16@Slf4j
17public class RedisCache implements Cache {
18 private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
19 private String id;
20 private RedisTemplate<String, Object> redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
21
22 // redis过期时间
23 private static final long EXPIRE_TIME_IN_MINUTES = 30;
24
25 public RedisCache(String id) {
26 if (id == null) {
27 throw new IllegalArgumentException("Cache instances require an ID");
28 }
29 this.id = id;
30 }
31
32 @Override
33 public String getId() {
34 return id;
35 }
36
37 /**
38 * Put query result to redis
39 *
40 * @param key
41 * @param value
42 */
43 @Override
44 @SuppressWarnings("unchecked")
45 public void putObject(Object key, Object value) {
46 System.out.println(key);
47 redisTemplate.opsForValue().set(key.toString(), value, EXPIRE_TIME_IN_MINUTES, TimeUnit.SECONDS);
48 log.debug("Put query result to redis");
49 }
50
51 /**
52 * Get cached query result from redis
53 *
54 * @param key
55 * @return
56 */
57 @Override
58 public Object getObject(Object key) {
59 log.debug("Get cached query result from redis");
60 return redisTemplate.opsForValue().get(key.toString());
61 }
62
63 /**
64 * Remove cached query result from redis
65 *
66 * @param key
67 * @return
68 */
69 @Override
70 public Object removeObject(Object key) {
71 redisTemplate.delete(key.toString());
72 log.debug("Remove cached query result from redis");
73 return null;
74 }
75
76 /**
77 * Clears this cache instance
78 */
79 @Override
80 @SuppressWarnings("unchecked")
81 public void clear() {
82 redisTemplate.execute((RedisCallback) connection -> {
83 connection.flushDb();
84 return null;
85 });
86 log.debug("Clear all the cached query result from redis");
87 }
88
89 @Override
90 public int getSize() {
91 return Objects.requireNonNull(Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection().dbSize()).intValue();
92 }
93
94 @Override
95 public ReadWriteLock getReadWriteLock() {
96 return readWriteLock;
97 }
98}
99
redis 序列化配置
1import com.fasterxml.jackson.annotation.JsonAutoDetect;
2import com.fasterxml.jackson.annotation.PropertyAccessor;
3import com.fasterxml.jackson.databind.ObjectMapper;
4import online.keepon.mybatis.cache.FastJson2JsonRedisSerializer;
5import org.springframework.context.annotation.Bean;
6import org.springframework.context.annotation.Configuration;
7import org.springframework.data.redis.connection.RedisConnectionFactory;
8import org.springframework.data.redis.core.RedisTemplate;
9import org.springframework.data.redis.serializer.StringRedisSerializer;
10
11/**
12 * @author felix
13 * @ 日期 2019-12-06 15:15
14 */
15@Configuration
16public class RedisConfig {
17 @Bean
18 public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
19 RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
20 redisTemplate.setConnectionFactory(redisConnectionFactory);
21 FastJson2JsonRedisSerializer fastJson2JsonRedisSerializer = new FastJson2JsonRedisSerializer(Object.class);
22 ObjectMapper objectMapper = new ObjectMapper();
23 objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
24 objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
25 fastJson2JsonRedisSerializer.setObjectMapper(objectMapper);
26
27 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
28 // key采用String的序列化方式
29 redisTemplate.setKeySerializer(stringRedisSerializer);
30 // string的value采用fastJson序列化方式
31 redisTemplate.setValueSerializer(fastJson2JsonRedisSerializer);
32 // hash的key也采用String的序列化方式
33 redisTemplate.setHashKeySerializer(stringRedisSerializer);
34 // hash的value采用fastJson序列化方式
35 redisTemplate.setHashValueSerializer(fastJson2JsonRedisSerializer);
36 redisTemplate.afterPropertiesSet();
37 return redisTemplate;
38 }
39}
1
2import com.alibaba.fastjson.JSON;
3import com.alibaba.fastjson.parser.ParserConfig;
4import com.alibaba.fastjson.serializer.SerializerFeature;
5import com.fasterxml.jackson.databind.JavaType;
6import com.fasterxml.jackson.databind.ObjectMapper;
7import com.fasterxml.jackson.databind.type.TypeFactory;
8import org.springframework.data.redis.serializer.RedisSerializer;
9import org.springframework.data.redis.serializer.SerializationException;
10import org.springframework.util.Assert;
11
12import java.nio.charset.Charset;
13
14/**
15 * @author felix
16 * @ 日期 2019-12-06 15:57
17 */
18public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
19
20 private ObjectMapper objectMapper = new ObjectMapper();
21 public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
22
23 private Class<T> clazz;
24
25 static {
26 ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
27 ParserConfig.getGlobalInstance().addAccept("com.openailab.oascloud");
28
29 }
30
31 public FastJson2JsonRedisSerializer(Class<T> clazz) {
32 super();
33 this.clazz = clazz;
34 }
35
36 @Override
37 public byte[] serialize(T t) throws SerializationException {
38 if (t == null) {
39 return new byte[0];
40 }
41 return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
42 }
43
44 @Override
45 public T deserialize(byte[] bytes) throws SerializationException {
46 if (bytes == null || bytes.length <= 0) {
47 return null;
48 }
49 String str = new String(bytes, DEFAULT_CHARSET);
50
51 return JSON.parseObject(str, clazz);
52 }
53
54 public void setObjectMapper(ObjectMapper objectMapper) {
55 Assert.notNull(objectMapper, "'objectMapper' must not be null");
56 this.objectMapper = objectMapper;
57 }
58
59 protected JavaType getJavaType(Class<?> clazz) {
60 return TypeFactory.defaultInstance().constructType(clazz);
61 }
62}
63
ApplicationContextHolder
需要注意的是,这里不能通过autowire的方式引用redisTemplate,
因为RedisCache并不是Spring容器里的bean。所以我们需要手动地去调用容器的getBean方法来拿到这个bean;
1import org.springframework.beans.BeansException;
2import org.springframework.context.ApplicationContext;
3import org.springframework.context.ApplicationContextAware;
4import org.springframework.stereotype.Component;
5
6@Component
7public class ApplicationContextHolder implements ApplicationContextAware {
8 private static ApplicationContext applicationContext;
9
10 @Override
11 public void setApplicationContext(ApplicationContext ctx) throws BeansException {
12 applicationContext = ctx;
13 }
14
15
16 public static ApplicationContext getApplicationContext() {
17 return applicationContext;
18 }
19
20
21 public static <T> T getBean(Class<T> clazz) {
22 return applicationContext.getBean(clazz);
23 }
24
25
26 @SuppressWarnings("unchecked")
27 public static <T> T getBean(String name) {
28 return (T) applicationContext.getBean(name);
29 }
30}
31
测试使用
1@SpringBootTest
2class MybatisCacheApplicationTests {
3 @Autowired
4 UserMapper userMapper;
5
6 @Test
7 void select() {
8 User user = userMapper.selectById(1);
9 System.out.println(user);
10 }
11
12 @Test
13 public void update() {
14 User user = new User();
15 user.setId(1);
16 user.setUsername("user");
17 userMapper.update(user);
18
19 }
20
21}
22