Spring Boot + Mybatis + Redis二級快取例項
二級快取是多個SqlSession共享的,其作用域是mapper的同一個namespace,不同的sqlSession兩次執行相同namespace下的sql語句且向sql中傳遞引數也相同即最終執行相同的sql語句,第一次執行完畢會將資料庫中查詢的資料寫到快取(記憶體),第二次會從快取中獲取資料將不再從資料庫查詢,從而提高查詢效率。Mybatis預設沒有開啟二級快取需要在setting全域性引數中配置開啟二級快取。
下面是使用Redis來作為Mybatis二級快取的例項:
Redis的安裝使用的是Docker,Docker的簡介
application.properties
在application.properties檔案中配置Redis,Mybatis,開啟Mybatis二級快取等:
server.port=80
# 資料來源配置
spring.datasource.url=jdbc:mysql://localhost:3306/ssb_test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
#連線池配置
#spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
#mybatis
#entity掃描的包名
mybatis.type-aliases-package=com.xiaolyuh.domain.model
#Mapper.xml所在的位置
mybatis.mapper-locations=classpath*:/mybaits/*Mapper.xml
#開啟MyBatis的二級快取
mybatis.configuration.cache-enabled=true
#pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
#日誌配置
logging.level.com.xiaolyuh=debug
logging.level.org.springframework.web=debug
logging.level.org.springframework.transaction=debug
logging.level.org.mybatis=debug
#redis
#database name
spring.redis.database=0
#server host
spring.redis.host=192.168.195.128
#server password
spring.redis.password=
#connection port
spring.redis.port=6378
#spring.redis.pool.max-idle=8 # pool settings ...
#spring.redis.pool.min-idle=0
#spring.redis.pool.max-active=8
#spring.redis.pool.max-wait=-1
#spring.redis.sentinel.master= # name of Redis server
#spring.redis.sentinel.nodes= # comma-separated list of host:port pairs
debug=false
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">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-student-mybatis-redis</artifactId>
<packaging>jar</packaging>
<name>spring-boot-student-mybatis-redis</name>
<description>Demo Mybatis Redis for Spring Boot</description>
<parent>
<groupId>com.xiaolyuh</groupId>
<artifactId>spring-boot-student</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath />
</parent>
<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-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!--pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
Redis配置類替換序列化實現方式
package com.xiaolyuh.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
/**
* 重寫Redis序列化方式,使用Json方式:
* 當我們的資料儲存到Redis的時候,我們的鍵(key)和值(value)都是通過Spring提供的Serializer序列化到資料庫的。RedisTemplate預設使用的是JdkSerializationRedisSerializer,StringRedisTemplate預設使用的是StringRedisSerializer。
* Spring Data JPA為我們提供了下面的Serializer:
* GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
* 在此我們將自己配置RedisTemplate並定義Serializer。
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 設定值(value)的序列化採用Jackson2JsonRedisSerializer。
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// 設定鍵(key)的序列化採用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
Spring容器獲取Bean工具類
通過Spring Aware(容器感知)來獲取到ApplicationContext,然後根據ApplicationContext獲取容器中的Bean。
package com.xiaolyuh.holder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 以靜態變數儲存Spring ApplicationContext, 可在任何程式碼任何地方任何時候中取出ApplicaitonContext.
*/
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* 實現ApplicationContextAware介面的context注入函式, 將其存入靜態變數.
*/
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext; // NOSONAR
}
/**
* 取得儲存在靜態變數中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
checkApplicationContext();
return applicationContext;
}
/**
* 從靜態變數ApplicationContext中取得Bean, 自動轉型為所賦值物件的型別.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
checkApplicationContext();
return (T) applicationContext.getBean(name);
}
/**
* 從靜態變數ApplicationContext中取得Bean, 自動轉型為所賦值物件的型別.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
checkApplicationContext();
return (T) applicationContext.getBeansOfType(clazz);
}
/**
* 清除applicationContext靜態變數.
*/
public static void cleanApplicationContext() {
applicationContext = null;
}
private static void checkApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext未注入,請在applicationContext.xml中定義SpringContextHolder");
}
}
}
自定的Mybatis快取
自定義快取需要實現Mybatis的Cache介面,我這裡將使用Redis來作為快取的容器。
package com.xiaolyuh.cache;
import com.xiaolyuh.holder.SpringContextHolder;
import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 使用Redis來做Mybatis的二級快取
* 實現Mybatis的Cache介面
*/
public class MybatisRedisCache implements Cache {
private static final Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);
// 讀寫鎖
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
private RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");
private String id;
public MybatisRedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.info("Redis Cache id " + id);
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
if (value != null) {
// 向Redis中新增資料,有效時間是2天
redisTemplate.opsForValue().set(key.toString(), value, 2, TimeUnit.DAYS);
}
}
@Override
public Object getObject(Object key) {
try {
if (key != null) {
Object obj = redisTemplate.opsForValue().get(key.toString());
return obj;
}
} catch (Exception e) {
logger.error("redis ");
}
return null;
}
@Override
public Object removeObject(Object key) {
try {
if (key != null) {
redisTemplate.delete(key.toString());
}
} catch (Exception e) {
}
return null;
}
@Override
public void clear() {
logger.debug("清空快取");
try {
Set<String> keys = redisTemplate.keys("*:" + this.id + "*");
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
} catch (Exception e) {
}
}
@Override
public int getSize() {
Long size = (Long) redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.dbSize();
}
});
return size.intValue();
}
@Override
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
}
}
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.xiaolyuh.domain.mapper.PersonMapper">
<cache type="com.xiaolyuh.cache.MybatisRedisCache">
<property name="eviction" value="LRU" />
<property name="flushInterval" value="6000000" />
<property name="size" value="1024" />
<property name="readOnly" value="false" />
</cache>
<resultMap id="BaseResultMap" type="com.xiaolyuh.domain.model.Person">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
<result column="address" property="address" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
id, name, age, address
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
select
<include refid="Base_Column_List"/>
from person
where id = #{id,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
delete from person
where id = #{id,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.xiaolyuh.domain.model.Person">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into person (name, age, address
)
values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{address,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="com.xiaolyuh.domain.model.Person">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into person
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null">
name,
</if>
<if test="age != null">
age,
</if>
<if test="address != null">
address,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="age != null">
#{age,jdbcType=INTEGER},
</if>
<if test="address != null">
#{address,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.xiaolyuh.domain.model.Person">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
update person
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="age != null">
age = #{age,jdbcType=INTEGER},
</if>
<if test="address != null">
address = #{address,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.xiaolyuh.domain.model.Person">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
update person
set name = #{name,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
address = #{address,jdbcType=VARCHAR}
where id = #{id,jdbcType=BIGINT}
</update>
<!-- 對這個語句useCache="true"預設是true,可以不寫 -->
<select id="findAll" resultMap="BaseResultMap" useCache="true">
select
<include refid="Base_Column_List"/>
from person
</select>
<!-- 對這個語句禁用二級快取 -->
<select id="findByPage" resultMap="BaseResultMap" useCache="false">
select
<include refid="Base_Column_List"/>
from person
</select>
</mapper>
Mapper介面
package com.xiaolyuh.domain.mapper;
import com.github.pagehelper.Page;
import com.xiaolyuh.domain.model.Person;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper//宣告成mybatis Dao層的Bean,也可以在配置類上使用@MapperScan("com.xiaolyuh.domain.mapper")註解宣告
public interface PersonMapper {
int deleteByPrimaryKey(Long id);
int insert(Person record);
int insertSelective(Person record);
Person selectByPrimaryKey(Long id);
int updateByPrimaryKeySelective(Person record);
int updateByPrimaryKey(Person record);
/**
* 獲取所有資料
* @return
*/
List<Person> findAll();
/**
* 分頁查詢資料
* @return
*/
Page<Person> findByPage();
}
實體類
package com.xiaolyuh.domain.model;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
/**
* 名稱
*/
private String name;
/**
* 年齡
*/
private Integer age;
/**
* 地址
*/
private String address;
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;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
Service介面
package com.xiaolyuh.service;
import com.github.pagehelper.Page;
import com.xiaolyuh.domain.model.Person;
import java.util.List;
/**
* Created by yuhao.wang on 2017/6/19.
*/
public interface PersonService {
List<Person> findAll();
/**
* 分頁查詢
* @param pageNo 頁號
* @param pageSize 每頁顯示記錄數
* @return
*/
Page<Person> findByPage(int pageNo, int pageSize);
void insert(Person person);
}
Service實現類
package com.xiaolyuh.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xiaolyuh.domain.mapper.PersonMapper;
import com.xiaolyuh.domain.model.Person;
import com.xiaolyuh.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* Created by yuhao.wang on 2017/6/19.
*/
@Service
@Transactional(readOnly = true)
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonMapper personMapper;
@Override
public List<Person> findAll() {
return personMapper.findAll();
}
@Override
public Page<Person> findByPage(int pageNo, int pageSize) {
PageHelper.startPage(pageNo, pageSize);
return personMapper.findByPage();
}
@Override
@Transactional
public void insert(Person person) {
personMapper.insert(person);
}
}
測試類
package com.xiaolyuh;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.xiaolyuh.domain.model.Person;
import com.xiaolyuh.holder.SpringContextHolder;
import com.xiaolyuh.service.PersonService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonMapperTests {
private Logger logger = LoggerFactory.getLogger(PersonMapperTests.class);
@Autowired
private PersonService personService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
Person person = null;
@Before
public void testInsert() {
person = new Person();
person.setName("測試");
person.setAddress("address");
person.setAge(10);
personService.insert(person);
Assert.assertNotNull(person.getId());
logger.debug(JSON.toJSONString(person));
}
@Test
public void testFindAll() {
List<Person> persons = personService.findAll();
Assert.assertNotNull(persons);
logger.debug(JSON.toJSONString(persons));
}
@Test
public void testFindByPage() {
Page<Person> persons = personService.findByPage(1, 2);
Assert.assertNotNull(persons);
logger.debug(persons.toString());
logger.debug(JSON.toJSONString(persons));
}
// 測試mybatis快取
@Test
public void testCache() {
long begin = System.currentTimeMillis();
List<Person> persons = personService.findAll();
long ing = System.currentTimeMillis();
personService.findAll();
long end = System.currentTimeMillis();
logger.debug("第一次請求時間:" + (ing - begin) + "ms");
logger.debug("第二次請求時間:" + (end - ing) + "ms");
Assert.assertNotNull(persons);
logger.debug(JSON.toJSONString(persons));
}
// 測試Redis儲存和獲取一個List
@Test
public void testRedisCacheSetList() {
List<Person> persons = new ArrayList<>();
persons.add(person);
persons.add(person);
persons.add(person);
redisTemplate.opsForValue().set(person.getId() + "", persons, 2, TimeUnit.MINUTES);
persons = (List<Person>) redisTemplate.opsForValue().get(person.getId() + "");
System.out.println(JSON.toJSONString(persons));
}
// 測試Redis儲存和獲取一個Object
@Test
public void testRedisCacheSetObject() {
redisTemplate.opsForValue().set(person.getId() + "", person, 2, TimeUnit.MINUTES);
Object p = redisTemplate.opsForValue().get(person.getId() + "");
if (p instanceof Person) {
Person person1 = (Person) p;
System.out.println(JSON.toJSONString(person1));
}
}
// 測試 通過Spring Aware獲取Spring容器中的額Bean
@Test
public void testApplicationContextAware() {
RedisTemplate redisTemplate = SpringContextHolder.getBean("redisTemplate");
System.out.println(redisTemplate);
}
}
日誌
2017-06-29 15:22:22.351 DEBUG 12976 --- [ main] com.xiaolyuh.domain.mapper.PersonMapper : Cache Hit Ratio [com.xiaolyuh.domain.mapper.PersonMapper]: 0.5
2017-06-29 15:22:22.351 DEBUG 12976 --- [ main] org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2b2a4332]
2017-06-29 15:22:22.351 DEBUG 12976 --- [ main] org.mybatis.spring.SqlSessionUtils : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2b2a4332]
2017-06-29 15:22:22.351 DEBUG 12976 --- [ main] org.mybatis.spring.SqlSessionUtils : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2b2a4332]
2017-06-29 15:22:22.351 DEBUG 12976 --- [ main] org.mybatis.spring.SqlSessionUtils : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2b2a4332]
2017-06-29 15:22:22.353 DEBUG 12976 --- [ main] com.xiaolyuh.PersonMapperTests : 第一次請求時間:92ms
2017-06-29 15:22:22.354 DEBUG 12976 --- [ main] com.xiaolyuh.PersonMapperTests : 第二次請求時間:68ms
原始碼
https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases
spring-boot-student-mybatis-redis工程
相關文章
- spring boot redis做mybatis二級快取Spring BootRedisMyBatis快取
- Spring Boot + Mybatis + Redis二級快取開發指南SpringbootMyBatisRedis快取
- Java Web現代化開發:Spring Boot + Mybatis + Redis二級快取JavaWebSpring BootMyBatisRedis快取
- Mybatis的二級快取、使用Redis做二級快取MyBatis快取Redis
- mybatis快取-二級快取MyBatis快取
- MyBatis快取機制(一級快取,二級快取)MyBatis快取
- Spring Boot Cache Redis快取Spring BootRedis快取
- Mybatis二級快取使用MyBatis快取
- Redis整合Spring結合使用快取例項RedisSpring快取
- mybatis二級快取引數MyBatis快取
- mybatis快取之一級快取(二)MyBatis快取
- MyBatis中的一級快取和二級快取介紹MyBatis快取
- Mybatis 二級快取應用 (21)MyBatis快取
- Spring Boot 整合 Redis 實現快取操作Spring BootRedis快取
- Spring Boot + Redis 快取方案深度解讀Spring BootRedis快取
- Spring Boot In Practice (1):Redis快取實戰Spring BootRedis快取
- mybatis原始碼學習:一級快取和二級快取分析MyBatis原始碼快取
- 基於Spring Cache實現二級快取(Caffeine+Redis)Spring快取Redis
- Spring Boot 揭祕與實戰(二) 資料快取篇 - Redis CacheSpring Boot快取Redis
- Mybatis 一級快取和二級快取原理區別 (圖文詳解)MyBatis快取
- Mybatis一級快取MyBatis快取
- MySQL與Redis實現二級快取MySqlRedis快取
- spring boot使用Jedis整合Redis實現快取(AOP)Spring BootRedis快取
- spring-boot-route(十二)整合redis做為快取SpringbootRedis快取
- 5、Spring Boot快取Spring Boot快取
- mybatis原始碼詳細解析(2)---- 一級,二級快取MyBatis原始碼快取
- mybatis二級快取應用及與ehcache整合MyBatis快取
- Spring-Boot專案中配置redis註解快取SpringbootRedis快取
- 分散式快取綜合指南:Kubernetes + Redis + Spring Boot分散式快取RedisSpring Boot
- 【Spring Boot架構】整合Mybatis-Plus的例項詳解Spring Boot架構MyBatis
- 【MyBatis原始碼解析】MyBatis一二級快取MyBatis原始碼快取
- myBatis原始碼解析-二級快取的實現方式MyBatis原始碼快取
- mybatis快取之一級快取(一)MyBatis快取
- Mybatis整合二級快取與同時使用快取與事務存在的坑MyBatis快取
- Mybatis的快取——一級快取和原始碼分析MyBatis快取原始碼
- 深入理解 MyBatis的二級快取的設計原理MyBatis快取
- Spring Boot + Mybatis + Spring MVC環境配置(二):Mybatis Generator配置Spring BootMyBatisMVC
- mybatis基礎系列(四)——關聯查詢、延遲載入、一級快取與二級快取MyBatis快取