尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
基于JSP+Servlet的会议室预约系统设计与实现
简介基于JAVA/JSP技术打造的会议室预约系统面向企业办公场景用于解决会议室资源冲突、预约流程混乱等问题。系统分为管理员与员工两类角色管理员可维护部门、员工、会议室信息并发布公告员工可查看公告、在线预订或取消会议室权限划分清晰。资源包共744个文件大小约5.01MB以gif演示图、js脚本、html页面、css样式、jsp动态页面及Java源码为主同时包含jar依赖库与sql数据库文件基本覆盖前端展示、后端业务和数据库配置等完整环节。文件类型较多适合按目录结构进行模块化学习。目前已吸引1656人学习下载。通过该资源可了解JSPServletJavaBean的经典Web开发模式掌握登录验证、权限管理、数据库CRUD等核心实现结合内置的页面素材和样式还能快速搭建同类预约管理项目对课程设计或毕业设计具有直接参考价值。 会议室预约这块需求说实话每个有点规模的公司都会碰到。我之前接手过一套内部系统改造当时行政还在用Excel排会议室经常出现“会议室撞车”然后扯皮的情况。后来我用JAVA JSP做了个会议室预约系统把预约、审批、查询这几个环节全部搬到线上。这里我先把结论放在前面JSP虽然被不少人当成老技术但在这种并发量不高、逻辑以增删改查为主、团队又要求快速上线的内部管理系统里它的落地成本和控制力仍然够用。这篇博文就把这套系统的设计思路、核心代码逻辑和我在部署过程中踩过的坑完整写出来给正在做课设、毕设或者在传统企业里需要快速交付内部工具的朋友一个参考。1. 选型背景为什么用JSP而不是前后端分离来写这个系统1.1 这个系统到底要解决什么问题整个项目源于一个非常具体的痛点公司有三层楼一共8间会议室但行政部用一张共享Excel表管理预约谁先打开谁先填。结果就是经常出现两个部门同时填了同一个时间段到了开会时间才发现会议室被占了一群人站在门口等。行政每天要花大量时间协调冲突、更新表格、发通知还里外不是人。所以这个系统的核心价值很直接把预约动作从“大家抢一个Excel文件”变成“系统统一校验、统一排队”。扩展出来就是四个功能点会议室信息管理、预约申请与审批、时间段冲突自动检测、预约记录查询和导出。没有复杂算法没有高并发抢购没有分布式事务就是一个典型的内部信息管理系统。1.2 为什么我选了JSP这套技术栈当时团队的情况比较现实后端就两个人都是Java出身前端没有专职岗位。如果上前后端分离意味着要维护Vue工程、处理跨域、设计接口文档、联调排期两周内上线基本不可能。而JSP Servlet MySQL这套组合一个人从数据库建表到页面渲染全链路都能拿下出问题也好排查。我的选型是JDK 8 Tomcat 8.5 JSP/Servlet MySQL 5.7JDBC用Druid连接池。没有引入Spring全家桶因为这种规模的项目用Servlet反而更直观——一个请求对应一个Servlet方法整个调用链非常清晰新人接手也容易看懂。技术组件版本选择选型理由JDK1.8稳定Tomcat 8.5完美兼容Lombok等工具支持好Tomcat8.5.x支持Servlet 3.1JSP解析成熟内存占用低JSP/ServletJSP 2.3 / Servlet 3.1服务端渲染页面模板直接复用Java逻辑MySQL5.7内部系统量级足够InnoDB支持行级锁Druid1.2.x自带监控连接池配置简单可能有朋友会说这都什么年代了还用JSP。我的看法是选技术不是选最时髦的而是选当前场景下成本最低、风险最可控的。会议室预约系统的并发量撑死几十人同时在线数据库层面加个锁就能解决冲突完全没有必要引入微服务、缓存中间件那一套。JSP这种服务端渲染模式在这种场景下有个天然优势页面上的数据是服务端拼好的权限控制可以在渲染层直接做不需要前端配合判断按钮显隐。2. 数据库设计预约记录表才是整个系统的心脏2.1 三张核心表的设计我把这个系统拆成三个核心实体用户、会议室、预约记录。用户和会议室都是基础数据预约记录是核心业务数据。这里直接分享一下我最终确定的表结构。用户表比较简单我加了department字段因为预约会议室通常需要按部门统计使用率。会议室表除了房间名称、位置、容量我还留了equipment字段用来记录投影仪、视频会议设备这些东西——行政说很多人预约前最关心的就是有没有投影仪。预约记录表是整个系统的关键字段设计直接决定后续业务逻辑好不好写CREATE TABLE reservation ( id INT PRIMARY KEY AUTO_INCREMENT, room_id INT NOT NULL COMMENT 会议室ID, user_id INT NOT NULL COMMENT 预约人ID, title VARCHAR(100) NOT NULL COMMENT 会议主题, start_time DATETIME NOT NULL COMMENT 开始时间, end_time DATETIME NOT NULL COMMENT 结束时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待审核 1已通过 2已拒绝 3已取消 4已结束, audit_user_id INT DEFAULT NULL COMMENT 审核人ID, audit_time DATETIME DEFAULT NULL COMMENT 审核时间, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_room_time (room_id, start_time, end_time), KEY idx_user_time (user_id, start_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT预约记录表;这里有两个设计细节要重点说。第一status字段我没有用cancelled这种字符串而是用TINYINT类型映射状态码因为Java后端用枚举维护状态流转非常方便数据库层面也省空间。第二我建了idx_room_time联合索引因为后续所有冲突检测都是按“会议室 时间范围”来查的这个索引能让查询走索引而不是全表扫描。2.2 时间段重叠判断的SQL逻辑会议室预约最核心的校验就是同一个会议室新预约的时间段不能和已有预约的时间段重叠。这个逻辑听起来简单但很多人第一次写都会搞错。重叠的判定条件是新预约开始时间小于已有预约结束时间并且新预约结束时间大于已有预约开始时间。SELECT COUNT(*) FROM reservation WHERE room_id ? AND status IN (0, 1) -- 待审核和已通过都会占用时段 AND start_time ? -- 新预约的结束时间 AND end_time ? -- 新预约的开始时间这个SQL很经典等于说只要新时间段的开始落在旧时间段结束之前同时新时间段的结束落在旧时间段开始之后两者必然有交集。我用待审核和已通过两种状态都参与冲突检测是因为一旦有人提交了预约申请哪怕还没审批也应该把这个时段锁住否则就会变成“我先申请了但别人还能抢到同一个时间审核却通过了我的单子”。2.3 为什么不建议在会议室表里放“当前状态”字段开发的时候行政提过一个需求能不能在会议室列表上直接看到“空闲/使用中”。一开始我在会议室表加了current_status字段后来发现这个方案是给自己挖坑。因为这个状态的判断依赖当前时间和预约记录如果只靠一个字段维护必须保证每次预约状态变更、每次会议结束都去更新它漏掉一次就出现数据不一致。更麻烦的是如果有人取消了预约但忘记更新状态页面上就永远显示“使用中”。正确的做法是会议室列表的“空闲/使用中”不查会议室表而是查预约记录表。查询当前时间点是否存在状态为待审核或已通过且时间范围包含当前时间的记录存在就是使用中否则就是空闲。用数据库查询去动态计算状态虽然看着多了一次查询但彻底避免了状态不一致的问题。3. 权限控制与会话管理Filter里写错一行可能让登录失效3.1 登录拦截器和角色区分这个系统的用户分两类普通员工和管理员。普通员工能提交预约、查看自己的预约、取消自己的预约管理员额外拥有审批、管理会议室、查看所有预约记录的权限。我没有引入Spring Security因为这种两个角色的系统用Filter Session就能控制得很干净。登录拦截Filter是整个权限控制的入口逻辑很简单检查Session里有没有loginUser没有就跳转到登录页。但这里有一个非常经典的问题——重定向死循环。因为登录页请求本身也被拦截了Session里没有用户结果登录页一直跳转登录页直接栈溢出。public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); // 放行登录页、登录接口、静态资源 if (uri.endsWith(/login.jsp) || uri.endsWith(/login) || uri.contains(/static/) || uri.endsWith(/logout)) { chain.doFilter(request, response); return; } Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(request, response); }admin相关的权限控制我是在Servlet层做的进入预约审批相关的Servlet时先从Session取出loginUser判断isAdmin字段不是管理员直接返回403页面。Filter负责“你有没有登录”Servlet负责“你有没有权限”这样分层比所有校验全部堆在Filter里要清晰得多。我之前试过把权限判断也放Filter导致Filter里要维护一份URL和角色的映射表改起来非常痛苦。3.2 会话失效和编码问题还有一个容易被忽略的坑是会话超时。Tomcat默认Session超时是30分钟管理员上午审批了几个会议室下午再点审核按钮Session已经失效请求被Filter拦回登录页但管理员不知道发生了什么以为系统出Bug了。我的处理方式是在登录页加一个提示参数Filter重定向的时候带上timeout1登录页读取到就在登录框上方显示“登录已过期请重新登录”。这个体验细节看起来小实际使用频率很高。中文乱码问题也在这个项目里出现过。JSP页面、Servlet接收请求参数、数据库连接三处的编码必须统一。我在web.xml里配置了CharacterEncodingFilter强制请求和响应都使用UTF-8同时数据库连接URL加了characterEncodingutf8参数。这三条链路只要漏掉一个就会出现“页面上显示正常存进数据库是问号”这种诡异问题。filter filter-nameencodingFilter/filter-name filter-classcom.yourpackage.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping4. 预约业务完整链路提交、审批、冲突校验、状态流转4.1 预约提交的后端处理流程预约提交是整个系统最核心的请求。我在Servlet里处理这个请求时严格按照“参数校验 → 登录校验 → 冲突检测 → 插入数据”的顺序来。参数校验不只是空值判断还包括时间合法性判断结束时间必须晚于开始时间、预约时间不能早于当前时间、单次预约时长不能超过4小时。这些规则如果不做用户随手选一个不合理时间段数据库里就会留下一堆垃圾数据。冲突检测这步我选择在Java代码里执行上面的SQL然后根据返回的count判断。这里有一个细节检测和插入两个操作存在时间差理论上两个用户同时提交时可能都通过检测然后都插入成功造成数据不一致。解决方法我放在下一节详细说这里先按下不表。数据插入成功之后我给预约单的初始状态设置为“待审核”。很多人会问为什么不让员工预约后直接生效因为实际业务里行政需要对会议室的使用做统一协调比如某间会议室可能临时要留给重要客户访团。设一个审批环节等于给管理员留了最后一道控制闸门。4.2 审批和状态机的流转规则管理员审批通过后预约单状态从“待审核”变成“已通过”。但状态流转不是单向的我梳理了整个生命周期的状态机操作原状态新状态说明用户提交预约无待审核创建记录管理员审核通过待审核已通过占用时间段管理员审核拒绝待审核已拒绝释放时间段用户取消预约待审核/已通过已取消释放时间段会议时间结束已通过已结束系统自动变更状态机这块我用Java枚举来管理禁止跨状态操作。比如一条已经被拒绝的预约记录用户不能对这个单子执行“取消”操作一条已经结束的预约管理员也不能再审批。我写了一个ReservationStatusEnum里面定义了canCancel、canAudit这些方法每次操作前先判断状态杜绝了非法流转。4.3 “会议自动结束”用的是懒检查而不是定时任务按常规思路预约记录到结束时间后要自动变成“已结束”很多人第一反应是写一个定时任务每个小时扫描一次数据库。我没有这么做。内部系统用户量不大定时任务反而增加部署复杂度而且扫描周期设不好会出现“会议明明已经结束了列表里还显示已通过”的延迟。我采用方案是查询的时候实时判断。也就是在查询预约记录列表的SQL里直接加一个条件如果当前时间已经大于end_time且状态还是“已通过”就动态把记录的状态在返回前改成“已结束”并同步更新数据库。这样既不需要定时任务又能保证页面展示永远符合当前时间。每次查询最多更新几分钟内的过期待办数据库压力完全可以忽略。if (currentTime.after(endTime) status APPROVED) { reservation.setStatus(FINISHED); // 执行UPDATE操作同步数据库 }这个方案对会议室预约这种低频系统非常合适。如果你的系统每天有几百万条预约记录那可能需要定时批量处理但那种量级的系统也不会用JSP写了。5. 并发预约冲突、JSP缓存和一串常见报错的现场还原5.1 两个同事同时提交同一间会议室怎么防止撞车这是预约系统最典型的并发场景部门A和部门B几乎同时提交了同一间会议室、同一时间段的预约申请。按照我在4.1节写的流程A的请求先进入冲突检测发现没冲突B的请求在A还没插入数据前也进行冲突检测发现也没冲突结果两条都插入成功。解决方案是给冲突检测加上数据库行级锁。MySQL的InnoDB引擎支持SELECT ... FOR UPDATE在事务里先锁住涉及时间范围内的预约记录再执行插入就能保证同一时间只有一个请求能通过检测。我用room_id作为锁的维度在事务里先查一次这条记录加上锁然后再执行冲突检测SQL和插入SQL。Connection conn dataSource.getConnection(); conn.setAutoCommit(false); // 锁住该会议室的记录防止并发插入 String lockSql SELECT id FROM reservation WHERE room_id ? AND status IN (0,1) FOR UPDATE; PreparedStatement ps conn.prepareStatement(lockSql); ps.setInt(1, roomId); ps.executeQuery(); // 执行冲突检测和插入 // ... conn.commit();这里有个容易弄错的点SELECT ... FOR UPDATE必须查实际存在的行才能起到锁的效果如果会议室表里没有这个room_id锁就不会生效。所以我在锁之前会先确保会议室表里存在对应记录或者直接用会议室表的主键来加锁——把锁粒度放在会议室级别代价是同一会议室不同时间段的预约也会互相阻塞但对这个系统来说完全够用。5.2 JSP改了不生效不是代码问题是缓存问题开发过程中几乎每个人都会遇到“我明明改了JSP刷新浏览器还是老样子”。我第一次碰到时一顿排查最后发现根本不是代码问题。JSP改了不生效通常有三个原因按概率排序第一是Tomcat的work目录缓存。JSP文件会被编译成Java源码再编译成ClassTomcat会缓存编译结果。如果IDE和Tomcat的部署同步出了问题work目录下还是旧的编译产物。清理方式很简单删除Tomcat的work/Catalina目录后重启或者在IDE里执行Clean项目。第二是浏览器缓存。浏览器会缓存JSP生成的HTML页面尤其是用response.setHeader(Cache-Control, no-cache)没设置好时。开发阶段我建议在JSP头部加缓存禁用声明或者直接用浏览器的无痕窗口调试。第三是部署目录不同步。Eclipse和IDEA的部署机制不一样IDEA是直接更新到Tomcat的webapps目录Eclipse有时候需要手动Publish。检查一下实际部署位置的JSP文件是不是最新的如果文件时间不对直接在IDE里重新发布。5.3 我遇到的几个Java/JVM相关报错项目部署阶段我碰到了两个典型的运行时报错。第一个是java.lang.NoClassDefFoundError: java/applet/Applet。JDK 8后续版本移除了Applet相关类但如果项目里有旧代码或依赖包间接引用了Applet就会出现这个错误。会议室预约系统本身没有一个地方用到Applet后来排查发现是某个老版本的工具包传递依赖了Applet类。解决办法有两个升级那个工具包或者检查并清理残留的旧依赖。对于这种内部系统最简单的处理就是找出引用链排除掉多余依赖。第二个是java.lang.OutOfMemoryError: Insufficient memory出现在系统运行了几天之后。这个通常是JVM堆内存配置不足导致的。Tomcat默认的堆内存上限偏保守我在catalina.sh里设置了JAVA_OPTS-Xms512m -Xmx1024m -XX:MaxPermGen128m这里要提醒一下JDK 8里MaxPermGen已经废弃应该换成-XX:MaxMetaspaceSize256m。我一开始照抄老博客配了MaxPermGen启动时虽然没报错但明显没生效后来查了版本差异才改过来。如果你的项目还用Lombok并且启动时报“You aren’t using a compiler supported by lombok”多半是JDK版本和Lombok版本不匹配升级Lombok版本就能解决。6. 部署Java环境与Tomcat时那些不起眼却致命的小事6.1 JAVA_HOME配置和路径里的坑把系统交付给运维同事部署时最先遇到的问题就是Java环境变量配置。很多开发机其实已经装了JDK但JAVA_HOME没配或者配错Tomcat启动脚本找不到java命令直接报错。这里我提供一个最能抗干扰的配置方式# 编辑 /etc/profile export JAVA_HOME/usr/local/jdk1.8.0_202 export PATH$JAVA_HOME/bin:$PATH export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar配置完成后用java -version验证版本。还有一个易错点Linux下JAVA_HOME千万不要在路径末尾加/bin应该指向JDK的根目录。Tomcat的catalina.sh会在$JAVA_HOME/bin基础上自己拼接java命令你如果配成/usr/local/jdk/bin最终生成的路径就变成了/usr/local/jdk/bin/bin/java必报错。6.2 MySQL连接串的时区和编码参数MySQL 5.7和JDBC驱动之间的时区问题也让我折腾了一阵。项目上线后发现预约时间在数据库里显示正常但JSP页面查出来总是差8个小时。原因是服务器时区是UTC而数据库连接串里没有指定时区。后来我在JDBC连接URL里加上了参数jdbc:mysql://localhost:3306/meeting_room?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiserverTimezoneAsia/Shanghai必须显式指定否则驱动会使用服务器默认时区通常是UTC导致日期时间偏移。这个问题在本地开发环境可能不出现因为本地电脑时区一般就是东八区但部署到云服务器上就原形毕露了。我还遇到过useSSLfalse不加上时MySQL驱动在部分版本下疯狂输出SSL警告日志虽然不影响功能但日志文件很快就被刷满。6.3 上线后的日常维护和备份会议室预约系统上线之后我给它配了一个简单的shell定时任务每天凌晨导出MySQL的数据到备份目录保留最近30天。内部系统数据量不大这种级别的备份已经足够。另外我建议运维至少每周重启一次Tomcat。JSP系统长期运行后PermGen/Metaspace会有轻微的内存膨胀定时重启可以提前规避一些内存问题。预约记录超过半年的可以定期归档避免核心表越来越大导致查询性能下降。最后再分享一个运维中的小技巧我把Tomcat的localhost.log和Druid连接池的监控页面都开放给了管理员只在内网访问。这个系统的使用量不算大但一旦出现连接池不够用或者慢SQL监控数据能直接指出问题在哪。做内部工具交付上去不是结束能让自己少被叫起来处理问题才是真正省心。本文还有配套的精品资源点击获取
RELATED

相关推荐

webpack 多入口应用代码拆分实战:用 splitChunks 同时拆出 commons 公共包与 vendor 第三方包

webpack 多入口应用代码拆分实战:用 splitChunks 同时拆出 commons 公共包与 vendor 第三方包

webpack 多入口应用代码拆分实战:用 splitChunks 同时拆出 commons 公共包与 vendor 第三方包 【免费下载链接】webpack A bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the ap…

📅 2026/9/8 22:39:21
Ruflo 分析蜂群策略实战:用 swarm_init、agent_spawn 与 CLI 构建分布式系统分析工作流

Ruflo 分析蜂群策略实战:用 swarm_init、agent_spawn 与 CLI 构建分布式系统分析工作流

Ruflo 分析蜂群策略实战:用 swarm_init、agent_spawn 与 CLI 构建分布式系统分析工作流 【免费下载链接】ruflo 🌊 The original agent meta-harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversatio…

📅 2026/9/8 22:39:21
Spring Boot LIS检验系统毕业设计:从业务流程到部署实战

Spring Boot LIS检验系统毕业设计:从业务流程到部署实战

简介:基于Java技术构建的LIS检验系统毕业设计项目,面向计算机相关专业学生及初级开发者,用于毕设参考、课程设计或实际项目练习。压缩包约30.41MB,内含完整项目源代码、数据库脚本以及环境配置说明,便于快速部署并完美…

📅 2026/9/8 22:39:21
MORE NEWS

更多资讯

📰

three.js KMZLoader 实战详解:在 Web 端加载并渲染 KML 压缩包中的 3D 模型

three.js KMZLoader 实战详解:在 Web 端加载并渲染 KML 压缩包中的 3D 模型 【免费下载链接】three.js JavaScript 3D Library. 项目地址: https://gitcode.com/GitHub_Trending/th/three.js KMZ 是由 Google Earth 生态衍生的一种压缩归档格式,常…

📰

8大网盘真实直链一次拿全:网盘直链下载全攻略,3步接入IDM

8大网盘真实直链一次拿全:网盘直链下载全攻略,3步接入IDM 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移…

📰

Pathway 实时数据处理监控实战:使用 OpenTelemetry Collector 与 Grafana Cloud 构建可观测性

Pathway 实时数据处理监控实战:使用 OpenTelemetry Collector 与 Grafana Cloud 构建可观测性 【免费下载链接】pathway Python ETL framework for stream processing, real-time analytics, LLM pipelines, and RAG. 项目地址: https://gitcode.com/GitHub_Trend…

📰

last30days v3.0.9「Self-Debug Release」技术解读:引擎拒绝门、跨平台顶级热评与多 Harness 部署

last30days v3.0.9「Self-Debug Release」技术解读:引擎拒绝门、跨平台顶级热评与多 Harness 部署 【免费下载链接】last30days-skill AI agent skill that researches any topic across Reddit, X, YouTube, HN, Polymarket, and the web - then synthesizes a gro…

📰

AutoGPT Platform 平台全景解析:架构、核心组件、模型目录与开源许可指南

AutoGPT Platform 平台全景解析:架构、核心组件、模型目录与开源许可指南 【免费下载链接】AutoGPT AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.…

📰

Ultralytics COCO-Pose 数据集解析:从 58,945 张图像的 17 关键点标注到 YOLO26-pose 实战训练

Ultralytics COCO-Pose 数据集解析:从 58,945 张图像的 17 关键点标注到 YOLO26-pose 实战训练 【免费下载链接】ultralytics Ultralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬