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

4次阅读

分页插件示例:

最新版分页插件:

项目地址:

以前为Mybatis分页查询发愁过,而且在网上搜过很多相关的文章,,最后一个都没采用。在分页的地方完全都是手写分页SQL和count的sql,总之很麻烦。

后来有一段时间想从Mybatis内部写一个分页的实现,我对LanguageDriver写过一个实现,自动分页是没问题了,但是查询总数(count)仍然没法一次性解决,最后不了了之。

最近又要用到分页,为了方便必须地写个通用的分页类,因此又再次参考网上大多数的Mybatis分页代码,本插件主要参考自:

实际上在很早之前,有人在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;}@Overridepublic 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();// 重写sqlString pageSql = buildPageSql(sql, page);//重写分页sqlmetaStatementHandler.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*/@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler || target instanceof ResultSetHandler) {return Plugin.wrap(target, this);} else {return target;}}@Overridepublic 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;}@Overridepublic String toString() {return "Page{" +"pageNum=" + pageNum +", pageSize=" + pageSize +", startRow=" + startRow +", endRow=" + endRow +", total=" + total +", pages=" + pages +’}’;}}}也有伤心的,既有令人兴奋的,也有令人灰心的,

正文完   
相关文章:
随机文章
eBay产品优化技巧:如何操作和优化产品页面?

eBay产品优化技巧:如何操作和优化产品页面?

在如今这个竞争激烈的电商市场中,提高产品的曝光率和销售量是每一位卖家都需要掌握的技巧。而在eBay上,产品优化...
海淘亚马逊物流快速的原因及常见的清关方式

海淘亚马逊物流快速的原因及常见的清关方式

海淘亚马逊物流快速的原因及常见的清关方式 随着国内市场的日益饱和,越来越多的消费者开始选择海外购物,其中海淘亚...
如何进行拼多多跨境商品的价格核算和定价策略?

如何进行拼多多跨境商品的价格核算和定价策略?

随着全球化和数字化的发展,跨境商务也越来越普及。拼多多作为中国电商巨头之一,其跨境业务也在不断扩展。如何进行拼...
跨境电商商品备案的流程是什么?所需准备的资料有哪些?

跨境电商商品备案的流程是什么?所需准备的资料有哪些?

随着全球经济的不断发展,跨境电商贸易也在迅猛增长。针对跨境电商商品备案的流程和所需准备的资料,我们将在以下内容...
eBay推出一站式寄送平台CpaSS,提供便捷服务!

eBay推出一站式寄送平台CpaSS,提供便捷服务!

eBay推出一站式寄送平台CpaSS,提供便捷服务! 随着电商业务的不断发展,消费者对于商品的选择和购买逐渐趋...