当前位置: 首页 > article >正文

Mybatis 分页查询的三种实现

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.kler.cn/a/154446.html

相关文章:

  • Flutter:Dio下载文件到本地
  • 【项目开发】理解SSL延迟:为何HTTPS比HTTP慢?
  • Excel超级处理器:高效实现2种批量生成二维码方式
  • Linux从0——1之shell编程4
  • 要查看你的系统是 x64(64位)还是 x86(32位),可以按照以下步骤操作
  • 阮一峰科技爱好者周刊(第 325 期)推荐工具:一个基于 Next.js 的博客和 CMS 系统
  • css取消移动端长按元素背景色
  • OCR原理解析
  • Redis面试常见问题
  • Python基础学习快速入门
  • ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO
  • SpringBoot拦截器、过滤器、自定义注解、监听器、全局异常-使用详解
  • Vue3中定义变量是选择ref还是reactive?
  • 使用KubeSphere练习故障注入
  • SELinux refpolicy详解(4)
  • Layer Normalization是什么
  • Oauth2.0 学习
  • 180天Java从小白到就业-Day03-03Java位运算符、赋值运算符、数据交换的三种方式
  • P1 什么是链表 C语言简单易懂
  • Sql Server数据库跨机器完整恢复(源文件恢复)
  • QPrinter 是 Qt 框架中的一个类,用于与打印机进行交互,并提供打印功能
  • Linux 进程(三)
  • 每日一练:冒泡排序
  • 7、Jenkins+Nexus3+Docker+K8s实现CICD
  • 最小生成树算法
  • C++的一些基础