成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

拒絕重復代碼,封裝一個多級菜單、多級評論、多級部門的統一工具類!

開發 開發工具
為了簡化開發過程并提高代碼的可維護性,我們可以創建一個統一的工具類來處理這些需求。在本文中,我將介紹如何使用SpringBoot創建一個返回多級菜單、多級評論、多級部門、多級分類的統一工具類。

一、介紹

你能看到很多人都在介紹如何實現多級菜單的效果,但是都有一個共同的缺點,那就是沒有解決代碼會重復開發的問題。如果我需要實現多級評論呢,是否又需要自己再寫一遍?

為了簡化開發過程并提高代碼的可維護性,我們可以創建一個統一的工具類來處理這些需求。在本文中,我將介紹如何使用SpringBoot創建一個返回多級菜單、多級評論、多級部門、多級分類的統一工具類。

介紹數據庫字段設計

數據庫設計

「主要是介紹是否需要tree_path字段。」

多級節點的數據庫大家都知道,一般會有id,parentId字段,但是對于tree_path字段,這個需要根據設計者來定。

優點:

  • 如果你對數據的讀取操作比較頻繁,而且需要快速查詢某個節點的所有子節點或父節點,那么使用tree_path 字段可以提高查詢效率。
  • tree_path 字段可以使用路徑字符串表示節點的層級關系,例如使用逗號分隔的節點ID列表。這樣,可以通過模糊匹配tree_path 字段來查詢某個節點的所有子節點或父節點,而無需進行遞歸查詢。
  • 你可以使用模糊匹配的方式,找到所有以該節點的 tree_path 開頭的子節點,并將它們刪除。而無需進行遞歸刪除。

缺點:

  • 每次插入時,需要更新tree_path 字段,這可能會導致性能下降。
  • tree_path 字段的長度可能會隨著樹的深度增加而增加,可能會占用更多的存儲空間。

因此,在設計數據庫評論字段時,需要權衡使用treepath字段和父評論ID字段的優缺點,并根據具體的應用場景和需求做出選擇。如果你更關注讀取操作的效率和查詢、刪除的靈活性,可以考慮使用tree_path 字段。如果你更關注寫入操作的效率和數據一致性,并且樹的深度不會很大,那么使用父評論ID字段來實現多級評論可能更簡單和高效。

二、統一工具類具體實現

1. 定義接口,統一規范

對于有 lombok 的小伙伴,實現這個方法很簡單,只需要加上@Data即可

/**
 * @Description: 固定屬性結構屬性
 * @Author: yiFei
 */
publicinterface ITreeNode<T> {
    /**
     * @return 獲取當前元素Id
     */
    Object getId();

    /**
     * @return 獲取父元素Id
     */
    Object getParentId();

    /**
     * @return 獲取當前元素的 children 屬性
     */
    List<T> getChildren();

    /**
     * ( 如果數據庫設計有tree_path字段可覆蓋此方法來生成tree_path路徑 )
     *
     * @return 獲取樹路徑
     */
    default Object getTreePath() { return""; }
}

2. 編寫工具類TreeNodeUtil

其中我們需要實現能將一個List元素構建成熟悉結構

我們需要實現生成tree_path字段

我們需要優雅的實現該方法

/**
 * @Description: 樹形結構工具類
 * @Author: yiFei
 */
publicclass TreeNodeUtil {

    privatestaticfinal Logger log = LoggerFactory.getLogger(TreeNodeUtil.class);

    publicstaticfinal String PARENT_NAME = "parent";

    publicstaticfinal String CHILDREN_NAME = "children";

    publicstaticfinal List<Object> IDS = Collections.singletonList(0L);

    publicstatic <T extends ITreeNode> List<T> buildTree(List<T> dataList) {
        return buildTree(dataList, IDS, (data) -> data, (item) -> true);
    }

    publicstatic <T extends ITreeNode> List<T> buildTree(List<T> dataList, Function<T, T> map) {
        return buildTree(dataList, IDS, map, (item) -> true);
    }
    
    publicstatic <T extends ITreeNode> List<T> buildTree(List<T> dataList, Function<T, T> map, Predicate<T> filter) {
        return buildTree(dataList, IDS, map, filter);
    }

    publicstatic <T extends ITreeNode> List<T> buildTree(List<T> dataList, List<Object> ids) {
        return buildTree(dataList, ids, (data) -> data, (item) -> true);
    }

    publicstatic <T extends ITreeNode> List<T> buildTree(List<T> dataList, List<Object> ids, Function<T, T> map) {
        return buildTree(dataList, ids, map, (item) -> true);
    }

    /**
     * 數據集合構建成樹形結構 ( 注: 如果最開始的 ids 不在 dataList 中,不會進行任何處理 )
     *
     * @param dataList 數據集合
     * @param ids      父元素的 Id 集合
     * @param map      調用者提供 Function<T, T> 由調用著決定數據最終呈現形勢
     * @param filter   調用者提供 Predicate<T> false 表示過濾 ( 注: 如果將父元素過濾掉等于剪枝 )
     * @param <T>      extends ITreeNode
     * @return
     */
    publicstatic <T extends ITreeNode> List<T> buildTree(List<T> dataList, List<Object> ids, Function<T, T> map, Predicate<T> filter) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        // 1. 將數據分為 父子結構
        Map<String, List<T>> nodeMap = dataList.stream()
                .filter(filter)
                .collect(Collectors.groupingBy(item -> ids.contains(item.getParentId()) ? PARENT_NAME : CHILDREN_NAME));
    
        List<T> parent = nodeMap.getOrDefault(PARENT_NAME, Collections.emptyList());
        List<T> children = nodeMap.getOrDefault(CHILDREN_NAME, Collections.emptyList());
        // 1.1 如果未分出或過濾了父元素則將子元素返回
        if (parent.size() == 0) {
            return children;
        }
        // 2. 使用有序集合存儲下一次變量的 ids
        List<Object> nextIds = new ArrayList<>(dataList.size());
        // 3. 遍歷父元素 以及修改父元素內容
        List<T> collectParent = parent.stream().map(map).collect(Collectors.toList());
        for (T parentItem : collectParent) {
            // 3.1 如果子元素已經加完,直接進入下一輪循環
            if (nextIds.size() == children.size()) {
                break;
            }
            // 3.2 過濾出 parent.id == children.parentId 的元素
            children.stream()
                    .filter(childrenItem -> parentItem.getId().equals(childrenItem.getParentId()))
                    .forEach(childrenItem -> {
                        // 3.3 這次的子元素為下一次的父元素
                        nextIds.add(childrenItem.getParentId());
                        // 3.4 添加子元素到 parentItem.children 中
                        try {
                            parentItem.getChildren().add(childrenItem);
                        } catch (Exception e) {
                            log.warn("TreeNodeUtil 發生錯誤, 傳入參數中 children 不能為 null,解決方法: \n" +
                                    "方法一、在map(推薦)或filter中初始化 \n" +
                                    "方法二、List<T> children = new ArrayList<>() \n" +
                                    "方法三、初始化塊對屬性賦初值\n" +
                                    "方法四、構造時對屬性賦初值");
                        }
                    });
        }
        buildTree(children, nextIds, map, filter);
        return parent;
    }


    /**
     * 生成路徑 treePath 路徑
     *
     * @param currentId 當前元素的 id
     * @param getById   用戶返回一個 T
     * @param <T>
     * @return
     */
    publicstatic <T extends ITreeNode> String generateTreePath(Serializable currentId, Function<Serializable, T> getById) {
        StringBuffer treePath = new StringBuffer();
        if (SystemConstants.ROOT_NODE_ID.equals(currentId)) {
            // 1. 如果當前節點是父節點直接返回
            treePath.append(currentId);
        } else {
            // 2. 調用者將當前元素的父元素查出來,方便后續拼接
            T byId = getById.apply(currentId);
            // 3. 父元素的 treePath + "," + 父元素的id
            if (!ObjectUtils.isEmpty(byId)) {
                treePath.append(byId.getTreePath()).append(",").append(byId.getId());
            }
        }
        return treePath.toString();
    }

}

這樣我們就完成了 TreeNodeUtil 統一工具類,首先我們將元素分為父子兩類,讓其構建出一個小型樹,然后我們將構建的子元素和下次遍歷的父節點傳入,遞歸的不斷進行,這樣就構建出了我們最終的想要實現的效果。

三、測試

定義一個類實現 ITreeNode

/**
 * @Description: 測試子元素工具類
 * @Author: yiFei
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@AllArgsConstructor
publicclass TestChildren implements ITreeNode<TestChildren> {

    private Long id;

    private String name;

    private String treePath;

    private Long parentId;

    public TestChildren(Long id, String name, String treePath, Long parentId) {
        this.id = id;
        this.name = name;
        this.treePath = treePath;
        this.parentId = parentId;
    }

    @TableField(exist = false)
    private List<TestChildren> children = new ArrayList<>();
}

測試基本功能

測試基本功能代碼:

public static void main(String[] args) {
    List<TestChildren> testChildren = new ArrayList<>();
    testChildren.add(new TestChildren(1L, "父元素", "", 0L));
    testChildren.add(new TestChildren(2L, "子元素1", "1", 1L));
    testChildren.add(new TestChildren(3L, "子元素2", "1", 1L));
    testChildren.add(new TestChildren(4L, "子元素2的孫子元素", "1,3", 3L));

    testChildren = TreeNodeUtil.buildTree(testChildren);

    System.out.println(JSONUtil.toJsonStr(Result.success(testChildren)));
}

返回結果:

{
 "code": "00000",
"msg": "操作成功",
"data": [{
"id": 1,
"name": "父元素",
"treePath": "",
"parentId": 0,
"children": [{
   "id": 2,
   "name": "子元素1",
   "treePath": "1",
   "parentId": 1,
   "children": []
  }, {
   "id": 3,
   "name": "子元素2",
   "treePath": "1",
   "parentId": 1,
   "children": [{
    "id": 4,
    "name": "子元素2的孫子元素",
    "treePath": "1,3",
    "parentId": 3,
    "children": []
   }]
  }]
 }]
}

測試過濾以及重構數據

測試代碼:

public static void main(String[] args) {
    List<TestChildren> testChildren = new ArrayList<>();
    testChildren.add(new TestChildren(1L, "父元素", "", 0L));
    testChildren.add(new TestChildren(2L, "子元素1", "1", 1L));
    testChildren.add(new TestChildren(3L, "子元素2", "1", 1L));
    testChildren.add(new TestChildren(4L, "子元素2的孫子元素", "1,3", 3L));

    testChildren = TreeNodeUtil.buildTree(testChildren);

    System.out.println(JSONUtil.toJsonStr(Result.success(testChildren)));
}

返回結果 :

{
 "code": "00000",
"msg": "操作成功",
"data": [{
"id": 1,
"name": "父元素",
"treePath": "",
"parentId": 0,
"children": [{
   "id": 2,
   "name": "子元素1",
   "treePath": "1",
   "parentId": 1,
   "children": []
  }, {
   "id": 3,
   "name": "子元素2",
   "treePath": "1",
   "parentId": 1,
   "children": [{
    "id": 4,
    "name": "子元素2的孫子元素",
    "treePath": "1,3",
    "parentId": 3,
    "children": []
   }]
  }]
 }]
}

測試過濾以及重構數據

測試代碼:

// 對 3L 進行剪枝,對 1L 進行修改
testChildren = TreeNodeUtil.buildTree(testChildren, (item) -> {
    if (item.getId().equals(1L)) {
        item.setName("更改了 Id 為 1L 的數據名稱");
    }
    return item;
}, (item) -> item.getId().equals(3L));

返回結果:

{
 "code": "00000",
"msg": "操作成功",
"data": [{
"id": 1,
"name": "更改了 Id 為 1L 的數據名稱",
"treePath": "",
"parentId": 0,
"children": [{
   "id": 2,
   "name": "子元素1",
   "treePath": "1",
   "parentId": 1,
   "children": []
  }]
 }]
}

接下來的測試結果以口述的方式講解

測試傳入錯誤的 ids

  • 返回傳入的 testChildren

測試傳入具有父子結構,但是 ids 傳錯的情況 (可以根據實際需求更改是否自動識別父元素)

  • 返回傳入的 testChildren

測試  testChildren 中 children元素為 null

  • 給出提示,不構建樹

測試 generateTreePath 生成路徑

  • 返回路徑
責任編輯:武曉燕 來源: 碼猿技術專欄
相關推薦

2019-08-01 08:36:51

緩存系統并發

2022-06-13 10:23:34

Helios緩存服務端

2024-11-27 16:07:45

2022-06-28 14:19:38

Vue路由監控

2009-06-30 10:46:05

多級指針

2010-05-17 14:08:18

MySQL 多級同步

2012-05-02 10:41:31

ASP.NET

2009-08-30 15:14:13

SCOUNIX多級域名

2010-05-17 11:26:49

MySQL 多級同步

2024-02-20 14:10:55

系統緩存冗余

2023-09-12 07:31:32

內存LyMemoryWIN10

2023-05-05 18:38:33

多級緩存Caffeine開發

2025-03-27 04:10:00

2015-08-17 10:50:51

美團多級下拉菜單簡潔

2009-11-27 16:35:01

php函數mkdir

2018-08-19 13:27:21

數據庫緩存數據庫減負

2023-05-05 06:13:51

分布式多級緩存系統

2024-12-30 08:55:09

2010-10-18 13:16:24

GalleryAndroid

2014-12-18 09:29:30

東華網智優化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: av免费入口 | 日韩精品免费在线观看 | av一区二区在线观看 | 精品国产乱码久久久久久丨区2区 | 日韩在线不卡 | 精品日韩一区 | 久久久久国产一区二区三区四区 | 国内精品免费久久久久软件老师 | 国产一区二区精品自拍 | 久久三区 | 亚洲高清视频一区二区 | 久久精品一区二区三区四区 | 在线一区视频 | 国产中文原创 | 手机在线观看 | 亚洲影视在线 | 午夜男人的天堂 | 日本天堂视频在线观看 | 成人亚洲性情网站www在线观看 | 欧美一级黄带 | 在线免费观看黄色av | 999久久久久久久久6666 | 日日操夜夜操天天操 | 日本不卡一区二区三区 | 亚洲精品一区二区在线观看 | 精品一区二区三区视频在线观看 | 羞羞视频网站免费观看 | 欧美一区二区三区视频在线 | 欧美日韩在线视频一区 | 中日字幕大片在线播放 | 91 在线| 日日操夜夜干 | 日韩精品国产精品 | 欧美日韩亚洲国产综合 | 91视频国产一区 | 香蕉久久久 | 青青草av | 国产精品久久久久久久久久免费看 | 久久69精品久久久久久久电影好 | 国产精品不卡一区二区三区 | 国产一区2区 |