使用 Spring Boot3.3 與 MyBatis-Plus 聯(lián)合實現(xiàn)多層次樹結構的異步加載策略
在使用 Spring Boot 和 MyBatis-Plus 實現(xiàn)多層次樹結構的異步加載策略時,理解其基本原理和技術背景是至關重要的。本文將詳細探討如何通過 Spring Boot 的 RESTful API 和 MyBatis-Plus 的強大功能實現(xiàn)多層次樹結構的異步加載,并展示如何使用 Thymeleaf、Bootstrap 和 JavaScript 實現(xiàn)前端的動態(tài)渲染。
什么是多層次樹結構?
多層次樹結構是許多應用場景中的常見需求,尤其是在分類管理、組織結構、權限管理等場景中。例如,電商平臺中的商品分類可以有多個層級,從根類別到具體商品的詳細分類,形成一個遞歸的樹形結構。為了有效管理和展示這些數(shù)據(jù),通常需要對其進行異步加載,即在用戶需要時才加載具體的層級數(shù)據(jù),而不是一次性加載所有數(shù)據(jù)。這不僅能夠減少初始數(shù)據(jù)加載的時間,還可以提高用戶體驗。
異步加載的意義
在處理大型樹結構時,性能是一個非常重要的考量因素。一次性加載所有層級的數(shù)據(jù)不僅可能會導致數(shù)據(jù)傳輸過大,還會引發(fā)前端頁面的性能問題。異步加載策略通過在用戶展開某個節(jié)點時,動態(tài)加載該節(jié)點下的子節(jié)點數(shù)據(jù),有效地減少了數(shù)據(jù)傳輸量,提高了頁面響應速度。這種方法尤其適用于需要處理大量數(shù)據(jù)并且層級較多的場景。
技術選型與實現(xiàn)思路
為了實現(xiàn)上述功能,我們將采用 Spring Boot 構建后端 API,使用 MyBatis-Plus 處理數(shù)據(jù)庫操作,并通過前端的 Thymeleaf 模板、Bootstrap 進行 UI 展示。具體實現(xiàn)步驟包括:
- 數(shù)據(jù)庫設計: 創(chuàng)建一個 category 表,包含 id、parent_id、name 等字段,用于存儲分類的層次結構。
 - 后端實現(xiàn): 使用 Spring Boot 構建 RESTful API,通過 MyBatis-Plus 進行數(shù)據(jù)查詢。后端 API 將支持根據(jù) parent_id 查詢子節(jié)點數(shù)據(jù),提供給前端進行異步加載。
 - 前端實現(xiàn): 使用 Thymeleaf 模板引擎生成 HTML 頁面,并通過 Bootstrap 提供的組件美化頁面。通過 JavaScript 實現(xiàn)異步加載功能,當用戶點擊某個分類節(jié)點時,發(fā)送請求加載其子分類數(shù)據(jù),并動態(tài)渲染到頁面上。
 - 代碼示例與配置: 文章中將提供完整的代碼示例,包括 Spring Boot 項目配置、MyBatis-Plus 的 Mapper 和 Service 實現(xiàn),以及前端 HTML、JavaScript 代碼,幫助開發(fā)者快速理解和實現(xiàn)多層次樹結構的異步加載。
 
本篇文章將深入講解如何使用 Spring Boot3.3 和 MyBatis-Plus 聯(lián)合實現(xiàn)多層次樹結構的異步加載,并提供完整的代碼示例。
運行效果:
圖片
若想獲取項目完整代碼以及其他文章的項目源碼,且在代碼編寫時遇到問題需要咨詢交流,歡迎加入下方的知識星球。
項目結構
我們將構建一個Spring Boot項目,使用MyBatis-Plus進行數(shù)據(jù)庫操作,并結合Thymeleaf模板引擎在前端展示樹結構。以下是項目的基本結構:
springboot-tree-async
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com.icoderoad.treeasync
│   │   │       ├── controller
│   │   │       ├── entity
│   │   │       ├── mapper
│   │   │       ├── service
│   │   │       └── serviceImpl
│   │   ├── resources
│   │   │   ├── templates
│   │   │   ├── application.yml
│   └── test
│       └── java
│           └── com.icoderoad.treeasync
└── pom.xml項目配置(pom.xml)
首先,我們需要配置pom.xml,包括Spring Boot和MyBatis-Plus的依賴:
<?xml versinotallow="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.icoderoad</groupId>
	<artifactId>treeasync</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>treeasync</name>
	<description>Demo project for Spring Boot</description>
	
	<properties>
		<java.version>17</java.version>
		 <mybatis-plus-boot-starter.version>3.5.7</mybatis-plus-boot-starter.version>
        <mybatis-spring.version>3.0.3</mybatis-spring.version>
	</properties>
	<dependencies>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
	        <groupId>com.baomidou</groupId>
	        <artifactId>mybatis-plus-boot-starter</artifactId>
	        <version>${mybatis-plus-boot-starter.version}</version>
	    </dependency>
	   
	     <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis-spring.version}</version>
      	</dependency>
        <!-- 數(shù)據(jù)庫驅動依賴 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>配置文件(application.yml)
接下來,我們配置application.yml,指定數(shù)據(jù)源及MyBatis-Plus的配置。
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/tree_db?useUnicode=true&characterEncoding=UTF-8&serverTimeznotallow=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  mapper-locations: classpath:/mapper/*.xml
  global-config:
    db-config:
      id-type: auto實體類與Mapper接口
我們使用一個簡單的Category實體來表示樹結構中的節(jié)點:
package com.icoderoad.treeasync.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("category")
public class Category {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long parentId;
    private String name;
}CategoryMapper接口定義了數(shù)據(jù)庫操作:
package com.icoderoad.treeasync.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.icoderoad.treeasync.entity.Category;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}Service層
在Service層,我們實現(xiàn)了獲取樹結構節(jié)點的邏輯:
package com.icoderoad.treeasync.service;
import com.icoderoad.treeasync.entity.Category;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface CategoryService extends IService<Category> {
    List<Category> getChildren(Long parentId);
}
package com.icoderoad.treeasync.service.impl;
import com.icoderoad.treeasync.entity.Category;
import com.icoderoad.treeasync.mapper.CategoryMapper;
import com.icoderoad.treeasync.service.CategoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
    @Override
    public List<Category> getChildren(Long parentId) {
        return lambdaQuery().eq(Category::getParentId, parentId).list();
    }
}控制器
控制器用來處理前端的異步請求:
package com.icoderoad.treeasync.controller;
import com.icoderoad.treeasync.entity.Category;
import com.icoderoad.treeasync.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/category")
public class CategoryController {
    @Autowired
    private CategoryService categoryService;
    @GetMapping("/children/{parentId}")
    public List<Category> getChildren(@PathVariable Long parentId) {
        return categoryService.getChildren(parentId);
    }
}前端頁面與異步加載實現(xiàn)
前端使用Thymeleaf模板結合Bootstrap和JavaScript實現(xiàn)樹結構的展示與異步加載:
在 src/main/resources/templates 目錄下創(chuàng)建 index.html 模板文件。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>分類樹結構</title>
    <link rel="stylesheet" >
    <link rel="stylesheet" >
    <style>
        .tree-item {
            cursor: pointer;
            padding-left: 1rem;
        }
        .tree-icon {
            margin-right: 0.5rem;
        }
    </style>
</head>
<body>
<div class="container">
    <h2 class="my-4">分類樹結構</h2>
    <ul id="categoryTree" class="list-group">
        <!-- 根節(jié)點會從服務器加載并插入到這里 -->
    </ul>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script>
    $(document).ready(function () {
        // 加載根節(jié)點
        loadTreeNodes(0, $('#categoryTree'));
        // 動態(tài)加載節(jié)點
        function loadTreeNodes(parentId, ulElement) {
            $.ajax({
                url: '/category/children/' + parentId,
                method: 'GET',
                success: function (data) {
                    $.each(data, function (index, category) {
                        const li = $('<li>').addClass('list-group-item tree-item');
                        const icon = $('<i>').addClass('bi bi-chevron-right tree-icon');
                        const span = $('<span>').text(category.name);
                        li.append(icon).append(span).data('id', category.id);
                        ulElement.append(li);
                        // 點擊展開或折疊
                        li.on('click', function (e) {
                            e.stopPropagation();
                            const iconElement = $(this).find('.tree-icon');
                            const childrenUl = $(this).find('ul');
                            if (childrenUl.length === 0) {
                                // 加載子節(jié)點
                                const newUl = $('<ul>').addClass('list-group ml-3');
                                loadTreeNodes($(this).data('id'), newUl);
                                $(this).append(newUl);
                                iconElement.removeClass('bi-chevron-right').addClass('bi-chevron-down');
                            } else {
                                // 切換展開/折疊
                                childrenUl.toggle();
                                iconElement.toggleClass('bi-chevron-right bi-chevron-down');
                            }
                        });
                    });
                }
            });
        }
    });
</script>
</body>
</html>數(shù)據(jù)庫結構
數(shù)據(jù)庫表category的SQL DDL語句如下:
CREATE TABLE `category` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `parent_id` bigint DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;初始化分類表數(shù)據(jù):
-- 插入第一層數(shù)據(jù)(根節(jié)點)
INSERT INTO category (parent_id, name) VALUES (0, '電子產品');
INSERT INTO category (parent_id, name) VALUES (0, '家用電器');
INSERT INTO category (parent_id, name) VALUES (0, '時尚服飾');
-- 插入第二層數(shù)據(jù)
INSERT INTO category (parent_id, name) VALUES (1, '手機');
INSERT INTO category (parent_id, name) VALUES (1, '筆記本電腦');
INSERT INTO category (parent_id, name) VALUES (2, '冰箱');
INSERT INTO category (parent_id, name) VALUES (2, '洗衣機');
INSERT INTO category (parent_id, name) VALUES (3, '男裝');
INSERT INTO category (parent_id, name) VALUES (3, '女裝');
-- 插入第三層數(shù)據(jù)
INSERT INTO category (parent_id, name) VALUES (4, '智能手機');
INSERT INTO category (parent_id, name) VALUES (4, '功能手機');
INSERT INTO category (parent_id, name) VALUES (5, '游戲筆記本');
INSERT INTO category (parent_id, name) VALUES (5, '超極本');
INSERT INTO category (parent_id, name) VALUES (6, '雙門冰箱');
INSERT INTO category (parent_id, name) VALUES (6, '單門冰箱');
INSERT INTO category (parent_id, name) VALUES (7, '滾筒洗衣機');
INSERT INTO category (parent_id, name) VALUES (7, '波輪洗衣機');
INSERT INTO category (parent_id, name) VALUES (8, '休閑裝');
INSERT INTO category (parent_id, name) VALUES (8, '正裝');
INSERT INTO category (parent_id, name) VALUES (9, '連衣裙');
INSERT INTO category (parent_id, name) VALUES (9, '上衣');
-- 插入第四層數(shù)據(jù)
INSERT INTO category (parent_id, name) VALUES (10, '安卓手機');
INSERT INTO category (parent_id, name) VALUES (10, '蘋果手機');
INSERT INTO category (parent_id, name) VALUES (13, '變形筆記本');
INSERT INTO category (parent_id, name) VALUES (13, '傳統(tǒng)筆記本');
INSERT INTO category (parent_id, name) VALUES (17, '辦公室連衣裙');
INSERT INTO category (parent_id, name) VALUES (17, '休閑連衣裙');總結
本文介紹了如何使用Spring Boot結合MyBatis-Plus實現(xiàn)多層次樹結構的異步加載策略。我們通過一個簡單的分類樹示例展示了如何在前端逐步加載節(jié)點,避免一次性加載大量數(shù)據(jù)帶來的性能問題。希望通過這篇文章,您能夠對Spring Boot和MyBatis-Plus的聯(lián)合使用有更深入的理解,并能夠將其應用到實際項目中。















 
 
 














 
 
 
 