类比前端知识来学习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仓库的前后端代码,创作不易,感谢支持点赞鼓励😉😉😉

相关推荐
淘源码d3 小时前
什么是医院随访系统?成熟在用的智慧随访系统源码
java·spring boot·后端·开源·源码·随访系统·随访系统框架
Q_Q19632884754 小时前
python+django/flask基于机器学习的就业岗位推荐系统
spring boot·python·django·flask·node.js·php
ArabySide4 小时前
【Spring Boot】REST与RESTful详解,基于Spring Boot的RESTful API实现
spring boot·后端·restful
程序定小飞5 小时前
基于springboot的学院班级回忆录的设计与实现
java·vue.js·spring boot·后端·spring
攀小黑5 小时前
基于若依-内容管理动态修改,通过路由字典配置动态管理
java·vue.js·spring boot·前端框架·ruoyi
郝开7 小时前
Spring Boot 2.7.18(最终 2.x 系列版本)1 - 技术选型:连接池技术选型对比;接口文档技术选型对比
java·spring boot·spring
知兀7 小时前
【Spring/SpringBoot】SSM(Spring+Spring MVC+Mybatis)方案、各部分职责、与Springboot关系
java·spring boot·spring
汤姆yu8 小时前
基于springboot的民间救援队救助系统
java·spring boot·后端·救援队
ruleslol8 小时前
SpringBoot面试题04-BeanDefinition
spring boot