多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

JavaWeb实验室预约管理系统:从冲突检测到一键预约的落地实践

JavaWeb实验室预约管理系统:从冲突检测到一键预约的落地实践 简介这是一套面向高校计算机相关专业课程设计、毕业设计及JavaWeb入门进阶学习者的实验室预约管理系统完整源码包采用Java语言结合MySQL数据库开发覆盖管理员、教师、学生三类角色的核心业务场景。系统功能涵盖用户管理、密码重置、公告发布、实验室信息维护、预约情况查看、高级搜索与排期表展示教师端还支持个人预约与课堂预约、课堂信息管理、学生名册导入导出、课堂任务发布及实验资料上传等模块适合作为课程作业参考或二次开发基础。资源包为zip格式压缩后约42.69MB内含项目源码、数据库脚本及配套说明文档目录结构清晰便于按模块查阅与调试。目前已有467人浏览学习读者可借助完整的三层角色权限设计与预约排期逻辑快速理解JavaWeb项目从建库、编码到功能联调的完整流程并在此基础上进行功能扩展与个性化改造。1. 实验室预约管理系统从排课冲突到一键预约的 JavaWeb 落地路径每到学期初实验室门口总会上演同一幕几拨学生拿着纸质登记本互相瞪眼管理员翻着 Excel 反复确认「这间屋子到底谁批的」。实验室预约管理系统要解决的就是这个——把实验室资源、时间段、申请人三方关系用数据库锁死用 Web 界面暴露给师生让冲突在提交那一刻就被拦下。这套基于 JavaWeb 的方案技术栈通常落在 Servlet/JSP 或 Spring Boot MyBatis MySQL 上前端用 JSP 或 Thymeleaf 渲染适合课程设计、毕业设计也适合中小实验室做内部工具。源码、数据库脚本、文档三件套齐全意味着你不用从零搭架子重点放在跑通和改造成自己场景。下面按「先跑起来、再讲清结构、最后填坑」的顺序拆。2. 环境搭建与项目跑通IDEA 导入 JavaWeb 项目的最小闭环2.1 JDK、Tomcat、MySQL 的版本对齐JavaWeb 项目跑不起来八成死在版本错配上。常见组合是 JDK 8 或 JDK 11、Tomcat 8.5/9.0、MySQL 5.7/8.0。JDK 17 配老版本 Tomcat 8 会出现Unsupported class file major versionMySQL 8 用旧版mysql-connector-java 5.1.x驱动会报Unknown system variable query_cache_size。我一般先确认三件事java -version输出、Tomcat 的lib目录里有没有对应驱动、MySQL 的character_set_server是不是utf8mb4。数据库连接串里serverTimezoneAsia/Shanghai和useSSLfalse这两个参数在 MySQL 8 下不加启动就抛时区异常。2.2 用 IDEA 导入并配置 Artifact导入步骤不复杂但 Artifact 配置是新手最容易漏的一环。没有它Tomcat 启动后访问 404。# 1. 解压源码用 IDEA 打开含 pom.xml 或 .iml 的根目录 # 2. File - Project Structure - Modules确认 Sources 里 src 标记为 Sources Root # 3. Project Structure - Artifacts - Add - Web Application: Exploded - From Modules # 4. Run - Edit Configurations - Add - Tomcat Server - Local # 在 Deployment 标签页加入刚建的 ArtifactApplication context 设为 /lab逻辑说明Artifact 决定 Tomcat 部署时把哪些编译产物和资源打包进webapps。Application context是访问路径前缀设成/lab后浏览器访问http://localhost:8080/lab/。参数上Output directory保持默认即可Before launch里要确保有Build Artifacts否则改了代码不生效。如果项目是 Maven 结构先在终端跑mvn clean package确认target下生成 war 包再导入能提前暴露依赖缺失。2.3 数据库脚本导入与连接验证数据库脚本一般是一个.sql文件含建库、建表、初始数据。导入前先建库字符集选utf8mb4排序规则utf8mb4_general_ci。CREATE DATABASE lab_booking DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE lab_booking; SOURCE /path/to/lab_booking.sql; -- 验证核心表是否就位 SHOW TABLES; SELECT COUNT(*) FROM lab_room; SELECT COUNT(*) FROM booking_record;逻辑说明SOURCE命令在 MySQL 客户端里执行脚本比图形化工具导入更少踩编码坑。参数上如果脚本里写死了CREATE DATABASE先注释掉那行再执行避免和已有库冲突。验证阶段重点看lab_room实验室表和booking_record预约记录表有没有数据空表说明脚本没跑完。连接验证在 IDEA 的 Database 面板里配一次能连上再启动 Tomcat省得在日志里大海捞针。3. 数据库设计预约冲突检测的表结构与 SQL 约束3.1 核心表关系与字段取舍预约系统的数据库设计核心就三张表实验室表、用户表、预约记录表。实验室表存房间号、容量、设备清单、开放状态用户表存账号、角色管理员/教师/学生预约记录表存实验室 ID、申请人 ID、开始时间、结束时间、用途、审批状态。字段类型上时间用DATETIME而不是VARCHAR否则排序和范围查询会翻车。状态字段用TINYINT加注释比VARCHAR省空间且索引效率高。表名关键字段说明lab_roomid, room_no, capacity, statusstatus 0 停用 1 可用sys_userid, username, password, rolerole 区分 admin/teacher/studentbooking_recordid, room_id, user_id, start_time, end_time, statusstatus 0 待审 1 通过 2 驳回3.2 用 SQL 唯一约束和查询拦截时间冲突光靠前端校验不够并发提交时两个请求可能同时通过。常见做法是在应用层用「查询 插入」加事务但更稳的是在数据库层加约束。MySQL 没有原生的时间段排他约束所以退而求其次在booking_record上建(room_id, start_time, end_time)的普通索引应用层用SELECT ... FOR UPDATE锁行。-- 冲突检测查询同一实验室时间段有重叠且状态为已通过 SELECT COUNT(*) FROM booking_record WHERE room_id ? AND status 1 AND start_time ? -- 新预约的结束时间 AND end_time ?; -- 新预约的开始时间逻辑说明重叠判断的条件是「已有记录的开始时间早于新记录的结束时间且已有记录的结束时间晚于新记录的开始时间」。参数顺序别搞反第一个?是新预约的end_time第二个?是新预约的start_time。返回大于 0 就说明冲突直接驳回。这个查询走room_id索引数据量不大时够用。如果实验室数量上千、预约记录上百万再考虑按时间分区或引入 Redis 做缓存锁。3.3 审批状态流转与软删除预约记录不要物理删除用status字段做软删除和状态流转。待审0→ 通过1或驳回2取消预约把状态改成 3。这样历史记录可追溯管理员查日志也方便。字段上加create_time和update_time默认CURRENT_TIMESTAMP排查问题时能还原操作时间线。4. 后端接口与前端页面预约提交、审批、查询的完整链路4.1 Servlet 层处理预约提交与参数校验以 Servlet 方案为例预约提交的入口是一个doPost。参数校验不能省空值、时间倒置、超出开放时间都要拦。protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { req.setCharacterEncoding(UTF-8); String roomId req.getParameter(roomId); String startTime req.getParameter(startTime); String endTime req.getParameter(endTime); // 基础校验非空 时间顺序 if (roomId null || startTime null || endTime null) { resp.getWriter().write({\code\:400,\msg\:\参数缺失\}); return; } if (startTime.compareTo(endTime) 0) { resp.getWriter().write({\code\:400,\msg\:\结束时间必须晚于开始时间\}); return; } // 调用 Service 做冲突检测和插入 boolean ok bookingService.createBooking(roomId, startTime, endTime); resp.getWriter().write(ok ? {\code\:200} : {\code\:409,\msg\:\时间冲突\}); }逻辑说明setCharacterEncoding(UTF-8)必须在取参数之前调用否则中文用途说明会乱码。时间比较用字符串的compareTo只在格式统一为yyyy-MM-dd HH:mm:ss时成立更稳妥的做法是SimpleDateFormat解析成Date再比。返回 JSON 而不是跳转页面是为了配合前端异步提交。参数上roomId建议用Integer.parseInt转一下防止 SQL 注入拼接。4.2 审批接口与角色权限过滤审批接口只对管理员开放。常见做法是在web.xml里配filter或者在 Servlet 里判断session.getAttribute(role)。Integer role (Integer) req.getSession().getAttribute(role); if (role null || role ! 1) { // 1 表示管理员 resp.sendRedirect(req.getContextPath() /login.jsp); return; } String recordId req.getParameter(recordId); String action req.getParameter(action); // pass 或 reject bookingService.updateStatus(Integer.parseInt(recordId), pass.equals(action) ? 1 : 2);逻辑说明角色判断放在业务逻辑之前避免越权操作。action参数用字符串而不是数字前端传参更直观。审批通过后可以加一步「发送通知」课程设计里通常省略实际部署时用邮件或站内信补上。4.3 前端页面渲染与分页查询查询预约记录要分页否则数据一多页面卡死。JSP 里用 JSTL 遍历后端算好offset和limit。SELECT b.id, r.room_no, u.username, b.start_time, b.end_time, b.status FROM booking_record b JOIN lab_room r ON b.room_id r.id JOIN sys_user u ON b.user_id u.id ORDER BY b.start_time DESC LIMIT ?, ?;逻辑说明LIMIT的两个参数分别是偏移量和每页条数偏移量 当前页码 - 1× 每页条数。JOIN 查询把房间号和用户名带出来前端不用再发请求。参数上每页条数设 10 到 20 比较合适太小翻页频繁太大渲染慢。如果查询条件带实验室或日期范围在WHERE里动态拼接注意用PreparedStatement防注入。5. 避坑与排查JavaWeb 预约系统最常见的 5 个翻车现场5.1 中文乱码从请求到数据库的全链路排查现象提交预约后用途说明在页面上显示成问号或方块。原因请求编码、响应编码、数据库连接编码、表字符集四者不一致。解决req.setCharacterEncoding(UTF-8)和resp.setContentType(text/html;charsetUTF-8)都要加连接串加characterEncodingutf8建库时指定utf8mb4。Tomcat 8 以上默认 URI 编码是 UTF-8但 GET 请求的中文仍可能乱建议统一用 POST。5.2 时间格式转换异常java.text.ParseException现象提交预约时后台抛ParseException: Unparseable date。原因前端传的时间格式和SimpleDateFormat的模式不匹配比如前端传2024/01/01 10:00后端按yyyy-MM-dd HH:mm:ss解析。解决前后端约定统一格式或者在解析前做一次替换。更稳的做法是用input typedatetime-local它输出的格式是yyyy-MM-ddTHH:mm后端解析时把T替换成空格再补:00。5.3 数据库连接池耗尽Too many connections现象系统跑一段时间后所有请求卡死日志报Too many connections。原因每次请求都DriverManager.getConnection新建连接用完没关。解决引入连接池Druid 或 HikariCP 都行配好maxActive和maxWait。同时检查代码里Connection、Statement、ResultSet是否在finally块里关闭。课程设计里用DBUtil封装获取和释放能省很多事。5.4 并发预约导致重复插入现象两个学生同时提交同一时间段系统都提示成功。原因冲突检测和插入之间有时间窗口两个请求都查到「无冲突」。解决在booking_record上加唯一索引不现实时间段无法直接唯一改用SELECT ... FOR UPDATE在事务里锁住该实验室的行或者用 Redis 分布式锁。数据量小的场景把冲突检测和插入放在同一个synchronized块里也能顶一阵但只适合单机。5.5 Tomcat 启动报 404Artifact 没配对现象Tomcat 启动无报错但访问http://localhost:8080/lab/返回 404。原因Artifact 没加到 Deployment或者Application context和访问路径不一致。解决Run - Edit Configurations - Deployment确认 Artifact 在列表里Application context填/lab。如果项目是 Maven 多模块检查pom.xml里packaging是不是warwebapp目录有没有被正确识别。6. 从能跑到好用预约系统的进阶改造与验证习惯跑通只是起点真正让这套系统在实验室里活下来靠的是几个小改造。第一把冲突检测从「提交时拦截」提前到「选择时间段时置灰」。前端用 AJAX 拉取某实验室某天的已占用时段日历上直接标红用户根本选不了冲突项体验提升明显。第二加一个「我的预约」页面支持取消和改期改期本质上是「取消旧记录 新建记录」放在一个事务里做。第三审批流加个备注字段管理员驳回时写原因学生能看到减少来回扯皮。验证方法上我习惯用 Postman 或 curl 直接打接口绕过前端看后端逻辑是否独立正确。比如提交一个时间倒置的预约看返回是不是 400提交一个冲突的看是不是 409。接口层稳了前端再出问题就只是展示层的事。数据库层面定期跑一遍EXPLAIN看冲突检测查询有没有走索引数据量涨到十万级时这一步很关键。# 用 curl 验证预约接口的边界情况 curl -X POST http://localhost:8080/lab/booking \ -d roomId1startTime2024-06-01 14:00:00endTime2024-06-01 12:00:00 # 预期返回 {code:400,msg:结束时间必须晚于开始时间}参数说明-d后面跟表单参数多个用连接。如果接口返回 500先看 Tomcat 日志里的堆栈定位到具体行号。这个习惯帮我省过很多次「前端看着没问题但就是提交失败」的排查时间。最后说个血泪教训数据库脚本一定要在导入前备份一份原始文件改表结构时先SHOW CREATE TABLE留底。我有次手滑把booking_record的status字段类型改了没留后悔药只能重跑脚本初始数据全丢。现在我的习惯是任何涉及表结构的操作先在测试库跑一遍确认无误再上正式库。希望帮到你。本文还有配套的精品资源点击获取
返回列表