Spring Boot 整合MyBatis-Plus 实现多层次树结构的异步加载功能

news2024/12/23 23:06:42

文章目录

    • 1,前言
    • 2,什么是多层次树结构?
    • 3,异步加载的意义
    • 4,技术选型与实现思路
    • 5,具体案例
      • 5.1,项目结构
      • 5.2,项目配置(pom.xml)
      • 5.3,配置文件(application.yml)
      • 5.4,实体类与Mapper接口
      • 5.5,Service层
      • 5.6,controller
      • 5.7,前端页面与异步加载实现
      • 5.8,数据库结构

1,前言

在使用 Spring Boot 和 MyBatis-Plus 实现多层次树结构的异步加载策略时,理解其基本原理和技术背景是至关重要的。本文将详细探讨如何通过 Spring Boot 的 RESTful API 和 MyBatis-Plus 的强大功能实现多层次树结构的异步加载,并展示如何使用 Thymeleaf、Bootstrap 和 JavaScript 实现前端的动态渲染。

2,什么是多层次树结构?

多层次树结构是许多应用场景中的常见需求,尤其是在分类管理、组织结构、权限管理等场景中。例如,电商平台中的商品分类可以有多个层级,从根类别到具体商品的详细分类,形成一个递归的树形结构。为了有效管理和展示这些数据,通常需要对其进行异步加载,即在用户需要时才加载具体的层级数据,而不是一次性加载所有数据。这不仅能够减少初始数据加载的时间,还可以提高用户体验。

3,异步加载的意义

在处理大型树结构时,性能是一个非常重要的考量因素。一次性加载所有层级的数据不仅可能会导致数据传输过大,还会引发前端页面的性能问题。异步加载策略通过在用户展开某个节点时,动态加载该节点下的子节点数据,有效地减少了数据传输量,提高了页面响应速度。这种方法尤其适用于需要处理大量数据并且层级较多的场景。

4,技术选型与实现思路

为了实现上述功能,我们将采用 Spring Boot 构建后端 API,使用 MyBatis-Plus 处理数据库操作,并通过前端的 Thymeleaf 模板、Bootstrap 进行 UI 展示。具体实现步骤包括:

  1. 数据库设计: 创建一个 category 表,包含 id、parent_id、name 等字段,用于存储分类的层次结构。
  2. 后端实现: 使用 Spring Boot 构建 RESTful API,通过 MyBatis-Plus 进行数据查询。后端 API 将支持根据 parent_id 查询子节点数据,提供给前端进行异步加载。
  3. 前端实现: 使用 Thymeleaf 模板引擎生成 HTML 页面,并通过 Bootstrap 提供的组件美化页面。通过 JavaScript 实现异步加载功能,当用户点击某个分类节点时,发送请求加载其子分类数据,并动态渲染到页面上。
  4. 代码示例与配置: 文章中将提供完整的代码示例,包括 Spring Boot 项目配置、MyBatis-Plus 的 Mapper 和 Service 实现,以及前端 HTML、JavaScript 代码,帮助开发者快速理解和实现多层次树结构的异步加载。本篇文章将深入讲解如何使用 Spring Boot3.3 和 MyBatis-Plus 联合实现多层次树结构的异步加载,并提供完整的代码示例。

5,具体案例

本章节将深入讲解如何使用 Spring Boot3.3 和 MyBatis-Plus 联合实现多层次树结构的异步加载,并提供完整的代码示例。运行效果:
在这里插入图片描述

5.1,项目结构

我们将构建一个Spring Boot项目,使用MyBatis-Plus进行数据库操作,并结合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

5.2,项目配置(pom.xml)

首先,我们需要配置pom.xml,包括Spring Boot和MyBatis-Plus的依赖:

<?xml version="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>
        <!-- 数据库驱动依赖 -->
        <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>

5.3,配置文件(application.yml)

接下来,我们配置application.yml,指定数据源及MyBatis-Plus的配置。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/tree_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=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

5.4,实体类与Mapper接口

我们使用一个简单的Category实体来表示树结构中的节点:

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接口定义了数据库操作:

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> {
}

5.5,Service层

在Service层,我们实现了获取树结构节点的逻辑:

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();
    }
}

5.6,controller

controller用来处理前端的异步请求:

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);
    }
}

5.7,前端页面与异步加载实现

前端使用Thymeleaf模板结合Bootstrap和JavaScript实现树结构的展示与异步加载:在 src/main/resources/templates 目录下创建 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" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.10.5/font/bootstrap-icons.min.css">
    <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">
        <!-- 根节点会从服务器加载并插入到这里 -->
    </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 () {
        // 加载根节点
        loadTreeNodes(0, $('#categoryTree'));

        // 动态加载节点
        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) {
                                // 加载子节点
                                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>

5.8,数据库结构

数据库表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;

初始化分类表数据:

-- 插入第一层数据(根节点)
INSERT INTO category (parent_id, name) VALUES (0, '电子产品');
INSERT INTO category (parent_id, name) VALUES (0, '家用电器');
INSERT INTO category (parent_id, name) VALUES (0, '时尚服饰');

-- 插入第二层数据
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, '女装');

-- 插入第三层数据
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, '上衣');

-- 插入第四层数据
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, '传统笔记本');
INSERT INTO category (parent_id, name) VALUES (17, '办公室连衣裙');
INSERT INTO category (parent_id, name) VALUES (17, '休闲连衣裙');

通过一个简单的分类树示例展示了如何在前端逐步加载节点,避免一次性加载大量数据带来的性能问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2164549.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++难点核心笔记(二)

系列文章目录 c难点&核心笔记(一) 继续接着上一章记录的重点内容包括函数&#xff0c;类和对象&#xff0c;指针和引用&#xff0c;C对象模型和this指针等内容&#xff0c;继续给大家分享&#xff01;&#xff01; 文章目录 系列文章目录友元全局函数做友元类做友元成员函…

国庆节怎么利用PHP发送文字短信

国庆节作为中国重要的法定节假日之一&#xff0c;不仅是全民欢庆的时刻&#xff0c;也是商家们进行促销活动的黄金时期。发送营销短信成为许多商家吸引顾客、提高销量的重要手段。 支持免费对接试用乐讯通PaaS平台 找好用的短信平台,选择乐讯通,短信群发|短信平台|群发短信软件…

分布式事务(1)

1.分布式事务 首先我们看看项目中的下单业务整体流程&#xff1a; 由于订单、购物车、商品分别在三个不同的微服务&#xff0c;而每个微服务都有自己独立的数据库&#xff0c;因此下单过程中就会跨多个数据库完成业务。而每个微服务都会执行自己的本地事务&#xff1a; 交易服…

Python办公自动化教程(002):PDF的拆分与合并

1、PyPDF2 介绍 介绍&#xff1a; PyPDF2是一个用于处理PDF文件的Python库&#xff0c;它提供了丰富的功能来读取、编辑、合并、拆分PDF文档&#xff0c;以及提取文本、图像和其他内容。 功能&#xff1a; 读取PDF&#xff1a;PyPDF2可以轻松地打开和读取PDF文件&#xff0c;获…

pytorch 神经网络模型 2D+3D 可视化,这个工具库够猛!

生信碱移 torch模块可视化 小编近期冲浪的时候发现一个torch模型架构可视化的神级python库VisualTorch&#xff0c;给各位铁子分享一下doge。 VisualTorch旨在帮助可视化基于Torch的神经网络架构&#xff0c;似乎是今年才上传到github上。它目前支持为PyTorch的Sequential和Cu…

jQuery——jQuery的2把利器

1、jQuery 核心函数 ① 简称&#xff1a;jQuery 函数&#xff0c;即为 $ 或者 jQuery ② jQuery 库向外直接暴露的是 $ 或者 jQuery ③ 引入 jQuery 库后&#xff0c;直接使用 $ 即可 当函数用&#xff1a;$&#xff08;xxx&#xff09; 当对象用&#xff1a;$.xxx&#x…

华为官宣,不支持安卓应用的纯血鸿蒙终于来了

华为前不久与苹果新品发布会撞车的全球首款量产三折叠屏幕手机 Mate XT&#xff0c;本以为已是其下半年狠活儿担当。 但直到看完昨天下午的华为秋季全场景发布会才发现&#xff0c;好家伙&#xff0c;此前那都叫小打小闹&#xff0c;原来大招全搁在后头呢&#xff01; 这场近两…

蒙语学习快速方法,速记蒙语单词怎么学习更高效!

要高效学习蒙古语和速记单词&#xff0c;首先要掌握基础知识&#xff0c;如字母表和发音规则。接着&#xff0c;专注于学习日常用语和基础词汇&#xff0c;并运用记忆技巧如联想、发音和构词法来帮助记忆。利用专门的学习软件&#xff0c;如“蒙语学习通”&#xff0c;可以提供…

进程间通信 (一)【管道通信(上)】

目录 1. 概况2. 管道通信的原理2.1 初步理解2.2 深入理解 1. 概况 是什么&#xff1a;两个及以上的进程实现数据层面的交互&#xff0c;称为进程间的通信。 因为进程独立性的存在&#xff0c;所以一个进程无法直接访问另一个进程的数据&#xff0c;即便是父子进程&#xff0c;子…

数字IC设计\FPGA 职位经典笔试面试整理--基础篇2

1. 卡诺图 逻辑函数表达式可以使用其最小项相加来表示&#xff0c;用所有的最小项可以转换为卡诺图进行逻辑项化简 卡诺图讲解资料1 卡诺图讲解资料2 卡诺图讲解资料3 最小项的定义 一个函数的某个乘积项包含了函数的全部变量&#xff0c;其中每个变量都以原变量或反变量的形…

从传统到智能:低代码平台在生产型企业中的应用实践

在全球数字化浪潮的推动下&#xff0c;生产型企业正面临前所未有的变革压力。为了在激烈的市场竞争中保持竞争力&#xff0c;企业迫切需要通过技术手段实现业务流程的优化和创新。然而&#xff0c;传统的软件开发方式往往耗时耗力&#xff0c;难以快速响应市场需求。低代码平台…

一些依赖库的交叉编译步骤

交叉编译链版本&#xff1a;12.3.0 一、curl-7.43.0库交叉编译 libcurl是一个跨平台的网络协议库&#xff0c;支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。libcurl同样支持HTTPS证书授权&#xff0c;HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传&a…

Django学习实战篇六(适合略有基础的新手小白学习)(从0开发项目)

前言&#xff1a; 上一章中&#xff0c;我们完成了页面样式的配置&#xff0c;让之前简陋的页面变得漂亮了些。 整理一下目前已经完成的系统&#xff0c;从界面上看&#xff0c;已经完成了以下页面: 首页分类列表页标签列表页口博文详情页 这离我们的需求还有些距离&#xff0…

哪款手机软件适合记事?记事本软件推荐

在这个信息爆炸的时代&#xff0c;手机已经成为我们生活中不可或缺的一部分。它不仅携带方便&#xff0c;而且功能强大&#xff0c;几乎可以完成我们日常所需的所有任务。随着生活节奏的加快&#xff0c;人们越来越需要一个可靠的工具来帮助自己记录重要信息和工作事项。这时候…

德勤校招网申笔试综合能力测试SHL题库与面试真题攻略

德勤的综合能力测试&#xff08;General Ability&#xff09;是其校园招聘在线测评的关键环节&#xff0c;旨在评估应聘者的多项认知能力。以下是对这部分内容的全面整合&#xff1a; 综合能力测试&#xff08;General Ability&#xff09; 测试时长为46分钟&#xff0c;包含…

ORA-12560:TNS:协议适配器错误

今天准备在数据库服务器创建一个用户&#xff0c;使用管理员账号进行登录 sqlplus / as sysdba时&#xff0c;突然报了个ORA-12560&#xff1a;TNS&#xff1a;协议适配器错误&#xff0c;吓的我一激灵&#xff0c;不应该啊&#xff0c;之前一直都是正常的&#xff0c;也是在网…

大漠yolo-数据集标注

参考 【按键精灵】大漠插件yolo环境配置_哔哩哔哩_bilibili 1. 2. 3.启动

MySQL高阶1873-计算特殊奖金

目录 题目 准备数据 分析数据 总结 题目 编写解决方案&#xff0c;计算每个雇员的奖金。如果一个雇员的 id 是 奇数 并且他的名字不是以 M 开头&#xff0c;那么他的奖金是他工资的 100% &#xff0c;否则奖金为 0 。 返回的结果按照 employee_id 排序。 准备数据 Crea…

记录踩坑 uniapp 引入百度地图(微信小程序,H5,APP)

前言 因为公司要求一定要用百度地图,网上引入百度地图的方法说的就三种(插件,异步,webview组件),因为我用的是VUE3 第一种方法引入插件(插件名vue-baidu-map)一直报错vue2没试过反正vue3引进去就是报错第二种方法用异步引入 如果只开发app和h5可以用,微信小程序反正不显示,但…

android studio 批量修改包名 app package name

1、批量修改包名&#xff1a;project view模式 我们可以看到&#xff0c;只可以修改myapplication的部分包名&#xff0c;前面的com.demo这个修改了&#xff0c;可以进行如下设置来达到修改demo的目的。 2、设置下&#xff0c;通过不同的目录来达到批量修改的目的&#xff1a;…