Mybatis自定义轻量级分页组件(易集成,易拓展)

Mybatis自定义轻量级分页组件(易集成,易拓展)

其实github有一个叫做PageHelper的开源分页组件,我也用过,封装的还可以。只是感觉他的量级偏重,其实很多参数,都是我们开发中不需要的参数,而且它的获取分页信息方式是通过构造方法,不是很优雅。所以我在查阅完他的源码后,结合自己的需求,实现了一个轻量级,便携式的分页组件SmallPage

在这里插入图片描述

SmallPage

轻量级分页组件,基于入参类型

核心依赖

		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.0</version>
		</dependency>

功能描述

  • 自动拦截进行分页
  • 基于Dao层方法入参类型
  • 自动count总条数

核心类 - PageInterceptorPlugin

  • 使用方式,只需将Dao查询方法的入参Bean继承于PageWrapperBean,设定Page对象则可以实现分页,分页完成后的信息也保存在Page对象中
  • 入参类型可以parameterType 定义,也可以@Param("")传入,多个入参时,只要其中一个入参类型为PageWrapperBean的上转类型,就可以实现分页
  • 入参类型不为PageWrapperBean的上转类型时,直接推进拦截链

实现方式

现在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
            Map<String,Object> paramMap = (Map<String, Object>) o;
            Set<String> 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&&currentPage>=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形式的

    List<TestEntity> query(@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();
    }

最后附上源码地址,后续还继续优化更新
https://github.com/DavidLei08/SmallPage

  • 17
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值