本文共 6437 字,大约阅读时间需要 21 分钟。
其实github有一个叫做PageHelper的开源分页组件,我也用过,封装的还可以。只是感觉他的量级偏重,其实很多参数,都是我们开发中不需要的参数,而且它的获取分页信息方式是通过构造方法,不是很优雅。所以我在查阅完他的源码后,结合自己的需求,实现了一个轻量级,便携式的分页组件SmallPage。
轻量级分页组件,基于入参类型
org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.0
现在java的持久层框架大多基于Mybatis的框架,所以实现分页的思路,也就拦截待执行的sql进行追加Limt限定,来实现分页。
/** * @author machenike */@Intercepts(@Signature(type = StatementHandler.class,method = "prepare",args={ Connection.class,Integer.class}))public class PageInterceptorPlugin implements Interceptor { private static final String META_OBJECT_KEY_SQL_ID = "delegate.mappedStatement.id"; private static final String META_OBJECT_KEY_BOUND_SQL = "delegate.boundSql.sql"; private static final Logger logger = LoggerFactory.getLogger(PageInterceptorPlugin.class); @Override public Object intercept(Invocation invocation) throws Throwable { //获取命令handler取得 StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); //获取入参handler ParameterHandler parameterHandler = statementHandler.getParameterHandler(); //获取参数对象 Object parameterObject = parameterHandler.getParameterObject(); PageBean pageBean = getPageBean(parameterObject); ThreadLocal threadLocal = new ThreadLocal(); if(pageBean!=null) { logger.debug("parameter type match,start intercept"); //获取meta对象取得 MetaObject metaObject = MetaObject.forObject( statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); //获取当前sqlId String sqlId = (String)metaObject.getValue(META_OBJECT_KEY_SQL_ID); //原来应该执行的sql吧 String sql = statementHandler.getBoundSql().getSql(); logger.debug("-originSql:" + sql); Connection connection = (Connection) invocation.getArgs()[0]; String countSql = bulidCountSql(sql); logger.debug("-countSql:" + countSql); //渲染参数 PreparedStatement preparedStatement = connection.prepareStatement(countSql); //条件交给mybatis parameterHandler.setParameters(preparedStatement); //让mybatis执行这个sql ResultSet resultSet = preparedStatement.executeQuery(); int count = 0; if (resultSet.next()) { count = resultSet.getInt(1); } resultSet.close(); preparedStatement.close(); //limit 1 ,10 十条数据 总共可能有100 count 要的是 后面的100 pageBean.setTotal(count); //拼接分页语句(limit) 并且修改mysql本该执行的语句 String pageSql = buildPageSql(pageBean, sql); logger.debug("-pageSql:" + pageSql); //重新绑定分页sql metaObject.setValue(META_OBJECT_KEY_BOUND_SQL, pageSql); } //推进拦截器调用链 return invocation.proceed(); } /** * 入参对象中取得分页对象 * @param o * @return */ private PageBean getPageBean(Object o){ PageWrapperBean pageWrapperBean = null; PageBean pageBean = null; //类型判断 //入参为Map 上转类型 if(o instanceof Map){ //迭代Map MapparamMap = (Map ) o; Set keySet = paramMap.keySet(); for(String key:keySet){ Object valueObject = paramMap.get(key); if(valueObject instanceof PageWrapperBean){ pageWrapperBean = (PageWrapperBean) valueObject; break; } } //入参为 PageWrapperBean 上转类型 } else if(o instanceof PageWrapperBean){ pageWrapperBean = (PageWrapperBean) o; } pageBean = pageWrapperBean.getPage(); return pageBean; } /** * 构建记录条数countSql * @param originSql * @return */ private String bulidCountSql(String originSql){ //优化一下就是讲select 到from 之间的字符串替换为 count(1) 即可,这里仅为了方便 String countSql = "select count(1) from ("+originSql+") a"; return countSql; } /** * 构建分页sql * @param pageBean * @param originSql * @return */ private String buildPageSql(PageBean pageBean,String originSql){ //拼接分页语句(limit) 并且修改mysql本该执行的语句 String pageSql = originSql+" limit "+pageBean.getStart()+","+pageBean.getLimit(); return pageSql; }}
所有的分页都是PageInterceptorPlugin,因为传入的分页信息都会封装到pageWrapperBean 当中的page对象中,同时page对象也是前端传输分页信息。本来就请求的当前线程所持有,完全不用考虑线程安全,不需要采用PageHelper中ThreadLocal去保存分页信息。
拦截StatementHandler中从StatementHandler取出原始的查询sql,通过ParameterHandler 获取分页参数。 然后将原始的sql通过上述代码中buildPageSql方法进行构建分页sql/** * 自动运算相关数值 */ public void autoCount(){ if(limit!=null&&limit>0&&total!=null){ pageCount = total/limit+1; } if(limit!=null&&limit>0&¤tPage>=1) { start = (currentPage - 1) * limit; } }
我将这个方法放入了,page对象的set方法中,一旦有分页参数变化,分页信息就会有所变化。
satrt的起始的索引的运算公式就是index = (currentPage - 1) * limit
当前页-1*limit
获取记录总条数的sql通上述的bulidCountSql去构建。“select count(1) from (”+originSql+") a";
最后使用preparedStatement这个命令对象完成查询,将返回的分页参数,设置到page对象中。
最后在外部使用getPage()方法既可以拿到所有的分页信息。
入参对象使用类继承的方式
public class TestEntity extends PageWrapperBean { private Integer id; private String text; }
分页使用方式
TestEntity testEntity = new TestEntity(); PageBean pageBean = new PageBean(); pageBean.setCurrentPage(1); pageBean.setLimit(10); //设定分页参数 testEntity.setPage(pageBean); //开始查询 testMapper.select(testEntity); //取得分页信息 PageBean pageInfo = testEntity.getPage();
入参对象使用@Param 这种Map形式的
Listquery(@Param("page")PageWrapperBean pageWrapperBean);
分页使用方式
PageBean pageBean = new PageBean(); pageBean.setCurrentPage(1); pageBean.setLimit(10); //设定分页参数 PageWrapperBean pageWrapperBean = new PageWrapperBean(pageBean); //开始查询 testMapper.query(pageWrapperBean); //取得分页信息 PageBean pageInfo = pageWrapperBean.getPage(); }
最后附上源码地址,后续还继续优化更新
转载地址:http://mhugn.baihongyu.com/