mybatis-plus技巧--动态表名-多语句-拼接sql--关于mybatis的mysql分页查询总数的优化思考

文章目录

  • 动态表名
    • xml表名填充
    • 表名拦截器
      • 每天按统计
      • 每次设置
  • 多语句操作
    • forEach动态拼接
  • 参数构建
  • java进行拼接sql
  • mysql分页查询总数
    • count
      • 不要使用count(常数),count(列名)代替count(*)
      • 自己计数
    • SQL_CALC_FOUND_ROWS
      • xml单条接口实现
      • mybatis拦截器

动态表名

xml表名填充

<select>
select *
from ${tableName}
</select>

传入tableName参数就可以了,不过只能用$不能用#

因为#会发生预编译,然后会在表名上加引号’'。

表名拦截器

新建一个表名拦截类实现TableNameHandler

@Component
public class MyTableHandler implements TableNameHandler {@Overridepublic String dynamicTableName(String sql, String tableName) {// 表名处理return tableName;}
}

mybatisPlus添加插件

    @Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//1.添加动态表名插件DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();dynamicTableNameInnerInterceptor.setTableNameHandler(new MyTableHandler());interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);return interceptor;}

实例:

每天按统计

如果表名为count则加上今天的时间

@Component
public class MyTableHandler implements TableNameHandler {@Overridepublic String dynamicTableName(String sql, String tableName) {// 表名处理if (StringUtils.equals(tableName,"count")){return tableName+ LocalDate.now();}return tableName;}
}

每次设置

@Component
public class MyTableHandler implements TableNameHandler {// 每个请求线程维护一个数据,避免多线程数据冲突。所以使用ThreadLocalprivate static final ThreadLocal<String> SUFFIX = new ThreadLocal<>();// 设置请求线程的month数据public static void setData(String suffix) {SUFFIX.set(suffix);}@Overridepublic String dynamicTableName(String sql, String tableName) {String suffix = SUFFIX.get();if(StringUtils.isNotBlank(suffix)){SUFFIX.remove();return suffix;}return tableName;}
}

直接设置名字,然后就会改变的。

   MyTableHandler.setData(tableName);list();

多语句操作

需要在配置文件中的url中新增条件允许多查询allowMultiQueries=true

在xml中多条sql用;隔开,就可以多条sql同时发送执行。

forEach动态拼接

传递List<Student>的参数

<update id="updateBatchById"><foreach collection="list" item="s" separator=";">update`user`set`name` = #{name},`salary` = #{salary}whereid = #{id}</foreach>
</update>

参数构建

java进行拼接sql

缺点,没有ide提示,容易敲错

mapper参数传递 @Param(“ew”) LambdaQueryWrapper
param中只能是ew不能是其他的。

在xml中使用:

    <select id="getAllInfo" resultType="com.yu.model.domain.Company">select *from table${ew.customSqlSegment}</select>

ew有多个属性,${ew.sqlSegment},${ew.sqlSelect},${ew.customSqlSegment}

属性介绍
customSqlSegment等同where+sql
sqlSegment等于sql不能用lambda的查询了
sqlSet需要更新的 update tableName set ${ew.sqlSet} where ${ew.sqlSegment}
sqlSelectselect参数, @Select(select ${ew.sqlSelect} from a )

mysql分页查询总数

count

不要使用count(常数),count(列名)代替count(*)

由于count不会统计null值,count(列名)可能会导致总数出错。
又因为COUNT(*)是SQL92定义的标准统计行数的语法,因为他是标准语法,所以MySQL数据库对他进行过很多优化。

自己计数

自己维护计数,如存入redis。比较麻烦但是高效。

SQL_CALC_FOUND_ROWS

我觉得如果有大量用这个更好。

SQL_CALC_FOUND_ROWS是MySQL的一个特殊修饰符,用于在执行SELECT查询时同时计算满足条件的总行数。

每次分页都需要count一次,就像mybatis-plus也是在查询之前先进行count一次,如果total不为0在进行下一轮查询。
SQL_CALC_FOUND_ROWS 语句会统计出符合筛选条件的记录总数,保存在mysql 端;

后面使用 SELECT FOUND_ROWS() 语句可以得到这个数字,不用再次计算。当然,如果你再次运行了SQL_CALC_FOUND_ROWS的话,这个数字就会被覆盖成最新的。

不过呢,这个东西说好的和不好的都有。也没有个说法,但是我在实际中可以知道,在某些情况下肯定是有比count快的。
我在sql也不是特别懂,我只能通过实践来进行了,哪个快用哪个。这个呢不太好说。

它的原理是在执行SELECT查询时,MySQL会先执行一次不带LIMIT子句的查询来计算满足条件的总行数,然后再执行带LIMIT子句的查询来返回实际的结果集。这样可以避免在查询结果集之前进行一次COUNT(*)查询,从而提高查询的性能。

使用SQL_CALC_FOUND_ROWS修饰符的查询语句的执行速率取决于满足条件的总行数和实际返回的结果集的大小。如果总行数很大,而实际返回的结果集较小,那么执行速率可能会比较慢。反之,如果总行数和实际返回的结果集大小相差不大,执行速率可能会比较快。

需要注意的是,使用SQL_CALC_FOUND_ROWS修饰符会增加查询的开销,因为MySQL需要执行两次查询。因此,在实际应用中,需要根据具体情况权衡使用SQL_CALC_FOUND_ROWS的优势和开销。

测试中SQL_CALC_FOUND_ROWS 确实会导致第一次查询变慢,但是得到总数快。2条sql进行查询,有时候会快点有时候慢点。

xml单条接口实现

<select id="selectList" resultMap="User,count">
SELECT SQL_CALC_FOUND_ROWS user_id,user_name
FROM user
LIMIT 10;
SELECT FOUND_ROWS() AS total;
</select>

mapper

List selectList();

servies

List<Object> list= baseMapper.selectList();
List<User> user = list.get(0);
Integer total = list.get(1);

这样,每次都需要list接收,一个数据一个是总数。

mybatis拦截器

我们来看一下对比图,这是mybatis-plus自带的分页插件,吞吐量11
在这里插入图片描述
在这里插入图片描述
下面是自定义的分页插件,吞吐量17,很明显快一些,当然小数据量/单表的时候速度和count差不多,主要在于多表数据量大的时候提升效果显著。
在这里插入图片描述
在这里插入图片描述
至于准确率方便,设置有响应断言,都没有异常,可以保证准确率
在这里插入图片描述

实现:
也可以当mybatis插件的一个练习吧,借鉴了mybatis-plus的分页插件。
依据是否存在page作为参数来进行是否进行分页查询,这个条件也可以依据自己的更换。

@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object target = invocation.getTarget();try {if (target instanceof Executor executor) {Object[] args = invocation.getArgs();Object parameter = args[1];// 处理参数为 IPage 的情况IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);if (null == page) {return invocation.proceed();}boolean isUpdate = args.length == 2;MappedStatement ms = (MappedStatement) args[0];if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) {RowBounds rowBounds = (RowBounds) args[2];BoundSql boundSql = ms.getBoundSql(parameter);// 处理 page中 orderBy 拼接boolean addOrdered = false;String buildSql = boundSql.getSql();List<OrderItem> orders = page.orders();if (CollectionUtils.isNotEmpty(orders)) {addOrdered = true;buildSql = this.concatOrderBy(buildSql, orders);}// size 小于 0 且不限制返回值则不构造分页sqlLong _limit = page.maxLimit() != null ? page.maxLimit() : 1000;if (addOrdered) {PluginUtils.mpBoundSql(boundSql).sql(buildSql);}handlerLimit(page, _limit);IDialect dialect = DialectFactory.getDialect(DbType.MYSQL);final Configuration configuration = ms.getConfiguration();DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);List<ParameterMapping> mappings = mpBoundSql.parameterMappings();Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();model.consumers(mappings, configuration, additionalParameter);// 加入SQL_CALC_FOUND_ROWSString selectSqlCalcFoundRows = model.getDialectSql().replaceFirst("(?i)SELECT", "SELECT SQL_CALC_FOUND_ROWS ");mpBoundSql.sql(selectSqlCalcFoundRows);mpBoundSql.parameterMappings(mappings);CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);Connection connection = executor.getTransaction().getConnection();Statement statement = connection.createStatement();// 查询数据 95%List<Object> query = executor.query(ms, parameter, rowBounds, (ResultHandler) args[3], cacheKey, boundSql);// 查询总数 占速率5%ResultSet resultSet = statement.executeQuery("SELECT FOUND_ROWS() AS total;");while (resultSet.next()) {String column = resultSet.getString(1);page.setTotal(Long.parseLong(column));}return query;}}}catch (Exception e){log.error("分页失败优化失败,原因:{}",e.getMessage());// 打印本次调用的方法和参数log.error("本次调用的方法为:{}",invocation.getMethod());log.error("本次调用的参数为:{}",invocation.getArgs());}return invocation.proceed();}protected List<OrderByElement> addOrderByElements(List<OrderItem> orderList, List<OrderByElement> orderByElements) {List<OrderByElement> additionalOrderBy = orderList.stream().filter(item -> StringUtils.isNotBlank(item.getColumn())).map(item -> {OrderByElement element = new OrderByElement();element.setExpression(new Column(item.getColumn()));element.setAsc(item.isAsc());element.setAscDescPresent(true);return element;}).collect(Collectors.toList());if (CollectionUtils.isEmpty(orderByElements)) {return additionalOrderBy;}// github pull/3550 优化排序,比如:默认 order by id 前端传了name排序,设置为 order by name,idadditionalOrderBy.addAll(orderByElements);return additionalOrderBy;}/*** 处理超出分页条数限制,默认归为限制数** @param page IPage*/protected void handlerLimit(IPage<?> page, Long limit) {final long size = page.getSize();if (limit != null && limit > 0 && (size > limit || size < 0)) {page.setSize(limit);}}

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

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

相关文章

win10提示mfc100u.dll丢失的解决方法,快速解决dll问题

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“mfc100u.dll丢失”。那么&#xff0c;mfc100u.dll是什么&#xff1f;mfc100u.dll是Microsoft Visual C Redistributable文件之一&#xff0c;它包含了用于MFC (Microsoft Foundation Class…

设计模式——命令模式(Command Pattern)+ Spring相关源码

文章目录 一、命令模式定义二、例子2.1 菜鸟教程例子2.1.1 定义命令类接口2.1.2 定义命令执行者2.1.3 被处理对象Stock。2.1.4 封装处理Stock的命令 2.2 JDK源码——Runnable2.2.1 命令接口2.2.2 命令处理者2.2.3 命令实现类 2.3 SpringMVC——Controller2.3.1 请求对象 handle…

Linux ----------------------- 文本处理工具

&#xff08;一&#xff09;绪论 awk、grep、sed和cut 是Linux 操作文本的四大利器&#xff0c;合称文本四大金刚&#xff0c;也是必须掌握的Linux 命令之一。这四个工具的功能各有侧重&#xff1a; awk&#xff1a;功能最强大&#xff0c;用于处理和格式化文本&#xff0c;可…

Docker 安装ELK7.7.1

(注&#xff1a;在安装之前&#xff0c;本方法必须安装jdk1.8以上版本) (注&#xff1a;如果在虚拟机下用可以直接按方法走即可&#xff0c;如果是想进行备份后在别的机器上进行相关操作&#xff0c;必须把所有带有172.17.0.6、192.168.8.166:9200和端口号都改成你自己的方可使…

c++ 实现 AVL 树

AVL 树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家 G.M.Adelson-Velskii 和 E.M.Landis 在 1962 年发明了一…

python 深度学习 解决遇到的报错问题9

本篇继python 深度学习 解决遇到的报错问题8-CSDN博客 目录 一、can only concatenate str (not "int") to str 二、cant convert np.ndarray of type numpy.object_. The only supported types are: float64, float32, float16, complex64, complex128, int64, in…

数据中心系统解决方案

设计思路 系统设计过程中充分考虑各个子系统的信息共享要求&#xff0c;对各子系统进行结构化和标准化设计&#xff0c;通过系统间的各种联动方式将其整合成一个有机的整体&#xff0c;使之成为一套整体的、全方位的数据中心大楼综合管理系统&#xff0c;达到人防、物防和技防…

【vscode输出中文乱码】

vscode输出中文乱码为一个个的问号。 这个链接亲测有用 win11对应的界面在这里&#xff1a;

在PyCharm中直接启动mitmproxy并自动打开关闭系统代理

前言 在前面的文章中&#xff0c;有几篇是介绍mitmproxy 的。 这个mitmproxy 的确是个捕获数据的好工具&#xff0c;但在运行时候需要在命令行启动&#xff0c;这是很令人苦恼的。 之前也尝试过脱离命令行去启动mitmproxy&#xff0c;在Python中启动mitmproxy&#xff0c;脱离…

网络编程套接字(二)

目录 简单的TCP网络程序服务端创建套接字服务端绑定服务端监听服务端获取连接服务端处理请求单执行流服务器的弊端 多进程版TCP网络程序捕捉SIGCHLD信号让孙子进程提供服务多线程版的TCP网络程序客户端创建套接字客户端链接服务器客户端发起请求 线程池版的TCP网络程序 简单的T…

【Python 千题 —— 基础篇】乘方计算

题目描述 题目描述 编写一个程序&#xff0c;接受用户输入的两个数字&#xff0c;然后计算这两个数字的乘方结果&#xff0c;并输出结果。 输入描述 输入两个数字&#xff0c;用回车隔开两个数字。 输出描述 程序将计算这两个数字的乘方结果&#xff0c;并输出结果。 示…

【论文阅读】Equivariant Contrastive Learning for Sequential Recommendation

【论文阅读】Equivariant Contrastive Learning for Sequential Recommendation 文章目录 【论文阅读】Equivariant Contrastive Learning for Sequential Recommendation1. 来源2. 介绍3. 前置工作3.1 序列推荐的目标3.2 数据增强策略3.3 序列推荐的不变对比学习 4. 方法介绍4…

ELK + kafka 日志方案

概述 本文介绍使用ELK&#xff08;elasticsearch、logstash、kibana&#xff09;  kafka来搭建一个日志系统。主要演示使用spring aop进行日志收集&#xff0c;然后通过kafka将日志发送给logstash&#xff0c;logstash再将日志写入elasticsearch&#xff0c;这样elasticsearc…

如何在校园跑腿系统小程序中构建稳健的订单处理与分配系统?

1. 数据库设计 首先&#xff0c;设计订单数据结构。使用数据库&#xff08;例如MySQL、MongoDB等&#xff09;&#xff0c;创建订单表以存储订单相关信息&#xff0c;包括订单ID、用户信息、交付地址、订单状态等。 CREATE TABLE orders (order_id INT AUTO_INCREMENT PRIMAR…

Vue CRUD 弹出窗口 表单验证 增删改查 接口文档

目录 1. 准备工作 2. 弹出窗口 3. 新增更新功能 5.表单验证 5. 接口文档 1. 准备工作 后台服务接口&#xff0c;对书本的增删改查操作 2. 弹出窗口 进入ElementUi官网&#xff0c; 找到Dialog对话框&#xff0c;可以参考“嵌套表单的dialog”实现。 该步骤先实现弹出窗…

任务管理工具

1. 任务规划 有效的任务管理始于适当的规划。取决于预算、资源、范围和时间表&#xff1b;有效的任务规划需要通过结构化方法将工作分解为多个阶段。 有了一个好的任务管理工具&#xff0c;您应该能够将您的工作分解为易于管理的更简单的任务。此外&#xff0c;应该有一个根据…

列举一些常用的Webpack配置和插件

以下是一些常用的Webpack配置和插件&#xff0c;可以用来优化和扩展前端项目的构建过程&#xff1a; 1&#xff1a;入口和出口配置&#xff1a; module.exports {entry: ./src/index.js, // 入口文件output: {path: path.resolve(__dirname, dist), // 输出目录filename: …

一分钟理解npm run dev 和 npm run serve

前端开发过程中运行Vue项目的时候&#xff0c;有时候使用npm run serve命令可以启动项目&#xff0c;有时候却会报错&#xff1b;有时候使用npm run dev命令可以启动项目&#xff0c;有时候却也会报错。是什么原因造成这种情况呢&#xff0c;原因在于Vue脚手架版本的问题&#…

内存池设计实现

1.设计原理 1.内存池实际就是预先分配不同大小的内存块, 然如果需要调用的时候, 直接把这个块的指针返回. 图中, 就是内存池划分. 2.通过一个链表, 将这些分配的内存块串联起来, 每一块最头部都记录这这个块的信息 3.分配的时候, 会遍历一遍链表, 找到is_used未被置1, pool…

相机存储卡被格式化了怎么恢复?数据恢复办法分享!

随着时代的发展&#xff0c;相机被越来越多的用户所使用&#xff0c;这也意味着更多的用户面临着相机数据丢失的问题&#xff0c;很多用户在使用相机的过程中&#xff0c;都出现过不小心格式化相机存储卡的情况&#xff0c;里面的数据也将一并消失&#xff0c;相机存储卡被格式…