天天看點

利用java反射解決Mybatis Pagehelper插件聯表查詢分頁不準确的問題前言

前言

反射可以擷取任何一個已知名稱的類中定義的屬性,不論它是公有還是私有!使用反射你會發現原來java可以如此靈活,你不用再無窮無盡地寫循環、定義變量,它會讓你的代碼簡潔大方,耦合性更低。我本身剛剛接觸到反射,希望通過一個分頁功能的實作和大家一起去學習應用反射,在程式設計的不歸路上越走越遠。

背景

很多人在mybatis開發中都喜歡使用pagehelper當做自己的分頁插件,但是這個插件在使用過程中一直存在一個問題——多表關聯一對多的情況下分頁會出現不準确的情況,出現這種情況的原因是當你的查詢語句主表和附表之間是一對多的關系時,當sql語句查詢完成後mybatis會以主表為主将附表資訊封裝到你定義的主表對應的字段中。本人也曾嘗試去仔細閱讀pageheloer的api文檔,但是找來找去耽誤了很長時間也沒找到具體行之有效的方法。索性自己封裝一個pageutil,總體來說效果不錯~

使用架構與結構的定義

在案例中我們使用的前端架構是vue,這裡不做過多的介紹,如果大家感興趣我會在後期寫一篇使用vue-cli搭建vue項目的部落格。後端是springboot。

執行個體

首先封裝一個pageBO,用來存放傳回前端的資料

@Data
public class PageBO {
    private int size;//list長度
    private int total;//總查詢數
    private int startRow;//開始條數
    private int endRow;//結束條數
    private int firstPage;//第一頁頁碼
    private int lastPage;//最後一頁頁碼
    private int pages;//頁數
    private int[] navigatepageNums;//頁碼數組
    private int pageNum;//目前頁碼
    private int pageSize;//每頁資料量
    Object list;//資料清單
}
           

pageUtil源碼:

import com.easy.xbo.PageBO;
import org.apache.poi.ss.formula.functions.T;
import java.lang.reflect.Method;
import java.util.List;
public class PageUtil {
    public static PageBO getPageBOData(Object service,Object param) throws  Exception{
        PageBO pageBO=new PageBO();
        Class Pageclass=service.getClass();
        Class Paramclass=param.getClass();
        Method PagemethodSelectCount=Pageclass.getDeclaredMethod("selectCount",Paramclass);
        //可以通過Method類的invoke方法調用類方法
        //查詢總數,此時注意需要傳遞兩個參數,第一個是方法所在的類,第二個是方法需要的參數 
        //invoke方法第一個參數是固定的,是方法所在類,第二個是可選的,是方法所需參數
        int count=Integer.parseInt(PagemethodSelectCount.invoke(service,param)+"");
        Method ParammethodGet=Paramclass.getDeclaredMethod("getWhere");
        Object where=ParammethodGet.invoke(param);
        Class Whereclass=where.getClass();
        //第幾頁
        Method WheremethodPage=Whereclass.getDeclaredMethod("getPage");
        int page=Integer.parseInt(WheremethodPage.invoke(where)+"");
        //每頁條數
        Method WheremethodRows=Whereclass.getDeclaredMethod("getRows");
        int pageSize=Integer.parseInt(WheremethodRows.invoke(where)+"");
        //計算本頁從哪一條資料開始
        int startRow=(page-1)*pageSize;
        Method WheremethodStartrow=Whereclass.getDeclaredMethod("setStartrow",int.class);
        WheremethodStartrow.invoke(where,startRow);
        //計算頁數
        int pages=count/pageSize;
        if(count%pageSize>0){
            pages++;
        }
        //存放頁碼的數組
        int [] NavigatepageNums=new int[pages];
        for(int i=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());
        int endRow=startRow+pageBO.getSize();
        pageBO.setEndRow(endRow);
        return pageBO;
    }
}
           

Object 類型的 service 中必須有兩個方法:一個是 selectCount 用來查詢分頁的total,也就是所有符合查詢條件的資料總數;一個是 selectPage 這個方法查詢具體資料。

Object 類型的 param 包含兩個參數:一個是where;一個是xdo。(具體結構參照)

然後就是對反射的使用(具體參照)

以上是公用部分的代碼

下面舉個栗子~~:

公司與員工的關系,一個公司有多個員工,屬于一對多的關系,這裡插一句lombok很好用,有興趣的小夥伴可以了解一下

CompanyDO源碼:

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 源碼:

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 源碼:

package com.easy.xdo;
import lombok.Data;
@Data
public class CompanyParam {
	private CompanyDO xdo;
	private CompanyWhere where;
}
           

EmployeDO源碼:

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了

繼續閱讀