java分页拦截类实现sql自动分页

所属分类: 软件编程 / java 阅读数: 38
收藏 0 赞 0 分享

本文实例为大家分享了完整的java分页拦截类,供大家参考,具体内容如下

package com.opms.interceptor;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.RowBounds;

import com.wifi.core.page.Page;

/**
 * 通过拦截<code>StatementHandler</code>的<code>prepare</code>方法,重写sql语句实现物理分页。
 * 老规矩,签名里要拦截的类型只能是接口。
 * 
 * @author 湖畔微风
 * 
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PageInterceptor implements Interceptor {
 /**
  * 日志
  */
 private static final Log logger = LogFactory.getLog(PageInterceptor.class);
 /**
  * 声明对象
  */
 private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
 /**
  * 声明对象
  */
 private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
 /**
  * 数据库类型(默认为mysql)
  */
 private static String defaultDialect = "mysql"; 
 /**
  * 需要拦截的ID(正则匹配)
  */
 private static String defaultPageSqlId = ".*4Page$"; 
 /**
  * 数据库类型(默认为mysql) 
  */
 private static String dialect = ""; 
 /**
  * 需要拦截的ID(正则匹配)
  */
 private static String pageSqlId = ""; 
 /**
  * @param invocation 参数
  * @return Object
  * @throws Throwable 抛出异常
  */
 public Object intercept(Invocation invocation) throws Throwable {
  StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
  MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY,
    DEFAULT_OBJECT_WRAPPER_FACTORY);
  // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环可以分离出最原始的的目标类)
  while (metaStatementHandler.hasGetter("h")) {
   Object object = metaStatementHandler.getValue("h");
   metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
  }
  // 分离最后一个代理对象的目标类
  while (metaStatementHandler.hasGetter("target")) {
   Object object = metaStatementHandler.getValue("target");
   metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
  }
  dialect=defaultDialect;pageSqlId=defaultPageSqlId;
  /* Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
  dialect = configuration.getVariables().getProperty("dialect");
  if (null == dialect || "".equals(dialect)) {
   logger.warn("Property dialect is not setted,use default 'mysql' ");
   dialect = defaultDialect;
  }
  pageSqlId = configuration.getVariables().getProperty("pageSqlId");
  if (null == pageSqlId || "".equals(pageSqlId)) {
   logger.warn("Property pageSqlId is not setted,use default '.*Page$' ");
   pageSqlId = defaultPageSqlId;
  }*/
  MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
  // 只重写需要分页的sql语句。通过MappedStatement的ID匹配,默认重写以Page结尾的MappedStatement的sql
  if (mappedStatement.getId().matches(pageSqlId)) {
   BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
   Object parameterObject = boundSql.getParameterObject();
   if (parameterObject == null) {
    throw new NullPointerException("parameterObject is null!");
   } else {
    Object obj = metaStatementHandler
      .getValue("delegate.boundSql.parameterObject.page");
    // 传入了page参数且需要开启分页时
    if(obj!=null&&obj instanceof Page &&((Page)obj).isPagination()){
     Page page = (Page) metaStatementHandler
       .getValue("delegate.boundSql.parameterObject.page");
     String sql = boundSql.getSql();
     // 重写sql
     String pageSql = buildPageSql(sql, page);
     metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
     // 采用物理分页后,就不需要mybatis的内存分页了,所以重置下面的两个参数
     metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
     metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
     Connection connection = (Connection) invocation.getArgs()[0];
     // 重设分页参数里的总页数等
     setPageParameter(sql, connection, mappedStatement, boundSql, page);
    }
   }
  }
  // 将执行权交给下一个拦截器
  return invocation.proceed();
 }

 /**
  * 从数据库里查询总的记录数并计算总页数,回写进分页参数<code>PageParameter</code>,这样调用者就可用通过 分页参数
  * <code>PageParameter</code>获得相关信息。
  * 
  * @param sql 参数
  * @param connection 连接
  * @param mappedStatement 参数
  * @param boundSql 绑定sql
  * @param page 页
  */
 private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement,
   BoundSql boundSql, Page page) {
  // 记录总记录数
  String countSql = "select count(0) from (" + sql + ") as total";
  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.setTotalCount(totalCount);
   page.init(page.getCurPage(), page.getPageSize(), totalCount);

  } 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);
   }
  }

 }

 /**
  * 对SQL参数(?)设值
  * 
  * @param ps 参数
  * @param mappedStatement 参数
  * @param boundSql 绑定sql
  * @param parameterObject 参数对象
  * @throws SQLException 抛出sql异常
  */
 private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
   Object parameterObject) throws SQLException {
  ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
  parameterHandler.setParameters(ps);
 }

 /**
  * 根据数据库类型,生成特定的分页sql
  * 
  * @param sql 餐宿
  * @param page 页
  * @return String
  */
 private String buildPageSql(String sql, Page page) {
  if (page != null) {
   StringBuilder pageSql = new StringBuilder();
   if ("mysql".equals(dialect)) {
    pageSql = buildPageSqlForMysql(sql, page);
   } else if ("oracle".equals(dialect)) {
    pageSql = buildPageSqlForOracle(sql, page);
   } else {
    return sql;
   }
   return pageSql.toString();
  } else {
   return sql;
  }
 }

 /**
  * mysql的分页语句
  * 
  * @param sql 参数
  * @param page 页
  * @return String
  */
 public StringBuilder buildPageSqlForMysql(String sql, Page page) {
  StringBuilder pageSql = new StringBuilder(100);
  String beginrow = String.valueOf((page.getCurPage() - 1) * page.getPageSize());
  pageSql.append(sql);
  pageSql.append(" limit " + beginrow + "," + page.getPageSize());
  return pageSql;
 }

 /**
  * 参考hibernate的实现完成oracle的分页
  * 
  * @param sql 参数
  * @param page 参数
  * @return String
  */
 public StringBuilder buildPageSqlForOracle(String sql, Page page) {
  StringBuilder pageSql = new StringBuilder(100);
  String beginrow = String.valueOf((page.getCurPage() - 1) * page.getPageSize());
  String endrow = String.valueOf(page.getCurPage() * page.getPageSize());

  pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
  pageSql.append(sql);
  pageSql.append(" ) temp where rownum <= ").append(endrow);
  pageSql.append(") where row_id > ").append(beginrow);
  return pageSql;
 }
 /**
  * @param target 参数
  * @return Object
  */
 public Object plugin(Object target) {
  // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数
  if (target instanceof StatementHandler) {
   return Plugin.wrap(target, this);
  } else {
   return target;
  }
 }
 /**
  * @param properties 参数
  */
 public void setProperties(Properties properties) {
 }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

Java的面向对象编程基本概念学习笔记整理

这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
收藏 0 赞 0 分享

Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法

这篇文章主要介绍了Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法 的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

基于Java实现杨辉三角 LeetCode Pascal's Triangle

这篇文章主要介绍了基于Java实现杨辉三角 LeetCode Pascal's Triangle的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中Spring获取bean方法小结

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

如何计算Java对象占用了多少空间?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下
收藏 0 赞 0 分享

剖析Java中的事件处理与异常处理机制

这篇文章主要介绍了Java中的事件处理与异常处理机制,讲解Java是如何对事件或者异常作出响应以及定义异常的一些方法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Java的Struts2框架的结构及其数据转移方式

这篇文章主要介绍了详解Java的Struts2框架的结构及其数据转移方式,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

Java封装好的mail包发送电子邮件的类

本文给大家分享了2个java封装好的mail包发送电子邮件的类,并附上使用方法,小伙伴们可以根据自己的需求自由选择。
收藏 0 赞 0 分享

在Java的Struts中判断是否调用AJAX及用拦截器对其优化

这篇文章主要介绍了在Java的Struts中判断是否调用AJAX及用拦截器对其优化的方法,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

java多线程Future和Callable类示例分享

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。今天我们就来研究下Future和Callab
收藏 0 赞 0 分享
查看更多