简介:该资源是一套完整的JavaWeb学生宿舍管理系统毕业设计项目,包含可运行程序、毕业论文和MySQL数据库脚本,适用于计算机相关专业毕业设计、课程设计及JavaWeb入门学习者。系统基于JSP、SSM框架与MySQL实现,涵盖用户登录注册、学生信息、房间分配、来访登记、物品报修、日志管理等典型宿舍管理业务模块,并配有论文目录、系统功能图、数据库设计表及部署说明,便于从设计到实现全流程参考。资源包共1070个文件,约73.72MB,以Java源码、class文件、XML配置、Vue页面、SQL脚本等为主,附带大量SVG图标和JPG截图,可直接导入开发工具运行调试,也适合对照论文复习相关技术。已有23430人学习下载,从需求分析、数据库设计到系统测试的完整文档结构,能帮助读者快速理清开发思路并完成毕设答辩准备。
1. 为什么学生宿舍管理系统是这个学期最值得动手的 JavaWeb 题目
javaweb 学生宿舍管理系统这个题目,技术难度不算高,但交付物的颗粒度一点不少。我接过几次课程设计和毕业设计指导,发现选这个题目的人经常低估它的信息量:学生入住、退宿、调宿、报修、卫生检查共享同一张床位表,状态一旦对不上,页面列表和数据统计都会出问题。
这份题目的价值在于它的业务闭环足够完整:登录权限、宿舍分配、状态流转、报表展示都要靠 Servlet、JDBC 和 MySQL 配合完成,恰好是数据库课程设计和 javaweb 教学案例里最容易向评审老师展示的内容。提交时通常要求程序、论文、数据库脚本三样齐全,因此整个项目可以拆成设计、建库、编码、论文、验收五条线来推进。
我会按这么五段往下拆,每段都会给出可以直接抄走再改的代码和参数,新手能照着落地,已经做过几个 JavaWeb 项目的人也能从这里确认边界条件有没有遗漏。
2. 需求边界与 JavaWeb 技术选型:先决定项目是四张表还是十张表
管理类系统最怕一开始就把表拆得太多。一个标准的学生宿舍管理系统,角色可以收敛成三种:管理员、宿管员、学生。管理员负责楼栋和账号配置,宿管员负责日常业务办理,学生只提交申请或查看个人住宿信息。对应功能模块可以压到六块:基础信息、房间床位、学生住宿档案、报修、卫生检查、统计公告。这样分的好处是数据库表数量可控,论文里画用例图也容易叙述,不会被评委问“这部分需求由哪个角色触发”。
2.1 六个业务模块的数据库落点
在开始建工程前,我先会做一张模块与数据表映射表,这张表也是论文架构设计的雏形。通常参考:
| 业务模块 | 典型功能 | 数据表 |
|---|---|---|
| 基础配置 | 楼栋、楼层、宿舍管理员 | t_building |
| 宿舍管理 | 房间容量、床位状态 | t_room、t_bed |
| 学生住宿 | 入住、退宿、调宿、查寝名单 | t_student、t_checkin_record |
| 报修管理 | 报修登记、处理状态 | t_repair |
| 卫生检查 | 评分、按楼栋统计 | t_health_score |
| 系统公告 | 通知发布、登录日志 | t_notice、t_admin |
注意 t_bed 的 status 字段要单独维护,因为退宿之后床位必须从“占用”回到“空闲”,不是删除一条入住记录就能完事。入住、退宿、调宿都涉及状态流转:入住时先将床位状态置为占用,再写学生档案;退宿时先更新床位状态,再写退宿时间;调宿则要把旧床位释放和新床位占用放进同一个事务里。这个设计思想会在第三章的 SQL 和第四章的 Service 里落实到代码。
2.2 Servlet/JSP/JDBC 传统栈为什么比 SpringBoot 更适合这门课
这个题目的检索长尾基本集中在 javaweb、mysql、数据库课程设计,说明大部分场景是 JavaWeb 课程结课作业。我一般会建议选 Servlet + JSP + JDBC + MySQL 的传统组合,而不是一上来就 SpringBoot,原因有三:第一,课程评分点之一是 Servlet 生命周期、Filter、监听器这些概念,SpringBoot 把容器细节藏得太深,答辩容易说不上来;第二,手写 JDBC 可以让论文里的数据库连接部分有内容写,把连接池换掉和 Druid 参数讲清楚就有实际工作量;第三,传统 Maven war 包部署到 Tomcat 的过程更直观,适合做交付演示。
技术选型对照表如下,方便写论文时直接引用:
| 层次 | 选型 | 理由 |
|---|---|---|
| 控制层 | Servlet 3.1 | 自己控制 doGet/doPost,验证 HTTP 协议理解 |
| 视图层 | JSP + JSTL + Bootstrap 4 | 服务端渲染,减少前端工程复杂度 |
| 数据层 | JDBC + Druid 连接池 | 可手写 SQL,方便讲预编译防注入 |
| 数据库 | MySQL 5.7+ | 支持 utf8mb4,与 mysql-connector 版本匹配 |
| 运行容器 | Tomcat 8.5 或 9 | 与 javax.servlet-api 3.1/4.0 兼容 |
如果你学校一开始教的就是 MyBatis,把持久层从 JDBC 换成 MyBatis 也不算跑题,因为这个题目核心是学生宿舍管理系统,不是固定某一套框架。但课程名明确叫 JavaWeb 时,尽量保留 Servlet 和 JSP 的痕迹,否则答辩容易被误认为用了框架没学基础。
2.3 用 Maven 管理依赖并固定版本
如果用 IDEA 新建 JavaWeb 项目,我建议直接创建 Maven Archetype 里的maven-archetype-webapp,避免手工拷 jar 到WEB-INF/lib。依赖版本要固定,我一般会在 pom.xml 里写成这样:
<dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> </dependencies>这里scope=provided是为了让 Tomcat 提供自己的 Servlet 实现,避免 web 容器和依赖里的类重复。JSTL 1.2 只需要一个 jar,不需要单独引入 standard.jar。mysql-connector 5.1.49 对 MySQL 5.7 兼容性最稳,能少碰时区报错;如果使用 MySQL 8.0,就应该换成 8.x 版本,还需要在连接串里补serverTimezone=Asia/Shanghai。Druid 负责连接池监控,后面可以作为系统非功能需求写进论文。
2.4 JavaWeb 项目的分层包结构
项目骨架确定后,我通常会让src/main/java下面按 entity/dao/service/web 分四层,不用强行堆更多目录:
com.example.dorm ├── entity // 与数据表字段对应的类 ├── dao // JDBC 操作,只做增删改查 ├── service // 业务判断,入住退宿规则 └── web // Servlet,处理参数和转发entity 里每个属性对应一张表的一列,不要在这里出现集合和临时字段。dao 只接受简单类型或 entity,不做业务判断。service 只负责业务规则,比如房间满了不能入住、重复学号不能注册。web 层只做参数接收、类型转换、权限判断和页面跳转。这样的分包在代码量和答辩问询上都能留出余地,也方便后续把 DAO 换成 MyBatis Mapper 而几乎不碰 Service 代码。
3. 学生宿舍管理系统数据库设计:从 ER 关系、SQL 建表到并发锁
数据库设计是这个项目的得分大头,因为题目里没有复杂的算法,评审老师会把大量注意力放在表结构上。下面这套设计,核心是六张业务表外加一张住宿流水表,既能覆盖课程设计常见功能,也能在毕业论文里画出清晰的 E-R 图。
3.1 表关系如何定:不要把宿舍和床位合并
很多初学者会把宿舍设计成一张表,宿舍编号直接挂在学生表上,床位用逗号分隔的字段存。这种做法在第一次作业里能跑通,但学生退宿、换床位、查空床位时就会退化成字符串解析。常见做法是把宿舍房间和床位拆成两张表:t_room 记录房间号和容量,t_bed 记录每个床位属于哪个房间以及当前是否空闲。
| 关系 | 说明 | 外键字段 |
|---|---|---|
| 楼栋到房间 | 一栋楼包含多个房间 | t_room.building_id -> t_building.id |
| 房间到床位 | 一个房间对应多张床位 | t_bed.room_id -> t_room.id |
| 学生到床位 | 一名在住学生绑定一张空床位 | t_student.bed_id -> t_bed.id |
| 住宿流水 | 一条记录保留一个住宿周期 | t_checkin_record.bed_id -> t_bed.id |
其中 t_student 和 t_bed 是在校住宿才需要的一一对应关系。为了留下历史,我会再加一张 t_checkin_record,记录某位学生从入住到退宿的时间段以及过渡的床位。这张表在统计某栋楼历史入住量、导出进出宿舍流水时很有用,论文里也能多写一页。
3.2 一份可直接执行的 MySQL 建表脚本
下面这段 SQL 是典型的最小集合,按顺序执行就可以得到一个能挂到 JavaWeb 项目里的数据库骨架:
CREATE DATABASE IF NOT EXISTS db_dormitory DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE db_dormitory; CREATE TABLE t_building ( id INT PRIMARY KEY AUTO_INCREMENT, building_name VARCHAR(30) NOT NULL UNIQUE, floor_count TINYINT NOT NULL DEFAULT 6, manager_name VARCHAR(20) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_room ( id INT PRIMARY KEY AUTO_INCREMENT, building_id INT NOT NULL, room_no VARCHAR(10) NOT NULL, capacity TINYINT NOT NULL DEFAULT 4, UNIQUE KEY uk_building_room (building_id, room_no), FOREIGN KEY (building_id) REFERENCES t_building(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_bed ( id INT PRIMARY KEY AUTO_INCREMENT, room_id INT NOT NULL, bed_no VARCHAR(2) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT '0空闲 1占用 2锁定', UNIQUE KEY uk_room_bed (room_id, bed_no), FOREIGN KEY (room_id) REFERENCES t_room(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT '加盐哈希后的登录密码', real_name VARCHAR(30) NOT NULL, gender TINYINT NOT NULL DEFAULT 1 COMMENT '1男 2女', bed_id INT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT '1在住 0已退宿', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (bed_id) REFERENCES t_bed(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_checkin_record ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, bed_id INT NOT NULL, checkin_time DATETIME NOT NULL, check_out_time DATETIME NULL, FOREIGN KEY (student_id) REFERENCES t_student(id), FOREIGN KEY (bed_id) REFERENCES t_bed(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_repair ( id INT PRIMARY KEY AUTO_INCREMENT, room_id INT NOT NULL, content VARCHAR(255) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT '0待处理 1处理中 2已完成', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, finish_time DATETIME NULL, FOREIGN KEY (room_id) REFERENCES t_room(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有个细节必须说明:数据库名、表名、字段名使用小写加下划线,和运行平台有关。MySQL 在 Windows 下默认不区分表名大小写,Linux 下默认区分,写成小写能避免部署到不同环境时突然找不到表。字段里TINYINT用于状态值,比INT少占空间,也能防止状态位被填写超过三位数。status 字段保留 2 的锁定状态,是为后续保留床位、维修暂停预留的,课程设计可以直接忽略。外键都用FOREIGN KEY显式声明,答辩时能把外键约束和参照完整性说清楚。
3.3 空床位查询 SQL 和并发入住问题
宿舍分配的核心逻辑是查一张空闲床位,再占住这个床位。最直观的查询是这样的:
SELECT b.id FROM t_room r JOIN t_bed b ON b.room_id = r.id LEFT JOIN t_student s ON s.bed_id = b.id AND s.status = 1 WHERE r.id = ? AND b.status = 0 AND s.id IS NULL LIMIT 1;这段 SQL 做的事是:先把房间、床位和学生三张表关联起来,用WHERE b.status = 0过滤已经占用的床位,再用LEFT JOIN ... IS NULL排除只有床位状态没同步的情况。逻辑虽然能查出结果,但单独执行它并不能阻止并发分配。当两个人同时申请同一个空床位,两个请求都可能在对方提交事务前看到同一条记录。所以落地方案必须是把查空床位和更新床位状态放进同一个事务,并对这条床位记录加SELECT ... FOR UPDATE行锁,第二个连接只能在行锁释放后看到已被占用。这一点在第四章写事务代码时会再次用到。
3.4 数据库连接池和时区、编码参数设置
JDBC 直连数据库不适合作为最终交付,因为每次请求都在建立 TCP 连接。给 MySQL 配置 Druid 连接池后,连接生命周期交给池管理。常用配置如下:
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/db_dormitory?characterEncoding=utf8&useSSL=false username=root password=root initialSize=5 maxActive=20 maxWait=1000 testWhileIdle=true validationQuery=SELECT 1连接串里的characterEncoding=utf8保证中文写入不变成问号;MySQL 8 驱动需要补serverTimezone=Asia/Shanghai,否则会报时区错误。initialSize=5表示启动时创建 5 个连接,maxActive=20是并发峰值上限,maxWait=1000控制获取连接的等待时间,超过 1 秒直接抛异常,方便定位是不是连接泄漏。Druid 的testWhileIdle和validationQuery配合,会在空闲连接被申请前先发一个SELECT 1探活,避免拿到早已断开的连接。这里不需要把监控页面接入系统,但如果论文想写系统优化,提到 Druid 自带 SQL 监控页是可行的加分项。
4. DAO、Service、Servlet 三层实现:入住退宿和查询床位的核心代码
这一章直接解决程序怎么实现。JavaWeb 的请求路径由 Servlet 负责,业务规则由 Service 负责,SQL 封装在 DAO 里。常见做法是让 JSP 表单提交到同一个控制器,控制器拿到参数后调用 Service,Service 再调用 DAO。下面先看请求流转,再讨论两个通用工具。
4.1 请求流转和每层检查点
从 Tomcat 收到登录表单提交开始,Filter 先检验当前会话中是否存在登录用户,然后请求来到对应 Servlet。Servlet 里只做三件事:获取请求参数、类型转换、调用 Service。Service 返回结果之后,Servlet 选择转发到 JSP 还是重定向到列表页。写代码时,我给每层定了一条检查表,排错时按表走路:
| 层 | 常见故障 | 优先检查 |
|---|---|---|
| JSP | 表单 name 与请求参数不一致 | 浏览器 Network 面板 |
| Servlet | 类型转换异常、转发路径写错 | 控制台 Servlet 异常堆栈 |
| Service | 事务没提交、业务结果未封装 | 是否调用 commit/close |
| DAO | SQL 语法、占位符数量不对 | 预编译日志里的 SQL |
实际项目中,我尽量避免在 JSP 里直接拼<%= rs.getString("xxx") %>,而是由 Servlet 查询结果放进request.setAttribute("list", list),再用 JSTL 的c:forEach渲染。这样 JSP 页面干净,也能减少 MVC 分层混乱式的扣分。
4.2 用 BaseServlet 避免每个模块重复写 doGet 和 doPost
传统 Servlet 每个类都要重复实现 doGet/doPost,代码看起来很长。我一般会建一个 BaseServlet,把方法分发统一处理,子类只写业务方法:
public class BaseServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String methodName = request.getParameter("method"); if (methodName == null || methodName.isBlank()) { response.sendError(400, "missing method"); return; } try { Method target = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); target.setAccessible(true); target.invoke(this, request, response); } catch (NoSuchMethodException e) { response.sendError(404, "method not found"); } catch (Exception e) { throw new ServletException("invoke method failed", e); } } }这段代码的作用是:只要子类暴露一个名为checkin、参数为(HttpServletRequest, HttpServletResponse)的 public 方法,前端表单就能通过method=checkin直接触发它,而不用每个 Servlet 都写 if/else 判断。使用这个技巧时要注意两点:第一,method参数名会被各业务方法共用,表单里不能再把业务字段命名为 method;第二,反射调用抛出的异常会被包装成InvocationTargetException,排错时要拆开cause才能看到 Service 里的原始错误。
提示:这里的 BaseServlet 只处理
method=xxx的表单调用方式,如果改成 RESTful 的路径传参,需要换成路径解析,两种风格不要混用。
4.3 入住业务的事务边界是实现要点
入住流程涉及到学生档案、床位状态和住宿记录三处写入,常见错误是床位状态更新成功但入住记录插入失败,最终导致数据不一致。将这三步包进一个 JDBC 事务是常用做法:
public boolean checkin(Student student, int bedId) { Connection conn = null; try { conn = JdbcUtils.getConnection(); conn.setAutoCommit(false); // 锁定床位,防止并发重复分配 BedDao.lockBed(conn, bedId); // 1. 床位状态改为占用 BedDao.updateStatus(conn, bedId, 1); // 2. 学生档案绑定床位 StudentDao.updateBed(conn, student.getId(), bedId); // 3. 写入入住历史记录 CheckinDao.insert(conn, student.getId(), bedId); conn.commit(); return true; } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ignored) {} } throw new RuntimeException("入住失败,床位状态已回滚", e); } finally { JdbcUtils.close(conn); } }注意这里conn.setAutoCommit(false)会把三个 DAO 方法放到同一个事务里,任何一个方法抛出 SQLException 都会执行 rollback,所有中间状态全部还原。锁床位的BedDao.lockBed内部执行的是上一章提到的SELECT id FROM t_bed WHERE id = ? FOR UPDATE,它的作用是让并发请求拿不到同一张空闲床,行锁会一直持有到本事务 commit 或 rollback。事务完成后JdbcUtils.close(conn)不是真的关闭连接,而是把连接归还给 Druid 连接池,因此要避免在 commit 之后的 finally 里再执行另一个查询。
4.4 用 Ajax 返回 JSON,动态刷新可选床位
宿舍分配页面如果整页刷新,使用体验和代码结构都会很僵硬。常见做法是,学生选择楼层或宿舍后由 JSP 发起 Ajax 请求,Servlet 查询空余床位并返回 JSON。代码可以用最朴素的字符串拼接实现:
@WebServlet("/bed/available") public class AvailableBedServlet extends BaseServlet { public void query(HttpServletRequest request, HttpServletResponse response) throws Exception { int roomId = Integer.parseInt(request.getParameter("roomId")); List<Bed> beds = BedDao.findAvailable(roomId); StringBuilder sb = new StringBuilder(); sb.append("{\"code\":0, \"data\":["); for (int i = 0; i < beds.size(); i++) { Bed b = beds.get(i); if (i > 0) sb.append(","); sb.append("{\"id\":").append(b.getId()) .append(",\"bedNo\":\"").append(b.getBedNo()).append("\"}"); } sb.append("]}"); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(sb.toString()); } }这里有几个容易踩的编码问题:如果response.setContentType写成text/html,前端JSON.parse就会报错;如果没带charset=utf-8,中文字段大概率乱码。在 IDEA 的 JavaWeb 项目配置里,还可以给 Tomcat 的 VM 参数加上-Dfile.encoding=UTF-8,避免 JSP 动态内容与静态资源编码互相干扰。对于床位的bedNo,用 String 类型返回比用 Integer 更稳,因为有些学校会把床位命名成A01、B02,其中A/B指朝向,转成数字会丢失信息。
5. 论文怎么编排:需求分析、数据库设计和答辩话术
课程设计评分不只看代码,题目带含论文就要求把思考过程写成完整文档。论文和数据库交付脚本一样,必须有让人按着读就能复现的颗粒度。不要贴整个源码文件,要把关键表和关键事务讲清楚。
5.1 论文目录可以按六章结构组织
我见过评审老师会直接翻到论文目录看章节顺序,一个稳妥的结构包括绪论、相关技术、需求分析、系统设计、系统实现、测试。下面是一份可参考的目录骨架:
第1章 绪论 1.1 选题背景与意义 1.2 国内外研究现状 1.3 论文的章节安排 第2章 关键技术简介 2.1 JavaWeb 开发技术 2.2 MySQL 数据库 2.3 Tomcat 服务器 第3章 系统需求分析 3.1 可行性分析 3.2 功能性需求 3.3 非功能性需求 第4章 系统设计 4.1 总体架构设计 4.2 功能模块划分 4.3 数据库设计 第5章 系统实现 5.1 登录模块实现 5.2 学生住宿管理实现 5.3 报修和卫生检查实现 第6章 系统测试 6.1 测试环境 6.2 功能测试用例 6.3 测试结果与分析 第7章 总结与展望第 2 章千万不要整段从网上复制技术介绍,只挑本项目用到的部分,比如 Servlet 生命周期和 JDBC 预编译,而不是把 Java 语言发展史写进来。第 5 章每一节建议搭配一张运行截图、一段核心代码片段、一段实现流程说明,形成“页面展示-实现逻辑-功能验证”三件套。核心代码不需要把整个类贴进去,摘取 DAO 里的一个查询方法或 Service 里一个事务片段就足够。
5.2 数据库部分需要出现的表和测试用例
论文中数据库设计一节,评审老师最先找的是 E-R 图和表结构说明。E-R 图用 Visio 或 draw.io 画,把实体、属性、联系画清楚。表结构说明可以用表格呈现,例如:
| 字段名 | 类型 | 允许空 | 说明 |
|---|---|---|---|
| id | INT | 否 | 主键,自增 |
| student_no | VARCHAR(20) | 否 | 学号,唯一索引 |
| password | VARCHAR(64) | 否 | 加盐哈希后的登录密码 |
| bed_id | INT | 是 | 关联 t_bed 表,退宿时为 NULL |
| status | TINYINT | 否 | 1 在住,0 已退宿 |
测试部分需要一张功能测试用例表,至少覆盖登录失败、入住成功、重复学号提示、退宿后床位释放、报修流程完整跑通这五条路径。用例表应包含编号、操作步骤、预期结果、实际结果。不要写“打开页面正常”这种没有信息量的用例,要写“输入错误密码,点击登录,提示密码错误”。
5.3 答辩时三个几乎必问的技术问题
问题一:用户密码是明文存的吗?这类项目里密码应当做不可逆处理,如果使用 MD5 必须加盐。论文里不写“密码已加密”,而写“密码采用加盐哈希保存,不存储明文”,被追问时还能解释盐为什么要随机且每条记录不同。问题二:外键会影响性能,为什么还要用?你可以回答:课程设计的数据量下,外键能保证参照完整性,如果业务规模加大,未来可以去掉外键改由应用层保证。问题三:如何避免两个人同时入住选到同一个床位?这个问题要说出SELECT ... FOR UPDATE行锁和事务回滚,先说应用层先查再更新不够,再说数据库层用锁保证同一时刻只让一个事务提交。
6. 交付前用 mysqldump 把数据库脚本回放成可复现的交付件
如果你负责交付程序加论文加数据库,最后的验收动作比写代码还重要。常见做法是:最终目录里保留源码、SQL 文件、论文 word、运行说明文档,然后把数据库脚本从本机导出来,在另一台没装过这个库的机器上按零状态重放一次。
6.1 数据库脚本在干净环境重放一次
重放过程我一般走三步。先导出:
mysqldump -uroot -p --single-transaction \ --default-character-set=utf8mb4 \ db_dormitory > db_dormitory.sql再导入前把目标库重置干净:
mysql -uroot -p -e "DROP DATABASE IF EXISTS db_dormitory;" mysql -uroot -p -e "CREATE DATABASE db_dormitory DEFAULT CHARACTER SET utf8mb4;" mysql -uroot -p db_dormitory < db_dormitory.sql第一条命令里的--single-transaction是在导出时开启一个一致性快照,不影响正在运行的业务;--default-character-set=utf8mb4保证中文注释和表数据的字符集不随终端环境变化。第二三条命令必须严格先删后建,否则旧表里的外键或无效数据会污染回放结果。导入完成后执行SELECT COUNT(*) FROM t_student;验证行数和本机一致。最后在 IDEA 的 Run Configurations 里选上 Tomcat,配置 Artifact 为 war exploded,确认访问路径是/dormitory/后,把浏览器里的登录流程从头走一遍。
一个容易被忽略的检查项是:连接池里的 url 不要只写localhost,如果数据库装在 Docker 容器里,这里必须写映射到宿主机的端口,否则在另一台电脑上部署时日志里永远报Communications link failure。附加一个更隐蔽的检查点:把连接池的validationQuery从SELECT 1临时改成SELECT CURRENT_DATE,若日志出现时区偏移或类型转换异常,说明 JDBC 连接串和 MySQL 时区设置没对齐;修完时区参数后再改回SELECT 1,能避免运行几天后凌晨时段日志时间错位的异常场景。
本文还有配套的精品资源,点击获取