Mybatis极其(最)简(好)单(用)的一个分页插件

来源:互联网 发布:maya破解软件下载 编辑:程序博客网 时间:2024/04/28 00:21

分页插件示例: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


拦截器签名为:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),  
  2.         @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})  

这里的签名对整个实现和思想至关重要,首先我拦截prepare方法来改分页SQL,来做count查询。然后我拦截handleResultSets方法来获取最后的处理结果,将结果放到Page对象中。


下面是修改分页的代码,是针对Oracle数据进行的修改,如果有用其他数据库的,自己修改这里的代码就可以。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.      * 修改原SQL为分页SQL 
  3.      * @param sql 
  4.      * @param page 
  5.      * @return 
  6.      */  
  7.     private String buildPageSql(String sql, Page page) {  
  8.         StringBuilder pageSql = new StringBuilder(200);  
  9.         pageSql.append("select * from ( select temp.*, rownum row_id from ( ");  
  10.         pageSql.append(sql);  
  11.         pageSql.append(" ) temp where rownum <= ").append(page.getEndRow());  
  12.         pageSql.append(") where row_id > ").append(page.getStartRow());  
  13.         return pageSql.toString();  
  14.     }  

之后在下面的setPageParameter方法中一个selelct count语句,这里也需要根据数据库类型进行修改:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // 记录总记录数  
  2.         String countSql = "select count(0) from (" + sql + ")";  

为什么我不提供对各种数据库的支持呢,我觉得没必要,还有些数据库不支持分页,而且这个插件越简单对使用的开发人员来说越容易理解,越容易修改。修改成自己需要的分页查询肯定不是问题。


最后上完整代码(继续看下去,下面还有使用方法):(点击下载)

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.mybatis.util;  
  2.   
  3. import org.apache.ibatis.executor.parameter.ParameterHandler;  
  4. import org.apache.ibatis.executor.resultset.ResultSetHandler;  
  5. import org.apache.ibatis.executor.statement.StatementHandler;  
  6. import org.apache.ibatis.mapping.BoundSql;  
  7. import org.apache.ibatis.mapping.MappedStatement;  
  8. import org.apache.ibatis.plugin.*;  
  9. import org.apache.ibatis.reflection.MetaObject;  
  10. import org.apache.ibatis.reflection.SystemMetaObject;  
  11. import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;  
  12. import org.apache.log4j.Logger;  
  13.   
  14. import java.sql.*;  
  15. import java.util.List;  
  16. import java.util.Properties;  
  17.   
  18. /** 
  19.  * Mybatis - 通用分页拦截器 
  20.  * @author liuzh/abel533/isea 
  21.  * Created by liuzh on 14-4-15. 
  22.  */  
  23. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),  
  24.         @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})  
  25. public class PageHelper implements Interceptor {  
  26.     private static final Logger logger = Logger.getLogger(PageHelper.class);  
  27.   
  28.     public static final ThreadLocal<Page> localPage = new ThreadLocal<Page>();  
  29.   
  30.     /** 
  31.      * 开始分页 
  32.      * @param pageNum 
  33.      * @param pageSize 
  34.      */  
  35.     public static void startPage(int pageNum, int pageSize) {  
  36.         localPage.set(new Page(pageNum, pageSize));  
  37.     }  
  38.   
  39.     /** 
  40.      * 结束分页并返回结果,该方法必须被调用,否则localPage会一直保存下去,直到下一次startPage 
  41.      * @return 
  42.      */  
  43.     public static Page endPage() {  
  44.         Page page = localPage.get();  
  45.         localPage.remove();  
  46.         return page;  
  47.     }  
  48.   
  49.     @Override  
  50.     public Object intercept(Invocation invocation) throws Throwable {  
  51.         if (localPage.get() == null) {  
  52.             return invocation.proceed();  
  53.         }  
  54.         if (invocation.getTarget() instanceof StatementHandler) {  
  55.             StatementHandler statementHandler = (StatementHandler) invocation.getTarget();  
  56.             MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);  
  57.             // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环  
  58.             // 可以分离出最原始的的目标类)  
  59.             while (metaStatementHandler.hasGetter("h")) {  
  60.                 Object object = metaStatementHandler.getValue("h");  
  61.                 metaStatementHandler = SystemMetaObject.forObject(object);  
  62.             }  
  63.             // 分离最后一个代理对象的目标类  
  64.             while (metaStatementHandler.hasGetter("target")) {  
  65.                 Object object = metaStatementHandler.getValue("target");  
  66.                 metaStatementHandler = SystemMetaObject.forObject(object);  
  67.             }  
  68.             MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");  
  69.             //分页信息if (localPage.get() != null) {  
  70.             Page page = localPage.get();  
  71.             BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");  
  72.             // 分页参数作为参数对象parameterObject的一个属性  
  73.             String sql = boundSql.getSql();  
  74.             // 重写sql  
  75.             String pageSql = buildPageSql(sql, page);  
  76.             //重写分页sql  
  77.             metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);  
  78.             Connection connection = (Connection) invocation.getArgs()[0];  
  79.             // 重设分页参数里的总页数等  
  80.             setPageParameter(sql, connection, mappedStatement, boundSql, page);  
  81.             // 将执行权交给下一个拦截器  
  82.             return invocation.proceed();  
  83.         } else if (invocation.getTarget() instanceof ResultSetHandler) {  
  84.             Object result = invocation.proceed();  
  85.             Page page = localPage.get();  
  86.             page.setResult((List) result);  
  87.             return result;  
  88.         }  
  89.         return null;  
  90.     }  
  91.   
  92.     /** 
  93.      * 只拦截这两种类型的 
  94.      * <br>StatementHandler 
  95.      * <br>ResultSetHandler 
  96.      * @param target 
  97.      * @return 
  98.      */  
  99.     @Override  
  100.     public Object plugin(Object target) {  
  101.         if (target instanceof StatementHandler || target instanceof ResultSetHandler) {  
  102.             return Plugin.wrap(target, this);  
  103.         } else {  
  104.             return target;  
  105.         }  
  106.     }  
  107.   
  108.     @Override  
  109.     public void setProperties(Properties properties) {  
  110.   
  111.     }  
  112.   
  113.     /** 
  114.      * 修改原SQL为分页SQL 
  115.      * @param sql 
  116.      * @param page 
  117.      * @return 
  118.      */  
  119.     private String buildPageSql(String sql, Page page) {  
  120.         StringBuilder pageSql = new StringBuilder(200);  
  121.         pageSql.append("select * from ( select temp.*, rownum row_id from ( ");  
  122.         pageSql.append(sql);  
  123.         pageSql.append(" ) temp where rownum <= ").append(page.getEndRow());  
  124.         pageSql.append(") where row_id > ").append(page.getStartRow());  
  125.         return pageSql.toString();  
  126.     }  
  127.   
  128.     /** 
  129.      * 获取总记录数 
  130.      * @param sql 
  131.      * @param connection 
  132.      * @param mappedStatement 
  133.      * @param boundSql 
  134.      * @param page 
  135.      */  
  136.     private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement,  
  137.                                   BoundSql boundSql, Page page) {  
  138.         // 记录总记录数  
  139.         String countSql = "select count(0) from (" + sql + ")";  
  140.         PreparedStatement countStmt = null;  
  141.         ResultSet rs = null;  
  142.         try {  
  143.             countStmt = connection.prepareStatement(countSql);  
  144.             BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,  
  145.                     boundSql.getParameterMappings(), boundSql.getParameterObject());  
  146.             setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());  
  147.             rs = countStmt.executeQuery();  
  148.             int totalCount = 0;  
  149.             if (rs.next()) {  
  150.                 totalCount = rs.getInt(1);  
  151.             }  
  152.             page.setTotal(totalCount);  
  153.             int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1);  
  154.             page.setPages(totalPage);  
  155.         } catch (SQLException e) {  
  156.             logger.error("Ignore this exception", e);  
  157.         } finally {  
  158.             try {  
  159.                 rs.close();  
  160.             } catch (SQLException e) {  
  161.                 logger.error("Ignore this exception", e);  
  162.             }  
  163.             try {  
  164.                 countStmt.close();  
  165.             } catch (SQLException e) {  
  166.                 logger.error("Ignore this exception", e);  
  167.             }  
  168.         }  
  169.     }  
  170.   
  171.     /** 
  172.      * 代入参数值 
  173.      * @param ps 
  174.      * @param mappedStatement 
  175.      * @param boundSql 
  176.      * @param parameterObject 
  177.      * @throws SQLException 
  178.      */  
  179.     private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,  
  180.                                Object parameterObject) throws SQLException {  
  181.         ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);  
  182.         parameterHandler.setParameters(ps);  
  183.     }  
  184.   
  185.     /** 
  186.      * Description: 分页 
  187.      * Author: liuzh 
  188.      * Update: liuzh(2014-04-16 10:56) 
  189.      */  
  190.     public static class Page<E> {  
  191.         private int pageNum;  
  192.         private int pageSize;  
  193.         private int startRow;  
  194.         private int endRow;  
  195.         private long total;  
  196.         private int pages;  
  197.         private List<E> result;  
  198.   
  199.         public Page(int pageNum, int pageSize) {  
  200.             this.pageNum = pageNum;  
  201.             this.pageSize = pageSize;  
  202.             this.startRow = pageNum > 0 ? (pageNum - 1) * pageSize : 0;  
  203.             this.endRow = pageNum * pageSize;  
  204.         }  
  205.   
  206.         public List<E> getResult() {  
  207.             return result;  
  208.         }  
  209.   
  210.         public void setResult(List<E> result) {  
  211.             this.result = result;  
  212.         }  
  213.   
  214.         public int getPages() {  
  215.             return pages;  
  216.         }  
  217.   
  218.         public void setPages(int pages) {  
  219.             this.pages = pages;  
  220.         }  
  221.   
  222.         public int getEndRow() {  
  223.             return endRow;  
  224.         }  
  225.   
  226.         public void setEndRow(int endRow) {  
  227.             this.endRow = endRow;  
  228.         }  
  229.   
  230.         public int getPageNum() {  
  231.             return pageNum;  
  232.         }  
  233.   
  234.         public void setPageNum(int pageNum) {  
  235.             this.pageNum = pageNum;  
  236.         }  
  237.   
  238.         public int getPageSize() {  
  239.             return pageSize;  
  240.         }  
  241.   
  242.         public void setPageSize(int pageSize) {  
  243.             this.pageSize = pageSize;  
  244.         }  
  245.   
  246.         public int getStartRow() {  
  247.             return startRow;  
  248.         }  
  249.   
  250.         public void setStartRow(int startRow) {  
  251.             this.startRow = startRow;  
  252.         }  
  253.   
  254.         public long getTotal() {  
  255.             return total;  
  256.         }  
  257.   
  258.         public void setTotal(long total) {  
  259.             this.total = total;  
  260.         }  
  261.   
  262.         @Override  
  263.         public String toString() {  
  264.             return "Page{" +  
  265.                     "pageNum=" + pageNum +  
  266.                     ", pageSize=" + pageSize +  
  267.                     ", startRow=" + startRow +  
  268.                     ", endRow=" + endRow +  
  269.                     ", total=" + total +  
  270.                     ", pages=" + pages +  
  271.                     '}';  
  272.         }  
  273.     }  
  274. }  


使用该拦截器首先需要在Mybatis配置中配置该拦截器:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <plugins>  
  2.     <plugin interceptor="com.mybatis.util.PageHelper"></plugin>  
  3. </plugins>  
配置拦截器的时候需要注意plugins的位置,plugins位置顺序如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?  


最后是调用该方法的例子代码(Service层):

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public PageHelper.Page<SysLoginLog> findSysLoginLog(String loginIp,  
  3.                                          String username,  
  4.                                          String loginDate,  
  5.                                          String exitDate,  
  6.                                          String logerr,  
  7.                                          int pageNumber,  
  8.                                          int pageSize) throws BusinessException {  
  9.     PageHelper.startPage(pageNumber,pageSize);  
  10.     sysLoginLogMapper.findSysLoginLog(loginIp, username, loginDate, exitDate, logerr);  
  11.     return PageHelper.endPage();  
  12. }  

从上面可以看到使用该插件使用起来是很简单的,只需要在查询前后使用PageHelper的startPage和endPage方法即可,中间代码的调用结果已经存在于Page的result中,如果你在一个返回一个结果的地方调用PageHelper,返回的结果仍然是一个List,取第一个值即可(我想没人会在这种地方这么用,当然这样也不出错)。

另外在startPage和endPage中间的所有mybatis代码都会被分页,而且PageHelper只会保留最后一次的结果,因而使用时需要保证每次只在其中执行一个mybatis查询,如果有多个分页,请多次使用startPage和endPage。



由于这里只提供了Oracle的实现,所以我希望参考该分页插件实现的其他数据库的读者也能将相应的代码开源(本人不做要求),如果打算分享,欢迎回复留下地址。


from: http://blog.csdn.net/isea533/article/details/23831273

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 木板床一动就响怎么办 打工要不到工资怎么办 导师回复跟敷衍怎么办 在演讲时紧张怎么办 老房子怎么办不动产证 脚角质层厚粗糙怎么办 皮革包染色了怎么办 皮革包被染色了怎么办 面膜泥干了怎么办 淘宝店铺生意不好怎么办 淘宝商品换主图被下架了怎么办 壁纸店没生意怎么办 支付宝不能借钱怎么办 淘宝号想注销怎么办 被陌生号码骚扰怎么办 买家辱骂卖家怎么办 淘宝禁止创建店铺怎么办 闲鱼上买东西被骗了怎么办 恶意买家付款了怎么办 换手机号了淘宝怎么办 旺旺发不了图片怎么办 拼多多买家投诉怎么办 拼多多恶意用户怎么办 淘宝运单号填错了怎么办 淘宝退货卖家不处理怎么办 淘宝长时间不发货怎么办 实体店卖假货怎么办 淘宝店暂停服务怎么办 淘宝直播开始没人怎么办 店铺违规虚假交易怎么办 电视无频道信息怎么办 hdp直播频道丢失怎么办 小红书订单删了怎么办 退款售后删除我怎么办 毛衣袖子肥了怎么办 店面生意不好要怎么办 中国的农民以后怎么办 做到不好的梦怎么办 美瞳线眼睛肿了怎么办 淘宝买家限购怎么办 一楼房屋潮湿怎么办