利用java反射解决Mybatis Pagehelper插件联表查询分页不准确的问题

前言

反射可以获取任何一个已知名称的类中定义的属性,不论它是公有还是私有!使用反射你会发现原来java可以如此灵活,你不用再无穷无尽地写循环、定义变量,它会让你的代码简洁大方,耦合性更低。我本身刚刚接触到反射,希望通过一个分页功能的实现和大家一起去学习应用反射,在编程的不归路上越走越远。

背景

很多人在mybatis开发中都喜欢使用pagehelper当做自己的分页插件,但是这个插件在使用过程中一直存在一个问题——多表关联一对多的情况下分页会出现不准确的情况,出现这种情况的原因是当你的查询语句主表和附表之间是一对多的关系时,当sql语句查询完成后mybatis会以主表为主将附表信息封装到你定义的主表对应的字段中。本人也曾尝试去仔细阅读pageheloer的api文档,但是找来找去耽误了很长时间也没找到具体行之有效的方法。索性自己封装一个pageutil,总体来说效果不错~

使用框架与结构的定义

在案例中我们使用的前端框架是vue,这里不做过多的介绍,如果大家感兴趣我会在后期写一篇使用vue-cli搭建vue项目的博客。后端是springboot。

实例

首先封装一个pageBO,用来存放返回前端的数据

@DatapublicclassPageBO{privateintsize;//list长度privateinttotal;//总查询数privateintstartRow;//开始条数privateintendRow;//结束条数privateintfirstPage;//第一页页码privateintlastPage;//最后一页页码privateintpages;//页数privateint[]navigatepageNums;//页码数组privateintpageNum;//当前页码privateintpageSize;//每页数据量Object list;//数据列表}

pageUtil源码:

importcom.easy.xbo.PageBO;importorg.apache.poi.ss.formula.functions.T;importjava.lang.reflect.Method;importjava.util.List;publicclassPageUtil{publicstaticPageBOgetPageBOData(Object service,Object param)throwsException{PageBO pageBO=newPageBO();ClassPageclass=service.getClass();ClassParamclass=param.getClass();Method PagemethodSelectCount=Pageclass.getDeclaredMethod("selectCount",Paramclass);//可以通过Method类的invoke方法调用类方法//查询总数,此时注意需要传递两个参数,第一个是方法所在的类,第二个是方法需要的参数 //invoke方法第一个参数是固定的,是方法所在类,第二个是可选的,是方法所需参数intcount=Integer.parseInt(PagemethodSelectCount.invoke(service,param)+"");Method ParammethodGet=Paramclass.getDeclaredMethod("getWhere");Object where=ParammethodGet.invoke(param);ClassWhereclass=where.getClass();//第几页Method WheremethodPage=Whereclass.getDeclaredMethod("getPage");intpage=Integer.parseInt(WheremethodPage.invoke(where)+"");//每页条数Method WheremethodRows=Whereclass.getDeclaredMethod("getRows");intpageSize=Integer.parseInt(WheremethodRows.invoke(where)+"");//计算本页从哪一条数据开始intstartRow=(page-1)*pageSize;Method WheremethodStartrow=Whereclass.getDeclaredMethod("setStartrow",int.class);WheremethodStartrow.invoke(where,startRow);//计算页数intpages=count/pageSize;if(count%pageSize>0){pages++;}//存放页码的数组int[]NavigatepageNums=newint[pages];for(inti=1;i<=pages;i++){NavigatepageNums[i-1]=i;}pageBO.setTotal(count);pageBO.setStartRow(startRow+1);pageBO.setFirstPage(1);pageBO.setPages(pages);pageBO.setLastPage(pages);pageBO.setNavigatepageNums(NavigatepageNums);pageBO.setPageSize(pageSize);pageBO.setPageNum(page);Method PagemethodSelectPage=Pageclass.getDeclaredMethod("selectPage",Paramclass);Object pagelist=PagemethodSelectPage.invoke(service,param);pageBO.setList(pagelist);pageBO.setSize(((List)pagelist).size());intendRow=startRow+pageBO.getSize();pageBO.setEndRow(endRow);returnpageBO;}}

Object 类型的 service 中必须有两个方法:一个是 selectCount 用来查询分页的total,也就是所有符合查询条件的数据总数;一个是 selectPage 这个方法查询具体数据。

Object 类型的 param 包含两个参数:一个是where;一个是xdo。(具体结构参照

然后就是对反射的使用(具体参照

以上是公用部分的代码

下面举个栗子~~:

公司与员工的关系,一个公司有多个员工,属于一对多的关系,这里插一句lombok很好用,有兴趣的小伙伴可以了解一下

CompanyDO源码:

```java

package com.easy.xdo;

import lombok.Data;

@Data

public class CompanyDO {

//演示公司类

private String id;

private String name;

private String address;

private List<EmployeDO> employes;

}

```

CompanyWhere 源码:

```java

package com.easy.xdo;

import lombok.Data;

@Data

public class CompanyWhere extends CompanyDO{

/**

    * page -1 默认不分页

    */

    private int page;

    private int rows;

    private int startrow;

    private String suffix;

}

```

CompanyParam 源码:

```java

package com.easy.xdo;

import lombok.Data;

@Data

public class CompanyParam {

private CompanyDO xdo;

private CompanyWhere where;

}

```

EmployeDO源码:

```java

package com.easy.xdo;

import lombok.Data;

@Data

public class EmployeDO {

    //演示员工类

private String id;

private String name;

private String age;

private String companyid;

}

CompanyController源码:

package com.easy.controller;

import io.swagger.annotations.Api;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import com.easy.service.CompanyService;

import com.easy.util.PageUtil;

import com.easy.xbo.PageBO;

import com.easy.xdo.CompanyParam;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.PostMapping;

import io.swagger.annotations.ApiOperation;

@Api("API-公司接口")

@RestController

@RequestMapping("api/v1/company")

public class CompanyController {

@Autowired

    private CompanyService companyService;

@ApiOperation(value = "分页查询")

    @PostMapping("/page")

    public PageBO page() throws Exception{

CompanyParam param=new CompanyParam();

        return PageUtil.getPageBOData(companyService,param);

    }

}

CompanyService 源码:

package com.easy.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.easy.dao.CompanyDAO;

import com.easy.xdo.CompanyDO;

import com.easy.xdo.CompanyParam;

public class CompanyService {

@Autowired

private CompanyDAO companyDAO;

public List<CompanyDO> selectPage(CompanyParam param){

        return companyDAO.selectList(param);

    }

    public int selectCount(CompanyParam param){

        return companyDAO.selectCount(param);

    }

}

dao源码

package com.easy.dao;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.easy.xdo.CompanyDO;

import com.easy.xdo.CompanyParam;

@Repository

public interface CompanyDAO {

List<CompanyDO> selectList(CompanyParam param);

    int  selectCount(CompanyParam param);

}

xml源码

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.easy.dao.CompanyDAO">

  <resultMap id="BaseResultMap" type="com.easy.xdo.CompanyDO">

    <id column="id" property="id" />

    <result column="name" property="name" />

    <result column="address" property="address" />

    <collection property="employes" ofType="com.easy.xdo.EmployeDO">

      <id column="eid" property="id" />

      <result column="ename" property="name"/>

      <result column="age" property="age"/>

    </collection>

  </resultMap>

  <sql id="sqlBase">

    c.id,c.name,c.address,e.id as eid,e.name as ename,e.age

  </sql>

  <sql id="WhereModelSql">

    <if test="where != null">

      <where>

        <if test="where.id != null and where.id != ''"> AND c.id=#{where.id} </if>

        <if test="where.name != null and where.name != ''"> AND c.name=#{where.name} </if>

        <if test="where.address != null and where.address != ''"> AND c.address=#{where.address} </if>

      </where>

    </if>

  </sql>

  <select id="selectList" resultMap="BaseResultMap" parameterType="com.easy.xdo.CompanyParam">

    select

    <include refid="sqlBase" />

    from company c left join employe e on  c.id=e.companyid 

    where c.id in (select page.id from (select id from company

    <include refid="WhereModelSql" />  limit #{where.startrow},#{where.rows})

    page)

  </select>

  <select id="selectCount" resultType="java.lang.Integer" parameterType="com.easy.xdo.CompanyParam">

    select

    count(c.id)

    from company

    <include refid="WhereModelSql" />

  </select>

</mapper>

这里特殊说明一下in语句子语句中不能使用limit,但是孙语句中可以使用,很蛋疼所以多包了一层,sql语句这里大家可以根据自己的情况随意写,只要最后传参进去出来的数据准确就ok了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355