Springboot + Mybatis + Redis+mysql
Mybatis的二级缓存是多个SqlSession共享的,作用于是mapper配置文件中同一个namespace,不同的SqlSession两次执行相同namespace下的sql语句且参数如果也一样则最终执行的sql语句是相同的。每次查询都会先看看缓存中是否有对应查询结果,如果有就从缓存拿,如果没有就执行sql语句从数据库中读取,从而提高查询效率。
maven配置
<?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"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.domcj.seccache</groupId> <artifactId>secache</artifactId> <version>0.0.1-SNAPSHOT</version> <name>secache</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
#配置mybatis mybatis: mapper-locations: mapper/UserMapper.xml type-aliases-package: com.domcj.seccache.seccache.entity #配置redis spring: datasource: url: jdbc:mysql://localhost:3306/chenjian username: root password: chenjian driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource redis: host: localhost port: 6379 database: 0 password:
SpringContextHolder
组件,实现了Spring的ApplicationContextAware来获取ApplicationContext,从中获取容器的bean
package com.domcj.seccache.seccache.util; import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @description: This is a class! * @author: chenjian * @date: 2019/03/07 11:33 */ @Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ApplicationContextHolder.applicationContext = applicationContext; } public ApplicationContext getApplicationContext() { return applicationContext; } @SuppressWarnings("unchecked") public static <T> T getBean(String beanName) { checkApplicationContext(); return (T) applicationContext.getBean(beanName); } @SuppressWarnings("unchecked") public static <T> Map<String, T> getBean(Class<T> clazz) { checkApplicationContext(); return applicationContext.getBeansOfType(clazz); } private static void checkApplicationContext() { if (applicationContext == null) { throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder"); } } }
RedisCache Mybatis二级缓存默认使用的是其他的缓存,这里我们需要集成Redis就需要自己自定义写一个缓存类去实现二级缓存。
自定义缓存需要实现Mybatis的Cache接口。
package com.domcj.seccache.seccache.util; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.ibatis.cache.Cache; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; /** * @description: This is a class! * @author: chenjian * @date: 2019/03/07 11:45 */ public class RedisCache implements Cache { private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private String id; // cache instance id private RedisTemplate redisTemplate; private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间 public RedisCache(String id) { if (id == null) { throw new IllegalArgumentException("Cache instances require an ID"); } this.id = id; } @Override public String getId() { return id; } @Override public void putObject(Object key, Object value) { RedisTemplate redisTemplate = getRedisTemplate(); ValueOperations opsForValue = redisTemplate.opsForValue(); opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.SECONDS); } @Override public Object getObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); ValueOperations opsForValue = redisTemplate.opsForValue(); Object obj = opsForValue.get(key); return obj; } @Override public Object removeObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.delete(key); return null; } @Override public void clear() { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.execute((RedisCallback) connection -> { connection.flushDb(); return null; }); } @Override public int getSize() { return 0; } @Override public ReadWriteLock getReadWriteLock() { return readWriteLock; } private RedisTemplate getRedisTemplate() { if (redisTemplate == null) { redisTemplate = ApplicationContextHolder.getBean("redisTemplate"); } return redisTemplate; } }
Mapper.xml
mapper映射配置文件,只需要引入刚刚配置好的自定义缓存类。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.domcj.seccache.seccache.mapper.UserMapper"> <cache type="com.domcj.seccache.seccache.util.RedisCache"> <property name="eviction" value="LRU" /> <property name="flushInterval" value="600000" /> <property name="size" value="1024" /> <property name="readOnly" value="false" /> </cache> <select id="getUserById" parameterType="java.lang.Long" resultType="User"> select * from user where id=#{id, jdbcType=BIGINT} </select> </mapper>
eviction:定义缓存移除机制(算法),默认为LRU(最近最少使用),它会清除最少使用的数据,还有一种FIFO(先进先出),它会清除最先进来的数据。
flushInterval:定义缓存刷新周期,单位为毫秒。
size:标识缓存cache中容纳的最大元素,默认为1024。
readOnly:默认为false,可配置为true缓存只读。
实体类
实体类需要实现Serializable
package com.domcj.seccache.seccache.entity; import java.io.Serializable; /** * @description: This is a class! * @author: chenjian * @date: 2019/03/07 11:21 */ public class User implements Serializable { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }