• 前言

在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页。

前端分页

一次性请求数据表格中的所有记录(ajax),然后在前端缓存并且计算count和分页逻辑,一般前端组件(例如dataTable)会提供分页动作。

特点是:简单,很适合小规模的web平台;当数据量大的时候会产生性能问题,在查询和网络传输的时间会很长。

后端分页

在ajax请求中指定页码(pageNum)和每页的大小(pageSize),后端查询出当页的数据返回,前端只负责渲染。

特点是:复杂一些;性能瓶颈在MySQL的查询性能,这个当然可以调优解决。一般来说,web开发使用的是这种方式。

我们说的也是后端分页。

  • MySQL对分页的支持

简单来说MySQL对分页的支持是通过limit子句。请看下面的例子。

  1. limit关键字的用法是
  2. LIMIT [offset,] rows
  3. offset是相对于首行的偏移量(首行是0),rows是返回条数。
  4.  
  5. # 每页10条记录,取第一页,返回的是前10条记录
  6. select * from tableA limit ,;
  7. # 每页10条记录,取第二页,返回的是第11条记录,到第20条记录,
  8. select * from tableA limit ,;

这里提一嘴的是,MySQL在处理分页的时候是这样的:

limit 1000,10 - 过滤出1010条数据,然后丢弃前1000条,保留10条。当偏移量大的时候,性能会有所下降。

limit 100000,10 - 会过滤10w+10条数据,然后丢弃前10w条。如果在分页中发现了性能问题,可以根据这个思路调优。

  • Mybatis分页插件PageHelper

在使用Java Spring开发的时候,Mybatis算是对数据库操作的利器了。不过在处理分页的时候,Mybatis并没有什么特别的方法,一般需要自己去写limit子句实现,成本较高。好在有个PageHelper插件。

1、POM依赖

Mybatis的配置就不多提了。PageHelper的依赖如下。需要新的版本可以去maven上自行选择

  1. <dependency>
  2. <groupId>com.github.pagehelper</groupId>
  3. <artifactId>pagehelper</artifactId>
  4. <version>4.1.4</version>
  5. </dependency>

2、Mybatis对PageHelper的配置

打开Mybatis配置文件,一般在Resource路径下。我这里叫mybatis-config.xml。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2.  
  3. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
  4.  
  5. <configuration>
  6. <!-- 全局参数 -->
  7. <settings>
  8. <!-- 使全局的映射器启用或禁用缓存。 -->
  9. <setting name="cacheEnabled" value="true"/>
  10. <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
  11. <setting name="lazyLoadingEnabled" value="true"/>
  12. <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->
  13. <setting name="aggressiveLazyLoading" value="true"/>
  14. <!-- 是否允许单条sql 返回多个数据集 (取决于驱动的兼容性) default:true -->
  15. <setting name="multipleResultSetsEnabled" value="true"/>
  16. <!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
  17. <setting name="useColumnLabel" value="true"/>
  18. <!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 default:false -->
  19. <setting name="useGeneratedKeys" value="true"/>
  20. <!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分 FULL:全部 -->
  21. <setting name="autoMappingBehavior" value="PARTIAL"/>
  22. <!-- 这是默认的执行类型 (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新) -->
  23. <setting name="defaultExecutorType" value="SIMPLE"/>
  24. <!-- 使用驼峰命名法转换字段。 -->
  25. <setting name="mapUnderscoreToCamelCase" value="true"/>
  26. <!-- 设置本地缓存范围 session:就会有数据的共享 statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
  27. <setting name="localCacheScope" value="SESSION"/>
  28. <!-- 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型 -->
  29. <setting name="jdbcTypeForNull" value="NULL"/>
  30. </settings>
  31.  
  32. <plugins>
  33. <plugin interceptor="com.github.pagehelper.PageHelper">
  34. <property name="dialect" value="mysql"/>
  35. <property name="offsetAsPageNum" value="false"/>
  36. <property name="rowBoundsWithCount" value="false"/>
  37. <property name="pageSizeZero" value="true"/>
  38. <property name="reasonable" value="false"/>
  39. <property name="supportMethodsArguments" value="false"/>
  40. <property name="returnPageInfo" value="none"/>
  41. </plugin>
  42. </plugins>
  43. </configuration>  

这里要注意的是PageHelper相关的配置。

如果你没有加载Mybatis配置文件,那么使用的是Mybatis默认的配置。如何加载Mybatis配置文件呢?

到你的dataSrouce配置中。

在配置sqlSessionFactory的时候,指定Mybatis核心配置文件和mapper的路径,代码如下

  1. @Bean(name = "moonlightSqlSessionFactory")
  2. @Primary
  3. public SqlSessionFactory moonlightSqlSessionFactory(@Qualifier("moonlightData") DataSource dataSource) throws Exception {
  4. SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  5. bean.setDataSource(dataSource);
  6. bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis-mapper/*.xml"));
  7. bean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
  8. return bean.getObject();
  9. } 

说明:

这里配置的mapper.xml存放路径,在Resource/mybatis-mapper文件夹下

这里配置的mybatis-config.xml文件,在Resource/下

3、分页

准备一个mapper.xml,测试就随便写一个吧,干脆就用工程里的一个。

这里这个查询,是一个典型的多条件查询,我们要做的是对多条件匹配到的记录进行分页。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.kangaroo.studio.moonlight.dao.mapper.MoonlightMapper">
  4. <resultMap id="geoFenceList" type="com.kangaroo.studio.moonlight.dao.model.GeoFence">
  5. <constructor>
  6. <idArg column="id" javaType="java.lang.Integer" jdbcType="INTEGER" />
  7. <arg column="name" javaType="java.lang.String" jdbcType="VARCHAR" />
  8. <arg column="type" javaType="java.lang.Integer" jdbcType="INTEGER" />
  9. <arg column="group" javaType="java.lang.String" jdbcType="VARCHAR" />
  10. <arg column="geo" javaType="java.lang.String" jdbcType="VARCHAR" />
  11. <arg column="createTime" javaType="java.lang.String" jdbcType="VARCHAR" />
  12. <arg column="updateTime" javaType="java.lang.String" jdbcType="VARCHAR" />
  13. </constructor>
  14. </resultMap>
  15.  
  16. <sql id="base_column">id, name, type, `group`, geo, createTime, updateTime </sql>
  17.  
  18. <select id="queryGeoFence" parameterType="com.kangaroo.studio.moonlight.dao.model.GeoFenceQueryParam" resultMap="geoFenceList">
  19. select <include refid="base_column"/> from geoFence where 1=1
  20. <if test="type != null">
  21. and type = #{type}
  22. </if>
  23. <if test="name != null">
  24. and name like concat('%', #{name},'%')
  25. </if>
  26. <if test="group != null">
  27. and `group` like concat('%', #{group},'%')
  28. </if>
  29. <if test="startTime != null">
  30. and createTime >= #{startTime}
  31. </if>
  32. <if test="endTime != null">
  33. and createTime <= #{endTime}
  34. </if>
  35. </select>
  36. </mapper>

  

在Mapper.java接口中编写对应的方法

  1. List<GeoFence> queryGeoFence(GeoFenceQueryParam geoFenceQueryParam);

  

先上分页代码,后面再说明

  1. @RequestMapping(value = "/fence/query", method = RequestMethod.POST)
  2. @ResponseBody
  3. public ResponseEntity<Response> queryFence(@RequestBody GeoFenceQueryParam geoFenceQueryParam) {
  4. try {
  5. Map<String, Object> data = new HashMap<>();
  6. Integer pageNum = geoFenceQueryParam.getPageNum()!=null?geoFenceQueryParam.getPageNum():1;
  7. Integer pageSize = geoFenceQueryParam.getPageSize()!=null?geoFenceQueryParam.getPageSize():10;
  8. Page page = PageHelper.startPage(pageNum, pageSize, true);
  9. List<GeoFence> list = moonlightMapper.queryGeoFence(geoFenceQueryParam);
  10. data.put("total", page.getTotal());
  11. data.put("nowPage", pageNum);
  12. data.put("data", list);
  13. return new ResponseEntity<>(
  14. new Response(ResultCode.SUCCESS, "查询geoFence成功", data),
  15. HttpStatus.OK);
  16. } catch (Exception e) {
  17. logger.error("查询geoFence失败", e);
  18. return new ResponseEntity<>(
  19. new Response(ResultCode.EXCEPTION, "查询geoFence失败", null),
  20. HttpStatus.INTERNAL_SERVER_ERROR);
  21. }
  22. }

说明:

1、PageHelper的优点是,分页和Mapper.xml完全解耦。实现方式是以插件的形式,对Mybatis执行的流程进行了强化,添加了总数count和limit查询。属于物理分页。

2、Page page = PageHelper.startPage(pageNum, pageSize, true); - true表示需要统计总数,这样会多进行一次请求select count(0); 省略掉true参数只返回分页数据。

1)统计总数,(将SQL语句变为 select count(0) from xxx,只对简单SQL语句其效果,复杂SQL语句需要自己写)

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

long count = page.getTotal();

2)分页,pageNum - 第N页, pageSize - 每页M条数

A、只分页不统计(每次只执行分页语句)

PageHelper.startPage([pageNum],[pageSize]);

List<?> pagelist = queryForList( xxx.class, "queryAll" , param);

//pagelist就是分页之后的结果

B、分页并统计(每次执行2条语句,一条select count语句,一条分页语句)适用于查询分页时数据发生变动,需要将实时的变动信息反映到分页结果上

Page<?> page = PageHelper.startPage([pageNum],[pageSize],[iscount]);

List<?> pagelist = queryForList( xxx.class , "queryAll" , param);

long count = page.getTotal();

//也可以 List<?> pagelist = page.getList();  获取分页后的结果集

3)使用PageHelper查全部(不分页)

PageHelper.startPage(1,0);

List<?> alllist = queryForList( xxx.class , "queryAll" , param);

4)PageHelper的其他API

String orderBy = PageHelper.getOrderBy();    //获取orderBy语句

Page<?> page = PageHelper.startPage(Object params);

Page<?> page = PageHelper.startPage(int pageNum, int pageSize);

Page<?> page = PageHelper.startPage(int pageNum, int pageSize, boolean isCount);

Page<?> page = PageHelper.startPage(pageNum, pageSize, orderBy);

Page<?> page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable);    //isReasonable分页合理化,null时用默认配置

Page<?> page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable, isPageSizeZero);    //isPageSizeZero是否支持PageSize为0,true且pageSize=0时返回全部结果,false时分页,null时用默认配置

5)、默认值

//RowBounds参数offset作为PageNum使用 - 默认不使用

private boolean offsetAsPageNum = false;

//RowBounds是否进行count查询 - 默认不查询

private boolean rowBoundsWithCount = false;

//当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果

private boolean pageSizeZero = false;

//分页合理化

private boolean reasonable = false;

//是否支持接口参数来传递分页参数,默认false

private boolean supportMethodsArguments = false;

3、有一个安全性问题,需要注意一下,不然可能导致分页错乱。我这里直接粘贴了这篇博客里的一段话。

  1. 4. 什么时候会导致不安全的分页?
  2.  
  3. PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。
  4.  
  5. 只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper finally 代码段中自动清除了 ThreadLocal 存储的对象。
  6.  
  7. 如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。
  8.  
  9. 但是如果你写出下面这样的代码,就是不安全的用法:
  10.  
  11. PageHelper.startPage(1, 10);
  12. List<Country> list;
  13. if(param1 != null){
  14. list = countryMapper.selectIf(param1);
  15. } else {
  16. list = new ArrayList<Country>();
  17. }
  18. 这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
  19.  
  20. 上面这个代码,应该写成下面这个样子:
  21.  
  22. List<Country> list;
  23. if(param1 != null){
  24. PageHelper.startPage(1, 10);
  25. list = countryMapper.selectIf(param1);
  26. } else {
  27. list = new ArrayList<Country>();
  28. }
  29. 这种写法就能保证安全。
  30.  
  31. 如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:
  32.  
  33. List<Country> list;
  34. if(param1 != null){
  35. PageHelper.startPage(1, 10);
  36. try{
  37. list = countryMapper.selectAll();
  38. } finally {
  39. PageHelper.clearPage();
  40. }
  41. } else {
  42. list = new ArrayList<Country>();
  43. }
  44. 这么写很不好看,而且没有必要。

  

官方文档,给你参考:

https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md

Mybatis分页插件PageHelper的配置和使用方法的更多相关文章

  1. Mybatis分页插件PageHelper使用

    一. Mybatis分页插件PageHelper使用  1.不使用插件如何分页: 使用mybatis实现: 1)接口: List<Student> selectStudent(Map< ...

  2. Mybatis学习---Mybatis分页插件 - PageHelper

    1. Mybatis分页插件 - PageHelper说明 如果你也在用Mybatis,建议尝试该分页插件,这个一定是最方便使用的分页插件. 该插件目前支持Oracle,Mysql,MariaDB,S ...

  3. Mybatis分页插件PageHelper的实现

    Mybatis分页插件PageHelper的实现 前言 分页这个概念在做web网站的时候很多都会碰到 说它简单吧 其实也简单 小型的网站,完全可以自己写一个,首先查出数据库总条数,然后按照分页大小分为 ...

  4. 基于Mybatis分页插件PageHelper

    基于Mybatis分页插件PageHelper 1.分页插件使用 1.POM依赖 PageHelper的依赖如下.需要新的版本可以去maven上自行选择 <!-- PageHelper 插件分页 ...

  5. Mybatis分页插件-PageHelper的使用

    转载:http://blog.csdn.net/u012728960/article/details/50791343 Mybatis分页插件-PageHelper的使用 怎样配置mybatis这里就 ...

  6. (转)淘淘商城系列——MyBatis分页插件(PageHelper)的使用以及商品列表展示

    http://blog.csdn.net/yerenyuan_pku/article/details/72774381 上文我们实现了展示后台页面的功能,而本文我们实现的主要功能是展示商品列表,大家要 ...

  7. springmvc mybatis 分页插件 pagehelper

    springmvc mybatis 分页插件 pagehelper 下载地址:pagehelper 4.2.1 , jsqlparser 0.9.5 https://github.com/pagehe ...

  8. Java SSM框架之MyBatis3(三)Mybatis分页插件PageHelper

    引言 对于使用Mybatis时,最头痛的就是写分页,需要先写一个查询count的select语句,然后再写一个真正分页查询的语句,当查询条件多了之后,会发现真不想花双倍的时间写count和select ...

  9. MyBatis 分页插件PageHelper 后台报错

    今天遇到一个问题,使用MyBatis 分页插件PageHelper 进行排序分页后,能正常返回正确的结果,但后台却一直在报错 net.sf.jsqlparser.parser.ParseExcepti ...

随机推荐

  1. base64减少图片请求

    1. 使用base64减少 a)            2. 页面解析 CSS 生成的 CSSOM 时间增加 Base64 跟 CSS 混在一起,大大增加了浏览器需要解析CSS树的耗时.其实解析CSS ...

  2. sql的基本知识

    一.什么是sql? 全称:"结构化查询语言(Structured  Query Language)",是1974年由Boyce和Chamberlin提出来的,现已经成为关系数据库的 ...

  3. 利用cookies+requests包登陆微博,使用xpath抓取目标用户的用户信息、微博以及对应评论

    本文目的:介绍如何抓取微博内容,利用requests包+cookies实现登陆微博,lxml包的xpath语法解析网页,抓取目标内容. 所需python包:requests.lxml 皆使用pip安装 ...

  4. c# Invoke和Begininvoke区别

    一.对Invoke和Begininvoke的认识 1.Invoke():同步委托,会阻塞当前主线程的运行,等待invoke()方法返回才执行后面的代码: 2.Begininvoke():异步委托,调用 ...

  5. [板子]segTree

    segTree 参考:http://www.cnblogs.com/TenosDoIt/p/3453089.html#c 初学者建议先参考上面“一步一步理解线段树”学习理论. 在这里Code分别为区间 ...

  6. 前端安全之CSRF攻击

    前端安全之CSRF攻击 转载请注明出处:unclekeith: 前端安全之CSRF攻击 CSRF定义 CSRF,即(Cross-site request forgery), 中文名为跨站请求伪造.是一 ...

  7. awk命令练习

    文件 file.txt的内容格式: 文件中包含名字,电话号码和过去三个月里的捐款 具体内容如下: Mike Harrington:[510] 548-1278:250:100:175 Christia ...

  8. 如何部署Java_web项目到云服务器上

    步骤 1:购买 Linux 实例(略) 步骤2:安装JDK 本节介绍如何安装java jdk. 软件包中包含的软件及版本如下: Tomcat:1.8.0_121 说明:这是写文档时参考的软件版本.您下 ...

  9. Spring+Spring MVC+MyBatis框架集成

    目录 一.新建一个基于Maven的Web项目 二.创建数据库与表 三.添加依赖包 四.新建POJO实体层 五.新建MyBatis SQL映射层 六.JUnit测试数据访问 七.完成Spring整合My ...

  10. CSS和文档

    1. 块级元素: p,div,ul,ol,h1,h2 . . . h6等.块级元素独占一行,旁边不能有其他元素. 2. 行内元素:span,a,strong,em等. display属性可以使块级元素 ...