在开发中,可能经常使用mysql的各种join操作,当数据量小的时候,这种操作很ok。但当数据量达到几百万甚至几千万时,多表连接会出现耗时过大的问题。对于这种问题,可以将大表join操作拆分成小的表查询,再到代码层进行数据的整合,再传递给前端。

比如以下场景:教室和学生的一对多关系

一个教室有多个学生

一个学生只能存在于一个教室

假如我们需要做以下查询:

1. 查出每个教室的所有学生

操作步骤:

查出所有教室

拿到教室的id集合roomIdList

使用in操作,查出属于该roomIdList的所有学生集合studentList

将studentList进行分类,分属到每一个教室下

2. 查出每个学生所属的教室的教室名称

相当于

select student.*,classroom.name from student stu left join classroom room on stu.room_id = room.id;

操作步骤:

查出所有学生

拿到所有学生所属的roomId集合roomIdList,并且进行重复过滤

使用in 查出roomIdList里面的教室classroomList

根据stu.room_id = room.id进行关联,将room.name设置到每一个学生的属性中

以下为具体使用:(模拟部分数据)


    public static void main(String[] args) {

        boolean flag = true;


        List<Student> studentList = new ArrayList<>();

        List<Classroom> classroomList = new ArrayList<>();


        Classroom classroom = new Classroom();

        classroom.setRoomId(111);

        classroom.setRoomName("name+" + 111);

        Classroom classroom2 = new Classroom();

        classroom2.setRoomId(222);

        classroom2.setRoomName("name+" + 222);


        classroomList.add(classroom);

        classroomList.add(classroom2);


        Student student = new Student();

        for (int i = 0; i < 10; i++) {

            student = new Student();

            student.setRoomId(i);

            student.setName("name+" + i);


            if (flag) {

                student.setRoomId(classroom.getRoomId());

            } else {

                student.setRoomId(classroom2.getRoomId());

            }

            flag = !flag;


            studentList.add(student);

        }


        //为每个学生赋值他所在的教室

        MergeUtil.merge(studentList,classroomList,(stu,room) -> stu.getRoomId() == room.getRoomId(),(stu,room) -> stu.setRoomName(room.getRoomName()));


        studentList.forEach((data) -> {

            System.out.println(data.toString());

        });


        //为每个教室,赋值属于他的学生

        classroomList.stream().forEach(room -> {

            List dataList = studentList.stream().filter(student1 -> student1.getRoomId() == room.getRoomId() ).collect(Collectors.toList());

            room.setStudentList(dataList);

        });

//

        System.out.println(classroomList);


        // 使用工具类

        //为每个教室,赋值属于他的学生

        MergeUtil.mergeList(classroomList,studentList,

                (classroom1, student1) -> classroom1.getRoomId() == student1.getRoomId(),

                (classroom1, students) -> classroom1.setStudentList(students));


        System.out.println(classroomList);



    }

如果不使用java8的filter操作,使用for循环自己写,操作如下

 HashMap<Integer ,List<Student>> hashMap = new HashMap<>();

        for (Student stu:studentList){

            Integer roomId = stu.getRoomId();

            List<Student> tempList;

            if (hashMap.containsKey(roomId)){

                tempList = hashMap.get(roomId);

            }else {

                tempList = new ArrayList();

            }

            tempList.add(stu);

            hashMap.put(roomId,tempList);

        }

        for (Classroom room:classroomList){

            room.setStudentList(hashMap.get(room.getRoomId()));

        }

但经过试验,在100w个student的情况下进行测试,使用java8的filter操作耗时比自己for循环+hashMap要快一半左右, 所以比较建议使用filter;

最后贴上这部分操作要用到的工具类:

public class MergeUtil {


    /**

     * 把sourceList里面的一些属性合并到targetList里面

     * 基于testFunction的条件,合入逻辑实现为biConsumer

     *

     * @param targetList

     * @param sourceList

     * @param testFunction

     * @param biConsumer

     * @param <T>

     * @param <S>

     */

    public static <T, S> void merge(List<T> targetList, List<S> sourceList, BiFunction<? super T, ? super S, Boolean> testFunction,

                                    BiConsumer<? super T, ? super S> biConsumer) {


        targetList.forEach((t)->{

            Optional<S> optional = sourceList.stream().filter(s->testFunction.apply(t, s)).findFirst();

            if (optional.isPresent()) {

                biConsumer.accept(t, optional.get());

            }

        });


    }


    /**

     * 把sourceList里面的一些item分类合并到targetList的每一个item里面

     *

     * @param targetList

     * @param sourceList

     * @param testFunction

     * @param biConsumer

     * @param <T>

     * @param <S>

     */

    public static <T, S> void mergeList(List<T> targetList, List<S> sourceList, BiFunction<? super T, ? super S, Boolean> testFunction,

                                    BiConsumer<? super T, ? super List<S>> biConsumer) {


        targetList.forEach((t)->{

            List<S> dataList = sourceList.stream().filter(s->testFunction.apply(t, s)).collect(Collectors.toList());

            Optional<List<S>> optional = Optional.of(dataList);

            if (optional.isPresent()) {

                biConsumer.accept(t, optional.get());

            }

        });

    }

}