MyBatis:使用MyBatis执行SQL多出LIMIT ?的问题,MyBatis分页查询SQL自动执行SELECT count(0)的Bug,PageHelper.clearPage()清理缓存

2023-05-16

情景:

1.在使用MyBatis执行SQL(包含分页功能)的时候,明明SQL里没写LIMIT,执行时却多出了一个LiMIT。

2.在使用MyBatis执行SQL的时候,明明SQL里写的是SELECT * ...,执行时却执行了SELECT count(0) ...,后文中对此Bug进行说明。

 

解决方案:分页查询数据之前先清理分页缓存。

先执行

PageHelper.clearPage();

SQL代码如下:发现SQL中并没有LIMIT。

查询结果仅一页,期望的结果是多页。

查看执行的SQL日志,从控制台中发现SQL中也是没有LIMIT 10,但是为什么在执行中多出了一个LIMIT ?呢?且这个LIMIT的参数就是Parameters的值(我设置的是10)。

参考了一下MyBatis官网和其他博客发现需要清理一下之前设置过的Page缓存,调用PgageHelper.clearPage()清理一下page缓存即可。

加入以上代码以后问题完美解决。LIMIT也消失了。

查看前端页面分页显示也正常了。

参考GitHub官网:MyBatis分页插件

 https://github.com/pagehelper/Mybatis-PageHelper

 

MyBatis自动执行SELECT count(0) ...的Bug的解决方案:

正常情况下,执行的SQL如下:

出Bug时执行的SQL如下:

解决方案:添加PageHelper.clearPage();

附 PageHelper.clearPage()的代码。

package com.github.pagehelper.page;


import com.github.pagehelper.ISelect;

import com.github.pagehelper.Page;

import com.github.pagehelper.util.PageObjectUtil;


import java.util.Properties;


/**

* 基础分页方法

*

* @author liuzh

*/

public abstract class PageMethod {

protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();

protected static boolean DEFAULT_COUNT = true;


/**

* 设置 Page 参数

*

* @param page

*/

protected static void setLocalPage(Page page) {

LOCAL_PAGE.set(page);

}


/**

* 获取 Page 参数

*

* @return

*/

public static <T> Page<T> getLocalPage() {

return LOCAL_PAGE.get();

}


/**

* 移除本地变量

*/

public static void clearPage() {

LOCAL_PAGE.remove();

}


/**

* 获取任意查询方法的count总数

*

* @param select

* @return

*/

public static long count(ISelect select) {

Page<?> page = startPage(1, -1, true);

select.doSelect();

return page.getTotal();

}


/**

* 开始分页

*

* @param params

*/

public static <E> Page<E> startPage(Object params) {

Page<E> page = PageObjectUtil.getPageFromObject(params, true);

//当已经执行过orderBy的时候

Page<E> oldPage = getLocalPage();

if (oldPage != null && oldPage.isOrderByOnly()) {

page.setOrderBy(oldPage.getOrderBy());

}

setLocalPage(page);

return page;

}


/**

* 开始分页

*

* @param pageNum 页码

* @param pageSize 每页显示数量

*/

public static <E> Page<E> startPage(int pageNum, int pageSize) {

return startPage(pageNum, pageSize, DEFAULT_COUNT);

}


/**

* 开始分页

*

* @param pageNum 页码

* @param pageSize 每页显示数量

* @param count 是否进行count查询

*/

public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count) {

return startPage(pageNum, pageSize, count, null, null);

}


/**

* 开始分页

*

* @param pageNum 页码

* @param pageSize 每页显示数量

* @param orderBy 排序

*/

public static <E> Page<E> startPage(int pageNum, int pageSize, String orderBy) {

Page<E> page = startPage(pageNum, pageSize);

page.setOrderBy(orderBy);

return page;

}


/**

* 开始分页

*

* @param pageNum 页码

* @param pageSize 每页显示数量

* @param count 是否进行count查询

* @param reasonable 分页合理化,null时用默认配置

* @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置

*/

public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {

Page<E> page = new Page<E>(pageNum, pageSize, count);

page.setReasonable(reasonable);

page.setPageSizeZero(pageSizeZero);

//当已经执行过orderBy的时候

Page<E> oldPage = getLocalPage();

if (oldPage != null && oldPage.isOrderByOnly()) {

page.setOrderBy(oldPage.getOrderBy());

}

setLocalPage(page);

return page;

}


/**

* 开始分页

*

* @param offset 起始位置,偏移位置

* @param limit 每页显示数量

*/

public static <E> Page<E> offsetPage(int offset, int limit) {

return offsetPage(offset, limit, DEFAULT_COUNT);

}


/**

* 开始分页

*

* @param offset 起始位置,偏移位置

* @param limit 每页显示数量

* @param count 是否进行count查询

*/

public static <E> Page<E> offsetPage(int offset, int limit, boolean count) {

Page<E> page = new Page<E>(new int[]{offset, limit}, count);

//当已经执行过orderBy的时候

Page<E> oldPage = getLocalPage();

if (oldPage != null && oldPage.isOrderByOnly()) {

page.setOrderBy(oldPage.getOrderBy());

}

setLocalPage(page);

return page;

}


/**

* 排序

*

* @param orderBy

*/

public static void orderBy(String orderBy) {

Page<?> page = getLocalPage();

if (page != null) {

page.setOrderBy(orderBy);

} else {

page = new Page();

page.setOrderBy(orderBy);

page.setOrderByOnly(true);

setLocalPage(page);

}

}


/**

* 设置参数

*

* @param properties 插件属性

*/

protected static void setStaticProperties(Properties properties){

//defaultCount,这是一个全局生效的参数,多数据源时也是统一的行为

if(properties != null){

DEFAULT_COUNT = Boolean.valueOf(properties.getProperty("defaultCount", "true"));

}

}


}
LOCAL_PAGE.remove()代码:

 

/**

* Removes the current thread's value for this thread-local

* variable. If this thread-local variable is subsequently

* {@linkplain #get read} by the current thread, its value will be

* reinitialized by invoking its {@link #initialValue} method,

* unless its value is {@linkplain #set set} by the current thread

* in the interim. This may result in multiple invocations of the

* {@code initialValue} method in the current thread.

*

* @since 1.5

*/

public void remove() {

ThreadLocalMap m = getMap(Thread.currentThread());

if (m != null)

m.remove(this);

}

 官方的意思是删除此线程本地的当前线程的值变量。如果这个线程局部变量是随后的当前线程的{@linkplain #get read},其值为通过调用其{@link #initialValue}方法重新初始化,除非当前线程的值是{@linkplain #set set}在此期间。方法的多次调用当前线程中的{@code initialValue}。

来看看ThreadLocalMap的remove方法:

/**

* This class provides thread-local variables. These variables differ from

* their normal counterparts in that each thread that accesses one (via its

* {@code get} or {@code set} method) has its own, independently initialized

* copy of the variable. {@code ThreadLocal} instances are typically private

* static fields in classes that wish to associate state with a thread (e.g.,

* a user ID or Transaction ID).

*

* <p>For example, the class below generates unique identifiers local to each

* thread.

* A thread's id is assigned the first time it invokes {@code ThreadId.get()}

* and remains unchanged on subsequent calls.

* <pre>

* import java.util.concurrent.atomic.AtomicInteger;

*

* public class ThreadId {

* // Atomic integer containing the next thread ID to be assigned

* private static final AtomicInteger nextId = new AtomicInteger(0);

*

* // Thread local variable containing each thread's ID

* private static final ThreadLocal&lt;Integer&gt; threadId =

* new ThreadLocal&lt;Integer&gt;() {

* &#64;Override protected Integer initialValue() {

* return nextId.getAndIncrement();

* }

* };

*

* // Returns the current thread's unique ID, assigning it if necessary

* public static int get() {

* return threadId.get();

* }

* }

* </pre>

* <p>Each thread holds an implicit reference to its copy of a thread-local

* variable as long as the thread is alive and the {@code ThreadLocal}

* instance is accessible; after a thread goes away, all of its copies of

* thread-local instances are subject to garbage collection (unless other

* references to these copies exist).

*

* @author Josh Bloch and Doug Lea

* @since 1.2

*/

public class ThreadLocal<T> {


/**

* Removes the current thread's value for this thread-local

* variable. If this thread-local variable is subsequently

* {@linkplain #get read} by the current thread, its value will be

* reinitialized by invoking its {@link #initialValue} method,

* unless its value is {@linkplain #set set} by the current thread

* in the interim. This may result in multiple invocations of the

* {@code initialValue} method in the current thread.

*

* @since 1.5

*/

public void remove() {

ThreadLocalMap m = getMap(Thread.currentThread());

if (m != null)

m.remove(this);

}


/**

* Get the map associated with a ThreadLocal. Overridden in

* InheritableThreadLocal.

*

* @param t the current thread

* @return the map

*/

ThreadLocalMap getMap(Thread t) {

return t.threadLocals;

}


static class ThreadLocalMap {


/**

* Remove the entry for key.

*/

private void remove(ThreadLocal<?> key) {

Entry[] tab = table;

int len = tab.length;

int i = key.threadLocalHashCode & (len-1);

for (Entry e = tab[i];

e != null;

e = tab[i = nextIndex(i, len)]) {

if (e.get() == key) {

e.clear();

expungeStaleEntry(i);

return;

}
}
}
}
}

官网的解释是:ThreadLocal 这个类提供线程局部变量。这些变量与它们的普通对应变量的不同之处在于,每个访问一个变量的线程(通过其{@code get}或{@code set}方法)都有自己的、独立初始化的变量副本。实例通常是类中希望将状态与线程(例如,用户ID或事务ID)关联的私有静态字段。

remove方法 删除此线程局部变量的当前线程值。如果这个线程局部变量随后被当前线程{@linkplain #get read}所读取,它的值将通过调用它的{@link #initialValue}方法重新初始化,除非它的值在过渡期间被当前线程为{@linkplain #set set}。这可能导致在当前线程中多次调用{@code initialValue}方法。

ThreadLocal是一个线程内部的数据存储类,通过它可以在当前线程存储数据,其内部维护了一个ThreadLocalMap,其内部Entity继承WeakReference,key是当前threadLocal对象。

使用场景:

当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MyBatis:使用MyBatis执行SQL多出LIMIT ?的问题,MyBatis分页查询SQL自动执行SELECT count(0)的Bug,PageHelper.clearPage()清理缓存 的相关文章

  • C++枚举解读(enum)

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 枚举是什么 xff1f 二 使用步骤1 作用域2 隐式类型转换3 显式指定枚举值类型4 指定枚举值的值4 整形显式转换成
  • RK3399实际编码能力

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 RK3399简单介绍二 开始测试1 测试结果 总结 前言 最近在做一个项目 xff0c 需要用到RK3399的硬解码和硬
  • workbox学习笔记

    workbox学习笔记 一 PWA介绍 1 1 学习workbox之前先了解一下PWA xff08 如果了解请跳过 xff09 PWA xff08 全称 xff1a Progressive Web App xff09 也就是说这是个渐进式的
  • Python 学习笔记 (1)输出语句

    题主是大一学生 xff0c 刚刚开始学习python xff0c 但是题主有一定的c语言基础 xff0c 在这里以两者对比的形式做一些学习笔记 这里准备把输出语句单独拿出来写一篇文章 xff0c 因为笔者觉得python 的输出语句语法很繁
  • Error: L6218E: Undefined symbol XXXX (referred from main.o)

    学习keil5 问题记录 报错Error L6218E Undefined symbol XXXX referred from main o 是因为没有在User里添加需要的 c文件 在此处添加写好的文件 C 右击User点击Add Exu
  • Debian/Ubuntu 系统环境配置

    目录 一 Debian下使用Vi方向键变字母的解决办法二 Debian打开locales中文编码支持三 Debian 安装中文输入法四 Debian 超强vim配置文件简易安装方法 xff1a 自己手动安装 xff1a 其它VIM配置参考链
  • Ubuntu 20.04 安装配置 及 ZYNQMP开发环境搭建

    Ubuntu 20 04 安装配置 及 ZYNQMP开发环境搭建 一 磁盘文件选单个文件二 安装界面显示不全三 安装类型四 VMware tools安装失败五 更换软件源五 安装开发环境六 开机自动挂载硬盘七 Xilinx Vitis安装1
  • POSTGRESQL 插入数据时主键冲突异常

    异常 xff1a 表INSERT不了数据 postgres 61 insert into t rows name values 39 b 39 ERROR duplicate key value violates unique constr
  • C语言的变长参数 va_arg

    void simple va fun int i va list arg ptr char s 61 NULL va start arg ptr i s 61 va arg arg ptr char va end arg ptr print
  • 通俗讲解 同步、异步、阻塞、非阻塞 编程

    真正意义上的 异步IO 是说内核直接将数据拷贝至用户态的内存单元 xff0c 再通知程序直接去读取数据 select poll epoll 都是同步IO的多路复用模式 1 同步和异步 同步和异步关注的是消息通信机制 所谓同步 xff0c 就
  • Nginx 提示 504 Gateway Time-out(The gateway did not receive a timely response from the...)解决办法

    本文介绍nginx出现504 Gateway Time out问题的原因 xff0c 分析问题并提供解决方法 1 问题分析 nginx访问出现504 Gateway Time out xff0c 一般是由于程序执行时间过长导致响应超时 xf
  • MySQL8 设置远程访问授权

    开启 MySQL 的远程登陆帐号有三大步 xff1a 1 确定服务器上的防火墙没有阻止 3306 端口 MySQL 默认的端口是 3306 xff0c 需要确定防火墙没有阻止 3306 端口 xff0c 否则远程是无法通过 3306 端口连
  • 三次握手,四次挥手,为什么是三次握手四次挥手

    三次握手 两次握手 xff08 情况1 xff09 两次握手 xff08 情况2 xff09 OK xff0c 下面正经地来回答下这个问题 xff0c 要搞清楚这个问题 xff0c 首先得了解TCP究竟是如何保证可靠传输的 PS xff1a
  • VirtualBox 磁盘扩容(亲测有效)

    参考 xff1a VirtualBox和VMware虚拟机centos dev mapper centos root 磁盘扩容 亲测有效 蜡笔小新儿的博客 CSDN博客 virtualbox虚拟机磁盘扩容 虚拟机磁盘扩容一 VirtualB
  • 完美解决 Could not find a version that satisfies the requirement 安装包名字 (from versions: )

    大家在刚开始使用python 时会遇到缺少python 库的问题 xff0c 提示 No module named 安装包名字 问题 在解决安装包问题中在网上找了很多的方法 xff0c 方法很多各种各样 xff0c 对一部分人有用 xff0
  • Go语言实现对称加密算法AES、DES、3DES和非对称加密算法RSA

    1 对称加密算法 1 1 特点 加密和解密使用的是同一个密钥 数据私密性双向保证 也就是加密和解密都不能泄露密码 1 2 优缺点 优点 加密效率高 适合大些的数据加密 缺点 安全性相对非对称低 1 3 go语言实现对称加密算法 1 3 1
  • Ubuntu 上安装 MozJpeg 详解

    参考 xff1a How to Install MozJpeg on Ubuntu 18 04 3 CodeFAQ 2023 04 26 花了很多时间 xff0c 绕了很多弯路才成功安装 mozjpeg 图片压缩命令 xff1b 特记录一下
  • ElasticSearch + Grafana 实现日志监控告警

    配置步骤 点击左边栏 x1f514 进入告警管理中心 xff1a Alert rules xff1a 告警规则管理 Contact points xff1a 告警联系人管理 Notification policies xff1a 告警通知策
  • sprintf函数

    sprintf指的是字符串格式化命令 头文件 xff1a include lt stdio h gt 功能 xff1a 把格式化的数据读入某个字符串中 xff08 最终结果是字符串类型 xff09 格式 xff1a char str 100
  • android 127.0.0.1/localhost connection refused 问题的

    下载 php java javascript 相关 api 手册的下载 调试中通过android simulator模拟器链接localhost或者127 0 0 1 xff0c 因为我在电脑上面建立了apache xff0c 我的代码大概

随机推荐