Mybatis分页插件PageHelper的配置和简单使用方法(推荐)

 更新时间:2017年12月08日 11:49:25   作者:扎心了老铁  
在使用Java Spring开发的时候,Mybatis算是对数据库操作的利器了。这篇文章主要介绍了Mybatis分页插件PageHelper的配置和使用方法,需要的朋友可以参考下

前言

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

前端分页

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

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

后端分页

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

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

我们说的也是后端分页。

MySQL对分页的支持

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

limit关键字的用法是

LIMIT [offset,] rows

offset是相对于首行的偏移量(首行是0),rows是返回条数。

# 每页10条记录,取第一页,返回的是前10条记录
select * from tableA limit 0,10;
# 每页10条记录,取第二页,返回的是第11条记录,到第20条记录,
select * from tableA limit 10,10;

这里提一嘴的是,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上自行选择

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

2、Mybatis对PageHelper的配置

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

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

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

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

到你的dataSrouce配置中。

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

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

 说明:

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

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

3、分页

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

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

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kangaroo.studio.moonlight.dao.mapper.MoonlightMapper">
 <resultMap id="geoFenceList" type="com.kangaroo.studio.moonlight.dao.model.GeoFence">
  <constructor>
   <idArg column="id" javaType="java.lang.Integer" jdbcType="INTEGER" />
   <arg column="name" javaType="java.lang.String" jdbcType="VARCHAR" />
   <arg column="type" javaType="java.lang.Integer" jdbcType="INTEGER" />
   <arg column="group" javaType="java.lang.String" jdbcType="VARCHAR" />
   <arg column="geo" javaType="java.lang.String" jdbcType="VARCHAR" />
   <arg column="createTime" javaType="java.lang.String" jdbcType="VARCHAR" />
   <arg column="updateTime" javaType="java.lang.String" jdbcType="VARCHAR" />
  </constructor>
 </resultMap>
 <sql id="base_column">id, name, type, `group`, geo, createTime, updateTime </sql>
 <select id="queryGeoFence" parameterType="com.kangaroo.studio.moonlight.dao.model.GeoFenceQueryParam" resultMap="geoFenceList">
  select <include refid="base_column"/> from geoFence where 1=1
  <if test="type != null">
   and type = #{type}
  </if>
  <if test="name != null">
   and name like concat('%', #{name},'%')
  </if>
  <if test="group != null">
   and `group` like concat('%', #{group},'%')
  </if>
  <if test="startTime != null">
   and createTime >= #{startTime}
  </if>
  <if test="endTime != null">
   and createTime <= #{endTime}
  </if>
 </select>
</mapper>

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

List<GeoFence> queryGeoFence(GeoFenceQueryParam geoFenceQueryParam);

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

@RequestMapping(value = "/fence/query", method = RequestMethod.POST)
  @ResponseBody
  public Response queryFence(@RequestBody GeoFenceQueryParam geoFenceQueryParam) {
    try {
      Integer pageNum = geoFenceQueryParam.getPageNum()!=null?geoFenceQueryParam.getPageNum():1;
      Integer pageSize = geoFenceQueryParam.getPageSize()!=null?geoFenceQueryParam.getPageSize():10;
      PageHelper.startPage(pageNum, pageSize);
      List<GeoFence> list = moonlightMapper.queryGeoFence(geoFenceQueryParam);
      return new Response(ResultCode.SUCCESS, "查询geoFence成功", list);
    } catch (Exception e) {
      logger.error("查询geoFence失败", e);
      return new Response(ResultCode.EXCEPTION, "查询geoFence失败", null);
    }
  }

说明:

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

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

4. 什么时候会导致不安全的分页?

PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。

只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。

如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。

但是如果你写出下面这样的代码,就是不安全的用法:

PageHelper.startPage(1, 10);
List<Country> list;
if(param1 != null){
  list = countryMapper.selectIf(param1);
} else {
  list = new ArrayList<Country>();
}

这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。

上面这个代码,应该写成下面这个样子:

List<Country> list;
if(param1 != null){
  PageHelper.startPage(1, 10);
  list = countryMapper.selectIf(param1);
} else {
  list = new ArrayList<Country>();
}

这种写法就能保证安全。

如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:

List<Country> list;
if(param1 != null){
  PageHelper.startPage(1, 10);
  try{
    list = countryMapper.selectAll();
  } finally {
    PageHelper.clearPage();
  }
} else {
  list = new ArrayList<Country>();
}

这么写很不好看,而且没有必要。

总结

以上所述是小编给大家介绍的Mybatis分页插件PageHelper的配置和简单使用方法(推荐),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

相关文章

  • Java实现短信验证码的示例代码

    Java实现短信验证码的示例代码

    Java是一种流行的编程语言,验证码是一种常用的网络安全技术。Java发展至今,网上也出现了各种各样的验证码,下面是用Java实现短信验证码的总结,感兴趣的可以了解一下
    2023-03-03
  • 利用Java+OpenCV实现拍照功能

    利用Java+OpenCV实现拍照功能

    网上大多是利用C语言或者Python实现拍照功能,本文将为大家介绍另一种方法,即在Java中调用OpenCV实现拍照功能,感兴趣的可以了解一下
    2022-01-01
  • Guava Cache的使用简介

    Guava Cache的使用简介

    这篇文章主要介绍了Guava Cache的使用简介,帮助大家更好的理解和学习使用Guava Cache,感兴趣的朋友可以了解下
    2021-03-03
  • Java锁之可重入锁介绍

    Java锁之可重入锁介绍

    这篇文章主要介绍了Java锁之可重入锁介绍,可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响,需要的朋友可以参考下
    2014-11-11
  • java实现微信支付功能

    java实现微信支付功能

    这篇文章主要为大家详细介绍了java实现微信支付功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • 本地安装MinIO分布式对象存储服务器的详细步骤

    本地安装MinIO分布式对象存储服务器的详细步骤

    本地安装MinIO非常简单,MinIO提供了独立的二进制文件,无需额外的依赖,本文介绍如何在本地安装MinIO分布式对象存储服务器,感兴趣的朋友一起看看吧
    2024-01-01
  • java用split分割字符串的一个有趣现象

    java用split分割字符串的一个有趣现象

    最近在项目中使用了java中的split分割字符串,发现了一个bug,充分了展示了自己对java底层的认知有很多的不足和欠缺。下面将这次的经过总结出来分享给大家,有需要的朋友们可以参考借鉴,下面来一起看看吧。
    2016-12-12
  • 工作中Java集合的规范使用操作详解

    工作中Java集合的规范使用操作详解

    这篇文章主要为大家详细介绍了工作中Java集合使用时需要注意的规范,文中的示例代码讲解详细,对我们使用Java集合有一定的帮助,感兴趣的可以了解一下
    2022-11-11
  • 微信公众帐号开发教程之图文消息全攻略

    微信公众帐号开发教程之图文消息全攻略

    本篇主要介绍微信公众帐号开发中图文消息的使用,以及图文消息的几种表现形式。标题取名为"图文消息全攻略",这绝对不是标题党,是想借此机会把大家对图文消息相关的问题、疑虑、障碍全部清除掉。
    2016-12-12
  • java读取txt文件并输出结果

    java读取txt文件并输出结果

    这篇文章主要介绍了java读取txt文件并输出结果,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11

最新评论