1. 程式人生 > >資料庫單表查詢,list分頁關聯展示

資料庫單表查詢,list分頁關聯展示

應用背景

  一般我們在開發資料量不是很大的中小型企業系統來說,直接使用SQL關聯,多表聯合查詢就可以了,因為這樣在專案開發過程中非常的高效。但是一旦在遇到大資料量的背景前提下,原始的關聯查詢方式逐漸的顯現出了越來越多的弊端。

我們引入海爾電商技術總監Richie的觀點來說,主要有以下幾點:

從邏輯架構分層原則來看

  關聯關係代表了業務規則/邏輯,毫無約束大量使用關聯查詢,就是把大量的業務規則和邏輯放在資料庫來執行了,資料庫消耗cpu、記憶體、io等資源進行關聯操作,實際上是在做應用該做的事情。

從資源利用率方面看

  大部分場景下,並不是所有關聯查詢的結果都被有效使用了。例如後臺管理的列表介面,通常都會分頁顯示,關聯查詢的結果集,只有當前頁的資料被使用,其他都是無用的,但資料庫需要消耗額外資源得到全部結果集,再從中得到當前頁資料。

從架構的伸縮性方面看

  大量的關聯查詢會導致集中式的資料庫架構很難向分散式架構轉換,伸縮性方面的優化難度高。

總結:

  關聯查詢方便快速,開發效率比較好,如果系統、資料庫經過一些垂直優化手段完全能夠滿足效能要求是可以使用的,例如中小企業的內部管理系統等。

  不使用關聯查詢在架構層面有很多優點,但對系統分析和設計、開發能力要求高。一般在網際網路行業等使用者數較多的情況下最好重視這方面。

  理論上不存在什麼複雜場景,如果不使用資料庫的關聯查詢就無法滿足需求的。巨無霸的ERP系統SAP,基本整個系統功能都是用單表查詢實現的。

程式碼實現

  從前面的介紹中我們瞭解到,很多高效能的應用都會對關聯查詢進行分解。簡單的來說,可以對每一個表進行一次單表查詢,然後將結果在應用程式中進行關聯。

  比如說下面截圖中的資料就是利用這種原理實現的。

這裡寫圖片描述

  下面我以實際的程式碼為例來進行說明,其中由於資料均為模擬資料,所以說我直接通過手寫的方式代替資料庫查詢來給結果集進行賦值。

  • join()實現多表關聯
  • filter()實現where查詢
  • listPage()實現資料分頁功能。

實體類

Class

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Builder
public class Class {

    private String id;
    private
String name; }

Student

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Builder
public class Student {

    private String id;
    private String name;
    private String classId;

}

PageQuery

import com.google.common.collect.Lists;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
@ApiModel(value = "PageQuery",description = "這個是分頁查詢實體類",subTypes = PageQuery.class)
public class PageQuery<T> {

    //頁數
    @ApiModelProperty(name = "page",value = "頁數索引",notes = "請求的頁數",required = true)
    private int page;
    //每頁顯示的資訊數
    @ApiModelProperty(name = "limit",value = "資訊條數",notes = "每頁展示的資訊條數",required = true)
    private int limit;
    //開始座標
    @ApiModelProperty(name = "start",value = "sql查詢起始頁",notes = "開始座標")
    private int start;
    //截止座標
    @ApiModelProperty(name = "end",value = "list擷取截止座標",notes = "截止座標")
    private int end;

    public PageQuery(int page, int limit){
        this.start = (page - 1) * limit;
        this.limit = limit;
        this.end = this.start + this.limit;
    }

    /**
     * list陣列分頁
     * @param list
     * @return
     */
    public List<T> getListPage(List<T> list){
        List<T> subedList = null;
        //如果list陣列長度大於開始座標,則有效,否則返回空陣列。
        if(list.size() >= this.start){
            //如果list陣列的長度小於等於截止座標,則list索引值有可能會越界,故此時的最終list索引值應該修正為陣列的長度
            if(list.size() <= end){
                subedList = list.subList(this.start,list.size());
            } else {   //如果list陣列的長度大於截止座標,則list索引值始終有效,此時最終list索引值應該為截止座標
                subedList = list.subList(this.start,this.end);
            }
            //返回擷取的陣列
            return subedList;
        }
        return Lists.newArrayList();
    }

}

ClassStudentDTO

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@Builder
@ToString
public class ClassStudentDTO {

    private String id;
    private String studentId;
    private String classId;
    private String className;
    private String studentName;

}

測試程式碼

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.mysql.cj.core.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;

import java.util.List;
import java.util.Map;

@Slf4j
public class ListPageTest {

    private List<Class> classList = Lists.newArrayList();
    private List<Student> studentList = Lists.newArrayList();

    /**
     * 初始化班級,學生資訊
     */
    @Before
    public void initDB(){
        for(int i = 1;i <= 3;i ++){
            Class clazz = Class.builder()
                    .id(StringUtils.getUniqueSavepointId())
                    .name("班級" + i)
                    .build();
            classList.add(clazz);
            for(int j = 1;j <= 3;j ++){
                Student student = Student.builder()
                        .id(StringUtils.getUniqueSavepointId())
                        .name("學生" + i + j)
                        .classId(clazz.getId())
                        .build();
                studentList.add(student);
            }
        }
    }

    /**
     * 測試分頁
     */
    @Test
    public void test(){
        //連線表
        List<ClassStudentDTO> joinedTable = join(classList,studentList);
        //假設傳入的查詢條件是“班級2”
        //查詢目標班級資料
        List<Class> searchedClasses = getSearchedClass("班級2");
        //過濾表
        List<ClassStudentDTO> searchedClassStudentDTO = filter(joinedTable,searchedClasses);
        //假設前臺傳入的查詢分頁資訊為:第二頁,每頁顯示2條資料
        PageQuery pageQuery = new PageQuery(1,2);
        //list分頁
        List<ClassStudentDTO> pagedList = listPage(searchedClassStudentDTO,pageQuery);
        log.info("資訊總條數為:{}",searchedClassStudentDTO.size());
        log.info("查詢的內容:");
        //查詢的內容
        pagedList.forEach(classStudentDTO -> log.info(classStudentDTO.toString()));
    }

    /**
     * 連線表
     * @param classList
     * @param studentList
     * @return
     */
    private List<ClassStudentDTO> join(List<Class> classList,List<Student> studentList){
        Map<String,Class> classMap = Maps.newHashMap();
        classList.forEach(clazz -> {
            classMap.put(clazz.getId(),clazz);
        });
        List<ClassStudentDTO> classStudentDTOS = Lists.newArrayList();
        studentList.forEach(student -> {
            ClassStudentDTO classStudentDTO = ClassStudentDTO.builder()
                    .id(StringUtils.getUniqueSavepointId())
                    .studentId(student.getId())
                    .classId(student.getClassId())
                    .className(classMap.get(student.getClassId()).getName())
                    .studentName(student.getName())
                    .build();
            classStudentDTOS.add(classStudentDTO);
        });
        return classStudentDTOS;
    }

    /**
     * 查詢目標班級資料
     * @param className
     * @return
     */
    private List<Class> getSearchedClass(String className){
        List<Class> searchedClasses = Lists.newArrayList();
        classList.forEach(clazz -> {
            if(clazz.getName().equals(className)){
                searchedClasses.add(clazz);
            }
        });
        return searchedClasses;
    }

    /**
     * 過濾資料
     * @param joinedTable
     * @param searchedClasses
     * @return
     */
    private List<ClassStudentDTO> filter(List<ClassStudentDTO> joinedTable,List<Class> searchedClasses){
        List<ClassStudentDTO> searchedClassStudentDTO = Lists.newArrayList();
        Multimap<String,ClassStudentDTO> multimap = ArrayListMultimap.create();
        joinedTable.forEach(classStudentDTO -> {
            multimap.put(classStudentDTO.getClassId(),classStudentDTO);
        });
        searchedClasses.forEach(searchedClass -> {
            List<ClassStudentDTO> dtos = (List<ClassStudentDTO>) multimap.get(searchedClass.getId());
            searchedClassStudentDTO.addAll(dtos);
        });
        return searchedClassStudentDTO;
    }

    /**
     * list分頁
     * @param searchedClassStudentDTO
     * @param pageQuery
     * @return
     */
    private List<ClassStudentDTO> listPage(List<ClassStudentDTO> searchedClassStudentDTO,PageQuery pageQuery){
        return pageQuery.getListPage(searchedClassStudentDTO);
    }

}

測試結果

[13:05:32.571] INFO  com.baishun.login.listPage.ListPageTest 60 test - 資訊總條數為:3
[13:05:32.581] INFO  com.baishun.login.listPage.ListPageTest 61 test - 查詢的內容:
[13:05:32.582] INFO  com.baishun.login.listPage.ListPageTest 63 lambda$test$0 - ClassStudentDTO(id=cff92128_902a_471f_ad67_d75427d17006, studentId=6a673faa_0e62_4081_9cb4_1719489cfce6, classId=acc94722_e24a_4a11_b632_d23d9c5eddc8, className=班級2, studentName=學生21)
[13:05:32.583] INFO  com.baishun.login.listPage.ListPageTest 63 lambda$test$0 - ClassStudentDTO(id=93600c44_2dd1_49c1_b903_1fb585822edd, studentId=d41caa65_dae2_48b6_b6e8_166f498a7ae3, classId=acc94722_e24a_4a11_b632_d23d9c5eddc8, className=班級2, studentName=學生22)