SpringBoot整合Redis配置MyBatis二级缓存
创始人
2025-05-28 03:17:42
0

目录

    • 写在前面
    • 源码获取
    • 一、MyBatis缓存机制
      • 1.1、一级缓存
      • 1.2、二级缓存
    • 二、集成Redis
      • 2.1、安装Redis
      • 2.2、项目引入Redis
        • 2.2.1、Maven依赖
        • 2.2.2、配置application.yml
        • 2.2.3、配置序列化规则
    • 三、配置二级缓存
      • 2.1、开启二级缓存
      • 2.2、自定义缓存类
      • 2.3、增加注解
      • 2.4、测试验证

写在前面

文中项目基于从0到1项目搭建-框架搭建,如果你是新手,可以跟着上期内容先动手把项目框架搭建起来,然后在结合本期内容继续深入学习,这样会有更好的效果。

接下来正式介绍本文,本文讲的是在 Spring Boot 项目中集成使用 Redis,并使用 Redis 实现 MyBatis 的二级缓存。使用场景就是在高并发的环境下,大量的查询直接落入DB,会导致数据库宕机,从而导致服务雪崩的情况。我们使用Redis作为MyBatis二级缓存,可以充分的缓解数据库的压力,从而达到服务的高可用。

源码获取

源码在 GitCodeGitHub 以及 码云,持续更新中,别忘了 star 喔~

GitCode

https://gitcode.net/qq_41779565/my-project.git

GitHub

https://github.com/micromaples/my-project

码云Gitee

https://gitee.com/micromaple/my-project

如果不会使用 Git 的小伙伴,我已经上传到了CSDN,资源下载传送门,有会员的小伙伴直接下载即可,没有会员的小伙伴私聊我Mybatis二级缓存可直接获取

一、MyBatis缓存机制

Mybatis 提供了查询缓存来缓存数据,以提高查询效率。缓存级别分为一级缓存二级缓存

1.1、一级缓存

一级缓存为 SqlSession 级别的缓存,也就是会话级缓存,是基于HashMap的本地缓存,当同一个SqlSession执行两次相同的SQL语句时,第一次执行完后会将数据库中查询到的结果写到缓存,第二次查询时直接从缓存中读取,不经过数据库了。一级缓存默认是开启的。

1.2、二级缓存

二级缓存为mapper级别的缓存,多个 SqlSession 去操作同一个 Mapper 的 sql 语句,多个 SqlSession 去操作数据库得到数据会存在二级缓存区域,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。其作用域是 mapper 的同一个 namespace,不同的 sqlSession 两次执行相同 namespace下的 sql 语句且向 sql 中传递参数也相同即最终执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis 默认没有开启二级缓存需要在 setting 全局参数中配置开启二级缓存。

二、集成Redis

2.1、安装Redis

使用Docker Compose 安装Redis。docker-compose.yml内容如下:

version: '3.1'
services:redis:image: redis:6.2.4container_name: redisrestart: alwayscommand: redis-server --requirepass 123456ports:- '6379:6379'volumes:- ./data:/dataenvironment:TZ: Asia/Shanghai

安装启动完成后,可使用Redis连接工具测试

在这里插入图片描述

2.2、项目引入Redis

2.2.1、Maven依赖

org.springframework.bootspring-boot-starter-data-redis

org.apache.commonscommons-pool2

额外引入commons-pool2是因为data-redis底层Redis连接池基于apache commons-pool2开 发,不加入依赖会报ClassNotFoundException

2.2.2、配置application.yml

spring:redis:host: 192.168.110.158port: 6379password: 123456lettuce:pool:#最大允许连接数max-active: 100#最小空闲连接数,最少准备5个可用连接在连接池候着min-idle: 5#最大空闲连接数,空闲连接超过10个后自动释放max-idle: 10#当连接池到达上限后,最多等待30秒尝试获取连接,超时报错max-wait: 30000timeout: 2000

2.2.3、配置序列化规则

RedisTemplateConfiguration配置类如下:

package com.micromaple.my.project.server.config;import com.fasterxml.jackson.annotation.JsonInclude;
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;/*** RedisTemplate配置* Title: RedisTemplateConfiguration* Description:** @author Micromaple*/
@Configuration
public class RedisTemplateConfiguration {/*** redisTemplate** @param redisConnectionFactory* @return*/@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactoryredisConnectionFactory) {RedisTemplate redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);// 使用Jackson2JsonRedisSerialize 替换默认序列化Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();//对于Null值不输出objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);// 设置key和value的序列化规则redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// 设置hashKey和hashValue的序列化规则redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);//afterPropertiesSet和init-method之间的执行顺序是afterPropertiesSet 先执行,init - method 后执行。redisTemplate.afterPropertiesSet();return redisTemplate;}
}

三、配置二级缓存

配置实现MyBatis二级缓存的方式有多种,比如:EhCacheJBossCacheRedis,其核心原理就是客户端实现 MyBatis 提供的Cache 接口,并重写其中的方法,达到二级缓存的效果。

本文以 Redis 为例。

2.1、开启二级缓存

application.yml 中增加如下配置:

# 开启MyBatis二级缓存
mybatis:configuration:cache-enabled: true

如果使用的是 MyBatis-Plus ,则使用如下配置:

# MyBatis-Plus开启二级缓存
mybatis-plus:configuration:cache-enabled: true

2.2、自定义缓存类

MybatisRedisCache 缓存工具类如下:

package com.micromaple.my.project.server.utils;import com.micromaple.my.project.server.config.ApplicationContextHolder;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;/*** MybatisRedisCache 缓存工具类* Title: MybatisRedisCache* Description:** @author Micromaple*/
public class MybatisRedisCache implements Cache {private static final Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private final String id; // cache instance idprivate RedisTemplate redisTemplate;private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间public MybatisRedisCache(String id) {if (id == null) {throw new IllegalArgumentException("Cache instances require an ID");}this.id = id;}@Overridepublic String getId() {return id;}/*** Put query result to redis** @param key* @param value*/@Overridepublic void putObject(Object key, Object value) {try {redisTemplate = getRedisTemplate();if (value != null) {redisTemplate.opsForValue().set(key.toString(), value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);}logger.debug("Put query result to redis");} catch (Throwable t) {logger.error("Redis put failed", t);}}/*** Get cached query result from redis** @param key* @return*/@Overridepublic Object getObject(Object key) {try {redisTemplate = getRedisTemplate();logger.debug("Get cached query result from redis");return redisTemplate.opsForValue().get(key.toString());} catch (Throwable t) {logger.error("Redis get failed, fail over to db", t);return null;}}/*** Remove cached query result from redis** @param key* @return*/@Override@SuppressWarnings("unchecked")public Object removeObject(Object key) {try {redisTemplate = getRedisTemplate();redisTemplate.delete(key.toString());logger.debug("Remove cached query result from redis");} catch (Throwable t) {logger.error("Redis remove failed", t);}return null;}/*** Clears this cache instance*/@Overridepublic void clear() {redisTemplate = getRedisTemplate();Set keys = redisTemplate.keys("*:" + this.id + "*");if (!CollectionUtils.isEmpty(keys)) {redisTemplate.delete(keys);}logger.debug("Clear all the cached query result from redis");}/*** This method is not used** @return*/@Overridepublic int getSize() {return 0;}@Overridepublic ReadWriteLock getReadWriteLock() {return readWriteLock;}private RedisTemplate getRedisTemplate() {if (redisTemplate == null) {redisTemplate = ApplicationContextHolder.getBean("redisTemplate");}return redisTemplate;}
}

ApplicationContextHolder如下:

package com.micromaple.my.project.server.config;import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;/*** Spring bean的工具类* Title: ApplicationContextHolder* Description:** @author Micromaple*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware, DisposableBean {private static final Logger logger = LoggerFactory.getLogger(ApplicationContextHolder.class);private static ApplicationContext applicationContext;/*** 获取存储在静态变量中的 ApplicationContext** @return*/public static ApplicationContext getApplicationContext() {assertContextInjected();return applicationContext;}/*** 从静态变量 applicationContext 中获取 Bean,自动转型成所赋值对象的类型** @param name* @param * @return*/public static  T getBean(String name) {assertContextInjected();return (T) applicationContext.getBean(name);}/*** 从静态变量 applicationContext 中获取 Bean,自动转型成所赋值对象的类型** @param clazz* @param * @return*/public static  T getBean(Class clazz) {assertContextInjected();return applicationContext.getBean(clazz);}/*** 实现 DisposableBean 接口,在 Context 关闭时清理静态变量** @throws Exception*/public void destroy() throws Exception {logger.debug("清除 SpringContext 中的 ApplicationContext: {}", applicationContext);applicationContext = null;}/*** 实现 ApplicationContextAware 接口,注入 Context 到静态变量中** @param applicationContext* @throws BeansException*/public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {ApplicationContextHolder.applicationContext = applicationContext;}/*** 断言 Context 已经注入*/private static void assertContextInjected() {Validate.validState(applicationContext != null, "applicationContext 属性未注入,请在 spring-context.xml 配置中定义 SpringContext");}
}

2.3、增加注解

在 Mapper 接口中增加 @CacheNamespace(implementation = MybatisRedisCache.class) 注解,声明需要使用二级缓存。

package com.micromaple.my.project.server.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.micromaple.my.project.server.domain.SysUser;
import com.micromaple.my.project.server.utils.MybatisRedisCache;
import org.apache.ibatis.annotations.CacheNamespace;/*** 

* 用户表 Mapper 接口*

** @author Micromaple* @since 2022-09-21 21:51:15*/ @CacheNamespace(implementation = MybatisRedisCache.class) public interface SysUserMapper extends BaseMapper {}

2.4、测试验证

访问查询所有用户接口http://localhost:8899/sys-user/get/all

访问完成后,我们打开Redis查询工具,可以看到已经将我们查询出来的数据缓存起来了。效果图如下:
在这里插入图片描述

接着,我们再次访问查询所有用户接口,我们可以在控制台日志中看到,第二次查询并没有走数据库,而是直接在Redis中取出来了

在这里插入图片描述

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...