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的配置和简单使用方法(推荐),希望对大家有所帮助