Mybatis 分页查询的三种实现

news2025/2/28 10:32:28

Mybatis 分页查询

  • 1. 直接在 sql 中使用 limit
  • 2. 使用 RowBounds
  • 3. 使用 Mybatis 提供的拦截器机制
    • 3.1 创建一个自定义拦截器类实现 Interceptor
    • 3.2 创建分页查询函数 与 sql
    • 3.3 编写拦截逻辑
    • 3.4 注册 PageInterceptor 到 Mybatis 拦截器链中
    • 3.5 测试

准备一个分页查询类

@Data
public class Page {
    // 开始下标 由当前页和每页大小计数而来
    // 这里也可以是当前页,在sql中进行开始下标的计算
    private int size;
    private int currentPage;
}

1. 直接在 sql 中使用 limit

创建一个分页查询函数

List<User> selectUserPage(@Param("page")Page page, @Param("name") String name);

编写sql语句

    <select id="selectUserPage" resultType="com.hzy.demo.pojos.User">
        select *
        from user2
        <where>
            <if test="name != null and name != ''">
                name like '%${name}%'
            </if>
        </where>
        <if test="page != null">
            limit #{page.start},#{page.size}
        </if>
    </select>

测试

    @Test
    void test08() {
        int currentPage = 1; // 当前页
        int size = 2; // 每页大小
        Page page = new Page();
        page.setSize(size);
        page.setStart((currentPage-1)*size);
        // 第一页 不匹配姓名
        List<User> users = userMapper.selectUserPage(page, null);
        for (User user : users){
            System.out.println(user);
        }
        // 第一页 匹配姓名
        page.setStart((currentPage-1)*size);
        users = userMapper.selectUserPage(page, "s");
        for (User user : users){
            System.out.println(user);
        }
        // 第三页 不匹配姓名
        currentPage = 3;
        page.setStart((currentPage-1)*size);
        users = userMapper.selectUserPage(page, null);
        for (User user : users){
            System.out.println(user);
        }
    }

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2. 使用 RowBounds

RowBounds 是 MyBatis 中用于分页查询的一种简单实现方式,它并不涉及数据库分页查询,而是在查询结果返回后进行截取。

RowBounds 的构造函数需要传入两个参数:

  • offset:偏移量,相当于前面的 start

  • limit:限制数量,相当于前面的 size

在这里插入图片描述

RowBounds 的实现原理比较简单,它适用于一些简单的分页需求,但在处理大量数据时可能会导致性能问题,因为所有的数据都会被查询出来,然后在内存中进行截取。对于更复杂的分页需求,可以考虑使用 MyBatis 提供的分页插件或其他更高级的分页方案。

创建查询函数

List<User> selectUserPage(RowBounds rowBounds, @Param("name") String name);

编写 sql

    <select id="selectUserPage" resultType="com.hzy.demo.pojos.User">
        select *
        from user2
        <where>
            <if test="name != null and name != ''">
                name like '%${name}%'
            </if>
        </where>
    </select>

测试

    @Test
    void test09() {
        int currentPage = 1;
        int size = 2;
        RowBounds rowBounds = new RowBounds((currentPage - 1) * size, size);
        List<User> users = userMapper.selectUserPage(rowBounds, null);
        for (User user : users) {
            System.out.println(user);
        }
        users = userMapper.selectUserPage(rowBounds, "s");
        for (User user : users) {
            System.out.println(user);
        }
    }

在这里插入图片描述

3. 使用 Mybatis 提供的拦截器机制

MyBatis提供了拦截器(Interceptor)的机制,允许用户在执行SQL语句的过程中进行拦截和干预。拦截器是在执行SQL语句前、后或者代替执行SQL语句的过程中插入自定义的逻辑,从而可以实现一些额外的功能。

一些常见的分页插件比如 PageHelper 也是基于拦截器实现的,这里我们自定义一个拦截器实现分页查询。

3.1 创建一个自定义拦截器类实现 Interceptor

public class PageInterceptor implements Interceptor {
    //  该方法会在真正的SQL语句执行前后被调用,可以在这里编写拦截逻辑。
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return null;
    }

    // 用于包装目标对象,返回一个代理对象,该代理对象会拦截目标对象的方法调用。
    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }
    // 用于设置拦截器的属性,这些属性可以在配置拦截器时传递。
    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}

注意要引入 Mybatis 提供的 Interceptor

在这里插入图片描述

3.2 创建分页查询函数 与 sql

查询函数

List<User> selectUserPage(@Param("page") Page page, @Param("name") String name);

sql

    <select id="selectUserPage" resultType="com.hzy.demo.pojos.User">
        select *
        from user2
        <where>
            <if test="name != null and name != ''">
                name like '%${name}%'
            </if>
        </where>
    </select>

3.3 编写拦截逻辑

public Object intercept(Invocation invocation) throws Throwable {}

Invocation 接口是 MyBatis 中拦截器机制中的一个核心接口,用于描述拦截的方法调用。Invocation 接口定义了以下方法:

  1. Object getTarget()
    • 获取被拦截的目标对象。在 MyBatis 中,通常是获取到某个 StatementHandler、Executor、ParameterHandler 或 ResultSetHandler 等对象。
  2. Method getMethod()
    • 获取被拦截的目标方法。
  3. Object[] getArgs()
    • 获取被拦截方法的参数。
  4. Object proceed()
    • 调用被拦截方法,相当于执行原始的方法调用。拦截器可以选择是否调用该方法,如果调用,会继续执行原始的方法,如果不调用,可以在拦截器中实现自己的逻辑。

使用 getTarget() 获取目标对象

 StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

StatementHandler 是 MyBatis 中负责处理 SQL 语句的核心接口,它定义了对数据库的操作方法。在 MyBatis 的执行过程中,StatementHandler 负责创建 PreparedStatement 对象、设置参数、执行 SQL 语句等操作。

可以看到 StatementHandler 对象 里面的 boundSql 对象包含了 sql语句 与 方法参数,利用这个就可以实现分页查询。

在这里插入图片描述

拦截逻辑

// @Intercepts 注解配置表明该拦截器会拦截 StatementHandler 接口的 prepare 方法
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class })
})
public class PageInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        Map parameterObject = (Map)boundSql.getParameterObject();
        Page page = (Page) parameterObject.getOrDefault("page",null);
        if (page != null){
            // 获取原始 SQL 语句
            String originalSql = boundSql.getSql();
            // 修改 SQL 语句
            StringBuilder sb = new StringBuilder();
            sb.append(originalSql)
                    .append(" limit ")
                    .append(page.getStart())
                    .append(",")
                    .append(page.getSize());
            // 将修改后的 SQL 设置回 BoundSql
            MetaObject metaObject = SystemMetaObject.forObject(boundSql);
            metaObject.setValue("sql",sb.toString());
        }
        // 继续执行 SQL 语句
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}

3.4 注册 PageInterceptor 到 Mybatis 拦截器链中

@Configuration
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        // 添加自定义拦截器
        Interceptor[] interceptors = new Interceptor[]{new PageInterceptor()};
        sessionFactory.setPlugins(interceptors);
        return sessionFactory.getObject();
    }
}

3.5 测试

    @Test
    void test10() {
        int currentPage = 1;
        int size = 4;
        Page page = new Page();
        page.setSize(size);
        page.setStart((currentPage-1)*size);
        List<User> users = userMapper.selectUserPage(page, null);
        for (User user : users){
            System.out.println(user);
        } 
    }

在这里插入图片描述

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

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

相关文章

算法工程师面试八股(搜广推方向)

文章目录 机器学习线性和逻辑回归模型逻辑回归二分类和多分类的损失函数二分类为什么用交叉熵损失而不用MSE损失&#xff1f;偏差与方差Layer Normalization 和 Batch NormalizationSVM数据不均衡特征选择排序模型树模型进行特征工程的原因GBDTLR和GBDTRF和GBDTXGBoost二阶泰勒…

报错:执行sudo gedit时 No protocol specifiedUnable to init server: 无法连接: 拒绝连接

1.问题描述 在执行sudo gedit编辑文件时&#xff0c;报错连接不上服务&#xff1a; 2.问题解决 2.1先安装Vncserver sudo apt-get update sudo apt-get install tightvncserver2.2执行 vncserver 按提示输入密码&#xff0c;不宜过短 2.3若出现提示warning 则按提示执行&…

打印元素绘制协议Java实现

我一直提倡的面向接口和约定编程&#xff0c;而打印元素绘制协议一直是我推荐的打印实现方式&#xff0c;我以前只是强调按打印元素绘制协议输出数据就行了&#xff0c;有实现程序按协议控制打印&#xff0c;说是可以用任何语言实现客户端程序而不影响打印业务&#xff0c;那么…

C++——初始化列表

初始化列表&#xff1a;一一个冒号开始&#xff0c;接着是一个以逗号分隔的数据成员列表&#xff0c;每个“成员变量”后面跟一个放在括号中的初始值或表达式。 #include <iostream> using namespace std; class Date { public:Date(int year, int month, int day): _ye…

国标GBT 27930关键点梳理

1、充电总流程 整个充电过程包括六个阶段:物理连接完成、低压辅助上电、充电握手阶段、充电参数配置阶段、充电阶段和充电结束阶段。 在各个阶段,充电机和 BMS 如果在规定的时间内没有收到对方报文或没有收到正确报文,即判定为超时(超时指在规定时间内没有收到对方的完整数据包…

Hdoop学习笔记(HDP)-Part.06 安装OracleJDK

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

酷开科技 | 酷开系统,让家庭娱乐方式焕然一新!

在这个快节奏的社会&#xff0c;家庭娱乐已成为我们日常生活中不可或缺的一部分&#xff0c;为了给家庭带来更多欢笑与感动&#xff0c;酷开科技发力研发出拥有丰富内容和技术的智能电视操作系统——酷开系统&#xff0c;它集合了电影、电视剧、综艺、游戏、音乐等海量内容&…

大数据Doris(三十二):Doris高级功能

文章目录 Doris高级功能 一、​​​​​​​表结构变更

基于spring boot电子商务系统

一、 系统总体结构设计 (一) 功能结构图 图1-1 后台管理子系统 图1-2 电子商务子系统功能结构图 (二) 项目结构目录截图&#xff08;例如下图&#xff09; 图 1-3 系统目录图 (三) 系统依赖截图 图 1-2 所有依赖截图 (四) 配置文件 1、 全局配置文件 2、 其他配置文…

链表数组插入排序

InsertSort 插入排序算法&#xff0c;比如打扑克牌的算法时&#xff0c;按照从左到右&#xff0c;找到对应的位置插入排序 最重要的是位置移动 找到对应位置值 #include "iostream" #include "bits/stdc.h"using namespace std;void sort(vector<in…

高级前端面试中的三个 “送命题” !!!

原型与原型链 说到原型&#xff0c;就不得不提一下构造函数&#xff0c;首先我们看下面一个简单的例子&#xff1a; function Dog(name,age){this.name name;this.age age; }let dog1 new Dog("哈士奇",3); let dog2 new Dog("泰迪",2);首先创造空的…

Go GORM简介

GORM&#xff08;Go Object-Relational Mapping&#xff09;是一个用于Go语言的ORM库&#xff0c;它提供了一种简单、优雅的方式来操作数据库。GORM支持多种数据库&#xff0c;包括MySQL、PostgreSQL、SQLite和SQL Server。以下是GORM的一些主要特性 全功能ORM&#xff1a;GORM…

leetCode 47. 全排列 II + 回溯算法 + 图解 + 笔记

给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a; [[1,1,2],[1,2,1],[2,1,1]] 示例 2&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2…

uniapp 微信小程序连接蓝牙卡死

解决方法&#xff0c;需要同意隐私保护协议&#xff0c;否则不能开启蓝牙权限和定位权限&#xff0c;会导致定位失败

.NET8构建统计Extreme Optimization Numerical Libraries

为 .NET 8 构建统计应用程序 Extreme Optimization Numerical Libraries for .NET V8.1.22 添加了对 .NET 8 的支持&#xff0c;使您可以使用最新版本的 Microsoft 平台。 Extreme Optimization Numerical Libraries for .NET 是通用数学和统计类的集合&#xff0c;为技术和统计…

基于 Python+flask 构建态势感知系统(附完整源码)

一、开发 一个基于linux的态势感知系统&#xff0c;基于python和flask框架开发&#xff0c;项目文件目录如下&#xff1a; admin -核心算法 charts -图表生成 model -类 app.py -主文件 config.py -配置文件 install.py -安装文件 二、安装 1、配置 数据库密码默认设…

进程的创建:fork()

引入 创建进程的方式我们已经学习了一个&#xff01;在我们运行指令(或者运行我们自己写的可执行程序)的时候不就是创建了一个进程嘛&#xff1f;那个创建进程的方式称为指令级别的创建子进程&#xff01; 那如果我们想要在代码中创建进程该怎么办呢&#xff1f; fork() for…

SSM框架(五):Maven进阶

文章目录 一、分模块开发1.1 分模块开发的意义1.2 步骤 二、依赖管理2.1 依赖传递2.2 可选依赖和排除依赖 三、继承与聚合3.1 聚合3.2 继承3.3 聚合和继承区别 四、属性4.1 pom文件的依赖使用属性4.2 资源文件使用属性 五、多环境开发六、跳过测试七、私服7.1 下载与使用7.2 私…

禁止谷歌浏览器自动更新

禁止谷歌浏览器自动更新 在使用Python包selenium的时候浏览器版版本发生变化后产生很多问题如&#xff1a; 1、直接版本不对应无法运行 2、版本不一致导致debug启动浏览器超级慢 这里是已谷歌浏览器为代表的。 禁止自动更新的方法如下&#xff1a; 1、WinR调出运行&#x…

一小时玩转【负载均衡】

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…