ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JSP+Servlet医院预约挂号系统源码:解压到跑通的完整指南

JSP+Servlet医院预约挂号系统源码:解压到跑通的完整指南 简介这是一份基于JSPServlet技术栈开发的医院预约挂号系统完整源码适合毕业设计选题、Java Web课程实训与初学MVC模式的开发者参考。资源以项目工程形式组织共1431个文件涵盖JSP动态页面、Java业务处理类、Servlet控制器、CSS/JS前端资源、SQL数据库脚本以及项目配置文件等压缩包整体约30.98MB目录结构清晰便于按模块定位和学习。系统讲解覆盖用户登录与权限控制、医生空闲时段查询、预约请求处理、数据库设计、并发控制、安全防护与日志记录等关键环节学习热度较高目前已有208人学习。通过研读该源码读者可以理解前后端交互流程、MVC分层思想及预约挂号业务的实际落地方式也适合在此基础上扩展功能或直接作为课程设计与毕业设计的项目蓝本。1. JSPServlet 医院预约挂号系统源码解压之后怎么跑起来距离交毕设还有三周你从同学U盘里拷来一份“基于JspServlet医院预约挂号系统源码.zip”右键zip解压之后里面是几十个JSP页面、一堆Servlet类和一个看着眼熟的DAO包接下来就不知道该双击哪个文件了。如果是这个场景这份资源对你还挺对路它走的是经典 JSPServletJDBCMySQL 组合按 MVC 模式把患者注册登录、医生排班查询、在线预约挂号、后台维护拆成了完整 JavaWeb 工程能直接导入 Eclipse/IDEA 跑起来演示。适合用它当毕业设计底子、做 Java 课程设计案例源码参考、或者对 Servlet 请求处理链路还不熟的新手。下面我把运行环境、预约核心流程、数据库并发坑和导入时最容易翻车的地方过一遍。2. 项目结构与运行环境MVC 骨架和 Tomcat/JDK/MySQL 版本怎么搭2.1 先看懂三层谁负责显示谁负责逻辑拿到这份源码第一步不是急着找某一段代码而是先把工程放进能运行的环境里。老式 JavaWeb 项目最常见的组织方式就是三层JSP 负责把数据渲染成页面Servlet 负责接收请求、调用业务、控制跳转DAO 配合 JavaBean 负责和数据库打交道。对应到预约系统里注册页面提交表单到注册 ServletServlet 调 UserDao 里的 insert 方法成功后转发回登录页这就是一个完整的 MVC 请求链路。我一般建议先按这个思路把目录看一遍而不是双击某个页面。因为直接打开 JSP 只能看到 HTML 静态部分看不到数据从哪来真正决定页面展示内容的是 Servlet 里 request.setAttribute 塞进去的对象。明白了这条链后面改功能才有方向。对答辩也友好老师问“你的请求是怎么走的”你能清晰说出一条线比背代码强得多。这套老工程适合做毕设还有一个现实原因它没有 Spring 那套复杂配置没有 Maven 依赖地狱核心逻辑都在你能直接改的地方。对于那些只是想“把功能跑起来”的人这种黑匣子反而少所有代码你都能看得到。2.2 解压、建库、配连接让工程先“站起来”解压这一步Windows 下右键“全部解压缩”就行终端党也可以用命令。我建议先建一个干净的目录再把压缩包放进去解压避免路径里有中文空格导致某些老工具识别出问题。# 建目录并解压 mkdir -p ~/hospital cd ~/hospital unzip 基于JspServlet医院预约挂号系统源码.zip -d project # 确认关键目录存在 ls project解压后正常应该能看到 src 源码目录、WebContent 或 web 目录、数据库脚本和项目配置文件。如果只有一堆 .java 和 .jsp没有工程描述文件那就是导工程而不是开文件的问题下面会处理。环境版本上学校机房和老教程里最常出现的是 JDK 1.8 Tomcat 8.5 MySQL 5.7 这套组合。我的建议版本宁保守勿新潮尤其原工程直接用 JDK 11 以上编译时经常冒出 javax.servlet 找不到之类的兼容问题纯属给自己上难度。组件推荐版本说明JDK1.8老 JavaWeb 工程最稳答辩演示也不会出幺蛾子Tomcat8.5支持 Servlet 3.1和 JDK 1.8 组合最常见MySQL5.7教程和驱动兼容性最好MySQL 8 需要换驱动IDEEclipse EE 或 IDEA Ultimate需要原生支持 Web 工程和 Tomcat驱动包mysql-connector-java 5.1.49和 MySQL 5.7 搭配最省心数据库连接信息一般集中在配置文件里常见文件名是 db.properties、jdbc.properties 或者 DBUtil.java 顶部的常量。找到后改成你自己的账号密码jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hospital?useUnicodetruecharacterEncodingUTF-8useSSLfalse jdbc.usernameroot jdbc.password你的密码这里三个参数值得记一下characterEncodingUTF-8 解决中文乱码useSSLfalse 避免连不上时打印一堆握手日志useUnicodetrue 是老驱动常见搭配。如果你手头只有 MySQL 的 zip 免安装包记得下载后先跑 mysqld --initialize-insecure 初始化 data 目录再改 my.ini 里的端口和字符集而不是直接双击 mysqld 就以为装好了。接下来建库。数据库脚本一般在 sql 或 db 目录里文件名叫 hospital.sql 之类的概率很大。用命令行导入最省事CREATE DATABASE IF NOT EXISTS hospital DEFAULT CHARACTER SET utf8mb4; USE hospital; SOURCE /你的路径/hospital.sql;注意脚本里的表如果有外键关联导入顺序不能乱报错就看哪个表先建实在不行把外键语句后置。导入成功后第 2 章的核心骨架就算立住了。2.3 一个请求怎么从 JSP 走到 Servlet 再到数据库工程跑起来后你需要确认项目里用的是 web.xml 映射还是注解式 Servlet这决定了改路径时去哪里找。servlet servlet-nameLoginServlet/servlet-name servlet-classcom.hospital.web.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/user/login/url-pattern /servlet-mapping// 常见做法直接用注解写在类上和 web.xml 二选一别同时配 WebServlet(/user/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest request, HttpServletResponse response) { // 业务代码在这里 } }页面表单里的 action 写的路径必须和 url-pattern 对应上。比如 login.jsp 里写 actionuser/login那 Servlet 映射就必须是 /user/login少一个斜杠都 404。这一条是新手最容易忽略的坑后面第 5 章我会专门展开。3. 核心业务实现登录、排班查询与防重复预约3.1 登录与角色权限从 Session 到拦截器预约系统至少要区分三种身份患者、医生、管理员。最轻量做法是登录成功后把用户对象和角色塞进 Session再写一个过滤器统一拦截受保护页面。public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String path request.getRequestURI(); // 放行登录页、登录接口和静态资源避免循环跳转 if (path.endsWith(login.jsp) || path.contains(/user/login) || path.endsWith(.css) || path.endsWith(.js) || path.endsWith(.jpg) || path.endsWith(.png)) { chain.doFilter(req, resp); return; } // 没登录的一律踢回登录页 if (request.getSession().getAttribute(currentUser) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }过滤器在 web.xml 里配置到 /* 即可。这里的放行清单要写全否则会出现登录页能打开但 CSS 全丢的尴尬情况。角色区分的常见做法是登录时把 role 字段也放进 Session管理员入口页面单独判断 role 是否为 2不是就提示无权限。这种方案虽然原始但胜在直观答辩时也容易讲清楚。3.2 医生排班查询按科室和日期组合查数据患者预约前先看医生排班这是个典型的多条件查询场景。前端页面通常是一个科室下拉框加一个日期选择框提交后传给 Servlet再由 Servlet 调 DAO 拼 SQL。SELECT d.id, d.name, d.title, d.department_id, s.work_date, s.total_count, s.remain_count FROM doctor d JOIN schedule s ON d.id s.doctor_id WHERE d.department_id ? AND s.work_date ? ORDER BY s.work_date, d.id;Java 侧用 PreparedStatement 而不是字符串拼接这个选择很重要既是防 SQL 注入的第一道防线也避免日期值拼进去时格式对不上。String sql SELECT * FROM doctor WHERE department_id ?; try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, Integer.parseInt(deptId)); // 参数从 String 转 int try (ResultSet rs ps.executeQuery()) { while (rs.next()) { // 封装成 Doctor 对象放进 List } } }这里有个细节页面传过来的科室 id 是 StringDAO 里必须先做类型转换否则 SQL 参数类型不匹配会直接报错。如果日期是空字符串还要在前面判空不然 SQL 会变成 work_date 而不是查询全部。处理方式通常是在 Service 层判断日期为空就只按科室查日期不为空再追加时间条件。3.3 预约挂号的核心事务与防重复预约是整个系统里最容易出问题的操作因为一次成功的预约涉及两条写操作插入预约记录、扣减号源余量。只要有一条失败另一条成功数据就不一致。常见做法是把这两步放在同一个事务里任何一个环节失败就整体回滚。public boolean createAppointment(int patientId, int doctorId, String workDate) { String checkSql SELECT id FROM appointment WHERE patient_id? AND doctor_id? AND work_date?; String updateSql UPDATE schedule SET remain_count remain_count - 1 WHERE doctor_id? AND work_date? AND remain_count 0; String insertSql INSERT INTO appointment(patient_id, doctor_id, work_date, create_time, status) VALUES (?,?,?,NOW(),1); try (Connection conn dataSource.getConnection()) { conn.setAutoCommit(false); // 1. 先查同一患者同一天同一医生是否已经预约过 int duplicateId 0; try (PreparedStatement ps conn.prepareStatement(checkSql)) { ps.setInt(1, patientId); ps.setInt(2, doctorId); ps.setString(3, workDate); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { duplicateId rs.getInt(1); } } } if (duplicateId 0) { conn.rollback(); return false; } // 2. 条件更新号源余量受影响行数为 0 说明没号了 try (PreparedStatement ps conn.prepareStatement(updateSql)) { ps.setInt(1, doctorId); ps.setString(2, workDate); int rows ps.executeUpdate(); if (rows 0) { conn.rollback(); return false; } } // 3. 插入预约记录 try (PreparedStatement ps conn.prepareStatement(insertSql)) { ps.setInt(1, patientId); ps.setInt(2, doctorId); ps.setString(3, workDate); ps.executeUpdate(); } conn.commit(); return true; } catch (SQLException e) { try (Connection connDest dataSource.getConnection()) { connDest.rollback(); } catch (SQLException ignored) { } return false; } }这段代码里有三个关键点。条件更新里加上 remain_count 0相当于把号源判断和扣减合并成一条原子 SQL避免“先查再减”造成的超卖。数据库层面再给 appoint ment 表加唯一约束同一天同一患者同一医生只能有一条预约记录这是兜底防线。最后事务千万别漏掉 setAutoCommit(false)否则两条 SQL 各自独立提交扣号成功但插入失败号就白丢了。4. 数据库设计与并发控制四张表、事务和唯一约束4.1 核心表怎么建科室、医生、患者、预约这套系统通常围绕四张表展开科室表、医生表、患者表、预约记录表。建表时把字符集统一成 utf8mb4能避免一部分中文乱码的问题。CREATE TABLE department ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, intro VARCHAR(255) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE doctor ( id INT PRIMARY KEY AUTO_INCREMENT, department_id INT NOT NULL, name VARCHAR(30) NOT NULL, title VARCHAR(30), phone VARCHAR(20), CONSTRAINT fk_doctor_dept FOREIGN KEY (department_id) REFERENCES department(id) ) ENGINEInnoDB; CREATE TABLE patient ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(30), id_card VARCHAR(18), phone VARCHAR(20) ) ENGINEInnoDB; CREATE TABLE appointment ( id INT PRIMARY KEY AUTO_INCREMENT, patient_id INT NOT NULL, doctor_id INT NOT NULL, work_date DATE NOT NULL, status TINYINT DEFAULT 1, create_time DATETIME, UNIQUE KEY uk_patient_doctor_date (patient_id, doctor_id, work_date), CONSTRAINT fk_appt_patient FOREIGN KEY (patient_id) REFERENCES patient(id), CONSTRAINT fk_appt_doctor FOREIGN KEY (doctor_id) REFERENCES doctor(id) ) ENGINEInnoDB;如果导入时因为外键顺序报错可以在脚本头部加 SET FOREIGN_KEY_CHECKS0导入完成后再设回 1。预约记录里的唯一索引 uk_patient_doctor_date 是防止重复挂号的最后一道防线代码里的判断只能降低概率不能绝对保证并发下不出问题。4.2 时间字段统一别用 String 存日期我在不少毕设源码里见过 work_date 用 varchar 存储当时图省事JSP 页面显示确实没问题但后面要做“查今天有哪些号”“按日期范围统计”就傻眼了字符串比较日期会得到完全错误的顺序。这种骗过自己的写法属于典型的“先爽后坑”。正确做法是用 DATE 类型存日期DATETIME 存创建时间Java 侧对应 java.sql.Date 或 LocalDate。如果你用的是 MySQL 的 zip 安装方式my.ini 里最好把时区也定下来[mysqld] character-set-serverutf8mb4 default-time-zone08:00连接串里也要带 serverTimezone否则新版驱动会报时区错误。修改后重启 MySQL 服务再把原来的 varchar 列改成 DATE老数据如果格式不统一会报错所以这种改动要放在造数据阶段之前做。4.3 高并发下别只靠“先查再插”医院的热门科室放号那几秒请求量很容易集中爆发。很多初版代码的逻辑是查号源余量、余量大于 0 就插入记录。这在单用户测试时完全正常人一多就翻车两个请求同时查到余量是 1又同时去插入最后可能出现一条预约记录配上了负号源。解决思路是把判断放到更新语句里也就是第 3.3 节那套写法UPDATE ... SET remain_count remain_count - 1 WHERE remain_count 0。还有一种常用做法是行级锁START TRANSACTION; SELECT id, remain_count FROM schedule WHERE doctor_id ? AND work_date ? FOR UPDATE; -- 应用层判断 remain_count 0 UPDATE schedule SET remain_count remain_count - 1 WHERE id ?; INSERT INTO appointment (patient_id, doctor_id, work_date, create_time, status) VALUES (?, ?, ?, NOW(), 1); COMMIT;SELECT FOR UPDATE 会把这一行锁住别的会话只能等它提交。逻辑上好理解代价是并发稍微上去就可能出现锁等待而且必须在 finally 里确保释放。对比起来条件更新更适合这个场景一条 SQL 就把判断和扣减做完了代码量也更少。5. 避坑与排查导入运行源码时的高频问题1. Eclipse 导入后项目名上全是红叉现象源码文件都在但所有类都报错HttpServlet 找不到。原因没有把 Tomcat 运行环境挂到项目的 Build Path 上servlet-api.jar 不在编译路径里。解决右键项目 → Build Path → Add Library → Server Runtime → 选已配置的 Tomcat。IDEA 里类似问题是 Project Structure 里没有给模块添加 Web Facet补上后重新部署。2. JSP 和数据库同时中文乱码现象页面标题乱码、患者姓名存进去再读出来变成问号。原因三个环节的编码不一致。JSP 页面编码、Servlet 里 request 解析编码、数据库表的字符集任何一环掉队都会出乱码。解决统一 UTF-8。JSP 文件头设置 pageEncodingServlet 的 doPost 开头加 request.setCharacterEncoding(UTF-8)连接串保持 characterEncodingUTF-8数据库表用 utf8mb4。三处都对齐后乱码基本消失。3. 点登录一直 404或用同一个路径有时通有时不通现象表单提交后浏览器地址栏路径是对的但控制台报 404。原因web.xml 和注解 WebServlet 同时配置了同一个路径或表单 action 少写了开头斜杠。项目里如果既有 web.xml 又有注解两套机制指向同一路径时行为不确定。解决统一用一种方式用注解就把 web.xml 里对应 servlet 定义删掉用 web.xml 就检查 url-pattern 是否和表单 action 完全一致注意前导斜杠。4. MySQL 连接失败Communications link failure 或 Public Key Retrieval is not allowed现象Tomcat 启动后访问页面控制台抛 SQLException连接串看起来没问题。原因MySQL 8 的认证插件和旧版驱动不兼容或者缺少 serverTimezone 参数。这类问题不是源码 bug是版本搭配问题。解决MySQL 8 就换驱动把 com.mysql.jdbc.Driver 改成 com.mysql.cj.jdbc.Driver连接串加上 serverTimezoneAsia/Shanghai想省事就回退到 MySQL 5.7 加老驱动这在毕设环境下完全够用。5. 静态资源和图片加载不出来JSP 页面总感觉要刷新一次才会好现象页面能打开但 CSS 全丢图片裂开有时刷新一下才正常。原因资源路径写成了绝对路径比如 /css/style.css换项目名或部署路径后自然 404。JSP 里用相对路径又容易跟着当前 URL 改变基址表现为“第一次不太对刷新就好了”。解决统一用 EL 表达式拼上下文路径%-- 原来写死 /hospital/css/style.css 一换项目名就失效 --% link relstylesheet href${pageContext.request.contextPath}/css/style.css img src${pageContext.request.contextPath}/images/doctor.jpg alt医生图片这五个坑是我陆续在几套老工程里真实碰到的每一个都能让“代码没问题”变成“跑不起来”。遇到先别怀疑源码损坏按上面顺序排查环境绝大多数都能救回来。6. 把源码改出自己的毕设验证清单与三个加分项拿到源码并跑通后先用一条主线把功能走一遍确认没有隐藏问题再动手改代码。验证项操作路径期望结果注册打开注册页填写手机号和身份证号能入库并跳转登录登录用刚注册的账号登录进入患者首页Session 有用户信息排班查询选择科室和日期点击查询只显示该科室当天的可预约医生预约选择一个医生点击预约号源余量减 1预约记录新增重复预约再次预约同一医生同一天提示已预约或操作失败后台维护管理员登录维护医生排班能增删改排班信息主流程没问题后我建议做三个小改造花不了多少时间但能明显加分。第一密码不要明文存患者表里的 password 字段改成 MD5 加盐后的密文答辩时被问安全性至少能接上话。第二手机号和身份证号做双重校验前端用正则先过滤后端 Servlet 里再校验一遍。第三在预约成功和失败的分支里加一行日志用 java.util.logging 就行记录患者 id、医生 id 和操作时间这会让数据流程有迹可循也显得工程化。从那以后我每次导入这类老 JavaWeb 工程都会强制走一遍环境核对清单JDK 版本、Tomcat 版本、连接串编码、数据库字符集、静态资源路径五条查完再动手改代码。这套习惯帮我省下过不少无用功希望帮到你。本文还有配套的精品资源点击获取
返回列表