
简介面向高校计算机专业学生、JavaWeb初学者及需要完成课程设计或毕业设计的人员这是一套基于JavaWeb和MySQL的实验室预约管理系统完整源码包涵盖实验室信息展示、预约申请、管理员审核、用户管理等典型业务模块贴合真实项目开发流程。压缩包共808个文件以Java源文件、JSP页面、JS脚本、CSS样式及MySQL数据库脚本为主同时包含编译后的class文件、依赖jar包以及大量png截图和gif演示动画便于直接部署与直观理解流程整体大小54.54MB。目前已有8818人在CSDN浏览学习该资源侧面说明其实用性和参考价值。借助完整源码与数据库文件读者可系统掌握JavaWeb分层架构、Servlet控制器、JSP视图、JDBC数据访问等关键技能同时可依据界面预览快速还原运行效果适合作为项目练习与二次开发的基础。 做JavaWeb练手项目我真的推荐从“管理系统”这类需求入手——业务逻辑清晰、技术栈覆盖全面、还能直接写到简历里。今天这篇就完整拆解我最近做的一个实验室预约管理系统。它解决的是高校实验室管理里最真实的痛点多个老师学生抢同一间实验室、预约靠线下表格、审批状态不透明、使用记录难追溯。系统的核心目标就两个预约流程线上化实验室资源调度可视化。项目从需求分析到部署上线走完了一整个JavaWeb开发周期。技术选型上我没有强行堆SpringBoot而是用Servlet JSP MySQL Tomcat这套经典组合原因后面会细说。适合谁参考正在学JavaWeb、准备课程设计或毕业设计、想完整走通一个项目的同学都很合适。我会把数据库设计、预约冲突检测、权限过滤、审核状态流转这几个关键点逐个讲透最后再分享几个实操中踩过的坑。1. 项目核心思路与技术选型拆解1.1 实验室预约的痛点在哪里先说说需求背景。我接手这个项目时实验室管理员还在靠Excel管理预约有人发邮件申请有人直接口头说管理员手动记录到表格里时间一长表格就乱了。最折磨人的是冲突检测管理员需要对着表格人工比对时间段预约一多漏看错排几乎是常态。所以做这套系统第一优先级不是界面好看而是把预约行为规范化。我把核心流程抽象成一条链路用户提交预约、系统自动检测时间冲突、管理员审核、状态反馈给用户。这个闭环基本覆盖了大多数实验室管理场景。做管理系统这么多年我最大的体会是启动前一定要先把“痛点到功能”的映射列清楚否则写代码时很容易被各种临时需求牵着走最后做出来的东西既不解决痛点结构也乱。1.2 技术选型ServletJSP还是SpringBoot这个选择当时纠结过一阵。SpringBoot开发效率高、生态完善但对一个以练手和课设为目标的JavaWeb项目来说Servlet JSP JDBC这套组合反而更合适。原因有三点。第一Servlet和JSP是JavaWeb的核心基础Filter过滤器、Session会话、请求转发这些机制在SpringMVC里都被封装掉了直接上手SpringBoot容易变成配置搬运工。第二ServletJSP项目分层直观JSP负责视图、Servlet负责控制、DAO负责数据访问每一层都能看透适合学习阶段完全掌握。第三部署成本低一个Tomcat加一个war包就能跑起来无需额外容器。不过要说明如果目标是快速交付生产级系统那直接上SpringBoot MyBatis-Plus更合理。但我的建议很明确先把ServletJSP完整写一遍再迁移到SpringBoot你会发现请求生命周期、过滤器链、会话管理这些概念一下就通了迁移过程本身也是一次高质量的提升。1.3 角色划分与功能矩阵系统设计了三种角色学生、教师、管理员每种角色入口不同、权限不同。学生和教师都能注册登录、浏览实验室列表、提交预约、查看自己的预约记录、取消预约。教师的区别在于部分实验室设置了“仅教师可预约”而且教师预约优先级更高。管理员负责维护实验室信息、审批预约、查看全部预约记录、统计实验室使用率。角色权限不能只在代码里写死我通过Filter过滤器统一拦截。普通学生访问管理端URL直接被拦截并跳转到无权限提示页。功能矩阵整理如下功能模块学生教师管理员注册登录支持支持支持浏览实验室支持支持支持提交预约支持支持不支持审核预约不支持不支持支持维护实验室信息不支持不支持支持查看预约记录仅本人仅本人全部这样一张矩阵图放在项目文档开头后面开发每个模块都不会乱。2. 数据库设计预约系统的地基2.1 从需求到ER模型的转化数据库设计是管理系统项目的胜负手表结构不合理后面写业务代码会特别难受。我习惯先画ER图再转成表结构。这个项目的核心实体有四个用户、实验室、预约记录、管理员。为了以后筛选方便我还加了实验室类型表。用户和实验室本质是多对多关系但通过预约记录表拆成了两个一对多这是典型的多对多关联转中间表思路。预约记录表既是桥表又携带了时间段、用途、状态这些关键业务属性是整个系统数据模型的核心。2.2 核心表结构与字段说明先看最核心的预约表建表语句如下CREATE TABLE reservation ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, user_id INT NOT NULL COMMENT 预约人ID, lab_id INT NOT NULL COMMENT 实验室ID, reserve_date DATE NOT NULL COMMENT 预约日期, start_time TIME NOT NULL COMMENT 开始时间, end_time TIME NOT NULL COMMENT 结束时间, purpose VARCHAR(255) COMMENT 预约用途, status TINYINT DEFAULT 0 COMMENT 状态: 0待审核 1已通过 2已拒绝 3已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_user (user_id), INDEX idx_lab_time (lab_id, reserve_date, start_time, end_time), INDEX idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;几个字段设计上的考量值得说说。status用数字而不是字符串查询效率高也方便状态流转判定时写条件时间拆成reserve_date、start_time、end_time三个字段而不是直接存一个datetime是为了灵活处理“某一天某个时间段”的预约场景统计每天使用情况时也方便联合索引idx_lab_time专门为冲突检测查询服务数据量大以后这个索引的价值会非常明显。users表和lab表相对常规但有三个字段建议一定要加user表的role字段区分学生和教师lab表的need_approve字段控制该实验室是否需要管理员审核lab表的capacity字段记录容纳人数。这些字段看着不起眼实际做业务时非常关键缺一个后面都要补数据。2.3 预约时间冲突检测的设计思路冲突检测是预约系统最核心的业务规则。规则定义很直接同一个实验室、同一天预约时间段重叠就属于冲突。判断时间段重叠用这段逻辑(现有预约结束时间 新预约开始时间) AND (现有预约开始时间 新预约结束时间)举个例子实验室A在2024-05-20已经有一条08:00-10:00的预约现在新提交09:00-11:00因为现有结束时间10:00大于新开始时间09:00且现有开始时间08:00小于新结束时间11:00判定冲突。边界情况也要考虑新预约是08:00-10:00现有预约是10:00-12:00两者端点相等不重叠可以连续预约。这条判断逻辑我会在SQL里用NOT EXISTS子查询实现并且要过滤状态已拒绝和已取消的预约不能参与冲突计算。这个细节非常关键如果不过滤一条被拒绝的预约会一直占着时间段导致后续所有人无法预约。3. 核心功能实现与关键代码解析3.1 IDEA 2023创建JavaWeb项目骨架实操第一步是创建项目。我用的是IntelliJ IDEA 2023版本它创建JavaWeb项目的流程和早期版本有些差别记录一下完整步骤。新建Project时选择Jakarta EE模板选Web ApplicationApplication Server选Tomcat 9。项目生成后是标准的Maven结构源码目录在src/main/javaWeb资源在src/main/webapp这和以前直接在web目录下写代码的老结构不一样需要适应。然后在pom.xml里添加Servlet、JSP、MySQL驱动、JSTL这几个依赖。这里我的建议是优先用Maven结构不要用普通JavaWeb工程。Maven管理依赖太省心了后面要加Jackson、JSTL之类的库pom.xml里加一行就行不用满世界找jar包再手动丢进WEB-INF/lib。3.2 登录认证与权限过滤登录功能用Session实现用户登录成功后把User对象放进Session然后通过Filter拦截所有需要登录才能访问的路径。核心过滤器代码如下WebFilter(/*) public class AuthFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; String uri req.getRequestURI(); if (uri.endsWith(/login.jsp) || uri.endsWith(/login) || uri.endsWith(.css) || uri.endsWith(.js) || uri.endsWith(.png)) { chain.doFilter(request, response); return; } HttpSession session req.getSession(false); if (session ! null session.getAttribute(user) ! null) { chain.doFilter(request, response); } else { resp.sendRedirect(req.getContextPath() /login.jsp); } } }两个细节提醒。第一获取Session时传false不要无脑调用getSession()否则每一个请求包括静态资源请求都会被强制创建Session既浪费内存也容易被刷新攻击。第二白名单URL里要包含所有静态资源否则登录页的CSS和JS都会被Filter拦住页面样式全丢。角色权限的处理是在管理员操作之前再校验一次Session里的role字段双保险防止有人绕过页面直接请求管理接口。3.3 预约申请与冲突检测实现预约提交是整个系统最值得看的一段代码。我把冲突检测和插入合并成一条SQL用INSERT...SELECT WHERE NOT EXISTS的写法String sql INSERT INTO reservation (user_id, lab_id, reserve_date, start_time, end_time, purpose) SELECT ?, ?, ?, ?, ?, ? WHERE NOT EXISTS ( SELECT 1 FROM reservation WHERE lab_id ? AND reserve_date ? AND status IN (0, 1) AND start_time ? AND end_time ? );这套写法最大的价值是原子性。如果先查再插两个请求同时通过检测然后都插入成功的情况在高并发下真实存在一旦出现就是严重的资源冲突事故。用一条SQL把检查和插入绑在一起数据库层面就保证了唯一性。参数位置一定要对应清楚前六个问号是插入值后四个问号是检测条件其中检测条件的start_time和end_time是新预约的时间和插入时间传同样的值即可。建议写完后自己构造几组重叠、边界、不重叠的数据用例在测试环境完整验证一遍。3.4 审核流程与状态流转管理员端审核逻辑不复杂但状态流转不能乱。我用了简单的状态机管理规则如下待审核状态可以流转到已通过或已拒绝已通过状态用户可以主动取消变为已取消待审核状态用户也可以取消变为已取消。已拒绝和已取消是终态不能再变回其他状态。有人问审核通过后需不需要单独加锁表我的做法是不加独立锁表直接依赖前面那条带冲突检测的SQL和数据库索引来保证一致性。如果项目对并发要求更高也可以给lab_id、reserve_date、start_time、end_time加唯一索引但这会和“已拒绝/已取消不参与冲突”的规则冲突需要根据业务取舍。管理员界面我会展示预约人姓名、联系方式、实验室名称、时间段、用途和当前状态审核操作只提供通过和拒绝两个按钮拒绝时还可以填一下拒绝原因。4. 踩坑实录常见问题与排查技巧4.1 中文乱码从页面到数据库的完整链路JavaWeb最经典的坑没有之一。这次我一次性踩了三层乱码JSP页面中文变问号、表单提交到Servlet后乱码、数据库存进去乱码。JSP页面乱码在页面头部加% page contentTypetext/html;charsetUTF-8 languagejava %同时确认文件编码本身是UTF-8。表单POST乱码在Servlet第一次读取请求参数之前执行request.setCharacterEncoding(UTF-8)或者全局配置一个编码过滤器。数据库存储乱码JDBC连接URL里必须带characterEncodingutf8建表用utf8mb4字符集。注意setCharacterEncoding这个调用必须在第一次读取参数之前执行放在后面完全无效。4.2 数据库连接池Druid配置经验项目开始时我用DriverManager直接获取连接每来一个请求就新建一次数据库连接并发一高立刻报连接超时。后来换成Druid连接池情况马上好转。Druid核心配置如下bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/lab_manager?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf8/ property nameusername valueroot/ property namepassword value******/ property nameinitialSize value5/ property nameminIdle value5/ property namemaxActive value20/ /bean这里有两个高频报错点。第一serverTimezone参数不配新版MySQL驱动会直接报时区错误。第二MySQL 8.x的驱动类名是com.mysql.cj.jdbc.Driver不是com.mysql.jdbc.Driver用老类名虽然能启动但会有警告个别版本直接连接失败。4.3 时间冲突判断的边界情况重叠判断的逻辑看起来简单实际操作中还是会踩坑。我在测试阶段发现如果新预约完全包含现有预约比如现有09:00-10:00新预约08:00-11:00只判断“开始小于且结束大于”也能覆盖到真正容易出错的是时间解析。TIME字段如果用字符串拼接比较MySQL对时间的字符串格式容忍度很高但Java这边解析不统一就容易出错比如09:00:00和9:0:0都表示同一个时间直接比较字符串会得到错误结果。我的建议是统一用java.sql.Time对象传给PreparedStatement或者在Java内存里先做一次重叠判断再做数据库判重双重保险。测试时多造几组边界数据比如刚好端点相接、跨天预约、深夜时段预约把这些场景都跑通再上线。5. 部署上线与后续扩展方向5.1 打包部署到Tomcat开发完成后部署我用的IDEA打包warBuild菜单下选择Build Artifacts构建lab_manager:war包然后把war包丢进Tomcat的webapps目录启动Tomcat会自动解压部署。部署前要核对三件事JDK版本和Tomcat版本兼容性Tomcat 9要求JDK 8及以上数据库账号密码正确配置文件里的数据库连接信息是否独立出来。我的做法是把数据库配置放到一个config.properties文件里部署时只改配置文件完全不动代码这个习惯强烈建议养成上线后改配置会频繁很多。5.2 后续值得做的扩展方向项目跑通后可以按这几个方向继续深入。第一个方向把项目迁移到SpringBoot MyBatis-Plus这是一次非常好的重构练习对比两种写法后你会对框架为什么存在理解更深。第二个方向增加预约日历视图用FullCalendar把预约记录展示成日历管理体验提升非常明显。第三个方向引入定时任务在预约开始前自动发送提醒邮件可以用Quartz或者Spring Task实现。第四个方向做统计报表按实验室、按周统计使用率管理员界面用ECharts展示柱状图和饼图。我自己的体会是当初在表结构里预留的status字段和状态机设计在这几个扩展方向上完全够用不需要大改数据库。最后再分享一个实操心得做这类管理系统核心从来不是CRUD而是业务流程的严谨性。预约冲突检测那一条看似简单的SQL实际要考虑状态过滤、边界时间、并发原子性这比任何花哨的前端效果都重要。如果你正在做类似的JavaWeb项目先把数据库设计和状态流转想清楚代码只是最后翻译一遍的过程。调试阶段多打日志、多造边界数据比反复盯着代码看要高效得多。本文还有配套的精品资源点击获取