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.rhkb.cn/news/179987.html

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

相关文章

压缩软件 7-Zip VS WinZips?

7-zip在联想应用商店给强烈推荐&#xff1f; 要说它好用还行&#xff0c;但每次压缩都显示网络连接失败等异常广告信息。 相反好用的7-ZIP必须鼠标点击右键点击更多才能够看到&#xff0c;这次更新体验也太差了吧&#xff1f; 用户放在第一位&#xff1f; 要不是更新后一直推…

【word技巧】ABCD选项如何对齐?

使用word文件制作试卷&#xff0c;如何将ABCD选项全部设置对齐&#xff1f;除了一直按空格或者Tab键以外&#xff0c;还有其他方法吗&#xff1f;今天分享如何将ABCD选项对齐。 首先&#xff0c;我们打开【替换和查找】&#xff0c;在查找内容输入空格&#xff0c;然后点击全部…

Required String parameter ‘name‘ is not present

[org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter name is not present] 服务端有参数name&#xff0c;客户端没有传上来

掌握RESTful API:规范与设计详解

前言 RAML (RESTful API Modeling Language) 和 OAS (OpenAPI Specification) 都是用于描述和定义 RESTful API 的规范。它们分别提供了不同的功能和优势。 RAML&#xff08;RESTful API Modeling Language&#xff09;&#xff1a; RAML简介 RAML&#xff08;RESTful API M…

Qt中正确的设置窗体的背景图片的几种方式

Qt中正确的设置窗体的背景图片的几种方式 QLabel加载图片方式之一Chapter1 Qt中正确的设置窗体的背景图片的几种方式一、利用styleSheet设置窗体的背景图片 Chapter2 Qt的主窗口背景设置方法一&#xff1a;最简单的方式是通过ui界面来设置&#xff0c;例如设置背景图片方法二 &…

开发环境配置之Linux安装golang

Linux安装golang 目录 1. 下载Go发行版2. 配置工作空间3. 版本升级 1. 下载Go发行版 从官方地址&#xff1a;https://golang.org/dl/ 上下载合适的 二进制发行版 可以使用wget、curl等工具下载具体的go的发行版。 wget https://go.dev/dl/go1.21.3.linux-amd64.tar.gz接着…

edge浏览器的隐藏功能

1. edge://version 查看版本信息 2. edge://flags 特性界面 具体到某一特性&#xff1a;edge://flags/#overlay-scrollbars 3. edge://settings设置界面 详情可参考chrome: 4. edge://extensions 扩展程序页面 5. edge://net-internals 网络事件信息 6. edge://component…

4 sql语法基础

1、DISTINCT 相同值只会出现一次。它作用于所有列&#xff0c;也就是说所有列的值都相同才算相同。 2、LIMIT 限制返回的行数。可以有两个参数&#xff0c;第一个参数为起始行&#xff0c;从 0 开始&#xff1b;第二个参数为返回的总行数。 返回前 5 行: SELECT * FROM myt…

绝地求生msvcp140.dll丢失报错怎么办,这四个方法都可以解决

在回答这个问题之前&#xff0c;我们先来了解一下什么是msvcp140.dll。msvcp140.dll是微软Visual C 2015 Redistributable的一个组件&#xff0c;它包含了许多运行库文件&#xff0c;用于支持各种应用程序的正常运行。当你在玩《绝地求生》&#xff08;俗称“吃鸡”&#xff09…

gma 2 教程(三)坐标参考系统:3.投影方法

安装 gma&#xff1a;pip install gma 地图投影是利用一定数学法则把地球表面的经、纬线转换到平面上的理论和方法。由于地球是一个赤道略宽两极略扁的不规则的梨形球体&#xff0c;故其表面是一个不可展平的曲面&#xff0c;所以运用任何数学方法进行这种转换都会产生误差和变…

工地现场智慧管理信息化解决方案 智慧工地源码

智慧工地系统充分利用计算机技术、互联网、物联网、云计算、大数据等新一代信息技术&#xff0c;以PC端&#xff0c;移动端&#xff0c;设备端三位一体的管控方式为企业现场工程管理提供了先进的技术手段。让劳务、设备、物料、安全、环境、能源、资料、计划、质量、视频监控等…

【广州华锐互动】VR野外求生技能学习,让你感受真实的冒险之旅!

随着科技的迅速发展&#xff0c;虚拟现实(VR)技术为人们提供了一个全新的、身临其境的探险体验。通过将用户带入一个仿真的、沉浸式的虚拟环境&#xff0c;VR互动体验让人们在安全的氛围中感受到野外探险的乐趣。本文将从视觉呈现、沉浸式体验、交互性和应用范围四个方面&#…

堆栈与队列算法-以链表来实现队列

目录 堆栈与队列算法-以链表来实现队列 C代码 堆栈与队列算法-以链表来实现队列 队列除了能以数组的方式来实现外&#xff0c;也可以用链表来实现。在声明队列的类中&#xff0c;除了和队列相关的方法外&#xff0c;还必须有指向队列前端和队列末尾的指针&#xff0c;即fron…

图数据库Neo4j概念、应用场景、安装及CQL的使用

一、图数据库概念 引用Seth Godin的说法&#xff0c;企业需要摒弃仅仅收集数据点的做法&#xff0c;开始着手建立数据之间的关联关系。数据点之间的关系甚至比单个点本身更为重要。 传统的**关系数据库管理系统(RDBMS)**并不擅长处理数据之间的关系&#xff0c;那些表状数据模…

AI大模型架构师专家,你会问什么来测试我的水平,如何解答上述问题,学习路径是什么

0. 沈剑老师的大模型产品应用经验&#xff1a; 提示词三步骤&#xff1a; 假如我是xxx专家&#xff0c;你会问什么来测试我的水平&#xff1b;假如你是xxx专家&#xff0c;你会如何解答上述问题&#xff1b;假如你是xxx专家&#xff0c;上述问题的学习路径是什么&#xff1b;…

node使用http模块

文章目录 前言一、创建http服务二、设置http的响应报文三、不同请求响应不同数据四、请求响应不同html文件1. 添加www文件夹2. js代码3. 效果 五、get和post请求的区别 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 一、创建http服务 // 1&#xff1a…

【Redis】使用Java操作Redis

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Redis》。&#x1f3af;&#x1f3af; &#x1f4…

【51单片机】串口与LED点阵屏(学习笔记)

一、串口 1、串口的概述 串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信。 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信&#xff0c;极大的扩展了单片机的应用…

jar包的精细化运营,Java模块化简介 | 京东云技术团队

图&#xff1a;模块化手机概念 一、什么是Java模块化 Java模块化&#xff08;module&#xff09;是Java9及以后版本引入的新特性。 官方对模块的定义为&#xff1a;一个被命名的&#xff0c;代码和数据的自描述集合。&#xff08; the module, which is a named, self-descri…

k8s-服务网格实战-入门Istio

istio-01.png 背景 终于进入大家都比较感兴趣的服务网格系列了&#xff0c;在前面已经讲解了&#xff1a; 如何部署应用到 kubernetes服务之间如何调用如何通过域名访问我们的服务如何使用 kubernetes 自带的配置 ConfigMap 基本上已经够我们开发一般规模的 web 应用了&#xf…