mybatis3.1分页自动添加总数
1.mybatis默认分页是内存分页的
类似于下面的DAO签名方法,只要有RowBounds入参,Mybatis即会自动内存分页:
Java代码
我们必须将其转换为物理分页,也即数据库分页。
2.分页一般都需要自动计算出总行数,而在mybatis中,你必须手动发起两次请求,烦人。
解决思路
1.Mybatis的拦截器是我们动手动脚的地方
Mybatis的架构是非常漂亮的,它允许对多个接口的多个方法定义拦截器(Interceptor),以下是Mybatis的调用粗线:

我们不但可以对Excutor的方法编写插件,还可以对StatementHandler或ResultSetHandler的方法编写插件。以下是一个Mybatis的插件:
Java代码
注意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代码
以下就是使用该方法完成的转换:
引用
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代码
好了,下面给出PaginationInterceptor的完整代码:
Java代码
类似于下面的DAO签名方法,只要有RowBounds入参,Mybatis即会自动内存分页:
Java代码

- @Select("SELECT * FROM cartan_common.t_app s WHERE s.author = #{param.author}")
- ArrayList<App> queryList(@Param("param")AppQueryParam appQueryParam,RowBounds rowBounds);
我们必须将其转换为物理分页,也即数据库分页。
2.分页一般都需要自动计算出总行数,而在mybatis中,你必须手动发起两次请求,烦人。
解决思路
1.Mybatis的拦截器是我们动手动脚的地方
Mybatis的架构是非常漂亮的,它允许对多个接口的多个方法定义拦截器(Interceptor),以下是Mybatis的调用粗线:

我们不但可以对Excutor的方法编写插件,还可以对StatementHandler或ResultSetHandler的方法编写插件。以下是一个Mybatis的插件:
Java代码

- package com.ridge.dao.mybatis;
- import org.apache.commons.lang.reflect.FieldUtils;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.apache.ibatis.executor.statement.PreparedStatementHandler;
- import org.apache.ibatis.executor.statement.RoutingStatementHandler;
- import org.apache.ibatis.executor.statement.StatementHandler;
- import org.apache.ibatis.mapping.BoundSql;
- import org.apache.ibatis.mapping.ParameterMapping;
- import org.apache.ibatis.plugin.*;
- import org.apache.ibatis.session.Configuration;
- import org.apache.ibatis.session.RowBounds;
- import org.hibernate.dialect.Dialect;
- import java.sql.Connection;
- import java.util.Properties;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- /**
- * 默认情况下
- * 数据库的类型
- *
- * @author : chenxh
- * @date: 13-7-5
- */
- @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
- public class PaginationInterceptor implements Interceptor {
- @Override
- public Object intercept(Invocation invocation) throws Throwable {
- ...
- }
- }
注意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代码

- 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代码

- package com.ridge.dao.mybatis;
- import com.ridge.dao.Paging;
- import org.apache.ibatis.session.RowBounds;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- public class MybatisHepler {
- /**
- * 获取查询总数对应的SQL
- * @param querySelect
- * @return
- */
- public static String getCountSql(String querySelect) {
- querySelect = compress(querySelect);
- int orderIndex = getLastOrderInsertPoint(querySelect);
- int formIndex = getAfterFormInsertPoint(querySelect);
- String select = querySelect.substring(0, formIndex);
- //如果SELECT 中包含 DISTINCT 只能在外层包含COUNT
- if (select.toLowerCase().indexOf("select distinct") != -1 || querySelect.toLowerCase().indexOf("group by") != -1) {
- return new StringBuffer(querySelect.length()).append(
- "select count(1) count from (").append(
- querySelect.substring(0, orderIndex)).append(" ) t")
- .toString();
- } else {
- return new StringBuffer(querySelect.length()).append(
- "select count(1) count ").append(
- querySelect.substring(formIndex, orderIndex)).toString();
- }
- }
- /**
- * 得到最后一个Order By的插入点位置
- *
- * @return 返回最后一个Order By插入点的位置
- */
- private static int getLastOrderInsertPoint(String querySelect) {
- int orderIndex = querySelect.toLowerCase().lastIndexOf("order by");
- if (orderIndex == -1) {
- orderIndex = querySelect.length();
- }else{
- if(!isBracketCanPartnership(querySelect.substring(orderIndex, querySelect.length()))){
- throw new RuntimeException("My SQL 分页必须要有Order by 语句!");
- }
- }
- return orderIndex;
- }
- /**
- * 将{@code paging}转换为{@link org.apache.ibatis.session.RowBounds}对象
- * @param paging
- * @return
- */
- public static final RowBounds toRowBounds(Paging paging){
- return new RowBounds(paging.getRowOffset(),paging.getPageSize());
- }
- /**
- * 得到分页的SQL
- *
- * @param offset 偏移量
- * @param limit 位置
- * @return 分页SQL
- */
- public static String getPagingSql(String querySelect, int offset, int limit) {
- querySelect = compress(querySelect);
- String sql = querySelect.replaceAll("[^\s,]+\.", "") + " limit " + offset + " ," + limit;
- return sql;
- }
- /**
- * 将SQL语句压缩成一条语句,并且每个单词的间隔都是1个空格
- * @param sql SQL语句
- * @return 如果sql是NULL返回空,否则返回转化后的SQL
- */
- private static String compress(String sql) {
- return sql.replaceAll("[ ]", " ").replaceAll("\s{2,}", " ");
- }
- /**
- * 得到SQL第一个正确的FROM的的插入点
- */
- private static int getAfterFormInsertPoint(String querySelect) {
- String regex = "\s+FROM\s+";
- Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
- Matcher matcher = pattern.matcher(querySelect);
- while (matcher.find()) {
- int fromStartIndex = matcher.start(0);
- String text = querySelect.substring(0, fromStartIndex);
- if (isBracketCanPartnership(text)) {
- return fromStartIndex;
- }
- }
- return 0;
- }
- /**
- * 判断括号"()"是否匹配,并不会判断排列顺序是否正确
- *
- * @param text 要判断的文本
- * @return 如果匹配返回TRUE, 否则返回FALSE
- */
- private static boolean isBracketCanPartnership(String text) {
- if (text == null
- || (getIndexOfCount(text, "(") != getIndexOfCount(text, ")"))) {
- return false;
- }
- return true;
- }
- /**
- * 得到一个字符在另一个字符串中出现的次数
- *
- * @param text 文本
- * @param ch 字符
- */
- private static int getIndexOfCount(String text, char ch) {
- int count = 0;
- for (int i = 0; i < text.length(); i++) {
- count = (text.charAt(i) == ch) ? count + 1 : count;
- }
- return count;
- }
- }
好了,下面给出PaginationInterceptor的完整代码:
Java代码

- package com.ridge.dao.mybatis;
- import org.apache.commons.lang.reflect.FieldUtils;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.apache.ibatis.executor.statement.PreparedStatementHandler;
- import org.apache.ibatis.executor.statement.RoutingStatementHandler;
- import org.apache.ibatis.executor.statement.StatementHandler;
- import org.apache.ibatis.mapping.BoundSql;
- import org.apache.ibatis.mapping.ParameterMapping;
- import org.apache.ibatis.plugin.*;
- import org.apache.ibatis.session.Configuration;
- import org.apache.ibatis.session.RowBounds;
- import org.hibernate.dialect.Dialect;
- import java.sql.Connection;
- import java.util.Properties;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- /**
- * 默认情况下
- * 数据库的类型
- *
- * @author : chenxh
- * @date: 13-7-5
- */
- @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
- public class PaginationInterceptor&
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。