Mybatis分页插件与limit分页

1.mybatis自带的分页RowBounds;

Mybatis提供了一个简单的逻辑分页使用类RowBounds(物理分页当然就是我们在sql语句中指定limit和offset值),在DefaultSqlSession提供的某些查询接口中我们可以看到RowBounds是作为参数用来进行分页的,如下接口:

 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)

RowBounds源码如下:


public class RowBounds {
 
  /* 默认offset是0**/
  public static final int NO_ROW_OFFSET = 0;
  
  /* 默认Limit是int的最大值,因此它使用的是逻辑分页**/
  public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
  public static final RowBounds DEFAULT = new RowBounds();
 
  private int offset;
  private int limit;
 
  public RowBounds() {
    this.offset = NO_ROW_OFFSET;
    this.limit = NO_ROW_LIMIT;
  }
 
  public RowBounds(int offset, int limit) {
    this.offset = offset;
    this.limit = limit;
  }
 
  public int getOffset() {
    return offset;
  }
 
  public int getLimit() {
    return limit;
  }
 
}

逻辑分页的实现原理:

在DefaultResultSetHandler中,逻辑分页会将所有的结果都查询到,然后根据RowBounds中提供的offset和limit值来获取最后的结果,DefaultResultSetHandler实现如下:

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
	//跳过RowBounds设置的offset值
    skipRows(rsw.getResultSet(), rowBounds);
	//判断数据是否小于limit,如果小于limit的话就不断的循环取值
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }
private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException {
	//判断数据是否小于limit,小于返回true
    return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
  }
  //跳过不需要的行,应该就是rowbounds设置的limit和offset
  private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
    if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
      if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
        rs.absolute(rowBounds.getOffset());
      }
    } else {
	  //跳过RowBounds中设置的offset条数据
      for (int i = 0; i < rowBounds.getOffset(); i++) {
        rs.next();
      }
    }
  }

总结:Mybatis的逻辑分页比较简单,简单来说就是取出所有满足条件的数据,然后舍弃掉前面offset条数据,然后再取剩下的数据的limit条

2.mybatis插件或者直接书写sql进行分页

          (1).通过自己的封装SQL根据beginNum(开始条数)和endNum(需要的条数)来进行分页

           (2).PageHelper分页插件

分页插件的原理就是实现mybatis的拦截器,实现里面的方法:

--> mybatis自带分页RowBounds:   //逻辑分页

      Java:   

        RowBounds rb=new RowBounds(offset, limit);  //offset(从多少条开始);limit(获取多少条)

       SqlSession sqlSession=sqlSessionFactory.openSession();//sqlSessionFactory通过读取mybatis配置文件的输入流然后通过new SqlSeesionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));最终得到SqlSessionFactory;

         List<Student> studentlist=sqlSession.selectList("xx.xx.Mapper.findStudent",null,rb);//第一个参数为具体Mapper文件的下的findStudent的ID,第二个参数为提供的条件参数,第三个参数为我们要进行对获取学生进行分页

         sqlSession.close();

         return studentlist;

      Mapper:   

        <select id="findStudent" resultType="Student">

扫描二维码关注公众号,回复: 2962031 查看本文章

            select * from Student

         </select>

      备注:通过以上例子,很明显的看出,在分页的时候,我们是把所有的数据都查询出来,然后通过RowBounds进行在内存分页.通过源码查看,也是通过ResuleSet结果集进行分页;

    --> mybatis自写sql或者通过分页插件PageHelper:   //物理分页

        (1).mybatis PageHelper分页插件 

        Mapper:

            <select id="findStudent" resultType="Student">

                select * from Student

           </select>

        Dao层-StudentDao:

            List<Student> findStudent();

        Service层:

             PageHelper.startPage(pageNum,pageSize);//pageNum 页数  pageSize 数量

           List<Student> stu=studentDao.findStudent();  //studentDao @Autowried注解获取; 在执行查询数据时,就会自动执行2个sql;执行上述Mapper下的ID为findStudent的sql 自动执行分页,通过PageHelper进行识别是何数据库拼接分页语句,若是mysql,自动通过limit分页,若是oracle自动通过rownum进行分页,另一个会自动拼接Mapper下不存在的ID为findStudent_COUNT,查询的总数;可以通过打印的日志进行跟踪;

           PageInfo<Student> page = new PageInfo<Student>(stu); //自动封装总数count以及分页,数据返回页面

           return page;//返回分页之后的数据

      (2).mybatis 自行使用SQL进行分页

            例:     

           SQL代码(mysql数据库):

               A:select * from Student  LIMIT #{beginNum,jdbcType=INTEGER},#{endNum,jdbcType=INTEGER}    //beginNum开始条数;endNum需要的条数

            

                B:select count(0) from Student

           JAVA:

                Map<String,Object> map=new HashMap<String,Object>();

                 map.put("beginNum",beginNum); 

                map.put("endNum",endNum);  //从第beginNum条开始,读取后面endNum条

                List<Student> studentlist=studentDao.findStudent(Map<String,Object> map);  //studentDao学生dao层

                 Integer count=studentDao.count();//获取总数返回页面

                 //需要手动进行封装总数以及分页信息,数据返回页面;       

        备注:查看如上例子代码,我们就发现了是直接通过SQL进行在数据库中直接分页,得到的数据就是我们想要分页之后的数据,就是物理分页;

下面有个实例是在我用插件是遇到的:

使用分页插件在查询的时候会分成两个sql语句来执行的,一个是查询出总共有多少条数据,在查出对应的页码数据。

如下面的sql语句在执行的时候会分出两个sql:

 SELECT iiid,title,detail,makeDate,paperMediaSource,infoUrl,keyWord,(SELECT imgUrl 
 FROM INFO_IMG img WHERE img.iiid=item.iiid) AS imgUrl 
   from info_item item WHERE item.makeDate<=#{expireDate} AND ChanNum='010' 
  AND InfoCls='001179' ORDER BY item.makeDate DESC

分成:

1、SELECT count(*) FROM info_item item WHERE item.makeDate <= ? AND ChanNum = '010' AND InfoCls = '001179'

2、SELECT TOP 20 iiid, title, detail, makeDate, paperMediaSource, infoUrl, keyWord, imgUrl
 FROM (SELECT ROW_NUMBER() OVER ( ORDER BY item.makeDate DESC) PAGE_ROW_NUMBER, iiid, title, detail, 
 makeDate, paperMediaSource, infoUrl, keyWord, (SELECT imgUrl FROM INFO_IMG img WHERE img.iiid = item.iiid)
 AS imgUrl FROM info_item item WHERE item.makeDate <= '2018-08-30 23:59:59' AND ChanNum = '010' AND InfoCls = '001179')
 AS PAGE_TABLE_ALIAS WHERE PAGE_ROW_NUMBER > 0 ORDER BY PAGE_ROW_NUMBER 

    总结:

    1:逻辑分页 内存开销比较大,在数据量比较小的情况下效率比物理分页高;在数据量很大的情况下,内存开销过大,容易内存溢出,不建议使用

    2:物理分页 内存开销比较小,在数据量比较小的情况下效率比逻辑分页还是低,在数据量很大的情况下,建议使用物理分页

猜你喜欢

转载自blog.csdn.net/wojiao228925661/article/details/82051654