Mybatis Cache(二)MybatisCache+Redis

前面提到了,使用mybatis cache,一般是结合redis使用。

一、demo

1、数据表
create table demo.t_address
(id           int auto_incrementprimary key,address_name varchar(200) null,address_code varchar(20)  null,address_type int          null
);

项目结构:

2、pom
<?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><groupId>org.example</groupId><artifactId>plus-mybatis-cache</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.4</version><relativePath/></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--尽量不要同时导入mybatis 和 mybatis_plus,避免版本差异--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.13</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency></dependencies>
</project>
3、配置文件
server.port=1112
server.servlet.context-path=/mybatisCacheDemo
#mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3308/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=wtyy
#mybatis
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
#打印日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 开启二级缓存
mybatis-plus.configuration.cache-enabled=true
#redis
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.password=
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=-1
spring.data.redis.lettuce.pool.max-idle=8
spring.data.redis.lettuce.pool.min-idle=0
spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.time-between-eviction-runs=30s
4、util
package com.pluscache.demo.util;import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import java.lang.annotation.Annotation;
import java.util.Map;@Component
public class ApplicationContextUtil implements ApplicationContextAware {private static ApplicationContext applicationContext ;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {ApplicationContextUtil.applicationContext = applicationContext;}public static Object getBean(String name) {return ApplicationContextUtil.applicationContext.getBean(name);}public static <T> T getBean(String name, Class<T> clazz) {return applicationContext.getBean(name, clazz);}public static <T> T getBean(Class<T> clazz) {return applicationContext.getBean(clazz);}public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {return applicationContext.getBeansOfType(clazz);}public static ApplicationContext getApplicationContext() {return applicationContext;}public static <T extends Annotation> T getAnnotation(Object bean, Class<T> annotationClass) {T annotation = bean.getClass().getAnnotation(annotationClass);if (annotation == null) {annotation = AopUtils.getTargetClass(bean).getAnnotation(annotationClass);}return annotation;}}
5、config
package com.pluscache.demo.config;import com.pluscache.demo.util.ApplicationContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;@Slf4j
//@Component
public class MybatisRedisCache implements Cache {/* * id是必须的带上的,这里的id会指定当前放入缓存的mapper的namespace* 如这里的id就是com.sample.dao.IEmployeeDao*/private final String id;private  RedisTemplate redisTemplate;public MybatisRedisCache(final String id) {//获取redis实例//redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");//指定key的序列化方式//redisTemplate.setKeySerializer(new StringRedisSerializer());//redisTemplate.setHashKeySerializer(new StringRedisSerializer());this.id = id;}@Overridepublic String getId() {return id;}@Overridepublic void putObject(Object key, Object value) {this.getRedisTemplate();redisTemplate.opsForHash().put(id.toString(),key.toString(),value);}private void getRedisTemplate() {if (redisTemplate == null) {//由于启动期间注入失败,只能运行期间注入,这段代码可以删除redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");}}@Overridepublic Object getObject(Object key) {this.getRedisTemplate();return redisTemplate.opsForHash().get(id.toString(),key.toString());}@Overridepublic Object removeObject(Object key) {return null;}@Overridepublic void clear() {this.getRedisTemplate();redisTemplate.delete(id.toString());}@Overridepublic int getSize() {this.getRedisTemplate();return redisTemplate.opsForHash().size(id.toString()).intValue();}
}
6、dto
package com.pluscache.demo.dto;import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;@Data
@TableName("t_address")
public class AddressDTO implements Serializable {private Integer id;private String addressName;private String addressCode;public Integer addressType;
}
7、dao
(1)repository

   可以省略,移到service中

package com.pluscache.demo.repository;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.pluscache.demo.dto.AddressDTO;
import com.pluscache.demo.mapper.AddressMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
@Slf4j
public class AddressRepository {@Autowiredprivate AddressMapper addressMapper;//plus新增public void insert(AddressDTO addressDTO) {addressMapper.insert(addressDTO);}//手动SQL新增public void save(AddressDTO addressDTO) {addressMapper.save(addressDTO);}public AddressDTO getById(Integer id) {LambdaQueryWrapper<AddressDTO> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(AddressDTO::getId, id);return addressMapper.selectOne(queryWrapper);}public List<AddressDTO> listByType(Integer type) {LambdaQueryWrapper<AddressDTO> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(AddressDTO::getAddressType, type);return addressMapper.selectList(queryWrapper);}public List<AddressDTO> listByTypeRecord(Integer type) {return addressMapper.listByTypeRecord(type);}public List<AddressDTO> listById(Integer id) {LambdaQueryWrapper<AddressDTO> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(AddressDTO::getId, id);return addressMapper.selectList(queryWrapper);}
}
(2)mapper

   在这里做的缓存

package com.pluscache.demo.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pluscache.demo.config.MybatisRedisCache;
import com.pluscache.demo.dto.AddressDTO;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.Param;import java.util.List;@CacheNamespace(implementation = MybatisRedisCache.class, eviction = MybatisRedisCache.class)
public interface AddressMapper extends BaseMapper<AddressDTO> {void save(@Param("record") AddressDTO addressDTO);List<AddressDTO> listByTypeRecord(@Param("type") Integer type);
}
(3)xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pluscache.demo.mapper.AddressMapper"><cache-ref namespace="com.pluscache.demo.mapper.AddressMapper"/><insert id="save">insert into t_address(address_name,address_code,address_type) values (#{record.addressName},#{record.addressCode},#{record.addressType})</insert><select id="listByTypeRecord" resultType="com.pluscache.demo.dto.AddressDTO">select address_name,address_code from t_address where address_type =#{type}</select></mapper>
8、service
package com.pluscache.demo.service.impl;import com.pluscache.demo.dto.AddressDTO;
import com.pluscache.demo.repository.AddressRepository;
import com.pluscache.demo.service.AddressService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service("addressService")
public class AddressServiceImpl implements AddressService {@Autowiredprivate AddressRepository addressRepository;@Overridepublic void insert(AddressDTO addressDTO) {addressRepository.insert(addressDTO);}@Overridepublic void save(AddressDTO addressDTO) {addressRepository.save(addressDTO);}@Overridepublic AddressDTO getById(Integer id) {return addressRepository.getById(id);}@Overridepublic List<AddressDTO> listByType(Integer type) {return addressRepository.listByType(type);}@Overridepublic List<AddressDTO> listByTypeRecord(Integer type) {return addressRepository.listByTypeRecord(type);}@Overridepublic List<AddressDTO> listById(Integer id) {return addressRepository.listById(id);}
}
9、controller
package com.pluscache.demo.controller;import com.pluscache.demo.dto.AddressDTO;
import com.pluscache.demo.service.AddressService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/address")
public class AddressController {@Autowiredprivate AddressService addressService;@RequestMapping("/insert")public void insert(AddressDTO addressDTO) {addressService.insert(addressDTO);}@RequestMapping("/save")public void save(AddressDTO addressDTO) {addressService.save(addressDTO);}@RequestMapping("/getById")public AddressDTO getById(Integer id) {return addressService.getById(id);}@RequestMapping("/listByType")public List<AddressDTO> listByType(Integer type) {return addressService.listByType(type);}@RequestMapping("/listByTypeRecord")public List<AddressDTO> listByTypeRecord(Integer type) {return addressService.listByTypeRecord(type);}@RequestMapping("/listById")public List<AddressDTO> listById(Integer id) {return addressService.listById(id);}}
10、启动类
package com.pluscache.demo;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.pluscache.demo.mapper")
@SpringBootApplication
public class MybatisPlusApplication {public static void main(String[] args) {SpringApplication.run(MybatisPlusApplication.class, args);}
}
11、测试 
(1)新增数据

    新增了几条数据

(2)SQL法根据type查询

① 第一次访问

localhost:1112/mybatisCacheDemo/address/listByTypeRecord?type=1

后台打印:

查看redis生成了缓存

 ② 再次访问,没有查询db了

(3) mybatis-plus根据type查询

  ①  第一次访问localhost:1112/mybatisCacheDemo/address/listByType?type=1

后台从db查询

查看redis的缓存:可以看到又生成了一条缓存

②  再次访问,没有从数据库查询

 (4)mybatis-plus根据id查询

①  第一次访问localhost:1112/mybatisCacheDemo/address/listById?id=1

走db查看

查看redis缓存:

 ②  再次访问,不再走db查询

 (5)执行add

这里生成的id是自增长的,理论上,只需要清空type=1的缓存,不需要清空id=1的缓存。但是刷新redis可以看到,整个大key都清除了。

二、缺点

从上面的demo可以看到,mybatis cache不够灵活;且一个mapper对应一个大key,存在redis存储bigkey的问题。由此引入了spring cache。当然,选择哪种缓存策略取决于你的具体需求,如果你需要一个轻量级的、与 MyBatis 紧密集成的缓存解决方案,可以选择 MyBatis Cache。如果你需要一个功能丰富的缓存解决方案,支持多种缓存策略、分布式缓存以及高级缓存管理,可以选择 Spring Cache。

两者的比较:

1、概念

MyBatis 的 Cache 默认是一个本地缓存,可以自定义引入redis,用于缓存查询的结果以提高性能。

Spring Cache 是一个抽象的缓存层,它可以支持多种缓存实现(如 EhCache、Redis、Caffeine等),并提供了一套统一的接口来进行缓存操作。

2、使用

MyBatis Cache 主要用于 MyBatis 的二级缓存,主要是缓存 SQL 查询的结果,主要是针对 MyBatis 层面。

Spring Cache 是一个通用的缓存抽象,它可以和任何技术栈的缓存框架集成,不仅限于 MyBatis。

3、配置复杂度:

MyBatis Cache 配置相对简单,通常在 MyBatis 配置文件中进行配置。

Spring Cache 的配置相对复杂,需要配置缓存管理器,并且可以使用注解或者 XML 配置方式。

4、缓存策略:

MyBatis Cache 只能使用内置的缓存策略。

Spring Cache 支持多种缓存策略,可以灵活配置和扩展。

5、分布式支持:

MyBatis Cache 不支持分布式环境下的缓存同步。

Spring Cache 支持分布式缓存,可以通过 Redis 等中间件来实现。

6、缓存数据形式:

MyBatis Cache 缓存的是对象的深拷贝。

Spring Cache 缓存的数据可以是任意数据类型。

7、缓存 key 的定义:

MyBatis Cache 需要自定义 key 生成机制。

Spring Cache 可以使用 SpEL 表达式定义 key。

8、缓存失效策略:

MyBatis Cache 需要自定义缓存的失效机制。

Spring Cache 可以通过注解定义缓存的失效时间。

9、缓存缓存的数据范围:

MyBatis Cache 缓存的数据范围仅限于当前 namespace。

Spring Cache 可以在多个 namespace 或者多个应用间共享缓存数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/840374.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

七个很酷的GenAI LLM技术性面试问题

不同于互联网上随处可见的传统问题库&#xff0c;这些问题需要跳出常规思维。 大语言模型(LLM)在数据科学、生成式人工智能(GenAI)和人工智能领域越来越重要。这些复杂的算法提升了人类的技能&#xff0c;并在诸多行业中推动了效率和创新性的提升&#xff0c;成为企业保持竞争…

2024年山东省安全员C证证模拟考试题库及山东省安全员C证理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年山东省安全员C证证模拟考试题库及山东省安全员C证理论考试试题是由安全生产模拟考试一点通提供&#xff0c;山东省安全员C证证模拟考试题库是根据山东省安全员C证最新版教材&#xff0c;山东省安全员C证大纲整理…

营收净利双降、股东减持,大降价也救不了良品铺子

号称“高端零食第一股”的良品铺子(603719.SH)&#xff0c;正遭遇部分股东的“用脚投票”。 5月17日晚间&#xff0c;良品铺子连发两份减持公告&#xff0c;其控股股东宁波汉意创业投资合伙企业、持股5%以上股东达永有限公司&#xff0c;两者均计划减持。 其中&#xff0c;宁…

全栈安全 为云而生 | 亚信安全信舱ForCloud全新品牌重磅发布

5月18日&#xff0c;亚信安全云安全全新品牌ForCloud正式发布。基于“全栈安全 为云而生”的创新理念&#xff0c;亚信安全云安全完成全新、全面、全栈升级。ForCloud的发布仪式在C3安全大会“云领未来&#xff1a;全栈一体化”云安全论坛上隆重举办&#xff0c;同时亚信安全还…

深度学习之基于Tensorflow识别人脸表情及情绪

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 人脸表情及情绪识别是计算机视觉领域的一个重要研究方向&#xff0c;它在人机交互、情感计算、…

C++贪心算法(2)

最大子矩阵 #include<bits/stdc.h> using namespace std; int main() {int n;cin>>n;int a[110][110]{0};int b[110][110]{0};for(int i1;i<n;i){for(int j1;j<n;j){cin>>a[i][j];if(i1&&j1){b[i][j]b[i][j-1]a[i][j];}else{b[i][j]b[i][j-1]…

接口使用实例——数组排序

对于基本数据类型的大小比较&#xff0c;我们可以使用<,>,或者equals方法进行比较&#xff0c;但是对象之间如何进行比较呢&#xff1f;要对对象进行比较&#xff0c;我们必须对同一个成员变量进行比较&#xff0c;如我们可以通过比较name的大小来得出两个对象的大小&…

【一步一步了解Java系列】:何为数组,何为引用类型

看到这句话的时候证明&#xff1a;此刻你我都在努力加油陌生人个人主页&#xff1a;Gu Gu Study专栏&#xff1a;一步一步了解Java 喜欢的一句话&#xff1a; 常常会回顾努力的自己&#xff0c;所以要为自己的努力留下足迹 喜欢的话可以点个赞谢谢了。 数组 数组是一推相同数据…

数据迁移利器登场!Elasticdumpv6.110震撼发布,助你轻松搬迁大数据!

简介 Elasticdump 是一个用于导出和导入 Elasticsearch 数据的工具。它能够从一个 Elasticsearch 集群读取数据并写入到另一个 Elasticsearch 集群、文件系统或其他数据存储&#xff08;例如 S3&#xff09;。这个工具非常有用&#xff0c;特别是在进行数据迁移、备份和恢复操作…

2024年学浪视频怎么下载到手机相册

随着2024年的到来&#xff0c;学浪平台继续为广大学习者提供优质的在线教育资源。然而&#xff0c;如何将这些宝贵的视频内容下载到手机相册&#xff0c;方便随时离线观看呢&#xff1f;无论您是想在旅途中学习&#xff0c;还是希望在没有网络的情况下复习课程&#xff0c;本文…

1106 2019数列

solution 维护长度为4的数组&#xff0c;对于第四位之后的数字&#xff0c;为所维护数组的所有元素之和 的个位数 #include<iostream> using namespace std; int main(){int n, a[4] {2, 0, 1, 9}, cnt 0, d;scanf("%d", &n);for(int i 0; i < n; …

kettle从入门到精通 第六十三课 ETL之kettle kettle调用python脚本的两种方法

想真正学习或者提升自己的ETL领域知识的朋友欢迎进群&#xff0c;一起学习&#xff0c;共同进步。若二维码失效&#xff0c;公众号后台加我微信入群&#xff0c;备注kettle。 kettle中不能直接调用python脚本&#xff0c;可以通过shell脚本和http进行调用pyton服务。 一、shel…

Golang | Leetcode Golang题解之第97题交错字符串

题目&#xff1a; 题解&#xff1a; func isInterleave(s1 string, s2 string, s3 string) bool {n, m, t : len(s1), len(s2), len(s3)if (n m) ! t {return false}f : make([]bool, m 1)f[0] truefor i : 0; i < n; i {for j : 0; j < m; j {p : i j - 1if i >…

C++的数据结构(十八):并查集

并查集&#xff08;Union-Find&#xff09;是一种用于处理一些不交集&#xff08;Disjoint Sets&#xff09;问题的数据结构。它主要支持两种操作&#xff1a;合并集合&#xff08;Union&#xff09;和查找元素所属集合&#xff08;Find&#xff09;。在解决诸如连通性问题、网…

Ruoyi框架学习——权限管理

权限分类 菜单权限&#xff1a;用户登录系统之后能看到哪些菜单按钮权限&#xff1a;用户在一个页面上能看到哪些按钮&#xff0c;比如新增、删除等按钮接口权限&#xff1a;用户带着认证信息请求后端接口&#xff0c;是否有权限访问&#xff0c;该接口和前端页面上的按钮一一对…

AI生成内容检测|Fast-DetectGPT:通过条件概率曲率对机器生成文本进行有效的零样本检测

【摘要】大型语言模型 (LLM) 已显示出生成流畅且有说服力的内容的能力&#xff0c;这既带来了生产力机会&#xff0c;也带来了社会风险。要构建值得信赖的 AI 系统&#xff0c;必须区分机器生成的内容和人类创作的内容。领先的零样本检测器 DetectGPT 展示了值得称赞的性能&…

Github 2024-05-25 Rust开源项目日报Top10

根据Github Trendings的统计,今日(2024-05-25统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Svelte项目1TypeScript项目1Python项目1Go项目1Dart项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust…

如果有多个文件夹,怎么快速获得文件夹的名字呢

上一篇写到怎么批量建立文件夹&#xff0c;那么怎么获取批量文件夹的名字呢&#xff1f; 一、啊这&#xff0c;这真是一个好问题二、这个得用Python&#xff08;文本末尾有打包程序&#xff0c;点击链接运行就可以了&#xff09;&#xff08;1&#xff09;首先建立一个py文件&a…

博客系统(Servlet实现)

目录 1.准备工作 2.数据库设计 2.1表设计 2.2封装数据库操作代码 2.3创建 Blog 类 和 User 类 2.4创建 BlogDao 类和 UserDao 类 3.读取博客列表功能 3.1约定前后端交互接口 3.2实现服务器代码 3.3实现客户端代码 4.实现博客详情 4.1约定前后端交互接口 4.2实现服…

AGI技术与原理浅析:曙光还是迷失?

前言&#xff1a;回顾以往博客文章&#xff0c;最近一次更新在2020-07&#xff0c;内容以机器学习、深度学习、CV、Slam为主&#xff0c;顺带夹杂个人感悟。笔者并非算法科班出身&#xff0c;本科学制药、研究生学金融&#xff0c;最原始的算法积累都来源于网络&#xff0c;当时…