本文记录 L06 作业的完整开发过程:用 SpringBoot + MyBatis-Plus 读取 MySQL 数据,实现用户管理的 5 个 REST 接口,并用 Postman 完成全部测试。
一、需求与整体思路
前端浏览器发起 HTTP 请求,后端 SpringBoot 程序响应;而数据需要永久保存(程序 crash 后仍在)、支持大量数据存储与高效访问,因此引入 MySQL。MyBatis 负责把 SQL 查询结果转成 Java 对象,供 SpringBoot 程序使用。
整体采用业界约定的四层结构,访问顺序自上而下:Controller(暴露 REST 接口)→ Service(业务逻辑)→ Mapper(数据访问接口)→ Mapper XML(转化为 SQL)→ MySQL 数据库;domain 包下的 User 类作为表数据对应的 Java 实体。
二、MySQL 数据库准备(Navicat)
在 Navicat 中创建数据库 mybatis,新建 user 表,包含 5 个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | int | 主键、自增、无符号、非空 |
| name | varchar(50) | 用户名 |
| birthday | date | 生日 |
| sex | varchar(1) | 性别 |
| create_time | datetime | 创建时间 |
表设计界面如下,注意 id 要设为主键并勾选"自动递增"和"无符号":

再插入 10 条模拟数据,作为接口测试的数据基础:

三、项目配置:pom 依赖与 application.yml
项目基于 Spring Boot 4.1.1 + Java 21(Maven)。pom.xml 中加入 MyBatis-Plus 的 Boot4 专用 starter(Boot 4.x 必须用 spring-boot4 版本,不能与 boot3 版混用)和 MySQL 驱动:
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
<version>3.5.16</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
在 src/main/resources 目录下创建 application.yml,配置项目端口、数据源以及 MyBatis-Plus 参数(log-impl 让 SQL 打印到控制台方便调试;id-type: auto 对应数据库自增;逻辑删除配置预留):
yaml
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: ******
application.yml 配置界面:

四、代码实现(controller / service / mapper / domain 四层)
按业界约定的目录结构新建四个包,并在 resources 下新建 mapper 资源目录(存放 XML 映射文件):

1. domain 实体类 User:@TableName 对应表名(user 是 MySQL 保留字,要加反引号),@TableId(type = IdType.AUTO) 对应自增主键,lombok @Data 自动生成 getter/setter:
java
@Data
@TableName("`user`")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private LocalDate birthday;
private String sex;
private LocalDateTime createTime;
}
2. mapper 层:UserMapper 接口继承 BaseMapper<User> 即可获得单表 CRUD 能力;启动类上加 @MapperScan("com.example.l06_cp.mapper") 批量扫描(注意 import 的是 org.mybatis.spring.annotation.MapperScan,不是 ibatis 包)。
3. service 层:UserService 接口继承 IService<User>;实现类 UserServiceImpl 继承 ServiceImpl<UserMapper, User> 并实现 UserService,加 @Service 注解交给 Spring 管理。
4. controller 层:统一返回 Result<T> 通用结构(code/message/data),通过 @Autowired 注入 UserService,向外提供 5 个 REST 接口。Controller 整体代码:

五、Postman 接口测试
启动项目后,用 Postman 对 5 个接口逐一测试,全部通过(统一返回 code=200):
1. 根据用户 id 获取用户信息 GET /api/user/{id},成功查到 id=1 的数据:

2. 获取所有用户信息 GET /api/user/list,返回 10 条模拟数据:

3. 添加用户信息 POST /api/user,Body 传 JSON,新增"陈鹏"成功(id 自动递增分配):

4. 更新某个用户信息 PUT /api/user,根据 id 把用户名改为"张三":

5. 删除某个用户信息 DELETE /api/user/{id},删除测试数据成功:

六、自定义 SQL:@Select 注解 与 mapper.xml 两种方式

方式一:@Select 注解直写 SQL(适合简单语句)。在 UserMapper 中添加 getAllUserSortByNameDesc 方法:
java
@Select("SELECT * FROM `user` ORDER BY name DESC")
List<User> getAllUserSortByNameDesc();
Service 层通过 baseMapper 调用,Controller 新增接口暴露。
Postman 测试------按名字倒序返回:

方式二:与 mapper.xml 配合(适合复杂 SQL)。在 resources/mapper 下新建 UserMapper.xml:namespace 对应 Mapper 接口全限定名,<select id> 对应接口方法名:
xml
<mapper namespace="com.example.l06_cp.mapper.UserMapper">
<select id="selectUserList" resultType="com.example.l06_cp.domain.User">
SELECT * FROM `user`
</select>
</mapper>
Postman 测试 selectUserList 接口:

七、踩坑记录
- 启动报 Access denied for 'root'@'localhost':application.yml 里的密码是教程的假设值,改成自己 MySQL 的真实密码即可;
- @MapperScan 导错包:它不在 org.apache.ibatis.annotations 里(那里只有 @Mapper),正确的是 org.mybatis.spring.annotation.MapperScan,来自 mybatis-spring 整合包;
- POST/PUT 传时间字段报 400:Jackson 默认只认 ISO 格式,LocalDateTime 传参要写成 2026-01-05T09:15:00(日期时间是字母 T 分隔),或在实体字段上加 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss");
- 自增 id 出现空洞:删除过的记录编号不会被复用,AUTO_INCREMENT 只进不退,属正常现象,判断条数要用 COUNT(*) 而不是 max(id);
- 8080 端口被占用:上次启动的 Java 进程没退干净,用 netstat -ano | findstr :8080 找到 PID,taskkill /F /PID xxx 结束即可。
八、总结
本项目完整走通了 SpringBoot + MyBatis-Plus 读写 MySQL 的最小闭环:Navicat 建库建表 → application.yml 配置数据源 → domain / mapper / service / controller 四层代码 → Postman 全接口测试,并分别用 @Select 注解和 mapper.xml 两种方式实现自定义 SQL。最大的收获是理解了四层结构各自的职责边界,以及"有业务逻辑才在 Service 层建方法,纯透传直接调 IService 内置方法"的取舍。