/** * 分页类 * * @author wl */ @Data public class Page implements Serializable { /** * 每页显示数量 */ @JsonProperty("per_page") private int pageSize; /** * 当前页码 */ @JsonProperty("current_page") private int curPage; /** * 总页数 */ @JsonProperty("total_pages") private int pages; /** * 总记录数 */ private int total; /** * 当前页数量 */ private int count; /** * 链接 */ private Link links; /** * 自动统计分页总数 */ private boolean autoCount; /** * 默认无参构造器,初始化各值 */ public Page() { this.pageSize = 20; this.curPage = 1; this.pages = 0; this.total = 0; this.count = 0; this.autoCount = true; } public Page(Page page) { this.pageSize = page.pageSize; this.curPage = page.curPage; this.pages = page.pages; this.total = page.total; this.count = page.count; this.links = page.links; this.autoCount = page.autoCount; } public void calculate(int total) { this.setTotal(total); this.pages = (total / pageSize) + ((total % pageSize) > 0 ? 1 : 0); // 如果当前页码超出总页数,自动更改为最后一页 //this.curPage = this.curPage > pages ? this.pages : this.curPage; if (curPage > pages) { throw new IllegalStateException("超出查询范围"); } } /** * 获取分页起始位置和偏移量 * * @return 分页起始位置和偏移量数组 */ public int[] paginate() { // 数量为零时,直接从0开始 return new int[]{total > 0 ? (curPage - 1) * pageSize : 0, pageSize}; } }View Code
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) // 或者: @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})拦截的方法不同,在拦截时获取的参数不同。逻辑会存在细微的区别。
/** * 分页SQL插件 * * @author wl * @date 2021-5-26 */ @Intercepts( @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) ) public class PagePlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 分页插件拦截处理 useMetaObject(invocation); return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }View Code
private void useMetaObject(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); // 调用MetaObject 反射类处理 //分离代理对象链 while (metaObject.hasGetter("h")) { Object obj = metaObject.getValue("h"); metaObject = SystemMetaObject.forObject(obj); } while (metaObject.hasGetter("target")) { Object obj = metaObject.getValue("target"); metaObject = SystemMetaObject.forObject(obj); } BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql"); // 存在分页标识 Page page = getPage(boundSql); if (Objects.nonNull(page)) { int total = getTotalSize(statementHandler, (Connection) invocation.getArgs()[0]); if (total <= 0) { // 返回数量小于零,查询一个简单的sql,不去执行明细查询 【基于反射,重新设置boundSql】 metaObject.setValue("delegate.boundSql.sql", "select * from (select 0 as id) as temp where id>0"); metaObject.setValue("delegate.boundSql.parameterMappings", Collections.emptyList()); metaObject.setValue("delegate.boundSql.parameterObject", null); } else { page.calculate(total); String sql = boundSql.getSql() + " limit " + (page.getCurPage() - 1) * page.getPageSize() + ", " + page.getPageSize(); metaObject.setValue("delegate.boundSql.sql", sql); } } }
/*** * 获取分页的对象 * @param boundSql 执行sql对象 * @return 分页对象 */ private Page getPage(BoundSql boundSql) { Object obj = boundSql.getParameterObject(); if (Objects.isNull(obj)) { return null; } Page page = null; if (obj instanceof Page) { page = (Page) obj; } else if (obj instanceof Map) { // 如果Dao中有多个参数,则分页的注解参数名必须是page try { page = (Page) ((Map) obj).get("page"); } catch (Exception e) { return null; } } // 不存在分页对象,则忽略下面的分页逻辑 if (Objects.nonNull(page) && page.isAutoCount()) { return page; } return null; }
/*** * 获取统计sql * @param originalSql 原始sql * @return 返回统计加工的sql */ private String getCountSql(String originalSql) { // 统一转换为小写 originalSql = originalSql.trim().toLowerCase(); // 判断是否存在 limit 标识 boolean limitExist = originalSql.contains("limit"); if (limitExist) { originalSql = originalSql.substring(0, originalSql.indexOf("limit")); } boolean distinctExist = originalSql.contains("distinct"); boolean groupExist = originalSql.contains("group by"); if (distinctExist || groupExist) { return "select count(1) from (" + originalSql + ") temp_count"; } // 去掉 order by boolean orderExist = originalSql.contains("order by"); if (orderExist) { originalSql = originalSql.substring(0, originalSql.indexOf("order by")); } // todo left join还可以考虑优化 int indexFrom = originalSql.indexOf("from"); return "select count(*) " + originalSql.substring(indexFrom); }
/** * 查询总记录数 * * @param statementHandler mybatis sql 对象 * @param conn 链接信息 */ private int getTotalSize(StatementHandler statementHandler, Connection conn) { ParameterHandler parameterHandler = statementHandler.getParameterHandler(); String countSql = getCountSql(statementHandler.getBoundSql().getSql()); PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = (PreparedStatement) conn.prepareStatement(countSql); parameterHandler.setParameters(pstmt); rs = pstmt.executeQuery(); if (rs.next()) { // 设置总记录数 return rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (rs != null) { rs.close(); } if (pstmt != null) { pstmt.close(); } } catch (SQLException e) { e.printStackTrace(); } } return 0; }
/** * 获取进项税信息 * * @param kid 单号 * @param page 分页参数 * @return 结果 */ @SelectProvider(type = LifeLogSqlProvider.class, method = "listInputTaxSql") List<TaxInput> listInputTax(@Param("kid") Integer kid, @Param("page") Page page);设置具体查询sql
public String listInputTaxSql(@Param("kid") Integer kid, @Param("page") Page page){ return new SQL() .select("input_tax_id, k_id,sup_id,k_sup_id,org_id,a.tax,invoice_title,remark") .from("tx_sup_goods_input_tax a") .innerJoin("tx_tax b on a.tax=b.tax") .where(kid>0,"a.k_id = #{kid}") .orderBy("a.k_id desc") .build(); }
设置查询接口
/** * 获取测试税务信息 * * @return 返回存储数据 */ @GetMapping("/tax") public List<TaxInput> listInputTax(int kid, Page page) { page.setAutoCount(true); List<TaxInput> taxInputList = lifeLogMapper.listInputTax(kid, page); if(page.getTotal()==0){ return Collections.emptyList(); }else{ return taxInputList; } }启动项目,运行效果如图:
private void useReflection(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); // 存在分页标识 Page page = getPage(boundSql); if (Objects.nonNull(page)) { int total = getTotalSize(statementHandler, (Connection) invocation.getArgs()[0]); if (total <= 0) { // 返回数量小于零,查询一个简单的sql,不去执行明细查询 【基于反射,重新设置boundSql】 Field fieldParameterMappings = BoundSql.class.getDeclaredField("parameterMappings"); fieldParameterMappings.setAccessible(true); fieldParameterMappings.set(boundSql, Collections.emptyList()); Field fieldSql = BoundSql.class.getDeclaredField("sql"); fieldSql.setAccessible(true); fieldSql.set(boundSql, "select * from (select 0 as id) as temp where id>0"); Field fieldParameterObject = BoundSql.class.getDeclaredField("parameterObject"); fieldParameterObject.setAccessible(true); fieldParameterObject.set(boundSql, null); } else { page.calculate(total); Field field = BoundSql.class.getDeclaredField("sql"); field.setAccessible(true); // 设置分页的SQL代码 field.set(boundSql, boundSql.getSql() + " limit " + (page.getCurPage() - 1) * page.getPageSize() + ", " + page.getPageSize()); } } }分页拦截函数版本3
private void useMetaObjectPlus(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); // 存在分页标识 Page page = getPage(boundSql); if (Objects.nonNull(page)) { int total = getTotalSize(statementHandler, (Connection) invocation.getArgs()[0]); MetaObject metaObject = SystemMetaObject.forObject(boundSql); if (total <= 0) { // 返回数量小于零,查询一个简单的sql,不去执行明细查询 【基于反射,重新设置boundSql】 metaObject.setValue("sql", "select * from (select 0 as id) as temp where id>0"); metaObject.setValue("parameterMappings", Collections.emptyList()); metaObject.setValue("parameterObject", null); } else { page.calculate(total); boolean limitExist = boundSql.getSql().trim().toLowerCase().contains("limit"); if (!limitExist) { String sql = boundSql.getSql() + " limit " + (page.getCurPage() - 1) * page.getPageSize() + ", " + page.getPageSize(); metaObject.setValue("sql", sql); } } } }综合对比,推荐分页拦截函数版本3。
/*** * 查看注解的自定义插件是否存在 * @param mappedStatement 参数 * @return 返回检查结果 * @throws Throwable 抛出异常 */ private boolean existEnhancer(MappedStatement mappedStatement) throws Throwable { //获取执行方法的位置 String namespace = mappedStatement.getId(); //获取mapper名称 String className = namespace.substring(0, namespace.lastIndexOf(".")); //获取方法名aClass String methodName = namespace.substring(namespace.lastIndexOf(".") + 1); Class<?> aClass = Class.forName(className); for (Method method : aClass.getDeclaredMethods()) { if (methodName.equals(method.getName())) { // 暂不考虑方法被重载 Enhancer enhancer = method.getAnnotation(Enhancer.class); if (Objects.nonNull(enhancer) && enhancer.autoPageCount()) { // 设置page return true; } } } return false; }
/*** * 获取分页的对象 * @param boundSql 执行sql对象 * @return 分页对象 */ private Page getPage(BoundSql boundSql) { Page page = null; // 参考源码,调试发现为一个map对象 Map<String, Object> parameterList = (Map<String, Object>) boundSql.getParameterObject(); if (Objects.isNull(parameterList)) { return null; } for (Map.Entry<String, Object> entry : parameterList.entrySet()) { if (entry.getValue() instanceof Page) { page = (Page) entry.getValue(); break; } } if (Objects.nonNull(page)) { return page; } return null; }