SpringBoot+MySQL+Redis 能源管理系统全栈实战:从踩坑到部署完整指南
一、前言:本学期《Web 应用项目开发》课程,我从零完成了赛克能源管理系统的全栈开发、接口联调与云服务器部署。项目采用 SpringBoot 2.7 + MyBatis-Plus + MySQL 8.0 + Redis 5.0 技术栈,实现了用户管理、登录认证、JWT 令牌校验、能耗统计等核心功能。开发过程中,我踩遍了 Web 开发的经典坑:接口 400/404/500 错误、数据库字段映射失败、参数绑定异常、跨域问题、云服务器部署失败等。本文将完整记录我的开发流程、踩坑经历、解决方案与学习心得,不仅是课程作业的总结,也希望能为其他 Java Web 初学者提供一份可落地的实战指南。
二、项目整体架构与技术栈
2.1 技术选型
本项目采用企业级主流的三层架构 + MVC 设计模式,前后端分离开发,技术栈如下:
表格
| 层级 | 技术 / 工具 | 版本 | 作用 |
|---|---|---|---|
| 前端 | 原生 HTML + CSS + JavaScript | - | 页面渲染与用户交互 |
| 后端 | SpringBoot | 2.7.6 | 快速搭建 Web 应用 |
| ORM 框架 | MyBatis-Plus | 3.5.5 | 简化数据库操作 |
| 数据库 | MySQL | 8.0.31 | 持久化存储业务数据 |
| 缓存 | Redis | 5.0.14 | 存储 JWT 令牌,实现会话管理 |
| 部署 | 阿里云轻量应用服务器 + 宝塔面板 | - | 线上部署与运维 |
| 开发工具 | IDEA 2023 + Navicat 16 + Postman | - | 代码编写、数据库管理、接口测试 |
2.2 项目目录结构
plaintext
ems/
├── src/main/java/com/lzpu/
│ ├── base/ # 基础类(R返回结果、User实体类)
│ ├── config/ # 配置类(跨域配置、MyBatis配置)
│ ├── controller/ # 控制层(接收请求,返回响应)
│ ├── service/ # 业务层(处理业务逻辑)
│ │ └── impl/ # 业务实现类
│ ├── mapper/ # 数据访问层(操作数据库)
│ └── utils/ # 工具类(MD5加密、JWT工具)
├── src/main/resources/
│ ├── mapper/ # MyBatis XML映射文件
│ ├── static/ # 静态资源(前端测试页面)
│ └── application.yml # 项目配置文件
└── pom.xml # Maven依赖配置

三、核心功能开发与经典踩坑解决
3.1 用户模块 CRUD 开发
用户模块是项目的基础,包含新增、删除、修改、查询单个用户、查询所有用户五大接口。这部分开发我踩了 3 个最典型的坑,也是初学者最容易遇到的问题。
坑 1:参数绑定异常(400 错误)
问题描述:一开始我在addUser方法上加上了@RequestBody注解,前端用普通表单提交时,直接抛出Required request body is missing 400 错误。
java
运行
// 错误写法
@PostMapping("/add")
public R addUser(@RequestBody User user) { ... }
错误分析:@RequestBody只能接收 JSON 格式的请求体,而普通表单提交的Content-Type是application/x-www-form-urlencoded,Spring 无法解析。
正确写法:表单提交直接用实体类接收,Spring 会自动根据表单名与实体属性完成绑定
java
运行
// 正确写法
@PostMapping("/add")
public R addUser(User user) { ... }
坑 2:数据库字段非空错误(500 错误)
问题描述:新增用户时,后端日志报错Field 'nick_name' doesn't have a default value,前端返回 500 错误。
plaintext
### Error updating database. Cause: java.sql.SQLException: Field 'nick_name' doesn't have a default value
### SQL: INSERT INTO user (user_name, password) VALUES (?, ?)
错误分析:数据库user表中nick_name字段设置为NOT NULL且无默认值,但我在新增时只传递了用户名和密码,导致数据库插入失败。
解决方案:
- 后端对必填字段做非空校验
- 对未传的必填字段设置合理的默认值
java
运行
@PostMapping("/add")
public R addUser(
@RequestParam("username") String username,
@RequestParam("password") String password
) {
// 1. 非空校验
if (username == null || username.isEmpty()) {
return R.fail("用户名不能为空");
}
if (password == null || password.isEmpty()) {
return R.fail("密码不能为空");
}
// 2. 手动封装对象,设置默认值
User user = new User();
user.setUsername(username);
user.setPassword(Md5Util.md5(password));
user.setNickName("默认昵称"); // 给nick_name设置默认值
int result = userService.addUser(user);
if (result > 0) {
return R.ok("新增成功!用户名:" + username);
}
return R.fail("新增失败");
}

坑 3:数据库字段与实体属性映射失败
问题描述:查询用户时,username字段始终为null,但数据库里明明有数据。
错误分析:数据库字段是user_name(下划线命名),而实体类属性是username(全小写),虽然 MyBatis 开启了下划线转驼峰,但全小写的username无法与user_name匹配。
解决方案:使用@TableField注解显式映射数据库字段
java
运行
@Data
@TableName(value="user")
public class User {
@TableId(value="user_id", type= IdType.AUTO)
private Integer userId;
@TableField(value="user_name") // 显式映射数据库字段
private String username;
@TableField(value="nick_name")
private String nickName;
// 其他字段...
}
3.2 登录认证模块(MD5 + JWT + Redis)
登录认证是本项目的核心,也是企业级 Web 应用的标配。我采用了MD5 密码加密 + JWT 令牌 + Redis 缓存的组合方案,三者分工明确:
- MD5:对密码进行不可逆加密,数据库只存密文,防止密码泄露
- JWT:生成包含用户信息的身份令牌,作为登录凭证
- Redis:缓存 JWT 令牌并设置过期时间,实现登录状态管理
登录流程
- 前端提交用户名和密码
- 后端根据用户名查询数据库
- 将前端传入的密码进行 MD5 加密,与数据库中的密文对比
- 密码正确则生成 JWT 令牌
- 将令牌存入 Redis,设置 1 小时过期
- 返回令牌给前端
核心代码实现
java
运行
@PostMapping("/login")
public R<String> login(@RequestBody LoginInfo loginInfo) {
// 1. 根据用户名查询用户
User loginUser = userService.getUserByUsername(loginInfo.getUsername());
// 2. 校验用户名
if (loginUser == null) {
return R.fail("用户名不存在");
}
// 3. 校验密码(MD5加密对比)
if (!Md5Util.md5(loginInfo.getPassword()).equals(loginUser.getPassword())) {
return R.fail("密码错误");
}
// 4. 生成JWT令牌
Map<String, Object> claims = new HashMap<>();
claims.put("userId", loginUser.getUserId());
claims.put("username", loginUser.getUsername());
String token = JWTUtil.genToken(claims);
// 5. 将令牌存入Redis,过期时间1小时
stringRedisTemplate.opsForValue().set(
loginInfo.getUsername(),
token,
1,
TimeUnit.HOURS
);
// 6. 返回令牌给前端
return R.ok(token);
}
3.3 接口路径 404 问题
问题描述:前端请求接口时,频繁出现 404 错误,但后端 Controller 明明已经写好了对应的接口。
错误分析:我在application.yml中配置了server.servlet.context-path: /home,给所有接口加了统一前缀/home,但前端请求时没有加上这个前缀。
yaml
server:
port: 9763
servlet:
context-path: /home # 统一接口前缀
解决方案:前端所有请求地址都加上/home前缀
javascript
运行
// 错误写法
fetch(`/user/add?username=测试用户&password=123456`, {method: 'POST'})
// 正确写法
fetch(`/home/user/add?username=测试用户&password=123456`, {method: 'POST'})

四、云服务器部署实战(宝塔面板)
本地功能调试完成后,我将项目部署到了阿里云轻量应用服务器,使用宝塔面板进行管理,部署步骤如下:
4.1 环境准备
- 阿里云服务器开放端口:80(网页)、9763(后端)、3306(MySQL)、6379(Redis)、8888(宝塔面板)
- 宝塔面板安装软件:Nginx 1.20、MySQL 8.0、Redis 6.2、OpenJDK 11
4.2 数据库部署
- 宝塔面板创建数据库
ems,设置用户名和密码 - 使用 Navicat 连接云数据库,执行建表语句,导入基础数据
4.3 后端部署
- 修改后端
application.yml配置,将数据库和 Redis 地址改为云服务器地址 - Maven 打包:
mvn clean package -DskipTests - 上传 Jar 包到服务器
/www/wwwroot/backend目录 - 后台启动后端服务:
bash
运行
cd /www/wwwroot/backend
pkill -f java # 停止旧进程
nohup java -jar ems-0.0.1-SNAPSHOT.jar > server.log 2>&1 &
- 查看运行日志:
tail -f server.log

4.4 前端部署
- 前端打包:
npm run build(如果是 Vue 项目) - 宝塔面板创建站点,端口 8081
- 上传前端打包文件到站点根目录
- 配置 Nginx 反向代理,解决跨域问题

五、学习心得与总结
经过一个学期的学习和实践,我不仅掌握了 SpringBoot 全栈开发技术,更重要的是培养了独立解决问题的能力和工程化思维。以下是我的几点深刻感悟:
5.1 规范大于技巧
编码规范是避免 80% 低级错误的关键。在开发中,我深刻体会到:
- 数据库字段使用下划线命名,Java 实体类使用驼峰命名
- 前后端参数名保持一致
- 接口路径遵循 RESTful 规范
- 代码分层清晰,Controller 只接收请求,Service 处理业务,Mapper 只操作数据库
5.2 报错优先看日志
遇到问题不要盲目改代码,后端日志是最好的老师。无论是 400、404 还是 500 错误,日志都会明确告诉你错误原因和位置。学会看日志、分析日志,是程序员的必备技能。
5.3 理论结合实操
课堂上学到的 UML 建模、HTTP 协议、TCP/IP、三层架构等理论知识,在实战中都有了具体的体现。只有将理论与实操结合起来,才能真正理解技术的本质,做到举一反三。
5.4 排错能力是核心竞争力
开发过程就是不断踩坑、不断排错的过程。每解决一个问题,都是对知识点的一次巩固。慢慢的,我形成了一套自己的排错思路:
- 复现问题,记录错误现象
- 查看后端日志,定位错误位置
- 分析错误原因,查阅资料
- 尝试解决方案,验证效果
- 总结经验,避免再次踩坑
六、后续计划
本次课程项目虽然完成了核心功能,但还有很多可以优化的地方:
- 增加统一异常处理,优化错误提示
- 实现接口参数校验,使用 Validation 框架
- 完善权限管理,实现不同角色的权限控制
- 优化前端页面,使用 Vue + Element UI 重构
- 学习 Docker 容器化部署,简化运维流程
七、结语
《Web 应用项目开发》这门课程让我从一个只会写 Hello World 的小白,成长为能够独立完成一个小型 Web 项目全流程开发的开发者。感谢老师的悉心指导,也感谢自己在无数个熬夜调试的夜晚没有放弃。
编程之路没有捷径,唯有不断学习、不断实践、不断踩坑,才能不断进步。希望这篇文章能对正在学习 Java Web 的同学有所帮助,也欢迎大家在评论区交流讨论。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)