ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot实战day02:接口、配置与MyBatis-Plus数据库集成

Spring Boot实战day02:接口、配置与MyBatis-Plus数据库集成 如果你跟着 day01 把 Spring Boot 跑起来了现在大概率卡在一个有点微妙的状态项目能启动控制台滚了一大堆日志但浏览器不知道访问什么也不知道下一步到底该学什么。这个阶段我太熟悉了带过不少新人第二天往往是最容易学歪的——有人急着去看源码有人开始刷中间件结果都没有形成一个完整的“开发反馈回路”。day02 不会碰微服务不会上消息队列更不会让你去啃源码。这一篇只带你做四件事写一个能调通的接口、把启动时端口号的问题搞清楚、把配置从代码里解放出来、让项目真正连上数据库。这四件事做完你手里就会有一个能跑、能调试、结构有点像个正经项目的后端雏形。不管你是昨天刚配好环境的新手还是以前用过 SSM、想快速过渡到 Spring Boot 的老开发这篇都适合你。如果连项目都还没创建先回到 day01 把基础环境补齐再往下走。1. 从“能启动”到“能调通一个接口”先把反馈回路建起来1.1 为什么 day02 的第一件事是写代码而不是背原理很多初学者有个误区觉得框架学得好不好取决于背了多少理论、看过多少源码。我把话放这儿Spring Boot 这套东西你先把接口跑通了再回头理解原理效率至少翻一倍。原因很简单。Spring Boot 之所以叫“Boot”核心价值就是两条内嵌容器和自动配置。你不需要像以前 SSM 时代那样手动去配置 Tomcat、手动装配 Spring MVC你只需要引入一个spring-boot-starter-web依赖项目启动时就会自动起一个内置的 Tomcat连端口、路由、JSON 序列化这些事全都替你安排好了。这个过程就像一个订好的餐车你不需要自己去砌灶台、拉水管打开车门就能开始做菜。如果你不亲手做一道菜光在旁边研究灶台原理很快就饿了。写第一个接口就是那道最基础的“蛋炒饭”。1.2 先写一个能返回数据的接口Spring MVC 的“自动挡”我见过太多种教科书写法上来就让你写一个“标准的三层架构”对着 Controller、Service、Mapper 一整套狂轰滥炸。day02 不用这么折腾先写一个最简单的接口感受一下“请求进来、响应出去”这个闭环。在项目的src/main/java下面找到你 day01 创建好的启动类所在的包路径新建一个HelloControllerpackage com.example.day02; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; RestController public class HelloController { GetMapping(/hello) public String hello() { return Hello Spring Boot; } }这里面的RestController是核心它实际上等于Controller加ResponseBody两个注解的合体。Controller告诉 Spring 这个类是一个 Web 控制器ResponseBody告诉 Spring 把方法返回值直接写进 HTTP 响应体而不是去解析成一个视图页面。GetMapping(/hello)则是把GET请求、路径/hello、当前方法这三者绑定在一起。保存文件直接重新启动项目。等控制台出现启动完成日志后打开浏览器访问http://localhost:8080/hello看到Hello Spring Boot出现在页面上你的第一个 Spring Boot 接口就成了。接着试一下返回 JSON。新建一个User类加上几个字段和 getter/setterpublic class User { private Long id; private String name; public User() {} public User(Long id, String name) { this.id id; this.name name; } public Long getId() { return id; } public void setId(Long id) { this.id id; } public String getName() { return name; } public void setName(String name) { this.name name; } }再改一下 Controller新增一个方法GetMapping(/api/user) public User getUser() { return new User(1L, day02); }重启后访问http://localhost:8080/api/user浏览器里会出现{id:1,name:day02}。这就是 Spring Boot 自动配置里的 Jackson 库在工作它发现在 Web 环境下要把一个 Java 对象写回响应于是自动把它序列化成 JSON。你不需要配置任何东西这就是“自动挡”的爽感。1.3 三种验证方式别只会开浏览器很多新手调接口有个习惯永远只开浏览器。浏览器确实能验证 GET 请求但项目往后做你会频繁用到 POST、PUT、DELETE还需要带请求头、带 body这时候你至少应该掌握下面三种验证方式里的两种。第一种浏览器直接访问。适合 GET 请求的快速验证缺点是只能做简单访问无法自定义请求头。第二种IDEA 自带的 HTTP Client。这个很多人不知道其实在项目根目录建一个后缀为.http的文件就能直接在里面写请求IDEA 会在编辑器里给你一个可点击的发送按钮。比如创建一个test.httpGET http://localhost:8080/api/user Accept: application/json然后点编辑器左侧的绿色三角图标就能运行底部会直接显示返回结果和响应耗时。这个工具比 Postman 轻量而且文件还能提交到 Git非常适合用来维护接口的冒烟测试用例。第三种curl命令。打开终端直接敲curl http://localhost:8080/api/user如果你用的是 WindowsIDEA 底部的 Terminal 窗口也支持 curl。这种方式的优势是方便脚本化以后你在服务器上排查问题没有图形界面全得靠 curl。1.4 一个新手特别容易忽略的坑改了代码没重启在 day02 阶段你可能会遇到一种情况改完代码刷新浏览器发现还是旧结果。这时候先别怀疑写法有问题大概率是项目没有重新编译启动。Spring Boot 在默认情况下改动 Java 代码之后必须重启应用进程才生效。你刷新浏览器只是重新发了一次 HTTP 请求服务端跑的仍然是旧代码。这个“改了没反应”的问题在开发期非常常见后面我会讲到 devtools 怎么解决但你现在至少得明白开发阶段改代码之后的第一动作是重启不是刷新页面。2. 启动后控制台不打印端口号的完整排查三个根因被我试了个遍2.1 现象还原日志还在端口没了这个问题的搜索量一直很高。现象很典型在 IDEA 里启动 Spring Boot 项目控制台确实打印了一大堆日志最后也能看到类似Started DemoApplication in 2.34 seconds这样的内容但就是找不到Tomcat started on port(s): 8080 (http)这行关键日志。对新手来说看不到端口号就等于不知道项目跑在哪自然也不知道该访问哪个地址。我在帮别人排查时发现这个问题的根因其实就三种按概率从高到低排列如下。2.2 最常见根因项目创建时漏掉了 Spring Web这是我发现的最容易踩的一个坑尤其是在 IDEA 里手动创建 Maven 项目、而不是用 Spring Initializr 初始化时很多人会忘记引入 Web 相关依赖。回想一下没有spring-boot-starter-web项目加载的 ApplicationContext 就是一个普通的非 Web 容器Spring Boot 根本不会启动内嵌 Tomcat当然也就没有端口。但让你产生困惑的是项目确实“启动成功”了因为控制台会正常打印Started DemoApplication in ... seconds。所以排查第一步永远是看pom.xml里有没有spring-boot-starter-webdependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency如果用的是 Spring Initializr 创建项目那就是在 Dependencies 面板里勾选Spring Web那个选项。缺了依赖就补上刷新 Maven 之后重启端口号百分百会出来。2.3 第二层问题启动类的位置和扫描边界还有一种情况很容易让人混淆端口日志打出来了但访问http://localhost:8080/hello一直 404。很多人会把这个也归类成“端口不显示”或“项目没启动”其实不是。根本原因在SpringBootApplication注解的扫描机制。这个注解默认只会扫描它所在类的那一个包以及所有子包。比如你的启动类在com.example.day02那 Spring 只会去com.example.day02和它的子包下面找 Controller、Service、Mapper。如果你的HelloController放在了com.example.controller这种跟启动类不在同一棵包路径下的地方扫描就不会覆盖到它接口自然 404。排查思路很清楚确认启动类包路径和你的业务类包路径是包含关系。最省心也最推荐的做法是所有业务代码都放在启动类所在包的下方比如启动类在com.example.day02那你新建 Controller 时包名就写成com.example.day02.controller新写的代码都能被扫到。2.4 第三道坎端口占用如果spring-boot-starter-web有包路径也正常但还是看不到端口那就要往系统层面查了。最常见的是 8080 端口被其他程序占用了。Spring Boot 默认端口是 8080如果这个端口被别的进程占着启动时会直接报错错误信息通常长这样Web server failed to start. Port 8080 was already in use.处理方式有两种。第一种是找到占用进程并干掉Windows 下在终端执行netstat -ano | findstr 8080会列出一个占用 8080 端口的进程 PID然后用taskkill /PID 进程号 /F把它强制结束。macOS 或 Linux 下用lsof -i :8080找到 PID再kill -9 进程号。第二种方式是干脆改端口。在application.yml里加一行配置server: port: 8081改成任意你喜欢的端口。这里多提一句如果配置的是server.port: 0Spring Boot 会随机挑选一个可用的端口启动这在多实例本地测试时会用到但 day02 阶段不推荐这么玩因为你会不知道上哪儿找端口。2.5 排查动作和修复总结把问题现象、可能原因、验证方法放一起对看排查起来会清晰很多。下面是我整理的对照表问题现象最可能原因修复方式验证方法启动了但没有 Tomcat 端口日志缺少 spring-boot-starter-webpom.xml 补 Web 依赖后刷新并重启重新启动后看端口日志端口日志正常但访问 404启动类包路径与 Controller 包路径不匹配把 Controller 移到启动类所在包及其子包重新访问对应地址启动时直接报端口被占用8080 被其他进程占用杀进程或改用其他端口访问新端口或重启应用控制台日志非常少没看到关键内容日志级别或 IDAE 输出缓冲区问题调整 logging.level.root或清理 IDEA 缓存重启后观察完整日志结合这个表“端口不显示”这个问题基本就能一次定位。说句实在话我平时帮人排查这类问题十次里面有八次都是第 2.2 节那种原因——Tomcat 根本没被启动。所以以后看到没有端口日志第一反应该是“Spring 没把 Web 容器拉起来”而不是急着去改配置。3. 配置别写死在代码里拆出 application.yml 与多环境 profile3.1 新手最快的翻车方式配置写死在类里有些新手图省事数据库地址、用户名、密码、Redis 地址、第三方接口密钥全直接用字符串写在 Service 类里。开发的时候没什么感觉等东西要部署到测试环境、生产环境就难了。你可能要全局搜索替换一不小心漏改一处整个环境就挂了。Spring Boot 从一开始就给了你一套很规整的配置管理体系核心就是application.yml文件。凡是会随环境变化的参数都应该放到配置文件里代码只负责读取不负责写死。3.2 YAML 语法速览和配置读取的两种姿势现在的新项目基本都用 YAML 而不是老的application.properties核心原因是结构更清晰。以我自己的习惯为例server: port: 8080 spring: application: name: day02-demo app: info: name: day02学习项目 version: v0.1用 YAML 有两件事得盯紧第一缩进绝不能用 Tab 键必须用空格第二冒号后面必须跟一个空格再写值。这两条错了启动时最经典的报错就是while scanning for the next token或者各种解析异常。读取这些配置常用的是两种姿势。简单取单个值用ValueValue(${app.info.name}) private String appName;如果是一组关联配置我强烈建议用ConfigurationProperties批量绑定到一个 Java 类上比到处写Value清爽很多Component ConfigurationProperties(prefix app.info) public class AppInfoProperties { private String name; private String version; // getter / setter 省略 }Spring Boot 拿到application.yml里app.info下面的键值后会自动按名称映射到这个类的属性上。用ConfigurationProperties的好处很明显配置多了以后结构不散而且比较容易做参数校验。3.3 多环境拆分dev、prod 一键切换的玩法一个项目从开发到上线至少要经历开发环境、测试环境、生产环境。这三套环境的数据库地址、日志级别、调试开关大概率都不一样。如果把所有环境的信息堆在一个application.yml里用注释区分那基本等于埋雷切环境时手动改来改去迟早出事。Spring Boot 原生支持多环境 profile做法很简单。首先application.yml只保留公共配置和当前激活的环境spring: profiles: active: dev application: name: day02-demo然后新建application-dev.ymlserver: port: 8080 logging: level: root: info com.example.day02: debug再新建一个application-prod.ymlserver: port: 80 logging: level: root: warn启动项目时Spring Boot 会先读公共配置再读application-dev.yml后者覆盖前者的同名字段。这样开发时默认走 dev端口 8080、日志详细部署到服务器时打包后加上启动参数--spring.profiles.activeprod端口变成 80、日志收敛代码一行不用动。激活方式也可以不依赖配置文件里的默认值。实际部署时更推荐这样写启动命令java -jar day02-demo-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod命令行参数在 Spring Boot 配置优先级里排得很靠前基本能覆盖掉application.yml里的默认设置。3.4 配置优先级与“外部配置覆盖内置配置”的实战意义刚接触 Spring Boot 的人往往意识不到配置优先级有多重要。这里先把它记住命令行参数最高其次是 Java 系统属性再其次是环境变量然后才是 jar 包内部的application.yml。这条规律在真实部署时非常有用。比如我部署项目到服务器需要临时调整某个配置完全不需要重新打 jar 包。我只需要在 jar 包所在目录外再放一份application.ymlSpring Boot 会自动读取外部文件并且覆盖 jar 内部同名配置。利用这一点运维可在不重新构建代码的情况下把端口、数据库地址、日志级别全部改掉。说到这里顺带提一个老坑有人用 IDEA 编辑application.yml时里面写中文注释或中文字符串部署到 Linux 服务器后变成乱码。原因基本是编码问题。建议在 IDEA 的 File Encoding 设置里把项目默认编码和 properties 文件编码都统一改成 UTF-8。这个事虽然小但真的遇到过不止一次。4. 打通数据库Spring Boot 集成 MyBatis-Plus以及 XML 和 Mapper 同目录的配置纠葛4.1 为什么选 MyBatis-Plus而不是教科书里的原生 MyBatis单表 CRUD 是后端项目的日常。原生 MyBatis 写起来有多啰嗦用过的都知道每个表都得写 Mapper 接口、写 XML、写对应的 insert/select/update/delete全是机械劳动。MyBatis-Plus 的价值在于它把单表 CRUD 的通用方法全都内置了你的 Mapper 接口只要继承一个BaseMapper常用的增删改查方法就齐了。更关键的是 MyBatis-Plus 对新手很友好它没把 MyBatis 本身复杂化只是在外面包了一层增强。很多新项目直接选它老项目从 MyBatis 迁移的成本也比较低。4.2 从依赖到第一个查询五步接上 MySQL以 MySQL 为例假设你已经建好了数据库day02里面有张t_user表。下面五步接完CRUD 就能跑起来。第一步在pom.xml里引入 MyBatis-Plus 和 MySQL 驱动依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.7/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency这里注意版本坑如果你用的是 Spring Boot 3MyBatis-Plus 必须用 3.5.3 以上的版本因为 Spring Boot 3 换了jakarta命名空间低版本会跑不起来。如果你还在 Spring Boot 2 上用 3.5.x 之前的老版本可能更稳但新项目我建议一步到位用 Spring Boot 3。第二步在application.yml里配置数据源spring: datasource: url: jdbc:mysql://localhost:3306/day02?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver第三步创建实体类。一个约定实体类名对应数据库表名字段对应表字段。如果表名和类名不一致用TableName显式指定TableName(t_user) public class User { TableId(type IdType.AUTO) private Long id; private String name; private String email; // getter / setter 省略 }第四步写 Mapper 接口。这是最简单的部分接口本身不需要任何方法public interface UserMapper extends BaseMapperUser { }第五步在启动类上加上MapperScan告诉 MyBatis-Plus 去哪里扫描 MapperSpringBootApplication MapperScan(com.example.day02.mapper) public class Day02Application { public static void main(String[] args) { SpringApplication.run(Day02Application.class, args); } }到这里你就可以在任意 Service 或 Controller 里注入UserMapper了RestController public class UserController { private final UserMapper userMapper; public UserController(UserMapper userMapper) { this.userMapper userMapper; } GetMapping(/api/users) public ListUser list() { return userMapper.selectList(null); } }selectList(null)就是查询全部用户列表。你没写一行 SQL但方法已经能用了。这就是 MyBatis-Plus 天天强调的“单表 CRUD 不用写 SQL”。4.3 条件构造器让动态 SQL 先跑起来光会selectList(null)还不够真实需求里永远有条件。比如按姓名精确查、按关键字模糊查、按创建时间倒序排列这些如果都去手写 SQL其实也能做但在 MyBatis-Plus 里有一个更简洁的工具——条件构造器。LambdaQueryWrapperUser wrapper Wrappers.lambdaQuery(User.class) .eq(User::getName, day02) .orderByDesc(User::getId); ListUser users userMapper.selectList(wrapper);这个是精确等值查询加倒序。看一下.eq(User::getName, day02)这行User::getName是 Java 里的方法引用MyBatis-Plus 会拿它去推断实体类对应表里的字段名所以不会出现字符串写错列名的低级错误编译期就能发现问题。如果哪天要加条件才生效的查询比如前端传了关键字才模糊查没传就查全部可以这么写String keyword day; LambdaQueryWrapperUser wrapper Wrappers.lambdaQuery(User.class) .like(StringUtils.hasText(keyword), User::getName, keyword);第二项是 boolean 条件只有StringUtils.hasText(keyword)为 true 时后面的模糊条件才会拼进 SQL。这就是典型的动态 SQL 场景MyBatis-Plus 用条件构造器帮你把 if 判断简化到了参数级别。4.4 Mapper 和 XML 要不要放一起放一起怎么配置才不踩坑排到这篇的核心问题了。很多人习惯了把UserMapper.java和UserMapper.xml放在同一个包下面觉得打开目录就能两边对照非常直观。这个想法本身没问题但实际操作时有一个非常折磨人的坑Maven 打包时默认不会把src/main/java目录下的 XML 文件打进 jar 包。换句话说你在 IDEA 里跑项目一切正常一打包部署服务起来后调用任何带 XML SQL 的 Mapper 方法都会报Invalid bound statement (not found): com.example.day02.mapper.UserMapper.findByXxx这个错排查起来挺要命因为它不是编译错误也不是端口问题而是部署后运行时才暴露。解决方式分两种。第一种是常规方案也是我推荐大多数人用的XML 统一放在src/main/resources/mapper/目录下application.yml里配置mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml这样项目结构是 resources 下面所有mapper子目录里都能放 XML打包时不用额外配置。第二种就是硬要让 XML 和 Mapper 接口同在一个 Java 包目录下。可以实现但除了application.yml要改成mybatis-plus: mapper-locations: classpath*:com/example/day02/mapper/*.xml还需要在pom.xml的 build 节点里把src/main/java目录下的 XML 文件也当作资源打进包build resources resource directorysrc/main/java/directory includes include**/*.xml/include /includes /resource /resources /build两种方案我列个表帮你对比对比项方案AXML放resources/mapper方案BXML与Mapper同目录打包是否要额外配置不需要必须在 pom.xml 加 resources 声明目录直观性一般Java 包和 XML 分开直观接口旁边就是 XML出错概率低较高容易忘了打包配置我的建议项目开始阶段优先对这个结构有执念时再用我自己更推荐方案A。原因很实际day02 阶段你手动发布项目的机会很少大部分时间在 IDEA 里点运行两种方案根本看不出差别。等第一次部署上线踩到Invalid bound statement的时候大概率是深夜线上告警摩擦成本太高。方案A从一开始就规避掉了这类问题的空间。如果你坚持方案B一定要养成一个习惯每次打包前看target/classes目录下有没有对应的 XML 文件有才放心。4.5 分页与逻辑删除day02 阶段就够用的两个配置数据库查列表不可能永远查全部分页是必做功能。MyBatis-Plus 的分页需要一个拦截器配置放到配置类里Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }之后配合Page对象使用PageUser page new Page(1, 10); PageUser result userMapper.selectPage(page, null); ListUser records result.getRecords(); long total result.getTotal();Page的第一个参数是页码从 1 开始第二个是每页条数。返回的Page里除了记录列表还带着总数、总页数这些信息前端分页组件直接能用。再说逻辑删除。现在实际项目里基本不用物理删除而是在表里加一个deleted字段0 表示正常1 表示删除。MyBatis-Plus 对这件事有原生支持实体类字段加注解就行TableLogic private Integer deleted;配上全局配置后你调用deleteById时它实际执行的是UPDATE ... SET deleted 1而不是DELETE。查询时也会自动带上deleted 0条件。这个机制可以统一团队的删除逻辑避免有的人写DELETE FROM有的人写UPDATE。5. day02 之后的路热部署、虚拟线程和“别急着学”的中间件5.1 先给开发装上“自动刷新”spring-boot-devtools 到底要不要加前面说改代码要手动重启这在项目小的时候还能忍等接口多了每改一次花十几秒等重启人的耐心很快就消耗完了。spring-boot-devtools就是来解决这个体验问题的它会在你改完代码并重新编译后自动重启应用。引入方式dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-devtools/artifactId optionaltrue/optional /dependency加了依赖之后在 IDEA 里需要额外开一个设置Build, Execution, Deployment - Compiler - Build project automatically打个勾。之后每次改代码IDEA 会自动编译devtools 检测到类文件变化就会触发重启你不用手动点按钮。不过我不建议整天挂着 devtools 跑大项目。它本质上是把整个 Spring 容器重启一遍项目大了会拖慢节奏。它更适合你这几天练习阶段用真实企业项目里很多人宁可手动重启也不用它因为团队里每个人配置稍微不一样容易出一堆奇怪的联动问题。5.2 Java 21 Spring Boot 3.5虚拟线程可以先知道搜索热词里提到 Java 21 和 Spring Boot 3.5 启用虚拟线程说明这块确实是当下的热点。简单说两句背景传统 Java Web 应用处理请求一个请求基本占用一个系统线程线程数量受系统资源限制高并发时容易成为瓶颈。Java 21 引入了虚拟线程它把线程调度放到了 JVM 内部用很少的系统资源就能支撑非常多的并发任务。Spring Boot 3.2 之后对虚拟线程做了集成开启方式很简单spring: threads: virtual: enabled: true就这一行配置Spring MVC 处理请求时就会使用虚拟线程。如果你是刚准备开新项目我建议 JDK 直接上 17 甚至 21不用犹豫。后面等基础内容学扎实了再拿这行配置在并发场景下对比测试你就会真实感受到差距。5.3 打包、GraalVM、MinIO、工作流什么时候碰还有些热词像 Spring Boot 打包插件换成 GraalVM、集成 MinIO、接入 deer-flow 工作流、中间件选型 Redis这些全是好方向但不是现在的你该碰的东西。先说打包。day02 阶段你只需要掌握最朴素的一套mvn clean package打出可执行 jar然后java -jar xxx.jar跑起来。这是所有部署方式的地基mvn clean package java -jar target/day02-demo-0.0.1-SNAPSHOT.jarGraalVM 原生镜像那个方向说的是把 Spring Boot 应用编译成原生可执行文件启动速度能压到几十毫秒内存占用也小很多。但代价是构建阶段极其挑剔依赖反射、动态代理的框架经常需要额外配置文件新手在这个阶段碰它大概率一天都跑不通。将来项目稳定了、需要极致的启动性能时再来折腾不迟。MinIO 是私有化部署的对象存储服务跟阿里云 OSS 类似适合自己搭一套存图片、存文件。它必然会在你做到“文件上传”相关功能的时候出现到时候再研究完全来得及。deer-flow 这类轻量工作流引擎是做审批流、工单系统、任务流转时的选择属于业务复杂度到了一定程度才有意义的组件。day02 连业务规则都没开始建模现在看只能是雾里看花。至于中间件选型Redis、MQ 这些你现在只需要知道名字。中间件的正确学习姿势是“遇到问题再学”比如发现数据库压力大了再去学缓存发现接口响应慢、需要异步削峰再去了解消息队列。提前囤一堆中间件知识而没有一个真实使用场景过两周就会忘得干干净净。day02 的核心还是那句话把手里的项目变成你自己的“玩具”。看到浏览器返回 JSON看到数据库多出一条记录这个正反馈一旦建立后面学什么都不慌。等你把今天这套“接口—配置—数据库”的闭环跑熟了再回头看这些问题自然就知道下一步该干什么了。
返回列表