Mybatis中支持缓存的query与不支持缓存的query

mybatis拦截器中,通常添加两个query的签名方法,如下:

@Intercepts({
    @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})

第一个,表示不支持缓存的query。

第二个,表示支持缓存的query。

a.某些数据变化不频繁,但查询非常频繁。缓存可以减少数据库查询次数,提高响应速度。

b.在分页查询中,缓存可以显著提高性能,尤其是当用户频繁浏览不同页面时。

c.对于复杂查询,生成的 SQL 可能涉及多个表的联接,执行时间较长。缓存可以显著减少这种查询的执行次数。

具体区别

  1. 访问频率和实时性

    • 不需要缓存:每次查询都直接访问数据库,适用于数据变化频繁或需要最新数据的场景。
    • 需要缓存:查询结果可以被缓存,适用于数据变化不频繁但查询频繁的场景。
  2. 性能和资源使用

    • 不需要缓存:每次都访问数据库,可能会增加数据库负载。
    • 需要缓存:利用缓存减少数据库访问次数,显著提高性能和响应速度。

有哪些方法,可以判断是否应用缓存:
1.通过sql语句标识:

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class CacheInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        String sqlId = mappedStatement.getId();

        // 假设我们有一个特定的SQL ID需要使用缓存
        if ("com.example.mapper.UserMapper.selectUsers".equals(sqlId)) {
            // 使用缓存逻辑
            CacheKey cacheKey = ...; // 创建 CacheKey
            BoundSql boundSql = ...; // 获取 BoundSql
            // 执行带缓存的查询
            return executor.query(mappedStatement, parameter, rowBounds, resultHandler, cacheKey, boundSql);
        } else {
            // 直接访问数据库
            return invocation.proceed();
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可选:设置一些属性
    }
}

2.通过注解或配置

<select id="selectUsers" resultType="User" useCache="true">
    SELECT * FROM users
</select>



@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class CacheInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        RowBounds rowBounds = (RowBounds) invocation.getArgs()[2];
        ResultHandler resultHandler = (ResultHandler) invocation.getArgs()[3];
        Executor executor = (Executor) invocation.getTarget();

        // 读取自定义属性
        Boolean useCache = (Boolean) mappedStatement.getConfiguration().getVariables().get("useCache");

        if (useCache != null && useCache) {
            // 使用缓存逻辑
            CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, mappedStatement.getBoundSql(parameter));
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            return executor.query(mappedStatement, parameter, rowBounds, resultHandler, cacheKey, boundSql);
        } else {
            // 直接访问数据库
            return invocation.proceed();
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可选:设置一些属性
    }
}

3.通过业务逻辑判断

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class CacheInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        RowBounds rowBounds = (RowBounds) invocation.getArgs()[2];
        ResultHandler resultHandler = (ResultHandler) invocation.getArgs()[3];
        Executor executor = (Executor) invocation.getTarget();

        // 根据业务逻辑判断
        if (shouldUseCache(mappedStatement, parameter)) {
            // 使用缓存逻辑
            CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, mappedStatement.getBoundSql(parameter));
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            return executor.query(mappedStatement, parameter, rowBounds, resultHandler, cacheKey, boundSql);
        } else {
            // 直接访问数据库
            return invocation.proceed();
        }
    }

    private boolean shouldUseCache(MappedStatement mappedStatement, Object parameter) {
        // 根据业务逻辑判断是否使用缓存
        // 示例:如果参数包含某个特定值,则使用缓存
        if (parameter instanceof Map) {
            Map<String, Object> paramMap = (Map<String, Object>) parameter;
            return "useCache".equals(paramMap.get("cacheFlag"));
        }
        return false;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可选:设置一些属性
    }
}

两个query方法的区别:

其实,mapper文件中useCache参数会用来构建MappedStatement对象。即ms.isUseCache()被用来判断是否走缓存逻辑。

或者 通过@Options注解方式配置useCache参数:

import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {
    
    @Select("SELECT * FROM your_table WHERE your_conditions")
    @Options(useCache = false)
    List<YourResultType> selectRealTimeData();
}
public abstract class BaseExecutor implements Executor {

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameter);
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        // 检查二级缓存
        if (ms.isUseCache() && resultHandler == null) {
            Cache cache = ms.getCache();
            if (cache != null) {
                // 从二级缓存中获取结果
                @SuppressWarnings("unchecked")
                List<E> list = (List<E>) cache.getObject(key);
                if (list != null) {
                    return list;
                }
            }
        }
        // 如果缓存没有命中,执行数据库查询
        List<E> result = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        // 将结果存入二级缓存
        if (ms.isUseCache() && resultHandler == null && cache != null) {
            cache.putObject(key, result);
        }
        return result;
    }

    protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
}

其中,mybatis中,启用二级缓存的配置方式:

1.全局配置

<configuration>
    <!-- 其他配置 -->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
</configuration>

2.映射文件配置

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 启用二级缓存 -->
    <cache/>

    <!-- 其他映射配置 -->
    <select id="selectUsers" resultType="User">
        SELECT * FROM users
    </select>
</mapper>

3.自定义缓存配置

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 启用二级缓存,并设置自定义属性 -->
    <cache
        eviction="LRU"       <!-- 缓存回收策略:LRU(默认) -->
        flushInterval="60000" <!-- 刷新间隔,单位:毫秒 -->
        size="512"           <!-- 缓存大小 -->
        readOnly="true"/>    <!-- 只读缓存 -->

    <!-- 其他映射配置 -->
    <select id="selectUsers" resultType="User">
        SELECT * FROM users
    </select>
</mapper>

4.使用注解配置

import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.cache.decorators.LruCache;

@CacheNamespace(
    eviction = LruCache.class,   // 缓存回收策略
    flushInterval = 60000,       // 刷新间隔,单位:毫秒
    size = 512,                  // 缓存大小
    readWrite = false            // 是否可读写
)
public interface UserMapper {
    
    @Select("SELECT * FROM users")
    List<User> selectUsers();
}

 

提取有效信息:

private Object extractRouteParameterValue(Invocation invocation, Set<String> routerPropertyNames) {

        Object routeValue = null;
        try {
            Object[] args = invocation.getArgs();
            MappedStatement mappedStatement = (MappedStatement) args[0];
            Object parameterObject = args[1];
            BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            TypeHandlerRegistry typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
            Configuration configuration = mappedStatement.getConfiguration();

            for (ParameterMapping parameterMapping : parameterMappings) {
                String rawPropertyName = parameterMapping.getProperty();
                String actualPropertyName = resolvePropertyName(rawPropertyName);

                if (!routerPropertyNames.contains(actualPropertyName.toLowerCase())) {
                    continue;
                }
                // copy from DefaultParameterHandler.setParameter方法
                //  ParameterMode.IN 输入, OUT、INOUT 是在存储过程中使用,暂时无视
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        routeValue = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        routeValue = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        routeValue = parameterObject;
                    } else {
                        MetaObject metaObject = configuration.newMetaObject(parameterObject);
                        routeValue = metaObject.getValue(propertyName);
                    }
                }
                if (routeValue != null && hasText(routeValue.toString())) {
                    return routeValue;
                }
                throw new DataSourceRoutingException(String.format("未检测到有效的数据库路由,请检测是否传入:(%s)", boundSql.getSql()));
            }
        } catch (DataSourceRoutingException dataSourceRoutingException) {
            throw dataSourceRoutingException;
        } catch (RuntimeException e) {
            throw new DataSourceRoutingException(String.format("数据库路由解析异常, invocation method:{%s}, args:{%s}, routerPropertyNames:{%s}",
                    invocation.getMethod().toGenericString(), Arrays.toString(invocation.getArgs()), routerPropertyNames), e);
        }
        return null;
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux基础篇-文件句柄数修改

目录 文件句柄数修改修改最大限制用户级的修改系统级的修改 文件句柄数修改 linux最大打开文件句柄数&#xff0c;即打开文件数最大限制&#xff0c;就是规定的单个进程能够打开的最大文件句柄数量&#xff08;Socket连接也算在里面&#xff0c;默认大小1024&#xff09; liu…

Fluent在Linux环境导入文本文件报错解决

问题描述 同样的文本文件&#xff08;如Profile文件、Chemkin反应机理文件等&#xff09;&#xff0c;Fluent在Windows环境中可正常读取和运算&#xff0c;但是在Linux环境中导入会出错。 Linux中&#xff0c;Fluent读取Chemkin文件报错 问题原因 可能原因之一&#xff1a;换…

交叉编译 tcpdump libpcap

文章目录 交叉编译 tcpdump & libpcap概述源码下载交叉编译 libpcap交叉编译 tcpdump 交叉编译 tcpdump & libpcap 概述 tcpdump 是一个强大的命令行包分析器&#xff0c;libpcap 是一个可移植的用于网络流量捕获的 C/C 库。tcpdump 依赖于 libpcap 库&#xff0c;同…

基于IMX8MPlus SMARC核心板的便携式床旁超声诊断仪应用解决方案

医学的高速发展&#xff0c;使得超声仪器得到了广泛的普及&#xff0c;便携式的床旁超声诊断仪&#xff0c;不仅满足临床医学对可视化、便携式、智能化的需求&#xff0c;还能满足基层患者随时随地快速筛查的需求。 便携式的床旁超声诊断仪&#xff0c;移动灵活方便&#xff0c…

检索增强生成RAG系列3--RAG优化之文档处理

在上一章中罗列了对RAG准确度的几个重要关键点&#xff0c;主要包括2方面&#xff0c;这一章就针对其中一方面&#xff0c;来做详细的讲解以及其解决方案。 目录 1 文档解析1.1 文档解析工具1.2 实战经验1.3 代码演示 2 文档分块2.1 分块算法2.2 实战经验2.3 代码演示 3 文档e…

OmniViD: A Generative Framework for Universal Video Understanding

标题&#xff1a;OmniViD:通用视频理解的生成框架 源文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2024/papers/Wang_OmniViD_A_Generative_Framework_for_Universal_Video_Understanding_CVPR_2024_paper.pdfhttps://openaccess.thecvf.com/content/CVPR202…

HarmonyOS Next开发学习手册——文本输入 (TextInput/TextArea)

TextInput、TextArea是输入框组件&#xff0c;通常用于响应用户的输入操作&#xff0c;比如评论区的输入、聊天框的输入、表格的输入等&#xff0c;也可以结合其它组件构建功能页面&#xff0c;例如登录注册页面。具体用法请参考 TextInput 、 TextArea 。 创建输入框 TextIn…

优化数据库字段使用位运算-php语言示例

背景&#xff1a;一个会员有三个状态&#xff0c;A、B、C&#xff0c;其中一个人可以为 A、B、C、AB&#xff1b;之前数据表结构加了三个字段is_a、is_b、is_c; 本人实在不想这样粗糙的实现需求&#xff0c;遂决定用位运算优化。 上代码&#xff1a; 位运算可以用来处理状态值…

注塑机压铸机比例泵PQ阀放大器

比例泵技术在注塑机和压铸机中的应用&#xff0c;主要通过调节液压泵的排量和压力&#xff0c;来实现对设备工作性能的优化和节能。比例泵技术的核心在于其能够根据实际需求&#xff0c;精确地控制液压系统的压力和流量。这一点对于注塑机和压铸机来说尤为重要&#xff0c;因为…

【面试系列】信息安全分析师高频面试题及详细解答

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&#xff1a;详细讲解AIGC的概念、核心技术、…

Pycharm一些问题解决办法

研究生期间遇到关于Pycharm一些问题报错以及解决办法的汇总 ModuleNotFoundError: No module named sklearn’ 安装机器学习库&#xff0c;需要注意报错的sklearn是scikit-learn缩写。 pip install scikit-learnPyCharm 导包提示 unresolved reference 描述&#xff1a;模块…

【Linux】计算机网络基础:协议、分层结构与数据传输解析

文章目录 前言1. 认识“协议”1.1. 什么是协议1.2. 网络分层结构——网络 vs OS之间的关系1.2.1. 软案分层1.2.2. 网络分层(为什么&#xff1f;是什么&#xff1f;怎么办&#xff1f;) 1.3. 站在语言角度&#xff0c;重新理解协议 2. 网络传输基本流程3. 数据包封装和分用4. 网…

【开放词汇分割】Side Adapter Network for Open-Vocabulary Semantic Segmentation

论文链接&#xff1a;Side Adapter Network for Open-Vocabulary Semantic Segmentation 代码链接&#xff1a;https://github.com/MendelXu/SAN 作者&#xff1a;Mengde Xu,Zheng Zhang,Fangyun Wei,Han Hu,Xiang Bai 发表单位&#xff1a;华中科技大学、微软亚洲研究院 会…

python(6)numpy的使用详细讲解

在numpy中&#xff0c;最基本的数据结构是数组&#xff0c;因此我们首先需要了解如何创建一个数组。numpy提供了多种数组创建方法&#xff0c;包括从列表或元组创建、从文件中读取数据、使用特定函数创建等。下面是一些常用的创建方法&#xff1a; 一、创建数组 1. 从列表或元…

CesiumJS【Basic】- #037 绘制轮廓线(Entity方式)

文章目录 绘制轮廓线(Entity方式)1 目标2 代码2.1 main.ts绘制轮廓线(Entity方式) 1 目标 使用Entity方式绘制轮廓线 2 代码 2.1 main.ts import * as Cesium from cesium;const viewer = new Cesium.Viewer(<

10月开始,所有新来日本的外国人都必须加入公共年金体系!

为了吸引更多外国人来日本工作并为他们提供更好的养老保障&#xff0c;日本厚生劳动省最近宣布了一项新政策。 从今年10月开始&#xff0c;所有新来日本的外国人都必须加入公共年金体系。 虽然之前已经有这个要求&#xff0c;但还是有不少人没加入。 因此&#xff0c;日本年金机…

Excel保存时弹出“请注意,您的文档的部分内容可能包含文档检查器无法删除的个人信息”

前言 Excel保存时弹出“请注意&#xff0c;您的文档的部分内容可能包含文档检查器无法删除的个人信息”&#xff0c;本节会介绍如何查看无法删除的个人信息是什么&#xff0c;以及如何关闭该提示窗口 一、关闭弹窗提醒 1、点击文件 – 选项 2、点击选择信任中心 – 信任中心…

烟台网站建设前需要了解哪些

在进行烟台网站建设之前&#xff0c;需要了解以下几个重要的方面&#xff1a; 1. 目标和定位&#xff1a;在建设网站之前&#xff0c;需要明确网站的目标和定位。是为了展示公司业务&#xff0c;还是为了销售产品&#xff0c;或者是为了提供信息和服务等。根据不同的目标和定位…

Soul打造安全社交元宇宙环境,全力守护用户线上社交安全

在数字化时代的浪潮中,智能安全线上社交正成为人们日常生活中的重要组成部分。随着人们对社交媒体和在线平台依赖程度的不断增加,保障个人信息安全和网络安全变得至关重要。在此背景下,社交平台致力于采取多种措施来保障用户的隐私安全,提升社交体验的质量和安全性。而Soul全方…

程序员日志之DNF手游55级版本全职业攻略

目录 传送门正文日志1、概要2、异界套和遗迹悲鸣套坑3、全职业攻略鬼剑士-狂战士鬼剑士-鬼泣鬼剑士-剑魂鬼剑士-阿修罗格斗家-散打格斗家-气功师神枪手-漫游枪手神枪手-枪炮师魔法师-元素师魔法师-魔道学者圣职者-圣骑士 传送门 SpringMVC的源码解析&#xff08;精品&#xff…