SpringBoot整合MybatisPlus基本的增删改查,保姆级教程

概述

MybatisPlus是国产的第三方插件, 它封装了许多常用的CURDapi,免去了我们写mapper.xml的重复劳动,这里介绍了基本的整合SpringBoot和基础用法。

引入依赖

在项目中 pom文件引入 mybatisplusmysql驱动依赖,如下图


        com.baomidou
        mybatis-plus-boot-starter
        3.5.1

         mysql
         mysql-connector-java
         8.0.26

配置连接信息

在项目中application.yaml文件中配置数据库的连接信息

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/didiadmin?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
    username: root
    password: 123456

新建两个表

本案例通过一个完整的业务流程来介绍如何使用 mybatispuls,首先在数据库中新建两个表,一个是字典类型表 sys_dict_type和一个字典数据表 sys_dict_data

CREATE TABLE sys_dict_type (
id CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '标识',
type_name VARCHAR (255) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型名称',
 type_code VARCHAR (255) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型标识',
 description VARCHAR (255) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型描述',
 enable CHAR (1) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否启用',
 create_by CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人ID',
  create_name CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人名称',
 create_time datetime DEFAULT NULL COMMENT '创建时间',
 update_by CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人ID',
 update_name CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人名称',
 update_time datetime DEFAULT NULL COMMENT '修改时间',
 remark VARCHAR (255) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
 PRIMARY KEY (id) USING BTREE
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC

CREATE TABLE sys_dict_data (
data_id CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '标识',
data_label CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典标签',
 data_value CHAR (20) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典值',
 type_code CHAR (20) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '所属类型',
 is_default CHAR (1) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否默认',
 update_by CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人ID',
   update_name CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人名称',
 update_time datetime DEFAULT NULL COMMENT '修改时间',
 create_by VARCHAR (255) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人ID',
   create_name CHAR (19) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人名称',
 create_time datetime DEFAULT NULL COMMENT '创建时间',
 remark VARCHAR (255) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
 enable CHAR (1) CHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否启用',
 PRIMARY KEY (data_id) USING BTREE
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC

在项目中创建相应的实体类

通过观察SQL语句发现两个表中有很多相同的字段,所有我们把相同的字段抽离出来,放到一个基础的实体类中,其他实体类通过集成方式获取公共的字段。

创建基础实体类

package com.didiplus.common.base;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* Author: didiplus
* Email: 972479352@qq.com
* CreateTime: 2022/4/29
* Desc: 基 础 实 体 类
*/

@Data
public class BaseDomain implements Serializable {

    /**
    * 创建时间
    */
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
    * 创建人
    */
    @TableField(value = "create_by", fill = FieldFill.INSERT)
    private String createBy;

    /**
    * 创建人名称
    */
    @TableField(value = "create_name", fill = FieldFill.INSERT)
    private String createName;

    /**
    * 修改时间
    */
    @TableField(value = "update_time", fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;

    /**
    * 修改人
    */
    @TableField(value = "update_by", fill = FieldFill.UPDATE)
    private String updateBy;

    /**
    * 修改人名称
    */
    @TableField(value = "update_name", fill = FieldFill.UPDATE)
    private String updateName;

    /**
    * 备注
    */
    private String remark;
}

@TableField 是mybatisplus中的一个注解,后面会讲解到的。

基础实体类创建好了,接着我们把剩下的两个实体类也一同创建吧。

SysDictType实体类

package com.didiplus.modules.sys.domain;

import com.didiplus.common.base.BaseDomain;
import com.didiplus.common.base.ValidGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.*;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/25
 * Desc: 字典类型领域模型
 */

@Data
@ApiModel(value = "字典类型")
public class SysDictType extends BaseDomain {

    /**
     * 标识
     */
    @Null(groups = ValidGroup.Crud.Create.class)
    @NotNull(groups = ValidGroup.Crud.Update.class,message = "字典ID不能为空")
    @ApiModelProperty("ID")
    private String id;

    /**
     * 字典名称
     */
    @NotBlank(message = "字典名称必填项")
    @ApiModelProperty(value = "字典名称",example = "用户ID")
    private String typeName;
    /**
     * 字典类型
     */
    @NotBlank(message = "字典编码不能为空")
    @ApiModelProperty(value = "字典编码")
    private String typeCode;
    /**
     * 字典描述
     */
    @ApiModelProperty(value = "字典描述")
    private String description;
    /**
     * 字典可用状态
     */
    @NotBlank(message = "字典状态不能为空")
    @ApiModelProperty(value = "字典状态")
    private Boolean enable;
}

SysDictData实体类

package com.didiplus.modules.sys.domain;

import com.baomidou.mybatisplus.annotation.TableField;
import com.didiplus.common.base.BaseDomain;
import com.didiplus.common.base.ValidGroup;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc: 字典值领域模型
 */

@Data
public class SysDictData extends BaseDomain {
    /**
     * id 编号
     */
    @Null(groups = ValidGroup.Crud.Create.class)
    @NotNull(groups = ValidGroup.Crud.Update.class,message = "字典数据ID不能为空")
    @ApiModelProperty("ID")
    @TableField(value = "data_id")
    private String dataId;
    /**
     * 字典显示
     */
    @NotBlank(message = "字典数据名称必填项")
    @ApiModelProperty(value = "字典数据名称")
    private String dataLabel;
    /**
     * 字典值
     */
    @NotBlank(message = "字典数据值不能为空")
    @ApiModelProperty(value = "字典数据值")
    private String dataValue;
    /**
     * 字典类型
     */
    @ApiModelProperty(value = "字典编码")
    @NotBlank(message = "字典数据值不能为空")
    private String typeCode;
    /**
     * 是否为默认
     */
    @ApiModelProperty(value = "字典编码")
    @NotBlank(message = "字典数据值不能为空")
    private String isDefault;

    /**
     * 是否启用
     */
    @NotBlank(message = "字典状态不能为空")
    @ApiModelProperty(value = "字典数据状态")
    private Boolean enable;
}

创建DAO继承MybatisPlus增强接口

为两个实体类中分别添加DAO继承MybatisPlus增强接口,这样就可以集成了增删改查的功能了。

SysDictTypeMapper

package com.didiplus.modules.sys.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.didiplus.modules.sys.domain.SysDictType;
import org.apache.ibatis.annotations.Mapper;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc: 字典类型接口
 */
@Mapper
public interface SysDictTypeMapper extends BaseMapper {
}

SysDictDataMapper

package com.didiplus.modules.sys.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.didiplus.modules.sys.domain.SysDictData;
import org.apache.ibatis.annotations.Mapper;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc: 字典数据接口
 */

@Mapper
public interface SysDictDataMapper extends BaseMapper {
}

进一步封装到Service层

定义Service接口中的抽象方法

SysDictDataService

package com.didiplus.modules.sys.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.didiplus.modules.sys.domain.SysDictData;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc:
 */

public interface SysDictDataService extends IService {
}

SysDictTypeService

package com.didiplus.modules.sys.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.didiplus.modules.sys.domain.SysDictType;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc:
 */
public interface SysDictTypeService extends IService {
}

实现Service接口中的方法

SysDictTypeServiceImpl

package com.didiplus.modules.sys.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.didiplus.modules.sys.domain.SysDictType;
import com.didiplus.modules.sys.mapper.SysDictTypeMapper;
import com.didiplus.modules.sys.service.SysDictTypeService;
import org.springframework.stereotype.Service;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc:
 */
@Service
public class SysDictTypeServiceImpl extends ServiceImpl implements SysDictTypeService {
}

SysDictDataServiceImpl

package com.didiplus.modules.sys.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.didiplus.modules.sys.domain.SysDictType;
import com.didiplus.modules.sys.mapper.SysDictTypeMapper;
import com.didiplus.modules.sys.service.SysDictTypeService;
import org.springframework.stereotype.Service;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc:
 */
@Service
public class SysDictTypeServiceImpl extends ServiceImpl implements SysDictTypeService {
}

在控制层上进行增删改查操作

SysDictTypeController

package com.didiplus.modules.sys.controller;

import com.didiplus.common.base.ValidGroup;
import com.didiplus.modules.sys.domain.SysDictType;
import com.didiplus.modules.sys.service.SysDictTypeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/25
 * Desc: 数据字典控制器
 */
@RestController
@Api(tags = "数据字典")
@RequestMapping("/api/sys/dictType")
public class SysDictTypeController {

    @Autowired
    SysDictTypeService sysDictTypeService;

    @ApiOperation("字典添加")
    @PostMapping("/add")
    public String add(@Validated(value = ValidGroup.Crud.Create.class) @RequestBody SysDictType sysDictType) {
        return  sysDictTypeService.save(sysDictType)? "添加成功":"添加失败";
    }

    @ApiOperation("字典修改")
    @PutMapping("/edit")
    public String edit(@Validated(value = ValidGroup.Crud.Update.class) @RequestBody SysDictType sysDictType) {
        return  sysDictTypeService.updateById(sysDictType)? "修改成功":"修改失败";
    }

    @ApiOperation("字典删除")
    @DeleteMapping("/del/{id}")
    public  String del( @PathVariable String id) {
        return  sysDictTypeService.removeById(id)? "删除成功":"删除失败";
    }

}

体验效果

新增数据

SpringBoot整合MybatisPlus基本的增删改查,保姆级教程
SpringBoot整合MybatisPlus基本的增删改查,保姆级教程

修改数据

SpringBoot整合MybatisPlus基本的增删改查,保姆级教程
SpringBoot整合MybatisPlus基本的增删改查,保姆级教程

删除数据

SpringBoot整合MybatisPlus基本的增删改查,保姆级教程

自动填充功能

原理:

  • 实现元对象处理器接口: com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
  • 注解填充字段 @TableField(.. fill = FieldFill.INSERT)生成器策略部分也可以配置!

自定义实现类 DomainInterceptor

package com.didiplus.common.web.interceptor;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/5/4
 * Desc: 字 段 填 充 拦 截 器
 */
@Component
public class DomainInterceptor implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        createField(metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        updateField(metaObject);
    }

    /**
     * @Field 创建时间
     * */
    public void createField(MetaObject metaObject){
        this.strictInsertFill(metaObject,"createTime", LocalDateTime.class,LocalDateTime.now());
    }

     /**
     * @Field 修改时间
     * */
    public void updateField(MetaObject metaObject) {
        this.strictUpdateFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());
    }
}

分页查询

添加分页插件

package com.didiplus.common.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/5/4
 * Desc: mybatis-plus分页插件
 */
@Configuration
public class MyBatisPlusConfig {

    /**
     * mybatis-plus分页插件
     */
    @Bean
    public MybatisPlusInterceptor  mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

定义分页接口

package com.didiplus.modules.sys.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.didiplus.common.web.domain.PageDomain;
import com.didiplus.modules.sys.domain.SysDictType;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc: 数据字典类型服务类
 */
public interface SysDictTypeService extends IService {

    IPage page(PageDomain pageDomain);
}

实现分页接口

package com.didiplus.modules.sys.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.didiplus.common.web.domain.PageDomain;
import com.didiplus.modules.sys.domain.SysDictType;
import com.didiplus.modules.sys.mapper.SysDictTypeMapper;
import com.didiplus.modules.sys.service.SysDictTypeService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/4/29
 * Desc:
 */
@Service
public class SysDictTypeServiceImpl extends ServiceImpl implements SysDictTypeService {
    @Resource
    SysDictTypeMapper sysDictTypeMapper;
    @Override
    public IPage page(PageDomain pageDomain) {
        IPage page = new Page<>(pageDomain.getPage(),pageDomain.getLimit());
        return sysDictTypeMapper.selectPage(page,null);
    }
}

控制层调用

@RestController
@Api(tags = "数据字典")
@RequestMapping("/api/sys/dictType")
public class SysDictTypeController {

    @Autowired
    SysDictTypeService sysDictTypeService;

    @ApiOperation("字典分页查询")
    @GetMapping
    public IPage list(@RequestBody PageDomain pageDomain){
        return sysDictTypeService.page(pageDomain);
    }
}

PageDomain定义了分页接收的两个参数

PageDomain类

package com.didiplus.common.web.domain;

import lombok.Data;

/**
 * Author: didiplus
 * Email: 972479352@qq.com
 * CreateTime: 2022/5/4
 * Desc: 分 页 参 数 封 装
 */
@Data
public class PageDomain {
    /**
     * 当前页
     */
    private  Integer page;
    /**
     * 每页数量
     */
    private  Integer limit;

}

体验效果

SpringBoot整合MybatisPlus基本的增删改查,保姆级教程
下一篇,我们继续来学习mybatisPlus的代码生成器

Original: https://www.cnblogs.com/alanlin/p/16224187.html
Author: 北根娃
Title: SpringBoot整合MybatisPlus基本的增删改查,保姆级教程

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/643753/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Switchhosts没有写入host文件的权限的解决方法

    第一次使用Switchhosts来修改host,记录一下遇到的小问题,这个通用的解决方法很多博客都记录了,浅浅说一下传统的解决思路,以及自己遇到的一个小tips! 通用解决方法: …

    技术杂谈 2023年7月11日
    091
  • tcpdump交叉编译及使用

    第一步.下载官方网站:http://www.tcpdump.org/需要下载libpcap包和tcpdump包我下载的版本是:libpcap-1.4.0.tar.gz和tcpdum…

    技术杂谈 2023年6月1日
    0115
  • 七、注释、标识符、数据类型与类型转换

    书写注释是一个好习惯 public class Comment { public static void main(String[] args) { //&#x5355;&…

    技术杂谈 2023年6月21日
    083
  • 上周热点回顾(7.18-7.24)

    热点随笔: · 聊一聊 C# 后台GC 到底是怎么回事? (一线码农)· 从工程师到技术leader思维升级 (温一壶清酒)· 超酷炫的转场动画?CSS 轻松拿下!(ChokCoc…

    技术杂谈 2023年5月31日
    092
  • Element-DatePicker的宽度

    打开一个vue文件,添加DatePicker日期选择器组件,设置默认日期为null。如图 Original: https://www.cnblogs.com/beichengshi…

    技术杂谈 2023年7月10日
    064
  • 分布式ID生成方案

    分布式ID策略 为什么要用分布式ID? 在我们业务数据量不大的时候,单库单表完全可以支撑现有业务,数据再大一点搞个 MySQL 主从同步读写分离也能对付。 但随着数据日渐增长,主从…

    技术杂谈 2023年6月21日
    0103
  • MyBatisPlus实现分页和查询操作就这么简单

    《SpringBoot整合MybatisPlus基本的增删改查,保姆级教程》在这篇文章中,我们详细介绍了分页的具体实现方法。但是,在日常的开发中还需要搜索功能的。下面让我们一起动起…

    技术杂谈 2023年6月21日
    0100
  • 5个节约生命的Python小技巧

    前言 Python是一种强大且易上手的语言,语法简洁优雅,不像Java那么繁琐废话,并且有一些特殊的函数或语法可以让代码变得更加 简短精悍。根据我的经验,下面介绍常用的5个Pyth…

    技术杂谈 2023年6月21日
    0107
  • flutter RN taro选型思考

    当前RN已经成熟,但是依赖于大平台(JD、携程),小公司想开箱即用还是有困难的 纯Flutter还远未成熟,更多的是和原生进行混合 但是作为个体又想要在某一个点切入市场,就是需要作…

    技术杂谈 2023年5月30日
    096
  • 职场感悟

    ============================================================================== 本博客已经废弃,不在维…

    技术杂谈 2023年6月1日
    092
  • Liunx-CentOS安装MySQL8

    0 卸载 0.1 卸载原有的MariaDB 查看MariaDB安装包 rpm -qa | grep mariadb 卸载MariaDB rpm -e mariadb-libs-5….

    技术杂谈 2023年6月21日
    0110
  • 矿池列表汇总

    比特币 (BTC)矿池信息整理 矿池名称挖矿模式矿池手续费矿池地址及端口备注 (蚂蚁矿池) PPS PPLNS PPS+ SOLO PPS: -5% PPLNS: 0% PPS+:…

    技术杂谈 2023年5月31日
    0131
  • 熟悉事件循环?那谈谈为什么会分为宏任务和微任务。

    什么是事件循环 在了解事件循环前,需要一些有关 JS 特性的前置知识。 JS 引擎是单线程的,直白来说就是一个时间点下 JS 引擎只能去做一件事情,而 Java 这种多线程语言,可…

    技术杂谈 2023年5月31日
    091
  • @Service注解是标注在实现类上的的接口中添加注解还是在实现类impl

    @Service注解是标注在实现类上的因为@Service是把spring容器中的bean进行实例化,也就是等同于new操作,只有实现类是可以进行new实例化的,而接口则不能,所以…

    技术杂谈 2023年5月30日
    092
  • 最近身边一个技术负责人裸辞了…

    原创不易,求分享、求一键三连 前段时间有个30多岁的技术负责人在群里感叹: 小钗啊,看了你那么多文章,虽然受益匪浅,但依旧做不好事啊!老板以为中年男人好拿捏,错,我不是,你让我心情…

    技术杂谈 2023年6月1日
    089
  • Redis基本操作

    windows 下载 https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100 然后解压 打开服务 然后…

    技术杂谈 2023年7月11日
    089
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球