利用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了

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。