Mybatis - 应用篇 (二、进阶应用)

发布于:2024-08-11 ⋅ 阅读:(150) ⋅ 点赞:(0)

一、MyBatis实现分页

        1、关于逻辑分类和物理分页

        分页可分为逻辑分页物理分页

        逻辑分页:是一次性把全部数据查询加载进内存 ,然后再进行分页。这样优点是减少IO次数,适合频繁访问、数据量少的情况。缺点是不适合大数据量,容易造成内存溢出。

        物理分页:是利用limit语法在数据库中进行分页。他的优点是适合分页大数据量数据。缺点是频繁查询数据库,消耗性能。

        

        2、Mybatis实现分页功能

        mybatis实现分页有三种方式:

  1. 直接使用SQL语句,利用limit关键字分页(物理分页)
  2. RowBounds(逻辑分页)
  3. 第三方插件PageHelper(物理分页)

        2.1  SQL语句实现分页

limit语法 limit [offset,] rows

offset 偏移量,从第条数据开始,默认为0

rows 行数,要拿多少条数据

         mapper接口中:意思就是 从第几行开始拿多少条数据。

    @Select("select count(*) from emp   ")
    int countEmp();

    @Select("select * from emp  limit #{offset},#{rows} ")
    List<Emp> pageEmp(@Param("offset") int offset,@Param("rows") int rows);

         如果我们要实现分页逻辑,一般前端会传 pageNum(第几页) , pageSize(多少条数据)。我们需要自己计算分页逻辑。
        比如我们接受到的 pageNum = 3 ,pageSzie = 5:

        int pageNum = 3;  //查第3页
        int pageSize = 5;  //每页显示5条数据

        int count = empMapper.countEmp();  //总条数
        //计算共有多少页 +1是因为如果相除有余数 就等于多了一页,这里粗略计算为多一页
        int pagesNum = (count / pageSize) + 1;
        // 这页的偏移量offset是多少 , 也就是从第几条开始
        int offset = (pageNum - 1) * pageSize;  //偏移量

        List<Emp> roles = empMapper.pageEmp(offset,pagesNum);
        System.out.println(roles);
        //最后把分页数据封装到Page类中返回,这里不做演示

        这种方式缺点是比较麻烦,要自己计算分页数据和封装Page类。优点是自定义性强。

        2.2  RowBounds实现分页

        原理:通过RowBounds实现分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。只是数组分页需要我们自己去实现分页逻辑,这里更加简化而已。
        存在问题:一次性从数据库获取的数据可能会很多,对内存的消耗很大,可能导师性能变差,甚至引发内存溢出。
        适用场景:在数据量很大的情况下,建议还是适用拦截器实现分页效果。RowBounds建议在数据量相对较小的情况下使用。

 

       RowBounds分页是Mybatis提供的一种分页方式,其原理主要是在执行SQL查询后,将返回的所有结果集加载到内存中,然后在内存中根据指定的偏移量(offset)和限制数(limit)进行分页处理。
        具体来说,当我们在Mybatis的Mapper接口中调用查询方法时,可以传入一个RowBounds对象作为参数。这个RowBounds对象包含了分页所需的信息,比如当前页码、每页显示的记录数等。在执行查询时,Mybatis会首先执行完整的SQL查询语句,获取到所有满足条件的结果集。然后,Mybatis会根据RowBounds对象中指定的偏移量和限制数,在内存中对这些结果集进行截取,从而得到当前页需要展示的数据。
        需要注意的是,RowBounds分页方式是一种逻辑分页,即在内存中进行分页处理。当数据量非常大时,这种方式可能会导致内存溢出的问题。因此,对于大数据量的分页需求,建议使用物理分页方式,即在SQL查询语句中添加LIMIT和OFFSET子句,直接在数据库层面进行分页处理。
        

        

        在mapper接口中:

    @Select("select count(*) from emp")
    int countEmp();

    @Select("select * from emp")
    List<Emp> pageEmp(RowBounds rowBounds);

  

        int pageNum = 1;  //查第几页
        int pageSize = 3;  //每页几条数据

        //总条数
        int count = empMapper.countEmp();  
        //计算出:共有多少页、这页的偏移量offset是多少
        int pagesNum = (count / pageSize) + 1;  //共有多少页
        int offset = (pageNum - 1) * pageSize;  //偏移量

        RowBounds rowBounds = new RowBounds(offset,pageSize);
        List<Emp> emps = empMapper.pageEmp(rowBounds);
        System.out.println(emps);
        //最后把分页数据封装到Page类中返回,这里不做演示

        

        2.3 第三方插件PageHelper

        PageHelper的分页原理:主要基于MyBatis的插件机制。具体来说,PageHelper内部实现了一个PageInterceptor拦截器,这个拦截器会在MyBatis执行SQL查询之前进行拦截。
        当我们在代码中调用PageHelper的startPage方法时,它会在当前线程上下文中设置一个ThreadLocal变量,用于保存分页的参数,如当前页码、每页显示的数量等。
        随后,当MyBatis执行SQL查询时,PageInterceptor拦截器会拦截到这一操作。拦截器会从ThreadLocal中获取到分页参数,并根据这些参数来改写原始的SQL语句,添加LIMIT和OFFSET子句,以实现分页查询。
        改写后的SQL语句会被发送到数据库执行,数据库返回的结果集就是根据分页参数查询得到的结果。
        最后,PageInterceptor拦截器会将ThreadLocal中的分页参数清除,避免对后续操作产生影响。
        通过这种方式,PageHelper实现了对MyBatis查询结果的分页处理,而无需修改原有的SQL语句、Mapper接口和XML文件,因此具有无侵入性和易用性。同时,由于分页操作是在数据库层面进行的,因此也具有较高的性能。
        需要注意的是,PageHelper使用了ThreadLocal来保存分页参数,因此分页参数是与线程绑定的,这意味着不同的线程之间不会共享分页参数,从而保证了分页的准确性和独立性。
        总的来说,PageHelper通过拦截MyBatis的SQL查询操作,并在查询语句中添加LIMIT和OFFSET子句,实现了对查询结果的分页处理,从而简化了分页操作的实现过程,提高了开发效率。
PageHelper 是一个 MyBatis 的分页插件,可以简化分页操作。

         导入第三方依赖包:

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
        </dependency>

        代码如下:

        PageHelper.startPage(1, 3);
        PageInfo<Emp> pageInfo = new PageInfo<>(empMapper.allEmp());
        System.out.println(pageInfo.getNextPage());  //下一页
        System.out.println(pageInfo.getPrePage());  //上一页
        System.out.println(pageInfo.getPageNum());  //当前页
        System.out.println(pageInfo.getPageSize());  //每页多少条
        System.out.println(pageInfo.getSize());  //当前页的条数
        System.out.println(pageInfo.getTotal());  //总条数
        System.out.println(pageInfo.getPages());  //总页数
        System.out.println(pageInfo.getList());

        直接执行上面代码发现不行,还需要手动加上一个拦截器:

package com.test.demo.config;


import com.github.pagehelper.PageInterceptor;

import org.apache.ibatis.plugin.Interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PageHelperConfigure {

    @Bean
    public Interceptor[] plugins() {
        return new Interceptor[]{new PageInterceptor()};
    }
}

 

  如果分页失效可以参考下面的文章:
SpringBoot+MyBatis使用pagehelper分页插件及其注意事项(含解决分页不生效问题)_springboot pagehelper查询生效页面不显示-CSDN博客


网站公告

今日签到

点亮在社区的每一天
去签到