牛骨文教育服务平台(让学习变的简单)
博文笔记

mybatis3.1分页自动添加总数

创建时间:2015-12-15 投稿人: 浏览次数:2950
1.mybatis默认分页是内存分页的
  类似于下面的DAO签名方法,只要有RowBounds入参,Mybatis即会自动内存分页: 
Java代码  收藏代码
  1. @Select("SELECT * FROM cartan_common.t_app s WHERE s.author = #{param.author}")  
  2. ArrayList<App> queryList(@Param("param")AppQueryParam appQueryParam,RowBounds rowBounds);  


   我们必须将其转换为物理分页,也即数据库分页。 

2.分页一般都需要自动计算出总行数,而在mybatis中,你必须手动发起两次请求,烦人。 

解决思路 

1.Mybatis的拦截器是我们动手动脚的地方 

  Mybatis的架构是非常漂亮的,它允许对多个接口的多个方法定义拦截器(Interceptor),以下是Mybatis的调用粗线: 


    我们不但可以对Excutor的方法编写插件,还可以对StatementHandler或ResultSetHandler的方法编写插件。以下是一个Mybatis的插件: 
Java代码  收藏代码
  1. package com.ridge.dao.mybatis;  
  2.   
  3. import org.apache.commons.lang.reflect.FieldUtils;  
  4. import org.apache.commons.logging.Log;  
  5. import org.apache.commons.logging.LogFactory;  
  6. import org.apache.ibatis.executor.statement.PreparedStatementHandler;  
  7. import org.apache.ibatis.executor.statement.RoutingStatementHandler;  
  8. import org.apache.ibatis.executor.statement.StatementHandler;  
  9. import org.apache.ibatis.mapping.BoundSql;  
  10. import org.apache.ibatis.mapping.ParameterMapping;  
  11. import org.apache.ibatis.plugin.*;  
  12. import org.apache.ibatis.session.Configuration;  
  13. import org.apache.ibatis.session.RowBounds;  
  14. import org.hibernate.dialect.Dialect;  
  15.   
  16. import java.sql.Connection;  
  17. import java.util.Properties;  
  18. import java.util.regex.Matcher;  
  19. import java.util.regex.Pattern;  
  20.   
  21. /** 
  22.  * 默认情况下 
  23.  * 数据库的类型 
  24.  * 
  25.  * @author : chenxh 
  26.  * @date: 13-7-5 
  27.  */  
  28. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})  
  29. public class PaginationInterceptor implements Interceptor {  
  30.     @Override  
  31.     public Object intercept(Invocation invocation) throws Throwable {  
  32.        ...  
  33.     }  
  34. }  

    注意PaginationInterceptor类的@Intercepts注解,如上所示,它将对StatementHandler的prepare(Connection connection)方法进行拦截。 

2.怎样将mybatis的语句转换为分页的语句呢 
   
    这得求助Hibernate的org.hibernate.dialect.Dialect接口及其实现类。我们知道不同数据库分页的方法是不一样的。mysql是limit x,y,而Oracle要用一个子查询,使用rownum来做到。Hibernater的org.hibernate.dialect.Dialect的以下方法可以非常方便地让我们做到这点: 
Java代码  收藏代码
  1. getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit())  

   以下就是使用该方法完成的转换: 
引用
SELECT * FROM cartan_common.t_app s WHERE s.author = ? 
对应的分页SQL: 
SELECT * FROM cartan_common.t_app s WHERE s.author = ? limit ?, ? 


3.怎样生成SQL对应的总条数SQL呢?   

   通过以下的类的MybatisHepler.getCountSql(originalSql)方法即可: 
Java代码  收藏代码
  1. package com.ridge.dao.mybatis;  
  2.   
  3.   
  4. import com.ridge.dao.Paging;  
  5. import org.apache.ibatis.session.RowBounds;  
  6.   
  7. import java.util.regex.Matcher;  
  8. import java.util.regex.Pattern;  
  9.   
  10. public class MybatisHepler {  
  11.   
  12.     /** 
  13.      * 获取查询总数对应的SQL 
  14.      * @param querySelect 
  15.      * @return 
  16.      */  
  17.     public static String getCountSql(String querySelect) {  
  18.   
  19.         querySelect = compress(querySelect);  
  20.         int orderIndex = getLastOrderInsertPoint(querySelect);  
  21.   
  22.         int formIndex = getAfterFormInsertPoint(querySelect);  
  23.         String select = querySelect.substring(0, formIndex);  
  24.   
  25.         //如果SELECT 中包含 DISTINCT 只能在外层包含COUNT  
  26.         if (select.toLowerCase().indexOf("select distinct") != -1 || querySelect.toLowerCase().indexOf("group by") != -1) {  
  27.             return new StringBuffer(querySelect.length()).append(  
  28.                     "select count(1) count from (").append(  
  29.                     querySelect.substring(0, orderIndex)).append(" ) t")  
  30.                     .toString();  
  31.         } else {  
  32.             return new StringBuffer(querySelect.length()).append(  
  33.                     "select count(1) count ").append(  
  34.                     querySelect.substring(formIndex, orderIndex)).toString();  
  35.         }  
  36.     }  
  37.   
  38.     /** 
  39.      * 得到最后一个Order By的插入点位置 
  40.      * 
  41.      * @return 返回最后一个Order By插入点的位置 
  42.      */  
  43.     private static int getLastOrderInsertPoint(String querySelect) {  
  44.         int orderIndex = querySelect.toLowerCase().lastIndexOf("order by");  
  45.         if (orderIndex == -1) {  
  46.             orderIndex = querySelect.length();  
  47.         }else{  
  48.            if(!isBracketCanPartnership(querySelect.substring(orderIndex, querySelect.length()))){  
  49.                throw new RuntimeException("My SQL 分页必须要有Order by 语句!");  
  50.            }  
  51.         }  
  52.         return orderIndex;  
  53.     }  
  54.   
  55.     /** 
  56.      * 将{@code paging}转换为{@link org.apache.ibatis.session.RowBounds}对象 
  57.      * @param paging 
  58.      * @return 
  59.      */  
  60.     public static final RowBounds toRowBounds(Paging paging){  
  61.         return new RowBounds(paging.getRowOffset(),paging.getPageSize());  
  62.     }  
  63.   
  64.     /** 
  65.      * 得到分页的SQL 
  66.      * 
  67.      * @param offset 偏移量 
  68.      * @param limit  位置 
  69.      * @return 分页SQL 
  70.      */  
  71.     public static String getPagingSql(String querySelect, int offset, int limit) {  
  72.   
  73.         querySelect = compress(querySelect);  
  74.   
  75.         String sql = querySelect.replaceAll("[^\s,]+\.", "") + " limit " + offset + " ," + limit;  
  76.   
  77.         return sql;  
  78.   
  79.     }  
  80.   
  81.     /** 
  82.      * 将SQL语句压缩成一条语句,并且每个单词的间隔都是1个空格 
  83.      * @param sql SQL语句 
  84.      * @return 如果sql是NULL返回空,否则返回转化后的SQL 
  85.      */  
  86.     private static String compress(String sql) {  
  87.         return sql.replaceAll("[ ]", " ").replaceAll("\s{2,}", " ");  
  88.     }  
  89.   
  90.     /** 
  91.      * 得到SQL第一个正确的FROM的的插入点 
  92.      */  
  93.     private static int getAfterFormInsertPoint(String querySelect) {  
  94.         String regex = "\s+FROM\s+";  
  95.         Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);  
  96.         Matcher matcher = pattern.matcher(querySelect);  
  97.         while (matcher.find()) {  
  98.             int fromStartIndex = matcher.start(0);  
  99.             String text = querySelect.substring(0, fromStartIndex);  
  100.             if (isBracketCanPartnership(text)) {  
  101.                 return fromStartIndex;  
  102.             }  
  103.         }  
  104.         return 0;  
  105.     }  
  106.   
  107.     /** 
  108.      * 判断括号"()"是否匹配,并不会判断排列顺序是否正确 
  109.      * 
  110.      * @param text 要判断的文本 
  111.      * @return 如果匹配返回TRUE, 否则返回FALSE 
  112.      */  
  113.     private static boolean isBracketCanPartnership(String text) {  
  114.         if (text == null  
  115.                 || (getIndexOfCount(text, "(") != getIndexOfCount(text, ")"))) {  
  116.             return false;  
  117.         }  
  118.         return true;  
  119.     }  
  120.   
  121.     /** 
  122.      * 得到一个字符在另一个字符串中出现的次数 
  123.      * 
  124.      * @param text 文本 
  125.      * @param ch   字符 
  126.      */  
  127.     private static int getIndexOfCount(String text, char ch) {  
  128.         int count = 0;  
  129.         for (int i = 0; i < text.length(); i++) {  
  130.             count = (text.charAt(i) == ch) ? count + 1 : count;  
  131.         }  
  132.         return count;  
  133.     }  
  134. }  


  好了,下面给出PaginationInterceptor的完整代码: 
Java代码  收藏代码
  1. package com.ridge.dao.mybatis;  
  2.   
  3. import org.apache.commons.lang.reflect.FieldUtils;  
  4. import org.apache.commons.logging.Log;  
  5. import org.apache.commons.logging.LogFactory;  
  6. import org.apache.ibatis.executor.statement.PreparedStatementHandler;  
  7. import org.apache.ibatis.executor.statement.RoutingStatementHandler;  
  8. import org.apache.ibatis.executor.statement.StatementHandler;  
  9. import org.apache.ibatis.mapping.BoundSql;  
  10. import org.apache.ibatis.mapping.ParameterMapping;  
  11. import org.apache.ibatis.plugin.*;  
  12. import org.apache.ibatis.session.Configuration;  
  13. import org.apache.ibatis.session.RowBounds;  
  14. import org.hibernate.dialect.Dialect;  
  15.   
  16. import java.sql.Connection;  
  17. import java.util.Properties;  
  18. import java.util.regex.Matcher;  
  19. import java.util.regex.Pattern;  
  20.   
  21. /** 
  22.  * 默认情况下 
  23.  * 数据库的类型 
  24.  * 
  25.  * @author : chenxh 
  26.  * @date: 13-7-5 
  27.  */  
  28. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})  
  29. public class PaginationInterceptor&
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。