类比前端知识来学习Java的Spring Boot实现MySql的全栈CRUD功能——搭配Svelte+Vite

前言

  • 本文梳理了后端的相关操作流程环节
  • 使用Svelte+Vite(前端)搭配Spring Boot(后端)
  • 实现了一个增删改查全栈项目
  • 有助于前端更好理解后端java的分层思想,数据流转控制
  • Svelte尝鲜学习了解
  • 完整前后端代码在github:github.com/shuirongshu...

大道至简,一些知识点是相通的比如------python里面也有闭包这个概念

所谓编程即:学习规则语法、理解规则语法、合理运用规则语法、从而自定义规则...

Java、Spring、Spring Boot ≈ Node.js、Express/Koa、Egg.js/NestJS

效果图

仓库代码图

对比理解后端宏观架构流程、数据流转

当我们知道后端具体做了什么事情以后,就更好理解了,即宏观架构流程、数据流转要清晰

Java之于Spring之于Spring Boot 相当于 Node.js之于Express/Koa之于Egg.js/NestJS

  • Java底层是JDK+JRE+JVM(JDK安装以后自带JRE和JVM,类似于Node安装后自带NPM)

  • 基于Java原生开发了Spring框架,基于Spring框架有了Spring Boot(开箱即用)

    • Spring MVC是Spring框架中的一部分,所谓的MVC指的是ModelViewController

    • 简约而言,后端主要做这几件事:

      1. 定义请求路由接口 (C路由)
      2. 请求参数验证 (C参数验证)
      3. 业务逻辑处理 (M业务逻辑)
      4. 操作数据库 (M业务逻辑)
      5. 返回响应数据JSON、下载返回流文件 (C路由返回 V视图概念消失弱化)
    • 前后端不分离JSP时代,MVC基本后端做。即:

      1. 过去: 后端 = M + C + V (渲染HTML)
      2. 现在: 后端 = M + C; 前端 = V (前端框架渲染) + 交互
  • 类比,Node.js --> Express.js / Koa.js --> Egg.js/NestJS (开箱即用)

  • 至于Java微服务Spring Cloud 实际上就是一堆Spring Boot的集合

技术栈类比

Spring Boot 生态 Node.js 对应技术 说明
JDK 8 Node.js ⚙️ 运行环境,学Java装JDK,就像学JS装Node
Spring && Spring MVC Express/Koa/Fastify 🚀 后端基础框架,快速搭建应用服务
Spring Boot 2.7.18 Egg.js/Nest.js 或 Express/Koa/Fastify + 一堆插件 🚀 后端进阶完善的框架,可开箱即用
MyBatis-Plus 3.5.3.1 Sequelize/Prisma 🗄️ ORM框架,简化数据库操作,不用手搓sql了
Swagger 3.0.0 + Knife4j 3.0.3 swagger-ui-express 📖 API文档自动生成
Hutool 5.8.22 lodash/day.js 🛠️ 工具库,提供各种实用函数
Apache POI 4.1.2 node-xlsx / xlsx 📊 Excel文件处理,导入导出解析excel的数据
数据库驱动(JDBC Driver) mysql或者mysql2 🔌 数据库连接
HikariCP mysql或者mysql2内置的连接池 🔌 数据库连接池,管理数据库连接
  • Maven 就像 npm,pom.xml 就是 package.json,依赖管理方式几乎一样!
  • Java 的包管理比 npm 更严格,但概念相同
  • Spring Boot 的注解就像 Vue的自定义指令

后端五件事(简约版)

    1. 定义请求路由接口
    1. 请求参数验证
    1. 业务逻辑处理
    1. 操作数据库
    1. 返回响应数据(JSON / 流)

整体流程

流程节点 核心操作
前端 → Nginx → Controller 前端发请求(如 GET /user/1),Controller 用 UserQueryDTO 接收参数(如 id=1),校验参数合法性
Controller → Service Controller 调用 Service 方法,传入 UserQueryDTO 或提取后的参数(如 id=1
Service → Mapper Service 处理业务逻辑(如权限判断),调用 Mapper 方法(如 userMapper.selectById(1)
Mapper → 数据库 Mapper 执行 SQL,将查询条件(id=1)转为数据库语句,同时通过 Entity 映射表结构(如 User 类对应 user 表)
数据库 → Mapper 数据库返回结果集,Mapper 自动将结果集转为 User 实体对象
Mapper → Service Mapper 将 User 实体返回给 Service
Service → Controller Service 将 User 实体通过转换器(或手动)转为 UserRespDTO(屏蔽敏感字段,如密码)
Controller → Nginx → 前端 Controller 将 UserRespDTO 转为 JSON 响应,返回给前端

下面以新增请求为例

当然还有别的 这里不赘述

1. 定义请求路由接口 (Controller层)

定义新增接口 /people

比如定义一个新增接口

  • 定义一个请求Url是 /people
  • Post 请求 Body传参
  • 传参示例:{ age: 20,home: "string",name: "string",remark: "string" }
  • curl命令调用如下
json 复制代码
curl -X POST http://localhost:8080/people \
  -H "Content-Type: application/json" \
  -d '{"age": 20, "home": "string", "name": "string", "remark": "string"}'

Controller控制层

PeopleController.java

java 复制代码
import com.people_sys.people.service.PeopleService; // 导入业务服务层 PeopleService
// @RestController = @Controller + @ResponseBody = 返回JSON格式的数据
@RestController 
@RestController // REST风格的接口
@RequestMapping("/people") // 接口请求url 统一请求前缀/people
public class PeopleController { // PeopleController类
    // 定义变量存储peopleService
    private final PeopleService peopleService;
    // 构造器注入(初始化执行)
    // 也可以使用注解 @Resource 或 @Autowired  一步到位
    public PeopleController(PeopleService peopleService) {
        this.peopleService = peopleService; // 存起来
    }
    
    // 新增人员 - POST /people
    @PostMapping
    public ApiResponse<Boolean> create(@Valid @RequestBody PeopleDTO people) throws Exception {
        // 调用peopleService层的create方法新增用户人员
        boolean result = peopleService.create(people);
        return ApiResponse.success(result);
    }
    // 根据id查询 - GET /people/{id}
    @GetMapping("/{id}")
    public ApiResponse<People> getById(@PathVariable Integer id) {
        // 调用peopleService层的getById方法,根据id查询对应人员的
        People people = peopleService.getById(id);
        return ApiResponse.success(people);
    }
    
    ......
}

何为注解&常见的注解举例

简而言之:

  • 注解有点像前端Vue中的指令,比如只要写了v-if以后,Vue框架会自动根据相应逻辑处理显示隐藏
  • 也像React中的Props,比如 @NotNull(message = "不能为空") 类比于 <input required ... />

注解(实际上是封装了一层),就是用特定的语法,告诉框架(组件),如何正确处理对应逻辑

常见的注解举例:

  • @RestController 控制器注解,标明定义的接口------适合前后端分析的项目,返回JSON

  • @Controller 适合前后端不分离的,比如返回html,用的少了

  • @Service 服务层注解,撰写具体业务逻辑

  • @Data Lombok注解,自动生成getter/setter/toString等

  • @RequestMapping系列注解,请求映射注解

    • @PostMapping // POST请求
    • @GetMapping("/{id}") // GET请求带路径参数
    • @PutMapping // PUT请求
    • @DeleteMapping // DELETE请求
    • @RequestMapping("/api") // 通用映射
  • 验证注解系列

    • @NotNull // 不为 null
    • @NotBlank // 去空格非空
    • @NotEmpty // 集合或数组至少一个元素
    • @Size(min=1, max=10) // 长度限制
    • @Email // 邮箱格式
    • @Min(0) @Max(150) // 数值范围
  • 跨域注解

java 复制代码
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
    // 仅当前接口支持跨域,允许所有来源(*)
    @CrossOrigin
    @GetMapping("/api/test")
    public String testCrossOrigin() {
        return "跨域请求成功!";
    }
}
  • 拿到前端传参的注解

    • @PathVariable注解

      • 能拿到/findById/100 这个100
      • 类似express中的req.params + 路由 :变量名
    • @RequestParam 注解

      • 能拿到query传参 ?name=xxx&age=88
      • 类似express中的req.query
    • @RequestBody

      • 能拿到body传参
      • 类似express中的req.body + 解析中间件app.use(express.json())
  • 等很多注解...

注解还可以自定义,有点像函数

5. 返回响应 JSON / 流

统一返回JSON

首先,前端请求接口时,后端统一返回格式,使用ApiResponse这个类统一控制

java 复制代码
package com.people_sys.people.config;
import lombok.Data;
@Data // 自动生成getter/setter
public class ApiResponse<T> {
    private int code;       // 状态码(如200成功,400参数错误,500系统错误)
    private String message; // 响应消息(成功时为"success",失败时为错误信息)
    private T data;         // 业务数据(成功时返回)
    public ApiResponse(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
    // 成功响应
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "success", data);
    }
    // 错误响应
    public static <T> ApiResponse<T> error(int code, String message) {
        return new ApiResponse<>(code, message, null);
    }
}

前端查询id为300的这条数据,http://localhost:8080/people/300返回

js 复制代码
{
    "code": 200,
    "message": "success",
    "data": {
        "id": 300,
        "name": "张三",
        "age": 3,
        "home": "山东",
        "remark": "zhangsan",
        "delFlag": 0,
        "createTime": "2025-11-04T14:48:05",
        "updateTime": "2025-11-04T17:23:32"
    }
}

ApiResponse实际上就是一个公共函数,统一加工处理数据的

返回流文件给前端下载

动态生成Excel并下载(伪代码示例)

java 复制代码
@GetMapping("/downloadExcel")
public void downloadExcel(HttpServletResponse response) throws IOException {
    // 1. 动态生成Excel(使用POI等工具)
    XSSFWorkbook workbook = new XSSFWorkbook(); // POI的Excel对象
    XSSFSheet sheet = workbook.createSheet("Sheet1");
    // ... 向sheet中写入数据 ...
​
    // 2. 设置响应头(同上)
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    String fileName = URLEncoder.encode("动态生成的Excel.xlsx", "UTF-8");
    response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);
​
    // 3. 直接将工作簿写入响应流
    workbook.write(response.getOutputStream());
    workbook.close(); // 关闭资源
}

workbook.write(response.getOutputStream()) 意思就是:将内存中动态生成的文件(如 Excel)以二进制流的形式写入 HTTP 响应输出流,最终返回给前端,以便于前端使用a标签实现文件下载

2. 请求参数验证 (Controller层)

数据新增接口细节拆解

接下来,看对应新增接口注释,新增接口前端Post的Body传参为

people: { name: 'tom', age: 2, home: 'New York', 'remark': 'xyz' }

java 复制代码
// 1. 接口请求类型注解,等价于:@RequestMapping(method = RequestMethod.POST)
@PostMapping
// 2. public公开的create方法,允许其他类调用(若写成private,Spring无法扫描到这个接口,前端会访问失败)
public ApiResponse<Boolean> create( // 方法名叫做create
    // 3. 数据校验触发注解(想要使用PeopleDTO里面的校验,必须要使用@Valid标明开启校验)
    @Valid  
    // 4. 请求体接收注解,通过这个可以拿到前端请求体里面的参数,并将其赋值给people参数
    @RequestBody  
    // 5. 方法参数(DTO 实体 + 参数名)
    PeopleDTO people  // people为函数的形参存储的前端参数
) throws Exception {  // 6. 异常则抛出声明
    // 7. 业务逻辑:把前端传递进来的people对象参数,调用 Service 层新增方法,得到布尔类型结果
    boolean result = peopleService.create(people);
    // 8. 返回统一响应结果,新增成功返回 {"code":200,"message":"success","data":true}
    return ApiResponse.success(result); 
}

通过@RequestBody 可以拿到前端参数 存到people变量里面,然后交由peopleService.create方法去做业务逻辑处理进而写入数据库

但是,前端可能乱传参,所以,需要搭配PeopleDTO和 @Valid 进行校验一下

什么是DTO,能做什么

简而言之:DTO就是规范接收前端传参、规范返回接口数据

新增的DTO

java 复制代码
package com.people_sys.people.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class PeopleDTO {
    @NotBlank(message = "姓名不能为空") // 必传,字符串类型
    private String name;
    @NotNull(message = "年龄不能为空") // 必传,数字类型
    private Integer age;
    private String home; // 非必传
    private String remark; // 非必传
}

编辑的DTO多了一个id,其他和新增一样(毕竟编辑要找到对应id再去编辑)

java 复制代码
@Data
public class PeopleUpdateDTO {

    @NotNull(message = "ID不能为空")
    private Integer id;

    @NotBlank(message = "姓名不能为空")
    private String name;

    ...
}

我们可以把DTO看成一个工具函数,在接到前端传参的时候,做规范限制------过滤掉不需要的参数,校验是否符合传参要求

用JavaScript模拟DTO功能

假设,我们要求前端传参规则是这样的:

name字段必传且为字符串类型、age字段非必传(若传了必须要是数字类型),若多传则忽略之

java 复制代码
const Dto = {
    name: {
        type: "string",
        required: true,
    },
    age: {
        type: "number",
        required: false,
    },
}
  • 假设用户传递{ name: '孙悟空', age: 500, home: '花果山' }那么经过DTO处理以后,能得到{ name: '孙悟空', age: 500}多传的home字段和其值,被忽略
  • 假设用户传递{ age: 500 }那么经过DTO处理以后,要校验提示name是必传的
  • 假设用户传递{ name: true }那么经过DTO处理以后,要校验提示name的数据类型不对

于是,就可以有以下模拟代码

js 复制代码
/**
 * 模拟定义DTO
 *  姓名为字符串,必传
 *  年龄为数字类型,非必传
 * */
const dtoDef = (params) => {
    // 定义Dto字段规则
    const Dto = {
        name: {
            type: "string",
            required: true,
        },
        age: {
            type: "number",
            required: false,
        },
    }
    /**
     * 1. 必传字段校验
     * */
    const mustHaveKeys = []
    // 1.1 收集那些字段是必传的
    for (const key in Dto) {
        if (Dto[key].required) {
            mustHaveKeys.push(key)
        }
    }
    // 1.2 收集传递进来的key组成的数组
    const paramsKeys = Object.keys(params)
    // 1.3 看看是否每一个必传字段,都在参数key数组里面
    const flag = mustHaveKeys.every((mk) => paramsKeys.includes(mk))
    // 1.4 必传参数校验
    if (!flag) {
        console.warn(`必传字段缺失,必传字段有这些:${mustHaveKeys.join(",")}`)
        return false
    }
    /**
    * 2. 字段类型校验
    * */
    const resDto = {}
    for (const key in params) {
        // 在Dto里的做对应校验
        if (key in Dto) {
            // 类型校验
            if (typeof params[key] === Dto[key].type) {
                // 校验通过则转存一份
                resDto[key] = params[key]
            } else {
                console.warn(`字段${key}类型错误,类型应为${Dto[key].type}`)
                return false
            }
        }
        // 不在Dto里面的忽略,这样resDto里存的就是定义好的
        else { }
    }
    return resDto
}

const par = { name: '孙悟空', age: 500, home: '花果山' }

// 经过dtoDef的校验、过滤就能得到符合要求的dto了
const result = dtoDef(par)
console.log('result', result) // {name: '孙悟空', age: 500}
  • 在java中,dto定义好以后,可在接受前端参数的时候使用

    • 只拿自己需要的字段值
    • 使用注解快速校验前端传参
  • 也可在从数据库捞出数据返回给前端的时候使用

    • 比如返回的时候,password和gender字段作为保密,不返回给前端
    • 只返回能返回的数据

所以,再总结一下:DTO就是规范接收前端传参、规范返回接口数据的一个工具

问:如果有一些额外的字段,要返回给前端,又不是前端要传递的字段,怎么定义呢?比如返数据时,需加一个字段isAdults来确认是否成年(规则是大于18岁成年为true,小于则为false)

答:这个时候,VO就闪亮登场了

VO是最终返回给前端数据结构格式

总结:VO是最终返回给前端数据结构格式------如果小项目,直接用dto也行(可以看成把dto当成vo用)

java 复制代码
@Data
public class PeopleVO {
    private Long id; // 额外字段:数据库主键(前端不用传,要展示)
    // 复用 DTO 中的字段
    private String name; 
    private Integer age;
    private String home;
    private String remark;
    private Boolean isAdults; // 额外字段:计算后信息,是否成年
}

3. 业务逻辑处理 & 4.操作数据库

先假设没有复杂业务逻辑处理,直接把前端传递的数据,存到数据库里,就需要编写sql语句

古法手搓sql

现在接口定义好了,且用DTO规范了前端传参,接下来应该把前端传递来的参数转成sql语句

比如,新增一条数据:{ "name": "tom", "age": 18 }

java 复制代码
public void addPerson(String name, int age) { // 拿到前端传进来的参数name和age
     // 拼接手搓sql
    String sql = "INSERT INTO people (name, age) VALUES (?, ?)";
    // 调用Spring的jdbcTemplate类的update方法新增数据
    int rowsAffected = jdbcTemplate.update(sql, name, age);
    // 成功插入 1 条记录 → 返回 1
    if (rowsAffected > 0) {
        System.out.println("成功插入 " + rowsAffected + " 条记录");
    }
}

但是这个手搓sql的方式,不优雅(可维护性、类型安全、重复劳动等),所以诞生了orm框架,通过orm框架去操作数据库

什么是ORM

  • ORM(Object-Relational Mapping,对象关系映射)是一种编程技术
  • 核心是把数据库中的 "表、行、列" 映射成程序中的 "类、对象、属性"
  • 让开发者能用面向对象(OOP)的方式操作数据库,而不用直接写复杂的 SQL 语句。

换句话说,ORM是翻译官

  • 数据库世界:用表(Table)、行(Row)、字段(Column)存储数据(比如 MySQL 的 user 表,有 id/name/age 字段);
  • 程序世界:用类(Class)、对象(Object)、属性(Attribute)处理数据(比如 Java 的 User 类,有 id/name/age 属性);
  • ORM 的作用:在两者之间做 "翻译"------ 假使我们操作程序里的对象(比如 user.name = "张三"),ORM 自动转换成对应的 SQL(UPDATE user SET name = "张三"),执行后再把数据库结果转回

ORM的核心价值就是不用写 或者少写 SQL,专注业务逻辑

上述案例,如果使用mybatis-plus这个orm框架(半自动化orm框架),则这样写即可

java 复制代码
public void addPerson(String name, int age) {
    // 创建实体对象并设置参数
    Person person = new Person();
    person.setName(name);
    person.setAge(age);

    // 调用 MyBatis-Plus 的 insert 方法插入数据
    int rowsAffected = personMapper.insert(person);

    if (rowsAffected > 0) {
        System.out.println("成功插入 " + rowsAffected + " 条记录");
    }
}

这里的personMapper继承自BaseMapper(自带一套通用的crud的方法)如

  • insert(T entity):插入一条记录
  • deleteById(Serializable id):根据主键删除
  • updateById(T entity):根据主键更新
  • selectById(Serializable id):根据主键查询
  • selectList(Wrapper<T> queryWrapper):条件查询列表

所以可以直接insert数据

java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 定义一个接口PersonMapper继承了BaseMapper上所用功能,比如crud的api
public interface PersonMapper extends BaseMapper<Person> {
    // 无需编写任何方法,BaseMapper 已提供 CRUD 基础功能
}

注意,BaseMapper<Person> 中的泛型 <Person> 就是告诉 MyBatis-Plus:这个 Mapper 要操作的是与 Person 类绑定的那张表(即 people 表)

所以,一定要告诉 MyBatis-Plus要操作那张表,怎么告诉呢 就通过entity告诉

所以,这里的<Person>就是一个entity,如下

java 复制代码
@Data  // 无需手动编写 getter、setter 等方法,@Data 会自动生成
@TableName("people") // 映射数据库表名------告诉mybatis,是那张表
public class Person {
    @TableId(type = IdType.AUTO) // 主键自增
    private Long id; 
    
    private String name; 
    private Integer age; 
}

所以ORM 一定是要搭配着entity,才能一块干活!!!

通俗而言,ORM是翻译官,他要把火星文翻译成中文,所以,它需要一个火星文对应中文词典,而entity就是这本词典

常见的 ORM 框架

  • Python:SQLAlchemy(通用)、Django ORM(Django 内置);
  • Java:Hibernate(重量级)、MyBatis(半 ORM,更灵活);
  • JavaScript/TypeScript:Sequelize(Node.js)、Prisma(现代主流);
  • PHP:Eloquent ORM(Laravel 内置)。

无论哪种 ORM 框架,都必须通过某种形式的 "实体" 来定义 "代码对象" 与 "数据库表" 的映射关系 (表名、字段名、类型、主键等)。这些 "实体" 可能叫 Model(PHP Prisma Python)Entity(Java、cSharp) 或直接是一个类 / 配置,但本质都是 ORM 框架的 "翻译词典"------ 没有它们,ORM 就无法完成 "对象操作→SQL 语句" 的转换。

ORM之Mybatis操作sql

回顾一下,使用ORM操作sql,首先,orm要知道操作那张表的哪些字段,当然,dto是老早就定义好了,如下提前定义好了的dto

java 复制代码
// DTO(接收前端)
public class PersonDTO {
    private String name;
    private Integer age;
    // getter/setter
}

我们会发现,dto中的东西不够用,毕竟DTO只是用来定义传输的部分数据,完整数据还是在表里。但是orm必须要知道完整数据,才方便操作数据库

而我们又不能把所有信息都丢到dto里面

所以,需要一个新的东西,来告知orm,完整的表、字段、数据类型是啥。用对象(类)的形式告知,映射数据库,于是就有了Entity这个文件

java 复制代码
// Entity(映射数据库)
@TableName("people") // 告知表名字
public class People {
    @TableId(type = IdType.AUTO) // 主键自增
    private Long id;
    private String name;
    private Integer age;
    // getter/setter
}

现在orm知道了操作的表名是people,和表里的对应字段信息,那么orm就方便操作数据库中的表了

java 复制代码
// 3. Service 层
public void addPerson(PersonDTO dto) {
    People people = new People();
    people.setName(dto.getName());
    people.setAge(dto.getAge());
    
    // 无需写 SQL!ORM 自动 INSERT
    boolean saved = peopleService.save(people);
    if (!saved) {
        throw new BusinessException("保存失败");
    }
}

ORM 框架的核心优势

  1. 简化开发:不用写 SQL,减少重复工作(比如拼接 SQL、解析结果),开发效率大幅提升;
  2. 屏蔽数据库差异:同一套代码,通过 ORM 适配 MySQL、PostgreSQL、SQLite 等不同数据库(ORM 负责翻译不同数据库的 SQL 语法);
  3. 降低学习成本:不用精通各种数据库的 SQL 细节,专注于面向对象编程;
  4. 安全性更高:自动防止 SQL 注入(比如拼接用户输入时,ORM 会自动转义)。

ORM总结

ORM 是连接 "面向对象编程" 和 "关系型数据库" 的桥梁,核心目标是让开发者用更熟悉的 OOP 方式操作数据库,减少 SQL 编写,提升开发效率和代码可维护性

Entity与DTO和VO对比

Entity 是"存数据的",DTO 是"传数据的",VO 是"给用户看的"。

  • Entity(实体类)------一般不直接返回 Entity 给前端
  • DTO(Data Transfer Object,数据传输对象)
  • VO(View Object / Value Object,视图对象)

如果项目简单、无敏感数据,可 DTO/VO 合并,但Entity 仍应隔离

维度 Entity DTO VO(View Object)
用途 数据库映射 层间/系统间数据传输 前端展示专用
是否持久化 是(对应 DB 表)
是否含敏感字段 可能有(如密码) 通常过滤掉 通常无
字段结构 与 DB 一致 灵活,可裁剪/组合 为 UI 定制,可能格式化/计算
使用位置 Repository / JPA 层 Service ↔ Controller / API 间 Controller → 前端
是否含逻辑 一般无(或简单 getter) 可能有简单格式化逻辑

3. 业务逻辑之新增的人员不能重名

java 复制代码
@Override
// 新增人员的校验:新增的人名字不能和数据库中已经有的人名字重复
public boolean create(PeopleDTO dto) throws Exception {
    // 校验名字是否重复
    if (lambdaQuery()
            .eq(People::getName, dto.getName())
            .eq(People::getDelFlag, 0)
            .count() > 0) {
        throw new BusinessException(400, "人员姓名已存在,请使用其他姓名");
    }

    People entity = BeanUtil.copyProperties(dto, People.class);
    return save(entity);
}

这里的save也是mybatis-plus提供的,比直接insert更加智能

由于我们的业务逻辑是------新增的人名字不能和数据库中已经有的人名字重复,所以,在写入数据库之前,还需要编写sql查询一下,数据库中有多少条数据,和当前人名一样。

Mybatis也提前准备好了lambdaQuery()以供我们进行链式调用,进行条件构造链式查询,语法简洁

若使用手写sql,则是如下写法

java 复制代码
@Autowired
private JdbcTemplate jdbcTemplate; // Spring 的 JDBC 工具类

public void checkDuplicateName(String name) {
    // 手写 SQL 字符串
    String sql = "SELECT COUNT(*) FROM people WHERE name = ? AND del_flag = 0";
    // 执行查询,获取记录数
    Integer count = jdbcTemplate.queryForObject(
        sql, 
        new Object[]{name},  // 绑定参数(姓名)
        Integer.class        // 返回类型
    );
    // 判断并抛异常
    if (count != null && count > 0) {
        throw new BusinessException(400, "人员姓名已存在,请使用其他姓名");
    }
}

由此可见,当真是使用ORM框架------Mybatis更优雅提效,便于我们处理业务路基,操作数据库

常用技术栈:

  • 数据库:MySQL + HikariCP(连接池)
  • 数据访问:MyBatis + MyBatis-Plus(ORM + 代码生成)
  • API 开发:Spring Web + SpringDoc-OpenAPI(接口 + 文档)
  • 缓存:Redis + Spring Cache(提升性能)
  • 消息队列:Kafka/RabbitMQ(削峰填谷)
  • 日志:SLF4J + Logback(日志记录)
  • 测试:JUnit 5 + Mockito(单元测试)
  • 部署:Maven + Docker(打包部署)

Maven打包构建

  • Maven之于Java如同Npm之于Node.js
  • pom.xml文件作依赖版本管理------package.json做依赖版本管理
  • mvn install安装项目依赖------相当于npm install
  • 项目内安装单个依赖,需手动在 pom.xml 文件中添加依赖后执行 mvn install------相当于npm install xxx
  • 卸载某个依赖,需手动删除 pom.xml 中依赖后执行 mvn clean install------相当于npm uninstall xxx
  • mvn package相当于npm run build
  • Maven构建打包功能,把一堆Java开发代码打包成一个.jar文件(压缩包)------Npm把一堆前端代码打包成一个dist文件夹

Java基础,面向对象、类、接口、抽象、封装、继承、多态、线程、IO流 本文暂不赘述...

Svelte的增删改查尝鲜

  • Svelte也有生命周期,如import { onMount } from "svelte";
  • 也可以数据双向绑定,如bind:value={variable}
  • 也有计算属性,如$: computed = expression
  • 可直接响应式变量,如let variable = value
  • 事件绑定语法是on:click={handler}
  • 阻止冒泡on:click|stopPropagation
  • 也有条件渲染,如
js 复制代码
{#if condition}
  <!-- 内容 -->
{:else}
  <!-- 其他内容 -->
{/if}
  • 也有循环v-for、map渲染
js 复制代码
{#each items as item (item.id)}
  <!-- 循环内容 -->
{/each}
  • 也可组件化开发项目(单文件直接和vue类似,直接 <script><style>HTML模板
  • 也可以import和export
  • 分页组件父组件传对象和做事件处理<Page {pageInfo} on:pageChange={handlePageChange} />
  • 对应子组件接收是
js 复制代码
// Props - 接收整个分页信息对象
export let pageInfo = {
    currentPage: 1,
    pageSize: 10,
    total: 0,
};
  • 子组件触发父组件使用createEventDispatcher,如
js 复制代码
import { createEventDispatcher } from "svelte";

// 创建事件分发器
const dispatch = createEventDispatcher();

// 按钮点击的回调
dispatch("pageChange", newPage);

比如和React语法对比:

  1. 更简洁的语法 : 无需 useStateuseEffect 等Hook
  2. 真正的响应式 : 直接赋值触发更新,不需要 setState()
  3. 更少的样板代码: 无需频繁的解构和回调
  4. 更小的包体积: 编译时优化,运行时更轻量
  5. 内置动画支持 : transitionanimate 指令
  6. CSS作用域自动化: 无需CSS Modules或CSS-in-JS

更多完整代码和注释,参见github仓库的前后端代码,创作不易,感谢支持点赞鼓励😉😉😉

相关推荐
汤姆yu2 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端
暮色妖娆丶2 小时前
Spring 源码分析 单例 Bean 的创建过程
spring boot·后端·spring
biyezuopinvip3 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
JavaGuide3 小时前
一款悄然崛起的国产规则引擎,让业务编排效率提升 10 倍!
java·spring boot
figo10tf4 小时前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
zhangyi_viva4 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端
橙露4 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
程序员敲代码吗4 小时前
Spring Boot与Tomcat整合的内部机制与优化
spring boot·后端·tomcat
NuageL4 小时前
原始Json字符串转化为Java对象列表/把中文键名变成英文键名
java·spring boot·json
jzheng86105 小时前
Spring Boot(快速上手)
java·spring boot·后端