MybatisPlus的联表分页查询+动态拼接主子表条件

本文介绍了在使用MyBatis-Plus进行多表联查时如何处理子表查询条件和字段显示,特别是在分页查询中遇到的效率和复杂性问题。通过引入mybatis-plus-join库,展示了如何进行联表查询和分页,同时讨论了解决联表分页查询丢失数据的问题。此外,还分享了一个利用SFunction工具类动态生成SQL的方法,以适应不同的实体字段和条件。
摘要由CSDN通过智能技术生成

问题:使用mybatis-plus进行列表分页查询数据时(多表联查),如果查询条件是子表的时候,或者显示的字段是子表的字段,我们应该怎么办?

一、我们先看看常规的查询是怎么实现的,这里的表关系是:一个user表对应多个car表(car的pkUser字段与user的id关联)

1.单表的分页查询(string是前端传的条件,然后我们动态的拼接wrapper。)

注:这里应该是遍历json数据,然后才动态拼接wrapper,这里就略过了

在这里插入图片描述

2.多表的分页查询(先查主表然后再查子表)

在这里插入图片描述

3.多表的分页查询,子表条件(先根据子表条件查询子表,然后把主表id提取出来,拼接到wrapper中,这样就实现了子表条件动态拼接)

在这里插入图片描述

问题:第3种情况虽然可以实现子表条件的动态拼接,但是效率有点低,而且不适用复杂的情况

二、下面我们来看看进阶版的mybatisplus的联表分页查询

引入的依赖如下:

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>com.github.yulichang</groupId>
        <artifactId>mybatis-plus-join</artifactId>
        <version>1.3.11</version>
    </dependency>

4.我们先看一下数据库的数据,id为1,2,3的user数据分别对应car表中id为1,2,3,4,5,6,7的数据。在这里插入图片描述下面的代码是mybatis-plus的联表查询和联表分页查询,一个user表对应多个car表

在这里插入图片描述
为什么要把两个查询都列出来,因为这里有个坑,我们先看一下联表查询的sql,以及最后返回的数据
在这里插入图片描述
这个是正常的联表查询,没有分页效果,因为是一对多的情况,所以sql查到的数据是16条,而不是user表中的12条,但是最后返回的list中是12条,而且一对多的关系也映射进去了,这个查询没有问题,但是联表分页查询时就有问题了
在这里插入图片描述
执行selectJoinPage方法后我们发现,查询到的总条数是12条,这个是单查主表数据用来拿到user的总条数,这个没什么问题,问题出现在后边的联表分页查询,经过之前的selectJoinList联表查询我们知道,正常的无条件一对多查询后的数据应该是16条,但是联表分页查询的时候,我们只拿到了10条数据,丢失了6条数据,这就是为什么最后查询到的数据只有6条的原因。

问题出现了我们要怎么解决呢?下面是我推荐的解决方案
注:只有联表分页查询的时候会出现这个情况,正常的单表分页查询是不会出现这个问题的;我们使用这个方法来判断是否出现一对多的情况
boolean resultMap = mpjLambdaWrapper.isResultMap();//是否存在一对多
在这里插入图片描述
然后我们执行一下看看效果
在这里插入图片描述
我们发现成功查询到了10条主表数据,总条数12条也没问题,关联关系也对上了,问题解决了

5.看到这我们已经基本的解决了联表分页查询主子表条件拼接的问题,以及返回值的主子表关联的问题。但是问题真的解决了么,细心的小伙伴会发现,我们的主题是动态的拼接sql,但是看代码会发现我们里面的实体对象UserExtend和UserExtend.class都是写死的,那么我们怎么实现动态的sql拼接呢?

在这里插入图片描述
这个是SFunctiionUtil工具类

package com.kuang.utils.SFunction的工具类;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**  最后一次更新时间:2022-12-29 14:40 **/
public class SFunctionUtil {
    /** 可序列化 */
    private static final int FLAG_SERIALIZABLE = 1;

    //mybatisplus的lambdaQueryWrapper在使用eq方法时,只能传入User::getId   不可以传入a->a.getId()
    //MyBatisPlus的条件构造器不会真的去调用SFunction这个函数式接口而是只解析实际的方法名.如果解析的是Lambbda表达式,
    //那么方法名跟数据库的列名匹配不上就会报错; 如果是方法引用那么方法名通过is/gey/set规则就能找到相应的字段名然后在根据规则转换成数据库表的列名.
    /**
     * SFunctionUtil.getSFunctionByName(UserExtend.class,"getUserName",String.class)  等同于  UserExtend::getUserName
     *
     * @param entityClass 对象的class  UserExtend.class
     * @param name        想要转换的名称   UserExtend::getUserName  中的 getUserName
     * @param typeClass   userName 字段的类型
     * @return
     */
    public static <T> SFunction<T,String> getSFunctionByName(Class<T> entityClass,String name,Class typeClass) {
        SFunction<T,String> func = null;
        final MethodHandles.Lookup lookup = MethodHandles.lookup();
        //po的返回Integer的一个方法
        MethodType methodType = MethodType.methodType(typeClass, entityClass);
        final CallSite site;
        try {
            //方法名叫做:getSecretLevel  转换为 SFunction function interface对象
            site = LambdaMetafactory.altMetafactory(lookup,
                    "invoke",
                    MethodType.methodType(SFunction.class),
                    methodType,
                    lookup.findVirtual(entityClass, name, MethodType.methodType(typeClass)),
                    methodType,FLAG_SERIALIZABLE);
            func = (SFunction) site.getTarget().invokeExact();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return func;
    }

    /**
     * 把对象中的每个字段的‘方法引用’->UserExtend::getUserName 返回的SFunction放到map中
     * @param tClass 查询对象的class
     * @param <T>
     * @return map.put("id",UserExtend::getId);
     *         map.put("userName",UserExtend::getUserName);
     * @throws ClassNotFoundException
     */
    public static <T> Map<String,SFunction<T,String>> getSFunctionMapByClass(Class<T> tClass) {
        //1.通过plus中的wrapper获取到对象中的全部字段
        LambdaQueryWrapper<T> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.select(tClass,i->true);
        String sqlSelect = lambdaQueryWrapper.getSqlSelect();
        String[] underlineNameList = sqlSelect.split(",");//下滑线名字集合
        //2.组装一个map,map中存放方法名 和 对应字段的数据类型   map.put("userName",String.class);
        Map<String,Class> methodNameAndTypeClassMap = new HashMap<>();
        //获取所有的bean中所有的成员变量(包括父类)的名称以及类型
        List<Field> list = new ArrayList<>();
        Class classNew = null;
        try{
            classNew = Class.forName(tClass.getName());
        }catch (Exception e){
            e.printStackTrace();
        }
        while (classNew != null) {
            Field[] declaredFields = classNew.getDeclaredFields();
            for (Field field : declaredFields) {
                if(field.getName().equals("serialVersionUID")){
                    continue;
                }
                if (!list.contains(field)) {
                    list.add(field);
                }
            }
            classNew = (Class<T>) classNew.getSuperclass();
        }
        //根据字段名分组
        Map<String, Field> fieldMap = list.stream().collect(Collectors.toMap(Field::getName, Function.identity(), (a, b) -> a));
        for (String underlineName : underlineNameList) {
            String camelName = StrUtil.toCamelCase(underlineName);//下滑线转驼峰
            Field field = fieldMap.get(camelName);
            if(Objects.nonNull(field)){
                //获取到对应字段的数据类型class,放到methodNameAndTypeClassMap中
                String classTypeName = field.getGenericType().getTypeName();
                try{
                    methodNameAndTypeClassMap.put(camelName,Class.forName(classTypeName));
                }catch (Exception e){
                    e.printStackTrace();
                    System.out.println(e);
                }
            }
        }
        //3.把每个字段都转成 SFunction函数
        Map<String,SFunction<T,String>> functionMap = new HashMap<>();
        for (Map.Entry<String, Class> entry : methodNameAndTypeClassMap.entrySet()) {
            String camelName = entry.getKey();
            String methodName = "get" + camelName.substring(0, 1).toUpperCase() + camelName.substring(1);//转成对应的get方法  getUserName

            //getSFunctionByName(UserExtend.class,"getUserName",String.class);
            SFunction sFunction = getSFunctionByName(tClass, methodName, entry.getValue());

            functionMap.put(camelName,sFunction);
        }
        return functionMap;
    }
}

这个是ClassUtilsNioa工具类

package com.kuang.utils.工具类;


import cn.hutool.core.util.StrUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 最后一次更新时间:2022-06-08 16:08:31
 */
public class ClassUtilsNioa {
    //获取到对象的第index个的字段名称   0为第一个字段
    public static <T> String getNameByIndex(T entity, int index) {
        return entity.getClass().getDeclaredFields()[index].getName();
    }

    //为对象中第index个字段赋值
    public static <T> void setVByIndex(T entity, int index, String setValue, String format) {
        String setName = getNameByIndex(entity, index);
        setV(entity, setName, setValue, format);
    }

    //1.批量对对象中的第index个字段赋值
    public static <T> void setVByIndexList(T entity, Map<Integer, String> map, String format) {
        for (Integer index : map.keySet()) {
            String setName = getNameByIndex(entity, index);
            setV(entity, setName, map.get(index), format);
        }
    }

    //2.批量对对象中的第index个字段赋值
    public static <T> void setVByIndexList(T entity, int[] indexList, String[] setValueList, String format) {
        if (indexList.length != setValueList.length) {
            return;
        }
        for (int i = 0; i < indexList.length; i++) {
            String setName = getNameByIndex(entity, indexList[i]);
            setV(entity, setName, setValueList[i], format);
        }
    }

    //为对象中的某个名称的字段赋值
    public static <T> void setV(T entity, String setName, String setValue) {
        setV(entity, setName, setValue, "");
    }

    //为对象中的某个名称的字段赋值
    public static <T> void setV(T entity, String setName, String setValue, String format) {
        Map<String, String> map = new HashMap<>();
        map.put(setName, setValue);
        setV(entity, map, format);
    }

    //批量为对象中的某个名称的字段赋值
    public static <T> void setV(T entity, Map<String, String> map) {
        setV(entity, map, "");
    }

    //批量为对象中的某个名称的字段赋值
    public static <T> void setV(T entity, Map<String, String> map, String format) {
        if (map == null || map.size() == 0) {
            return;
        }
        List<Field> list = getNameAll(entity);
        Map<String, List<Field>> fieldMap = list.stream().collect(Collectors.groupingBy(l -> l.getName()));
        Class clazz = entity.getClass();
        try {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String setName = entry.getKey();
                String setValue = entry.getValue();
                List<Field> fieldList = fieldMap.get(setName);
                if (fieldList != null && fieldList.size() > 0) {
                    String type = fieldList.get(0).getGenericType().toString();
                    setName = setName.substring(0, 1).toUpperCase() + setName.substring(1); // 将属性的首字符大写,方便构造get,set方法
                    if (type.equals("class java.lang.String")) { // 如果type是类类型,则前面包含"class ",后面跟类名
                        Method m = clazz.getMethod("set" + setName, String.class);
                        m.invoke(entity, setValue);
                    } else if (type.equals("class java.util.Date")) {
                        Method m = clazz.getMethod("set" + setName, Date.class);
                        Date date = new SimpleDateFormat((format == null || format.length() == 0) ? "yyyy-MM-dd" : format).parse(setValue);
                        m.invoke(entity, date);
                    } else if (type.equals("class java.lang.Integer")) {
                        Method m = entity.getClass().getMethod("set" + setName, Integer.class);
                        m.invoke(entity, Integer.valueOf(setValue));
                    } else if (type.equals("class java.lang.Double")) {
                        Method m = entity.getClass().getMethod("set" + setName, Double.class);
                        m.invoke(entity, Double.valueOf(setValue));
                    } else if (type.equals("class java.lang.Long")) {
                        Method m = entity.getClass().getMethod("set" + setName, Long.class);
                        m.invoke(entity, Long.valueOf(setValue));
                    } else if (type.equals("class java.lang.Boolean")) {
                        Method m = entity.getClass().getMethod("set" + setName, Boolean.class);
                        m.invoke(entity, Boolean.valueOf(setValue));
                    } else if (type.equals("class java.math.BigDecimal")) {
                        Method m = entity.getClass().getMethod("set" + setName, BigDecimal.class);
                        m.invoke(entity, new BigDecimal(setValue));
                    } else if (type.equals("int")) {
                        Method m = entity.getClass().getMethod("set" + setName, int.class);
                        m.invoke(entity, Integer.valueOf(setValue));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //获取到对象中第index个字段值
    public static <T> Object getVByIndex(T entity, int index) {
        String setName = getNameByIndex(entity, index);
        return getV(entity, setName);
    }

    //获取到对象中的某个名称的字段值
    public static <T> Object getV(T entity, String getName) {
        try {
            //获取到这个名字对应的Field
            List<Field> nameAll = getNameAll(entity);
            Map<String, List<Field>> map = nameAll.stream().collect(Collectors.groupingBy(n -> n.getName()));
            List<Field> fieldList = map.get(getName);
            String type = fieldList.get(0).getGenericType().toString();
            // 将属性的首字符大写,方便构造get,set方法
            getName = getName.substring(0, 1).toUpperCase() + getName.substring(1);
            Class<?> clazz = entity.getClass();
            //不对基础类型进行get  (因为一般的实体类没有int之类的类型,如果有父类中是有int等之类的,默认值是0,所以在get的时候不允许get出int之类的数据)
            if (type.equals("class java.lang.String") || type.equals("class java.util.Date") || type.equals("class java.lang.Integer") ||
                    type.equals("class java.lang.Double") || type.equals("class java.lang.Long") || type.equals("class java.lang.Boolean") ||
                    type.equals("class java.math.BigDecimal")
            ) {
                getName = getName.substring(0, 1).toUpperCase() + getName.substring(1); // 将属性的首字符大写,方便构造get,set方法
                Method m = entity.getClass().getMethod("get" + getName);
                Object value = m.invoke(entity);
                return value;
            }else{
                return null;
            }
        } catch (Exception e) {
            throw new RuntimeException("没有get"+getName + "方法");
        }
    }

    //获取到某个对象的所有非空字段值集合
    public static <T> List<Object> getVList(T entity) {
        return getVList(entity, 0, entity.getClass().getDeclaredFields().length);
    }

    //获取到某个对象在某个区间的全部非空字段值集合
    public static <T> List<Object> getVList(T entity, int begin, int end) {
        List<Object> objList = new ArrayList<>();
        Field[] fieldList = entity.getClass().getDeclaredFields();
        if (end > fieldList.length) {
            return null;
        }
        for (int i = begin; i < end; i++) {
            String name = fieldList[i].getName();
            Object v = getV(entity, name);
            if (!Objects.isNull(v)) {
                objList.add(v);
            }
        }
        return objList;
    }

    //获取到某个对象的全部非空字段值集合(包括父类)
    public static <T> List<Object> getVListAndSuper(T entity) throws Exception {
        List<Field> list = getNameAll(entity);//全部的字段名称集合
        List<Object> objList = new ArrayList<>();//非空字段集合
        for (Field field : list) {
            String name = field.getName();
            name = name.substring(0, 1).toUpperCase() + name.substring(1); // 将属性的首字符大写,方便构造get,set方法
            Method m = entity.getClass().getMethod("get" + name);
            Object value = m.invoke(entity); // 调用getter方法获取属性值
            if (!Objects.isNull(value)) {
                objList.add(value);
            }
        }
        return objList;
    }

    //获取到某个对象的全部字段集合
    public static <T> List<Field> getName(T entity) {
        Class clazz = entity.getClass();
        //获取所有的bean中所有的成员变量(包括父类)的名称以及类型
        List<Field> list = new ArrayList<>();
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            if(field.getName().equals("serialVersionUID")){
                continue;
            }
            if (!list.contains(field)) {
                list.add(field);
            }
        }
        return list;
    }

    //获取到某个对象的全部字段集合(包括父类)
    public static <T> List<Field> getNameAll(T entity) {
        Class clazz = entity.getClass();
        //获取所有的bean中所有的成员变量(包括父类)的名称以及类型
        List<Field> list = new ArrayList<>();
        while (clazz != null) {
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                if(field.getName().equals("serialVersionUID")){
                    continue;
                }
                if (!list.contains(field)) {
                    list.add(field);
                }
            }
            clazz = clazz.getSuperclass();
        }
        return list;
    }

    //获取到某个对象的所有非空字段值集合 返回Map
    public static <T> Map<String, Object> getVMapList(T entity) {
        return getVMapList(entity, 0, entity.getClass().getDeclaredFields().length, true);
    }

    //获取到某个对象的所有非空字段值集合 返回Map
    public static <T> Map<String, Object> getVMapList(T entity, boolean flag) {
        return getVMapList(entity, 0, entity.getClass().getDeclaredFields().length, flag);
    }

    //获取到某个对象在某个区间的全部非空字段值集合  返回Map  flag是判断是否转为下划线 true->转化为下划线 false->保持原来的值  默认为true
    public static <T> Map<String, Object> getVMapList(T entity, int begin, int end, boolean flag) {
        Map<String, Object> map = new HashMap<>();
        Field[] fieldList = entity.getClass().getDeclaredFields();
        if (end > fieldList.length) {
            return null;
        }
        for (int i = begin; i < end; i++) {
            String name = fieldList[i].getName();
            if ("serialVersionUID".equals(name)) {
                continue;
            }
            Object v = getV(entity, name);
            if (!Objects.isNull(v)) {
                if (flag) {
                    String simpleName = StrUtil.toUnderlineCase(name);//驼峰转下划线
                    map.put(simpleName, v);
                } else {
                    map.put(name, v);
                }
            }
        }
        return map;
    }

    //获取到某个对象的所有非空字段值集合 返回Map(包括父类)  flag:为空是否put  默认否
    public static <T> Map<String, Object> getVMapListAndSuper(T entity){
        return getVMapListAndSuper(entity, false);
    }

    //获取到某个对象的所有非空字段值集合 返回Map(包括父类)  flag:为空是否put  true:是  false:否
    public static <T> Map<String, Object> getVMapListAndSuper(T entity, boolean flag){
        List<Field> list = getNameAll(entity);//全部的字段Field集合
        Map<String, Object> map = new HashMap<>();//
        for (Field field : list) {
            String name = field.getName();
            if ("serialVersionUID".equals(name)) {
                continue;
            }
            Object value = getV(entity, name);

            if (Objects.isNull(value)) {
                //如果为空判断flag  如果是true则put一个空字符串如果是false则不进行操作
                if (flag) {
                    value = "";
                    map.put(name, value);
                }
            } else {
                //如果不为空,正常put
                map.put(name, value);
            }
        }
        return map;
    }
}

下面是完整的代码

//1.首先前台传给我们的json字符串,我们先转换成map形式
        //多表分页查询的条件   {"userName":"nioa1","car.name":"黑色"}
        Map<String, Object> conditionMap = new HashMap<>();
        conditionMap.put("userName","nioa1");
        conditionMap.put("car.colour","黑色");
        //2.我们在controller层先写好一对多关系的wrapper,这个位置我们是知道具体的class的
        Class primaryTableClass = UserExtend.class;//主表的class
        MPJLambdaWrapper<UserExtend> mpjLambdaWrapper = new MPJLambdaWrapper<UserExtend>()
                .selectAll(UserExtend.class)
                .selectCollection(CarExtend.class,UserExtend::getCarExtendList)
                .leftJoin(CarExtend.class,CarExtend::getPkUser,UserExtend::getId);
        //3.接下来我们需要做的就是动态的拼接sql了,在拼接前我们需要做一下准备操作,把主子表的字段对应的方法引用都提取出来
        //也就是说我们要把UserExtend和CarExtend中的字段的方法引用都拿出来,但是问题来了我们怎么知道他的主子表都有谁呢?
        //原来mybatis-plus在构建的时候就可以拿到所有的表信息(详细的过程可以去看一下官方的源码,我们这里只需要了解到这个方法可以获取到所有用了plus的实体即可)
        //包括但不限于UserExtend.class和CarExtend.class这两个反射类,只要是实现了plus的接口的实体都会在这里面 TableInfoHelper.getTableInfos()
        //4.我们先定义一个map缓存,可以在实例化的时候默认执行这个,可以不用每次都手动去转换了,使用的时候只需要用map中的数据就行
        /**
         *  用来储存每个表中的方法引用
         *  key: mybatis-plus缓存中的所有表名
         *  value: 每个表对应实体中的‘方法引用’     UserExtend::getId    UserExtend::getUserName
         *  Map<String, SFunction<T, String>   -->   map.put("id",UserExtend::getId);
         */
        Map<String,Map<String, SFunction<?, String>>> tableSFunctionCache = new ConcurrentHashMap<>();
        /**
         *  用来储存每个表中的方法引用
         *  key: mybatis-plus缓存中的所有表class
         *  value: 每个表对应实体中的‘方法引用’     UserExtend::getId    UserExtend::getUserName
         *  Map<String, SFunction<T, String>   -->   map.put("id",UserExtend::getId);
         */
        Map<Class,Map<String, SFunction<?, String>>> tableClassSFunctionCache = new ConcurrentHashMap<>();
        //获取所有实体映射表信息
        List<TableInfo> tableInfoList = TableInfoHelper.getTableInfos();
        if(CollectionUtil.isEmpty(tableInfoList)){
            return;
        }
        for (TableInfo tableInfo : tableInfoList) {
            String tableName = tableInfo.getTableName();//表名称
            Class entityType = tableInfo.getEntityType();//实体类型
            Map<String, SFunction<?, String>> functionMap = SFunctionUtil.getSFunctionMapByClass(entityType);//
            tableSFunctionCache.put(tableName,functionMap);
            tableClassSFunctionCache.put(entityType,functionMap);
        }
        //注:上边的操纵可以理解为   两个map一个是用来存名字的,一个是用来存class的
        //Map<String,SFunction<UserExtend,String>> userFunctionMap = new HashMap<>();
        //userFunctionMap.put("id",UserExtend::getId);
        //userFunctionMap.put("userCode",UserExtend::getUserCode);
        //userFunctionMap.put("userName",UserExtend::getUserName);
        //Map<String,SFunction<CarExtend,String>> carFunctionMap = new HashMap<>();
        //carFunctionMap.put("id",CarExtend::getId);
        //carFunctionMap.put("pkUser",CarExtend::getPkUser);
        //carFunctionMap.put("name",CarExtend::getName);
        //
        //Map<String,Map<String, SFunction<?,String>>> classFunctionMap = new HashMap<>();
        //classFunctionMap.put("user",userFunctionMap);
        //classFunctionMap.put("car",carFunctionMap);

        //5.当我们拿到所有表的每个方法引用后,我们就可以去拼接sql了
        for (Map.Entry<String, Object> entry : conditionMap.entrySet()) {
            String tableName = entry.getKey();//表名
            Object value = entry.getValue();//条件

            String[] split = tableName.split("\\.");
            if(split.length == 1){
                //如果长度是1,那么这个字段是主表的
                String primaryFieldName =  split[0];//主表的字段名
                Map<String, SFunction<?, String>> primaryFunctionMap = tableClassSFunctionCache.get(primaryTableClass);
                SFunction<?, String> fileSFunction = primaryFunctionMap.get(primaryFieldName);

                mpjLambdaWrapper.eq(fileSFunction,value);
            }else{
                //如果长度是2,那么这个字段是子表的
                String sublistTableName =  split[0];//子表的表名
                String sublistFieldName =  split[1];//子表的字段名
                Map<String, SFunction<?, String>> tableFunctionMap = tableSFunctionCache.get(sublistTableName);//子表的方法引用
                SFunction<?, String> fileSFunction = tableFunctionMap.get(sublistFieldName);

                mpjLambdaWrapper.eq(fileSFunction,value);
            }
        }
        //6.好了我们的主子表条件已经动态拼接完成了,接下来就是分页查询了,我们把之前的代码复制过来就行了
        //如果出现了一对多的情况
        PageImpl page;
        if(mpjLambdaWrapper.isResultMap()){
            SFunction<?, String> idFunction = tableClassSFunctionCache.get(primaryTableClass).get("id");
            //根据主表的查询条件和分页的值,获取到对应主表的id集合
            MPJLambdaWrapper wrapper = new MPJLambdaWrapper();
            wrapper.select(tableClassSFunctionCache.get(primaryTableClass).get("id"));
            //这个代码与上边的区别是没有子表条件了,因为要查询总数是查询的主表数据,没有子表的事,传入条件也只是主表的条件
            for (Map.Entry<String, Object> entry : conditionMap.entrySet()) {
                String tableName = entry.getKey();//表名
                Object value = entry.getValue();//条件

                String[] split = tableName.split("\\.");
                if(split.length == 1){
                    //如果长度是1,那么这个字段是主表的
                    String primaryFieldName =  split[0];//主表的字段名
                    Map<String, SFunction<?, String>> primaryFunctionMap = tableClassSFunctionCache.get(primaryTableClass);
                    SFunction<?, String> fileSFunction = primaryFunctionMap.get(primaryFieldName);
                    wrapper.eq(fileSFunction,value);
                }
            }

            IPage ipage = userMapper.selectJoinPage(new Page<>(0, 10),primaryTableClass,wrapper);
            long total = ipage.getTotal();//总条数
            List records = ipage.getRecords();
            List<String> idList = (List) records.stream().map(a -> ClassUtilsNioa.getV(a,"id")).collect(Collectors.toList());
            //把id集合拼接到查询条件中,然后不走联表分页查询,只用普通的分页查询即可
            mpjLambdaWrapper.in(idFunction,idList);
            List userExtendList = userMapper.selectJoinList(primaryTableClass, mpjLambdaWrapper);
            page = new PageImpl(userExtendList, PageRequest.of(0, 10), total);
        }else{
            IPage userExtendIPage = userMapper.selectJoinPage(new Page<>(0, 10), UserExtend.class, mpjLambdaWrapper);
            page = new PageImpl(userExtendIPage.getRecords(), PageRequest.of(0, 10), userExtendIPage.getTotal());
        }
        //这样我们成功的完成了联表分页查询+动态拼接主子表条件啦
        System.out.println(JSON.toJSONString(page));

以上就是我们全部的内容了,本文只是提供一种思路,小伙伴们可以根据代码中的工具类去定制自己的联表分页查询啦

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值