基於棧實現通過Oracle的PRIOR關鍵字查詢結果組裝java層級結構對象

1.需求描述

對於數據庫設計中,對於層級結構的設計一般使用parentId對於Id的引用實現。令我們愉悅的是,Oracle還提供了關鍵字PRIOR查詢樹狀結構的語句。下面是對於多級菜單的層級結構查詢的Sql語句。具體語法大家可以自行學習,這裏不對此做過多的解釋。

SELECT
    ID,
    NVL(TO_CHAR(PARENT_ID), 'NULL') PARENT_ID,
    MODULE_NAME,
    BS_URL,
    ICON,
    ORDER_NO,
    IS_VISIBLE,
    SYS_CONNECT_BY_PATH(MODULE_NAME, '/') PATH
FROM
    SM_MODULE
START WITH
    PARENT_ID IS NULL
CONNECT BY
    PRIOR ID = PARENT_ID

查詢的結果如下圖:

通過上面的結果我們知道,通過此Sql語句查詢到的數據是按先查詢PARENT_ID == NULL(也就是START WITH   PARENT_ID IS NULL)的記錄,然後以每一條記錄的ID作爲父級ID執行查詢,對於查詢的結果集合再以次ID作爲父級節點查詢,依次遞歸查詢。所以得到了上面的數據。而對於我們只要知道排在下面數據的父節點肯定在其上面存在(頂級節點除外)足以。

結果集查到了,那麼接下來是如何將其按照層級結構存入到Java對象中。下面我們分別以HashMap和棧的方式實現需求,通過比較,你會深深體會到基於棧實現此需求是多麼的香。

2.數據準備

爲了便於測試研究,也爲了照顧平時不怎麼使用Oracle的夥伴,我們並沒有直接連接Oracle查詢數據,而是模擬通過Oracle獲得的結果數據。

2.1 Model對象實現

package cn.surpass.jdk8newfuture.self.tree;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description
 * @Author SurpassLiang
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2020/4/11
 */
public class Model {
    private String id;
    private String parentId;
    private List<Model> modelList= new ArrayList<>();
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getParentId() {
        return parentId;
    }
    public void setParentId(String parentId) {
        this.parentId = parentId;
    }
    public Model(String id, String parentId) {
        this.id = id;
        this.parentId = parentId;
    }

    @Override
    public String toString() {
        return "Model{" +
                "id='" + id + '\'' +
                ", parentId='" + parentId + '\'' +
                ", modelList=" + modelList +
                '}';
    }

    public List<Model> getModelList() {
        return modelList;
    }
    public void setModelList(List<Model> modelList) {
        this.modelList = modelList;
    }
}

2.2 測試數據

我們使用Junit測試工具測試,所以我們初始化數據有如下代碼:

package cn.surpass.jdk8newfuture.self.tree;
import org.junit.Before;
import org.junit.Test;
import java.util.*;

/**
 * @Description
 * @Author SurpassLiang
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2020/4/11
 */
public class TreeTest {
    List<Model> modelList =new ArrayList<>();
    @Before
    public void testTree(){
        modelList.add(new Model("1",null));
        modelList.add(new Model("2","1"));
        modelList.add(new Model("3","1"));
        modelList.add(new Model("4","3"));
        modelList.add(new Model("5","1"));
        modelList.add(new Model("6",null));
        modelList.add(new Model("7","6"));
        modelList.add(new Model("8","6"));
        modelList.add(new Model("9","6"));
        modelList.add(new Model("10","9"));
        modelList.add(new Model("11","9"));
        modelList.add(new Model("12","9"));
        modelList.add(new Model("13","12"));
    }
}

3.基於HashMap實現需求

通過上面的數據我們分析到,針對當前的節點的父節點在上面的記錄已經存在,所以我們這裏引入HashMap記錄已經處理的數據。對於HashMap,key爲節點的ID,value爲當前節點對象。另外我們引入一個存放頂級節點的集合。針對一個對象,如果父級節點爲空,說明此節點爲頂級節點,應該放到頂級節點的結合中;如果不爲空,通過HashMap的get(Object key)方法查詢到當前節點的父節點,然後將當前節點放到父節點的modelList當中。整個代碼邏輯並不複雜。下面是代碼實現:

@Test
public void test1(){
    //根節點集合
    List<Model> rootModules = new ArrayList<>();
    Map<String, Model> tempMap = new HashMap<>();
    Model curModel;
    String parentId;
    for (int i = 0; i < modelList.size(); i++) {
        curModel = modelList.get(i);
        parentId = curModel.getParentId();
        tempMap.put(curModel.getId(),curModel);
        //HashMap的key是否包含當前節點的父級節點
        if (tempMap.containsKey(parentId)) {
            //包含,獲取父級節點並將當前節點加入到ModelList集合中
            tempMap.get(parentId).getModelList().add(curModel);
        } else {
            //如果不存在,則加入根節點集合中
            rootModules.add(curModel);
        }
    }
    rootModules.forEach(System.out::println);
}

4.基於棧(Stack)實現需求

4.1 棧的簡述

棧(stack)又名堆棧,它是一種運算受限的線性表。限定僅在表尾進行插入和刪除操作的線性表。這一端被稱爲棧頂,相對地,把另一端稱爲棧底。向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成爲新的棧頂元素;從一個棧刪除元素又稱作出棧或退棧,它是把棧頂元素刪除掉,使其相鄰的元素成爲新的棧頂元素。簡而言之,就是先進後出。如果想不明白,可以想一下子彈夾,先摁進去的子彈最後一個彈出去。

4.2 業務邏輯

1.對於第一個數據,按照上面數據結果的分析,第一個數據肯定是根節點,所以壓入棧頂。

2.對於第二個數據,通過其父節點與棧頂的元素ID進行比較,如果一樣,則將當前元素放入棧頂元素的modleList集合中,同時將第二個元素壓入棧頂。

3.對於第三個數據,通過其父節點與棧頂的元素ID進行比較,此時棧頂元素ID爲2,當前節點父級節點ID爲1,不滿足條件,將棧頂元素彈出,在此通過其父節點與棧頂的元素ID進行比較,此時棧頂元素ID爲1,滿足條件,則將當前元素放入棧頂元素的modleList集合中,同時將第三個元素壓入棧頂。

依次類推,最終遍歷所有的元素,以下是處理流程圖。

 

4.3.代碼實現

 @Test
public void test2(){
    List<Model> rootModules = new ArrayList<>();
    Stack<Model> moduleStack = new Stack<>();
    Model curModule;
    for (int i = 0; i < modelList.size(); i++) {
        curModule = modelList.get(i);
        if(curModule.getParentId() == null){
            rootModules.add(curModule);
            moduleStack.push(curModule);
        }else{
            while(moduleStack.peek().getId() != curModule.getParentId()){
                moduleStack.pop();
            }
            moduleStack.peek().getModelList().add(curModule);
            moduleStack.push(curModule);
        }
    }
    rootModules.forEach(System.out::println);
}

5.測試

test1是基於HashMap實現的業務邏輯,test2是基於棧實現的業務邏輯,在耗時上基本是棧完虐HashMap。所以瞭解一下數據結構還是很有必要的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章