[转]Mybatis极其(最)简(好)单(用)的一个分页插件
原文地址:http://blog.csdn.net/isea533/article/details/23831273
分页插件示例:http://blog.csdn.net/isea533/article/details/24700339
最新版分页插件:http://blog.csdn.net/isea533/article/details/25505413
项目地址:http://git.oschina.net/free/Mybatis_PageHelper
以前为Mybatis分页查询发愁过,而且在网上搜过很多相关的文章,最后一个都没采用。在分页的地方完全都是手写分页SQL和count的sql,总之很麻烦。
后来有一段时间想从Mybatis内部写一个分页的实现,我对LanguageDriver写过一个实现,自动分页是没问题了,但是查询总数(count)仍然没法一次性解决,最后不了了之。
最近又要用到分页,为了方便必须地写个通用的分页类,因此又再次参考网上大多数的Mybatis分页代码,本插件主要参考自:
http://blog.csdn.net/hupanfeng/article/details/9265341
实际上在很早之前,有人在github上开源过一个实现,支持mysql,oracle,sqlserver的,和上面这个参考的比较类似,考虑的
更全面。但是我觉得太多类太麻烦了,所以自己实现了一个只有一个拦截器的类,实际上可以分为两个类,其中一个类被我写成静态类放在了拦截器中,你也可以将
Page类提取出来,方便使用Page。
先说实现方法,该插件只有一个类:PageHelper.java
拦截器签名为:
- @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),
- @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
这里的签名对整个实现和思想至关重要,首先我拦截prepare方法来改分页SQL,来做count查询。然后我拦截handleResultSets方法来获取最后的处理结果,将结果放到Page对象中。
下面是修改分页的代码,是针对Oracle数据进行的修改,如果有用其他数据库的,自己修改这里的代码就可以。
- /**
- * 修改原SQL为分页SQL
- * @param sql
- * @param page
- * @return
- */
- private String buildPageSql(String sql, Page page) {
- StringBuilder pageSql = new StringBuilder(200);
- pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
- pageSql.append(sql);
- pageSql.append(" ) temp where rownum <= ").append(page.getEndRow());
- pageSql.append(") where row_id > ").append(page.getStartRow());
- return pageSql.toString();
- }
之后在下面的setPageParameter方法中一个selelct count语句,这里也需要根据数据库类型进行修改:
- // 记录总记录数
- String countSql = "select count(0) from (" + sql + ")";
为什么我不提供对各种数据库的支持呢,我觉得没必要,还有些数据库不支持分页,而且这个插件越简单对使用的开发人员来说越容易理解,越容易修改。修改成自己需要的分页查询肯定不是问题。
最后上完整代码(继续看下去,下面还有使用方法):(点击下载)
- package com.mybatis.util;
- import org.apache.ibatis.executor.parameter.ParameterHandler;
- import org.apache.ibatis.executor.resultset.ResultSetHandler;
- import org.apache.ibatis.executor.statement.StatementHandler;
- import org.apache.ibatis.mapping.BoundSql;
- import org.apache.ibatis.mapping.MappedStatement;
- import org.apache.ibatis.plugin.*;
- import org.apache.ibatis.reflection.MetaObject;
- import org.apache.ibatis.reflection.SystemMetaObject;
- import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
- import org.apache.log4j.Logger;
- import java.sql.*;
- import java.util.List;
- import java.util.Properties;
- /**
- * Mybatis - 通用分页拦截器
- * @author liuzh/abel533/isea
- * Created by liuzh on 14-4-15.
- */
- @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),
- @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
- public class PageHelper implements Interceptor {
- private static final Logger logger = Logger.getLogger(PageHelper.class);
- public static final ThreadLocal<Page> localPage = new ThreadLocal<Page>();
- /**
- * 开始分页
- * @param pageNum
- * @param pageSize
- */
- public static void startPage(int pageNum, int pageSize) {
- localPage.set(new Page(pageNum, pageSize));
- }
- /**
- * 结束分页并返回结果,该方法必须被调用,否则localPage会一直保存下去,直到下一次startPage
- * @return
- */
- public static Page endPage() {
- Page page = localPage.get();
- localPage.remove();
- return page;
- }
- @Override
- public Object intercept(Invocation invocation) throws Throwable {
- if (localPage.get() == null) {
- return invocation.proceed();
- }
- if (invocation.getTarget() instanceof StatementHandler) {
- StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
- MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
- // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环
- // 可以分离出最原始的的目标类)
- while (metaStatementHandler.hasGetter("h")) {
- Object object = metaStatementHandler.getValue("h");
- metaStatementHandler = SystemMetaObject.forObject(object);
- }
- // 分离最后一个代理对象的目标类
- while (metaStatementHandler.hasGetter("target")) {
- Object object = metaStatementHandler.getValue("target");
- metaStatementHandler = SystemMetaObject.forObject(object);
- }
- MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
- //分页信息if (localPage.get() != null) {
- Page page = localPage.get();
- BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
- // 分页参数作为参数对象parameterObject的一个属性
- String sql = boundSql.getSql();
- // 重写sql
- String pageSql = buildPageSql(sql, page);
- //重写分页sql
- metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
- Connection connection = (Connection) invocation.getArgs()[0];
- // 重设分页参数里的总页数等
- setPageParameter(sql, connection, mappedStatement, boundSql, page);
- // 将执行权交给下一个拦截器
- return invocation.proceed();
- } else if (invocation.getTarget() instanceof ResultSetHandler) {
- Object result = invocation.proceed();
- Page page = localPage.get();
- page.setResult((List) result);
- return result;
- }
- return null;
- }
- /**
- * 只拦截这两种类型的
- * <br>StatementHandler
- * <br>ResultSetHandler
- * @param target
- * @return
- */
- @Override
- public Object plugin(Object target) {
- if (target instanceof StatementHandler || target instanceof ResultSetHandler) {
- return Plugin.wrap(target, this);
- } else {
- return target;
- }
- }
- @Override
- public void setProperties(Properties properties) {
- }
- /**
- * 修改原SQL为分页SQL
- * @param sql
- * @param page
- * @return
- */
- private String buildPageSql(String sql, Page page) {
- StringBuilder pageSql = new StringBuilder(200);
- pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
- pageSql.append(sql);
- pageSql.append(" ) temp where rownum <= ").append(page.getEndRow());
- pageSql.append(") where row_id > ").append(page.getStartRow());
- return pageSql.toString();
- }
- /**
- * 获取总记录数
- * @param sql
- * @param connection
- * @param mappedStatement
- * @param boundSql
- * @param page
- */
- private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement,
- BoundSql boundSql, Page page) {
- // 记录总记录数
- String countSql = "select count(0) from (" + sql + ")";
- PreparedStatement countStmt = null;
- ResultSet rs = null;
- try {
- countStmt = connection.prepareStatement(countSql);
- BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
- boundSql.getParameterMappings(), boundSql.getParameterObject());
- setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());
- rs = countStmt.executeQuery();
- int totalCount = 0;
- if (rs.next()) {
- totalCount = rs.getInt(1);
- }
- page.setTotal(totalCount);
- int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1);
- page.setPages(totalPage);
- } catch (SQLException e) {
- logger.error("Ignore this exception", e);
- } finally {
- try {
- rs.close();
- } catch (SQLException e) {
- logger.error("Ignore this exception", e);
- }
- try {
- countStmt.close();
- } catch (SQLException e) {
- logger.error("Ignore this exception", e);
- }
- }
- }
- /**
- * 代入参数值
- * @param ps
- * @param mappedStatement
- * @param boundSql
- * @param parameterObject
- * @throws SQLException
- */
- private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
- Object parameterObject) throws SQLException {
- ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
- parameterHandler.setParameters(ps);
- }
- /**
- * Description: 分页
- * Author: liuzh
- * Update: liuzh(2014-04-16 10:56)
- */
- public static class Page<E> {
- private int pageNum;
- private int pageSize;
- private int startRow;
- private int endRow;
- private long total;
- private int pages;
- private List<E> result;
- public Page(int pageNum, int pageSize) {
- this.pageNum = pageNum;
- this.pageSize = pageSize;
- this.startRow = pageNum > 0 ? (pageNum - 1) * pageSize : 0;
- this.endRow = pageNum * pageSize;
- }
- public List<E> getResult() {
- return result;
- }
- public void setResult(List<E> result) {
- this.result = result;
- }
- public int getPages() {
- return pages;
- }
- public void setPages(int pages) {
- this.pages = pages;
- }
- public int getEndRow() {
- return endRow;
- }
- public void setEndRow(int endRow) {
- this.endRow = endRow;
- }
- public int getPageNum() {
- return pageNum;
- }
- public void setPageNum(int pageNum) {
- this.pageNum = pageNum;
- }
- public int getPageSize() {
- return pageSize;
- }
- public void setPageSize(int pageSize) {
- this.pageSize = pageSize;
- }
- public int getStartRow() {
- return startRow;
- }
- public void setStartRow(int startRow) {
- this.startRow = startRow;
- }
- public long getTotal() {
- return total;
- }
- public void setTotal(long total) {
- this.total = total;
- }
- @Override
- public String toString() {
- return "Page{" +
- "pageNum=" + pageNum +
- ", pageSize=" + pageSize +
- ", startRow=" + startRow +
- ", endRow=" + endRow +
- ", total=" + total +
- ", pages=" + pages +
- '}';
- }
- }
- }
使用该拦截器首先需要在Mybatis配置中配置该拦截器:
- <plugins>
- <plugin interceptor="com.mybatis.util.PageHelper"></plugin>
- </plugins>
配置拦截器的时候需要注意plugins的位置,plugins位置顺序如下:
- properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?
最后是调用该方法的例子代码(Service层):
- @Override
- public PageHelper.Page<SysLoginLog> findSysLoginLog(String loginIp,
- String username,
- String loginDate,
- String exitDate,
- String logerr,
- int pageNumber,
- int pageSize) throws BusinessException {
- PageHelper.startPage(pageNumber,pageSize);
- sysLoginLogMapper.findSysLoginLog(loginIp, username, loginDate, exitDate, logerr);
- return PageHelper.endPage();
- }
从上面可以看到使用该插件使用起来是很简单的,只需要在查询前后使用PageHelper的startPage和endPage方法即可,中间代码
的调用结果已经存在于Page的result中,如果你在一个返回一个结果的地方调用PageHelper,返回的结果仍然是一个List,取第一个值即
可(我想没人会在这种地方这么用,当然这样也不出错)。
另外在startPage和endPage中间的所有mybatis代码都会被分页,而且PageHelper只会保留最后一次的结果,因而使用时需要保证每次只在其中执行一个mybatis查询,如果有多个分页,请多次使用startPage和endPage。
[转]Mybatis极其(最)简(好)单(用)的一个分页插件的更多相关文章
- SpringBoot入门篇--整合mybatis+generator自动生成代码+druid连接池+PageHelper分页插件
原文链接 我们这一篇博客讲的是如何整合Springboot和Mybatis框架,然后使用generator自动生成mapper,pojo等文件.然后再使用阿里巴巴提供的开源连接池druid,这个连接池 ...
- 【spring boot】14.spring boot集成mybatis,注解方式OR映射文件方式AND pagehelper分页插件【Mybatis】pagehelper分页插件分页查询无效解决方法
spring boot集成mybatis,集成使用mybatis拖沓了好久,今天终于可以补起来了. 本篇源码中,同时使用了Spring data JPA 和 Mybatis两种方式. 在使用的过程中一 ...
- SpringBoot+MyBatis多数据源使用分页插件PageHelper
之前只用过单数据源下的分页插件,而且几乎不用配置.一个静态方法就能搞定. PageHelper.startPage(pageNum, pageSize); 后来使用了多数据源(不同的数据库),Page ...
- (转)淘淘商城系列——MyBatis分页插件(PageHelper)的使用以及商品列表展示
http://blog.csdn.net/yerenyuan_pku/article/details/72774381 上文我们实现了展示后台页面的功能,而本文我们实现的主要功能是展示商品列表,大家要 ...
- Mybatis插件原理和PageHelper结合实战分页插件(七)
今天和大家分享下mybatis的一个分页插件PageHelper,在讲解PageHelper之前我们需要先了解下mybatis的插件原理.PageHelper 的官方网站:https://github ...
- Mybatis插件原理分析(三)分页插件
在Mybatis中插件最经常使用的是作为分页插件,接下来我们通过实现Interceptor来完成一个分页插件. 虽然Mybatis也提供了分页操作,通过在sqlSession的接口函数中设置RowBo ...
- Springboot 系列(十二)使用 Mybatis 集成 pagehelper 分页插件和 mapper 插件
前言 在 Springboot 系列文章第十一篇里(使用 Mybatis(自动生成插件) 访问数据库),实验了 Springboot 结合 Mybatis 以及 Mybatis-generator 生 ...
- 【MyBatis】MyBatis分页插件PageHelper的使用
好多天没写博客了,因为最近在实习,大部分时间在熟悉实习相关的东西,也没有怎么学习新的东西,这周末学习了MyBatis的一个分页插件PageHelper,虽然没有那么的强大(我在最后会说明它的缺点),但 ...
- MyBatis Generator实现MySQL分页插件
MyBatis Generator是一个非常方便的代码生成工具,它能够根据表结构生成CRUD代码,可以满足大部分需求.但是唯一让人不爽的是,生成的代码中的数据库查询没有分页功能.本文介绍如何让MyBa ...
随机推荐
- 第14章 位图和位块传输_14.4 GDI位图对象(2)
14.4.7 在位图上绘图 (1)在内存设备环境中绘图(与真实DC不同的是,内存DC的显示表面是个位图) (2)GetTextExtentPoint32函数:用于确定文本字符串的像素大小.(此大小就是 ...
- 05章 OGNL
一.OGNL全称是Object Graph Navigation Language,即对象导航图语言 OGNL在框架中主要做两件事情:表达式语言和类型转换器 OGNL在框架中的作用以及数据的流入流出: ...
- 银狐云服务架构V0.1
首先这套程序是由JAVA开发的,为什么使用JAVA? 因为在本人的几年开发经历中,JDK在各种服务器上安装都很顺利, JAVA程序运行稳定,接口名不经常变动等,各种开源类库提升开发效率,给本人以信心, ...
- ios客户端快速滚动和回弹效果的实现
现在很多for Mobile的HTML5网页内都有快速滚动和回弹的效果,看上去和原生app的效率都有得一拼. 要实现这个效果很简单,只需要给容器加一行css代码即可 -webkit-overflow- ...
- swift基本用法-数组array
数组简单用法 //------------------------------------------------------------------------------ // 1. 数组定义 / ...
- Castle IOC容器与Spring.NET配置之比较
我本人对于Spring.NET并不了解,本文只是通过一个简单的例子来比较一下两者配置之间的区别.在Castle IOC容器中,提出了自动装配(Auto-Wiring)的概念,即由容器自动管理组件之间的 ...
- Java操作符
几乎所有运算符都只能操作"主类型"(Primitives).例外是"="."= ="和"! =",它们能操作所有对象.除 ...
- 简易安装python统计包
PythonCharm简易安装python统计包及 本文介绍使用pythonCharm IDE 来安装Python统计包或一些packages的简单过程,基本无任何技术难度,顺便提一提笔者在安装过程中 ...
- android:ToolBar详解(手把手教程)(转)
来源 http://blog.mosil.biz/2014/10/android-toolbar/ 编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅 ...
- NOI2018准备 Day11
今天7点半到9点我都不知道自己在干啥, 一共A了3道题,2道钻石,1道大师. 下午调一道线段树3个小时没调出来,一个单调栈2小时没搞出来...... 学了个算法:求极大子矩阵. 昨天定的目标是学指针, ...