Java遞歸實現多級菜單實現

一、引言

小編之前寫過如何實現一二級菜單的文章,回顧了下之前所實現的邏輯方式,簡直慘不忍睹~~

由於近期小編接觸新的項目需要實現展示菜單功能,但這次的菜單是需要多級,並且級數不固定。

像這種需求,一般很簡單的來說就是用遞歸實現了,可以從第一級一直往下查,一直查詢到爲空爲止。

二、數據庫表結構

這裏小編之列出來幾個基本的字段,但是夠用了

CREATE TABLE `mall_category` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '類別Id',
  `parent_id` int(11) DEFAULT NULL COMMENT '父類別id當id=0時說明是根節點,一級類別',
  `name` varchar(50) DEFAULT NULL COMMENT '類別名稱',
  `status` tinyint(1) DEFAULT '1' COMMENT '類別狀態1-正常,2-已廢棄',
  `sort_order` int(4) DEFAULT NULL COMMENT '排序編號,同類展示順序,數值相等則自然排序',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  `update_time` datetime DEFAULT NULL COMMENT '更新時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100034 DEFAULT CHARSET=utf8;

三、代碼實現

步驟一:一共有兩個實體類,一個是對應數據庫的實體類,還有一個是返回給前端的實體類

/**
 * @Auther: IT賤男
 * @Date: 2019/12/16 16:45
 * @Description: 商品類目
 */
@Data
public class Category {

    private Integer id;

    private Integer parentId;

    private String name;

    private Integer status;

    private Integer sortOrder;

    private Date createTime;

    private Date updateTime;
}
/**
 * @Auther: IT賤男
 * @Date: 2020/2/26 13:58
 * @Description: 返回前端對應實體類
 */
@Data
public class CategoryVo {

    private Integer id;

    private Integer parentId;

    private String name;

    private Integer sortOrder;

    /**
     * 存放類目子級目錄
     */
    private List<CategoryVo> subCategories;
}

步驟二:具體實現邏輯,小編這是從項目裏面直接截取出來的代碼,但是能夠實現多級菜單功能~~


import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Auther: IT賤男
 * @Date: 2020/2/26 13:47
 * @Description: 類目查詢
 */
@Service
public class ICategoryServiceImpl implements ICategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    /**
     * 對象轉換 將數據庫實體類對象轉還給前端實體
     *
     * @param category
     * @return
     */
    private CategoryVo categoryTOCategoryVo(Category category) {
        CategoryVo categoryVo = new CategoryVo();
        BeanUtils.copyProperties(category, categoryVo);
        return categoryVo;
    }

    @Override
    public ResponseVo selectCategoryAll() {

        // 先查詢出全部類目
        List<Category> categories = categoryMapper.selectCategoryAll();

        // 獲取一級菜單 , 0 代表一級目錄
        List<CategoryVo> rootCategory = categories.stream()
                .filter(e -> e.getParentId().equals("0"))
                .map(this::categoryTOCategoryVo)
                .sorted(Comparator.comparing(CategoryVo::getSortOrder).reversed())
                .collect(Collectors.toList());

        // 查找子節點
        findSubCategory(categories, rootCategory);

        // ResponseVo 這個是封裝類了,實際看小夥伴自己的需求
        return ResponseVo.success(rootCategory);
    }
     /**
     * 根據一級遞歸調用子級
     *
     * @param categoryList
     * @param rootCategory
     * @return
     */
    private void findSubCategory(List<Category> categoryList, List<CategoryVo> rootCategory) {
        // 遍歷一級
        for (CategoryVo categoryVo : rootCategory) {
            List<CategoryVo> subCategoryVoList = new ArrayList<>();
            // 查找子級
            for (Category category : categoryList) {
                // 判斷當前目錄是否是子父級關係
                if (categoryVo.getId().equals(category.getParentId())) {
                    subCategoryVoList.add(categoryTOCategoryVo(category));
                }
                // 遞歸調用,不管有幾級菜單,都能夠適用
                findSubCategory(categoryList, subCategoryVoList);
                // 類目顯示排序,reversed 表示數字越大靠前
                subCategoryVoList.sort(Comparator.comparing(CategoryVo::getSortOrder).reversed());
            }
            // 最後把查到的子級保存到一級目錄中
            categoryVo.setSubCategories(subCategoryVoList);
        }
    }

}

四、補充說明

一、小夥伴可能會有疑問,這樣循環嵌套會不會影響效率

其實像這些循環操作來說,都是基於內存操作,內存操作是很快的,這點可以不用考慮。

二、需要避免的問題

其實對於這種數據的操作,最好一次性能夠查詢出來最好,商品目錄最多也不過幾千條。千萬不要循環去查詢數據庫,這種是極度危險的行爲。小編就在工作當中遇到有在循環一直去查詢數據庫的情況,如果不考慮效率可以這麼操作。

 

好了,最後提醒大家,疫情期間,好好保護自己

不出門,就是對祖國做出最大的貢獻哈哈哈  (老闆要求去公司,俺也沒辦法,賺錢要緊,賺錢要緊) ~~~~

 

——————————————————————————————————————————————————

評論有小夥伴提到了ResponseVo這個對象,其功能就是包裝我們的返回數據的格式,如下:

因爲這些code,msg 這些字段內容都是規定好的,這樣做一是爲了規範,二也減少了代碼的冗餘。

// 沒包裝前的返回數據格式
{
    "data":{}
}


// 包裝後的返回數據格式
{
    "code": 1,
    "msg":"成功",
    "data":{}
}

代碼也貼出來給大家參考一下吧,代碼有簡化,但基本上也都差不多。


import com.fasterxml.jackson.annotation.JsonInclude;
import com.kane.mall.enums.ResponseEnum;
import lombok.Data;
import org.springframework.validation.BindingResult;

/**
 * @Auther: IT賤男
 * @Date: 2020/2/17 21:45
 * @Description: 返回結果集
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseVo<T> {

    private Integer status;

    private String msg;

    private T data;

    public ResponseVo(Integer status, String msg) {
        this.status = status;
        this.msg = msg;
    }

    public ResponseVo(Integer status, String msg, T data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }


    public static ResponseVo success() {
        return new ResponseVo(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getMsg());
    }

    public static <T> ResponseVo success(T data) {
        return new ResponseVo(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getMsg(), data);
    }

    public static ResponseVo error(ResponseEnum responseEnum) {
        return new ResponseVo(responseEnum.getCode(), responseEnum.getMsg());
    }
}

import lombok.Getter;

/**
 * @Auther: IT賤男
 * @Date: 2020/2/17 21:52
 * @Description: 返回參數枚舉
 */
// 這個註解使用Lombok
@Getter
public enum ResponseEnum {

    ERROR(-1, "服務端錯誤"),

    SUCCESS(0, "成功"),;

    private Integer code;

    private String msg;

    ResponseEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

 

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