一、项目概述
本次项目使用Spring Boot + MyBatis + SQL Server构建了一个完整的用户管理 RESTful API,实现了用户的增删改查(CRUD)功能。
项目结构:
pet_web_crud/ ├── src/main/java/com/example/ │ ├── PetWebCrudApplication.java // 启动类 │ ├── controller/ │ │ └── UserController.java // 控制器层(RESTful接口) │ ├── service/ │ │ ├── UserService.java // 业务接口 │ │ └── impl/ │ │ └── UserServiceImpl.java // 业务实现 │ ├── mapper/ │ │ └── UserMapper.java // 数据访问层 │ └── pojo/ │ ├── User.java // 实体类 │ └── Result.java // 统一返回结果 ├── src/main/resources/ │ └── application.properties // 配置文件 └── pom.xml // 依赖管理二、RESTful 风格的理解与实践
什么是 RESTful?
RESTful 是一种 API 设计规范,核心思想是:
用 URL 表示资源(名词,复数形式)
用 HTTP 方法表示操作(GET/POST/PUT/DELETE)
用状态码表示结果(200/404/500)
我的 RESTful 接口设计
| 功能 | HTTP方法 | URL | 说明 |
|---|---|---|---|
| 查询所有用户 | GET | /api/users | 获取用户列表 |
| 搜索用户 | GET | /api/users?keyword=xxx | 按关键词搜索 |
| 查询单个用户 | GET | /api/users/{id} | 获取指定ID用户 |
| 新增用户 | POST | /api/users | 添加新用户 |
| 修改用户 | PUT | /api/users | 更新用户信息 |
| 删除用户 | DELETE | /api/users/{id} | 删除指定用户 |
传统风格 vs RESTful 风格对比
| 操作 | 传统风格 | RESTful风格(我的实现) |
|---|---|---|
| 查询列表 | GET/user/list | GET/api/users |
| 查询单个 | GET/user/find?id=1 | GET/api/users/1 |
| 新增 | POST/user/add | POST/api/users |
| 修改 | POST/user/update | PUT/api/users |
| 删除 | GET/user/delete?id=1 | DELETE/api/users/1 |
RESTful 核心注解使用
java
@RestController // 标识为REST控制器,返回JSON @RequestMapping("/api/users") // 统一资源路径 @GetMapping // 处理GET请求 @PostMapping // 处理POST请求 @PutMapping // 处理PUT请求 @DeleteMapping // 处理DELETE请求 @PathVariable // 获取URL路径参数 @RequestBody // 获取请求体JSON数据 @RequestParam // 获取URL查询参数三、开发过程中的问题与解决
环境与配置问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 项目无法启动 | pom.xml缺少<packaging>jar</packaging> | 添加 packaging 标签 |
| 端口 8080 被占用 | 其他进程占用 | 修改端口或关闭占用进程 |
数据库连接问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 驱动类找不到 | 使用了 MySQL 驱动但用 SQL Server | 改用com.microsoft.sqlserver.jdbc.SQLServerDriver |
| SQL Server 语法错误 | MySQL 和 SQL Server 语法不同 | 用IDENTITY(1,1)替代AUTO_INCREMENT |
| 表名报错 | SQL Server 关键字冲突 | 用方括号[users]包裹表名 |
后端代码问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
Field peotMapper required a bean | MyBatis 没扫描到 Mapper | 添加@MapperScan("com.example.mapper") |
找不到符号 Result.error | Result.java缺少error方法 | 添加public static Result error(String msg) |
找不到符号 setPassword | User.java缺少setPassword方法 | 添加 Getter/Setter |
| 类名与文件名不匹配 | 改了类名没改文件名 | 保持类名和文件名一致 |
前端页面问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 页面 404 | HTML 不在static目录 | 移到src/main/resources/static/ |
| 数据不显示 | 请求地址错误 | 检查/api/users是否正确 |
| CORS 跨域错误 | 前后端端口不同 | 后端添加@CrossOrigin(origins = "*") |
项目名问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
Maven 显示peot_web_crud | pom.xml中 artifactId 是peot | 改成<artifactId>pet_web_crud</artifactId> |
SQL Server 保留关键字冲突
现象:
SQL: SELECT * FROM user ORDER BY id DESC Cause: 关键字 'user' 附近有语法错误
原因:user是 SQL Server 的保留关键字,不能直接作为表名
解决:两种方案
表名加方括号:
SELECT * FROM [user]表名改为复数:
user→users
我选择了方案2,并统一使用[users]避免冲突
收获:学习了 SQL Server 保留关键字问题,以及[]的转义用法
前后端接口路径不匹配
现象:访问/userfindAll返回404,实际接口是/api/users
原因:前端请求路径和后端Controller路径不一致
解决:
统一使用
/api/users作为基础路径利用
@RequestMapping("/api/users")统一管理
收获:理解了前后端分离中接口路径统一的重要性
静态页面无数据
现象:页面显示了,但表格是空的
原因:静态HTML不会自动获取数据,需要前后端联动
解决:
后端提供 RESTful API 接口
前端通过 fetch/AJAX 调用接口获取数据
收获:理解了前后端分离的工作模式
三、CRUD 功能完整流程
查询(Readext)
用户打开页面 → Vue mounted() → axios.get('/api/users') → UserController.list() → UserService.search() → UserMapper.findAll() → SQL查询 → 返回JSON → 表格渲染新增(Create)
点击"新增用户" → 跳转 user_insert.html → 填写表单 → 点击"确认新增" → axios.post('/api/users', data) → UserController.add() → UserService.insert() → UserMapper.insert() → SQL插入 → 跳转回列表页修改(Update)
点击"修改" → 跳转 user_edit.html?id=1 → 加载数据回显 → 修改表单 → 点击"保存修改" → axios.put('/api/users', data) → UserController.update() → UserService.update() → UserMapper.update() → SQL更新 → 跳转回列表页删除(Delete)
点击"删除" → 跳转 user_delete.html?id=1 → 显示确认信息 → 点击"确认删除" → axios.delete('/api/users/1') → UserController.delete() → UserService.delete() → UserMapper.delete() → SQL删除 → 跳转回列表页四、核心知识点
1. Spring Boot 注解
| 注解 | 作用 |
|---|---|
@SpringBootApplication | 启动类 |
@RestController | REST 控制器 |
@RequestMapping/@GetMapping | 路由映射 |
@Autowired | 依赖注入 |
@Service | 服务层组件 |
@Mapper | MyBatis Mapper |
2. MyBatis 注解
| 注解 | 作用 |
|---|---|
@Select | 查询 |
@Insert | 插入 |
@Update | 更新 |
@Delete | 删除 |
3. Vue.js 核心
| 概念 | 作用 |
|---|---|
el: "#app" | 绑定容器 |
data() | 数据 |
v-for | 循环渲染 |
v-model | 双向绑定 |
mounted() | 页面加载后执行 |
methods | 方法 |
4. HTTP 请求方法对应 CRUD
| 操作 | HTTP 方法 | 说明 |
|---|---|---|
| 查询 | GET | 获取数据 |
| 新增 | POST | 创建数据 |
| 修改 | PUT | 更新数据 |
| 删除 | DELETE | 删除数据 |
五、各层之间的链接关系
完整请求流程
浏览器/Apifox ↓ (GET /api/users) [Controller层] UserController ↓ 调用 userService.search(keyword) [Service层] UserServiceImpl ↓ 调用 userMapper.search(keyword) [Mapper层] UserMapper ↓ 执行 SQL: SELECT * FROM [users] WHERE ... [数据库] SQL Server ↓ 返回数据 List<User> [Mapper层] 将数据封装为 User 对象 ↓ 返回数据 [Service层] 处理业务逻辑 ↓ 返回 List<User> [Controller层] 封装为 Result.success(users) ↓ 返回 JSON 浏览器/Apifox (收到JSON数据)
各层职责说明
| 层级 | 职责 | 注解 |
|---|---|---|
| Controller | 接收请求,返回响应 | @RestController,@RequestMapping |
| Service | 处理业务逻辑 | @Service |
| Mapper | 操作数据库 | @Mapper |
| POJO | 数据实体 | 无特殊注解 |
数据流转过程
java
// 1. 前端发送请求 GET /api/users // 2. Controller 接收请求 @GetMapping public Result list(@RequestParam(required = false) String keyword) { // 3. 调用 Service List<User> users = userService.search(keyword); // 6. 返回统一格式响应 return Result.success(users); } // 4. Service 处理业务逻辑 @Override public List<User> search(String keyword) { // 5. 调用 Mapper 操作数据库 return userMapper.search(keyword); } // 7. Mapper 执行 SQL @Select("SELECT * FROM [users] WHERE username LIKE CONCAT('%', #{keyword}, '%')") List<User> search(String keyword);统一响应格式
json
{ "code": 200, "message": "success", "data": [ { "id": 1, "username": "admin", "name": "管理员", "role": "管理员", "phone": "13800000000", "email": "admin@example.com", "address": "北京市" } ] }各环节的详细链接
前端 → 后端(HTTP 请求)
javascript
// 前端 (user_list.html) axios.get('/api/users') .then(res => { this.userList = res.data.data; }); java // 后端 (UserController.java) @GetMapping("/api/users") public Result list() { return Result.success(userService.findAll()); }Controller → Service(依赖注入)
java
// UserController.java @Autowired // ← Spring 自动注入 private UserService userService; // UserServiceImpl.java @Service // ← 标记为 Service 组件 public class UserServiceImpl implements UserService { ... }Service → Mapper(数据访问)
java
// UserServiceImpl.java @Autowired // ← 自动注入 Mapper private UserMapper userMapper; // UserMapper.java @Mapper // ← 标记为 MyBatis Mapper public interface UserMapper { ... }Mapper → 数据库(SQL 执行)
java
@Select("SELECT * FROM [users] ORDER BY id DESC") List<User> findAll();六、关键技术栈总结
| 技术 | 作用 | 关键点 |
|---|---|---|
| Spring Boot | 框架基础 | 自动配置,简化开发 |
| MyBatis | ORM框架 | 通过注解操作数据库 |
| SQL Server | 数据库 | 注意保留关键字用[] |
| Maven | 项目构建 | 依赖管理,打包部署 |
| RESTful | 接口规范 | URL+HTTP方法=操作 |
七、学习收获
1. RESTful 设计规范
URL 使用名词复数表示资源
HTTP 方法表示操作(GET/POST/PUT/DELETE)
路径参数传递资源ID
2. Spring Boot 分层架构
Controller → Service → Mapper 三层结构
每层职责清晰,便于维护和测试
3. 前后端分离模式
后端只提供 JSON 数据接口
前端通过 AJAX/fetch 获取数据并渲染
4. 异常处理与调试
学会通过错误日志定位问题
理解 SQL 语法错误、路径错误、表名错误等常见问题
八、测试验证
所有接口通过 Apifox 测试验证通过:
| 接口 | 测试结果 |
|---|---|
GET/api/users | 返回所有用户列表 |
GET/api/users?keyword=admin | 返回搜索匹配的用户 |
GET/api/users/1 | 返回指定ID用户 |
POST/api/users | 成功新增用户 |
PUT/api/users | 成功修改用户 |
DELETE/api/users/1 | 成功删除用户 |