ARTICLE DETAIL

资讯详情

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

Jsp+Servlet医院预约挂号系统:部署拆解与避坑指南

Jsp+Servlet医院预约挂号系统:部署拆解与避坑指南 简介基于JspServlet技术栈开发的医院预约挂号系统采用传统JSPServlet模式不依赖复杂框架属于可直接导入运行的Java Web项目适合Java Web初学者、课程设计及毕业设计者使用。整个资源包共1451个文件以400个JavaScript、148个HTML、78个JSP视图、46个class编译文件、40个CSS样式及23个Java源文件为主体另含MySQL数据库SQL脚本、配置文件与部署说明文档压缩后约31.95MB。前端提供首页展示、用户注册、医生查询和公告信息浏览后端管理系统包含医院信息、科室信息、医生信息、会员信息以及系统公告的管理功能较为完整可直接在此基础上扩展预约挂号业务。资源附带完整部署教程、常用软件工具和数据库初始化脚本按源码、配置、文档分类组织并给出Eclipse/IDEA与Tomcat7/8/9等运行环境说明便于快速启动与二次开发。目前已有134人学习下载可作为实战参考模板。1. 先跑通再看代码JspServlet医院预约挂号系统到底给了什么上周一个朋友问我要一个能直接启动的毕设项目不想碰前后端分离那套只求在Eclipse里点一下Run就能看到登录页和后台管理。我直接把这套基于JspServlet医院预约挂号系统的完整资源丢给了他他导入后半小时内就把首页、注册、医生查询和后台管理员登录全部跑通了。这套系统能用传统JSPServlet模型把预约挂号完整业务串起来前端覆盖首页展示、用户注册、医生查询、公告信息后端覆盖医院、科室、医生、会员、公告全套管理功能。你如果正在做JavaWeb方向的项目或者需要一个内网环境下开箱即用的预约系统这份资源值得花一个下午复现一遍。下面按我实际拆解和部署的顺序展开讲。2. 项目拆解技术栈选型与预约流程的Servlet流转2.1 技术栈选型为什么JspServlet这种老组合还有价值这套系统的技术组合是html、css、JavaScript、jsp、servlet、bootstrap、jQuery、MySQL完全是JavaWeb课程的经典搭配。没有Spring、没有MyBatis连Maven都没有。你可能会问都2025年了为什么还要看这种老项目我的看法是它把Servlet的生命周期、JSP的请求转发、JDBC的数据库操作、Session的用户态管理全部裸露在你面前没有框架帮你屏蔽细节反而是最好的全链路教材。实际开发中Servlet是单例多线程模型容器负责管理它的生命周期。你在web.xml里配置的每个servlet-mapping决定了什么路径进入哪个处理逻辑。JSP页面其实也是Servlet在Tomcat的Jasper引擎下第一次被访问时会编译成.java再变成.class所以第一次打开页面慢是正常现象。前端的BootstrapjQuery不需要Node环境不用npm install所有静态资源放进webapp目录就能跑。这种做法在今天看起来有点原始但胜在零构建、零依赖非常适合内网部署和课设答辩。图标部分如果Bootstrap自带的Glyphicons不够用直接把饿了么的elment图标库下载后丢到webapp/static目录下就可以本地引用。2.2 前后台功能映射这份资源管理了什么拿到项目后先别急着启动花十分钟把前后台功能捋清楚后面部署完验证时心里有数。前台是给普通用户用的后台是给医院管理员用的两者共用同一个数据库用登录角色区分。下表是我梳理的功能与文件对应关系模块页面位置对应Servlet核心数据表首页展示index.jspIndexServlethospital_info、announcement用户注册/登录register.jsp、login.jspRegisterServlet、LoginServletmember医生查询doctor_list.jspDoctorListServletdoctor、department医生详情与预约doctor_detail.jsp、booking.jspDoctorDetailServlet、BookingServletdoctor、appointment个人信息与预约记录member_center.jspMemberServletmember、appointment后台医院信息管理admin/hospital_edit.jspAdminHospitalServlethospital_info后台科室管理admin/dept_list.jspAdminDeptServletdepartment后台医生管理admin/doctor_manage.jspAdminDoctorServletdoctor后台会员管理admin/member_list.jspAdminMemberServletmember后台公告管理admin/announcement_manage.jspAdminAnnouncementServletannouncement这里我有意把会员管理单独拿出来说因为member_center.jsp这种个人信息展示页面在很多类似系统里会被做成纯静态数据填充。但这套资源里个人信息页面是通过Servlet从member表带出用户基础信息同时关联查询appointment表展示当前用户的预约记录属于典型的jsp个人信息展示页面写法——request.setAttribute(member, member)然后JSP里用EL表达式或者JSTL渲染。2.3 预约挂号核心流程从查询到提交经过了几层转发预约是这套系统的核心业务。我按实际请求路径拆解了一遍整个流程在Servlet层面是三层流转用户从首页进入科室列表点击科室后跳转doctor_list.jsp?dept_id1DoctorListServlet接收dept_id参数调用DAO查询该科室下的在职医生列表其中要过滤status字段把离职医生排除掉。用户进入医生详情页doctor_detail.jsp?doctor_id2这个页面除了展示医生姓名、职称、简介最重要的是展示排班时段和剩余号源。剩余号源是通过total_num - booked_num算出来的这里有个重要的前置逻辑页面提交预约请求之前servlet端还要拿着doctor_id和当前日期去appointment表做一次重复预约校验防止一个用户一天内对同一医生重复提交。提交预约时BookingServlet接收doctor_id、user_id、appointment_date、time_slot开启事务后先检查号源余量执行update操作把doctor表的booked_num加1再insert一条appointment记录。这个先后顺序错了会出现号码被抢完但记录已经插入的问题。我在这个流程里最想强调的其实不是页面跳转而是那条更新号源和插入预约记录之间的数据一致性。这种老项目里事务写法往往很朴素常见做法是在DAO方法里拿到Connection后手动setAutoCommit(false)两个操作都成功才commit否则回滚。很少有系统真的用JTA或者编程式事务管理但理解这个手动事务的过程对后面看任何JavaWeb项目都有帮助。3. 数据库脚本落地导入、表关系与初始化数据改造3.1 数据库脚本的位置与导入方式这套资源的数据库脚本通常放在database或sql目录下文件名一般是hospital.sql也有的版本叫db_hospital.sql。脚本内容包含建库语句、建表语句和初始化数据三部分。我建议你在导入前先手动创建数据库并指定字符集避免用脚本里可能存在的CREATE DATABASE语句在不同MySQL版本下字符集表现不一致的问题。打开命令行终端按下面的顺序执行mysql -u root -p Enter password: ****** CREATE DATABASE hospital DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; USE hospital; SOURCE /your/path/hospital.sql;如果你用的是Navicat直接新建数据库命名为hospital字符集选utf8然后右键运行SQL文件选择hospital.sql也行。我习惯用命令行是因为能看到每一步报错尤其是某条SQL语法不兼容的时候Navicat往往只弹一个运行失败没有具体行号。导入完成后执行SHOW TABLES;如果能看到member、department、doctor、appointment、hospital_info、announcement这几张表说明脚本执行成功。3.2 核心表结构医生表、预约表怎么设计我把这套项目里最关键的两张表结构还原出来你对照手头的脚本核对一下字段是否一致。医生表是预约系统的核心资源表预约表是业务流转表。下面是一份典型的建表语句实际脚本可能会多一两个排序字段或状态字段但主体结构基本如此CREATE TABLE doctor ( doctor_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 医生ID, dept_id INT NOT NULL COMMENT 所属科室ID关联department表, doctor_name VARCHAR(50) NOT NULL COMMENT 医生姓名, title VARCHAR(30) DEFAULT NULL COMMENT 职称主任医师/副主任医师/主治医师, photo VARCHAR(255) DEFAULT NULL COMMENT 照片路径, schedule VARCHAR(100) DEFAULT NULL COMMENT 排班时段描述如周一至周五上午, total_num INT DEFAULT 20 COMMENT 号源总数, booked_num INT DEFAULT 0 COMMENT 已预约数, status TINYINT DEFAULT 1 COMMENT 1在职 0离职, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8;预约表重点在于time_slot字段它用来区分上午、下午还是晚间号段。真实场景里应该用独立槽位表来管理号段但这个系统的体量决定了它用一个varchar字段就能满足展示需求。预约记录的状态位很关键0代表待就诊1代表已完成2代表已取消3代表已过期。后台管理员在会员管理页面看到的预约状态就是根据这个字段渲染的。表之间通过外键逻辑关联而不是数据库物理外键这一点我特意确认过基本是每张表的id被另一张表的XXX_id字段引用查询时用join完成。比如查用户预约记录时常见的查询是这样SELECT d.doctor_name, d.title, dp.dept_name, a.appointment_date, a.time_slot, a.status FROM appointment a JOIN doctor d ON a.doctor_id d.doctor_id JOIN department dp ON d.dept_id dp.dept_id WHERE a.user_id 1;为什么不在appointment表里直接存医生姓名因为如果某个医生改了职称或者被调整到别的科室历史预约记录如果要跟着变直接存名字就得每一条都update而存doctor_id的话只需要关联查询数据永远是医生表的当前值。这个设计思路虽然简单但在做统计报表时能省很多事。3.3 初始化数据怎么改把示例医院换成自己的脚本里预设的医院信息、科室、医生都是示例数据你部署后第一件要做的事是把这些假数据改成自己想要的内容。注意不是让你手动逐条改页面而是直接更新数据库表。改医院名称和简介走hospital_info表它通常只有一行记录UPDATE hospital_info SET hospital_name 你想展示的医院名称, hospital_intro 简介内容 WHERE id 1;科室和医生的初始化数据也是同样处理。比较容易被忽略的是排班信息doctor表里的schedule字段脚本给的可能是周一至周五上午你拿到手后如果要做演示建议改成周一上午8:00-12:00、周三下午14:00-17:30这种明确时间段这样预约展示页面上用户能一眼看出可选时段演示效果比笼统的上午强得多。还要检查一件事后台管理员的账号是写在数据库还是硬编码在Servlet里。大多数这种项目会在member表里插入一条管理员记录通过某个字段标记角色比如is_admin1或者单独一张admin表。你导入脚本后先查一下member表看有没有类似admin的用户名。如果找不到去看admin登录相关的servlet代码一般在AdminLoginServlet.java里能看到它查询的是哪张表哪个字段。这一步不花时间但对后面首次进入后台管理至关重要。4. 本地部署完整操作Eclipse/IDEA配置与首启验证4.1 把源码导入Eclipse/IDEA两种IDE的导入细节这套项目的源码是标准目录结构src存放Java源码webapp或WebContent存放JSP和静态资源。导入方式取决于你用的是Eclipse还是IDEA我分别说。Eclipse导入路径File - Import - General - Existing Projects into Workspace - Select root directory选中项目根目录后Eclipse会自动识别为Web项目。如果导入后没有显示成Dynamic Web Project项目图标上没有小地球那是因为项目缺少.project和.classpath配置文件此时可以右键项目选择Properties - Project Facets勾选Dynamic Web Module。这一步不能跳过否则后续部署时Tomcat无法识别项目结构。IDEA导入路径不太一样File - New - Module from Existing Sources选中项目根目录然后一路Next。IDEA不会自动识别Web目录需要你手动在Project Structure里给webapp目录标记为Web资源目录。具体操作是进入Project Structure - Facets - Web - Web Resource Directories把webapp或WebContent路径添加进去。如果你跳过这一步启动Tomcat后访问项目路径会直接404页面文件根本没被包进去。4.2 配置Tomcat与JDK版本对不上会出现什么资源说明里写了JDK 1.8、Tomcat 7/8/9均可。这句话本身没错但不同Tomcat版本对应的Servlet规范不一样直接影响web.xml解析方式。Tomcat 7对应Servlet 3.0Tomcat 8对应Servlet 3.1Tomcat 9对应Servlet 4.0。如果项目的web.xml头部写的还是Servlet 2.3那种老式DTD声明在Tomcat 9上启动时会直接报错说你web.xml解析失败。我建议直接使用Tomcat 8.5加JDK 1.8的组合兼容性最稳。web.xml头用下面这个版本最通用?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 display-nameHospitalAppointment/display-name /web-app配完web.xml后在Eclipse的Servers面板里New - Server - Apache - Tomcat 8.5Runtime Environment指定到你本机的JDK 1.8安装路径然后把项目Add到Server上右键Start启动。IDEA里配置是Run - Edit Configurations - - Tomcat Server - LocalApplication Server选择本机Tomcat路径Deployment选项卡里点把项目的war exploded形式添加进去Application Context建议设置成/hospital这样访问路径就是http://localhost:8080/hospital。4.3 数据库连接配置与首次启动验证数据库连接配置是这套系统唯一需要你改代码的地方。配置位置通常在src目录下的jdbc.properties文件或者在DBUtil.java里直接硬编码。我在资源里看到的是properties文件方式内容类似这样jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hospital?useUnicodetruecharacterEncodingUTF-8 jdbc.usernameroot jdbc.password123456MySQL 5.7时代的驱动类名是com.mysql.jdbc.Driver如果你本机装的是MySQL 8这里要改成com.mysql.cj.jdbc.Driver并且url里还要额外加上serverTimezoneAsia/Shanghai否则驱动会报时区异常。这个细节是这类老项目最常见的启动拦路虎。启动Tomcat后第一次验证不要急着点预约流程。先在浏览器输入http://localhost:8080/hospital/能看到首页且医院名称和公告是从数据库查出来的说明DBUtil连接配置正确。然后注册一个新用户走一遍注册流程提交后去member表SELECT * FROM member WHERE username你注册的名字;能查到记录就证明insert也通。最后用管理员账号登录后台进入医生管理页面随便编辑一个医生的排班信息保存再刷新前台医生列表看数据是否同步变化。这一套走完整个系统才算真正在你的机器上跑通。5. 部署避坑记录五个最常见的报错与解决顺序5.1 Tomcat 9启动报JasperException或web.xml解析失败现象Tomcat启动到一半直接失败日志里出现org.apache.jasper.JasperException: Unable to compile class for JSP或者org.xml.sax.SAXParseException有时甚至报ClassNotFoundException: javax.servlet.jsp.tagext.TagLibraryInfo。原因项目web.xml里用的是Servlet 2.3或2.4的旧版DTD头Tomcat 9的Jasper引擎已经不再兼容这类声明格式。另一个可能原因是项目WEB-INF/lib目录下放了旧版servlet-api.jar或jsp-api.jar和Tomcat自带的类冲突。解决把web.xml头部替换成我上面给3.1版本的标准写法。然后检查WEB-INF/lib目录把servlet-api.jar、jsp-api.jar、el-api.jar这些Tomcat本身就有的包删掉。这是这类老项目的通病。5.2 页面和数据库全是中文乱码现象页面标题、公告内容、科室名称全部变成问号或者从页面提交到数据库的中文变成乱码。数据回显页面正常但存进MySQL是乱码。这类问题十次有八次栽在连接串上。原因JSP页面本身有contentTypetext/html; charsetUTF-8声明但Servlet接收请求时没有设置请求编码Post提交的中文到Servlet里已经变成ISO-8859-1。同时jdbc连接url缺了useUnicodetruecharacterEncodingUTF-8最后数据库连接时字符集不对三层叠加。解决在项目的web.xml里配置一个CharacterEncodingFilter是最省事的方式把请求和响应编码统一为UTF-8。另外确认一下jdbc连接串已经带上characterEncoding参数。MySQL端也检查一下SHOW VARIABLES LIKE character_set_database;如果还是latin1把database的字符集改掉。5.3 查询语句报ONLY_FULL_GROUP_BY错误现象在统计科室医生数量或预约次数时执行带GROUP BY的查询直接抛出Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column错误。原因MySQL 5.7及以后版本默认启用了ONLY_FULL_GROUP_BY模式要求SELECT列表里的每个字段要么出现在GROUP BY子句中要么被聚合函数包裹。老项目里的SQL往往没按这个规范写。解决优先修改SQL语句把不需要分组的列移出SELECT。比如统计每个科室的医生数你的SELECT应该只保留dept_id, COUNT(*)科室名称通过join或者子查询再取。如果你只是本地演示不想改代码也可以临时关闭这个模式SET GLOBAL sql_mode(SELECT REPLACE(sql_mode,ONLY_FULL_GROUP_BY,));但这不是长久之计提交作业或上线前必须把SQL改对。5.4 改完JSP后浏览器永远显示旧页面现象你修改了index.jsp或某个JSP页面的代码重启了Tomcat但刷新浏览器看到的还是修改前的内容。有人会根据经验强制CtrlF5发现偶尔能好但下次改动又出现。这个现象几乎每个做JSP开发的人都遇到过。原因Tomcat对JSP有缓存机制server.xml里配置了antiResourceLocking或者开发模式未开启reloadable属性时修改后的JSP不会立即重新编译。更深层的原因是浏览器缓存了JSP响应内容尤其是JSP输出中没有设置Cache-Control响应头时浏览器会直接使用本地缓存。此外还有一个玄学层面JSP文件修改时间戳在某些系统上没有正确更新导致Tomcat认为文件没变这也是为什么有人改了代码却像是催眠一样不生效。解决先排除浏览器缓存开发阶段按CtrlF5强制刷新。然后检查Tomcat的conf/server.xml里Context配置开发环境建议设置reloadabletrue这样JSP文件一旦变动容器就自动重新加载。如果改了代码还是显示旧页面去Tomcat的work目录把Catalina目录下对应当前项目的缓存文件全部删除再重启Tomcat。JSP第一次加载会重新编译。这是一个非常典型的场景正好对应热词里那个jsp页面加载完后刷新一次的诉求——因此我每次改完页面都会主动让浏览器刷新一次而不是依赖自动刷新。5.5 图片不显示、图标消失、头像位置错乱现象医生照片裂开、后台管理页面图标全部显示成小方框、会员头像偏移到页面边缘。你去检查源码发现img标签的src写的是一长串绝对路径D:/workspace/hospital/upload/xxx.jpg或者引用的图标是外部CDN链接。原因老项目里相当常见图片路径写成了本地绝对磁盘路径或者直接/upload/xxx.jpg这种没有加项目上下文前缀的写法。一旦项目部署路径变化所有图片全部失效。图标用的是外部CDN一旦网络不通就打不开。另一个被忽略的是jsp图片对坐标定位问题页面里如果用了CSS相对定位来控制图片显示区域需要给父容器设置position:relative否则图片以页面为基准定位看起来就是位置错乱。原因写具体点绝对路径的问题在于每台电脑的项目所在目录都不一样你换一台机器部署路径就失效了。正确的方式是通过${pageContext.request.contextPath}拼接虚拟路径比如src${pageContext.request.contextPath}/upload/doctor1.jpg。而JSP图片对坐标定位实际上说的是CSS控制图片的显示位置和裁剪比如用background-position来控制背景图显示某一块区域。这个技巧在做医生照片的缩略图展示时用得着。解决全局搜索图片路径写法把以file:///或者C:/开头的路径全部替换为${pageContext.request.contextPath}开头然后检查WEB-INF下是否有上传文件夹映射配置。图标方面把外部CDN引用全部换成饿了么elment图标库的本地化文件放在webapp/static/font目录下然后在JSP里用${pageContext.request.contextPath}/static/font/iconfont.css引入。父容器加position:relative图片定位精度就稳定了。6. 进阶改造给公告模块写JSON接口并验证改动6.1 一个返回JSON的Servlet前后端解耦的第一步这套系统的公告页面后端渲染方式还是传统JSP也就是Servlet把公告List塞到request域里再转发到announcement.jsp。这种方式在数据要同时给首页、后台管理页、公告详情页等多个页面复用时会写重复代码。我把公告查询改造成独立JSON接口一次写好前端统一用jQuery的$.getJSON消费。改造过程很简单新建一个MsgApiServlet只负责查公告表并输出JSON字符串WebServlet(/msgApi) public class MsgApiServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType(application/json;charsetUTF-8); request.setCharacterEncoding(UTF-8); ListAnnouncement list new AnnouncementDao().queryAll(); StringBuilder json new StringBuilder({\status\:0,\data\:[); for (int i 0; i list.size(); i) { Announcement a list.get(i); if (i 0) json.append(,); json.append({\id\:).append(a.getId()) .append(,\title\:\).append(a.getTitle()) .append(\,\createTime\:\).append(a.getCreateTime()) .append(\}); } json.append(]}); response.getWriter().write(json.toString()); } }这段代码里WebServlet(/msgApi)是Servlet 3.0的注解方式项目用了Tomcat8/9可以直接注解不用再去web.xml加映射。AnnouncementDao().queryAll()返回公告列表实际项目中要替换成你手头资源里对应的DAO类名和方法名。输出JSON时我没引入fastjson或Jackson用StringBuilder手动拼接是这套老项目里最常见的低成本方案避免多引两个jar包导致依赖冲突。前端在首页的JSP里用jQuery消费这个接口这是标准的jQuery ajax写法的$.getJSON(msgApi, function (resp) { if (resp.status ! 0) return; $.each(resp.data, function (index, item) { $(#noticeList).append( lia href# item.title /aspan item.createTime /span/li ); }); });注意这里msgApi是相对路径如果首页在/hospital/index.jsp下它会自动请求/hospital/msgApi。如果项目部署目录不是根路径建议改成绝对带上下文路径比如${pageContext.request.contextPath}/msgApi避免部署在带项目名的环境下路径404。这就是我在上一章踩坑记录里提到的路径问题在ajax场景下的又一次体现。改造完成后页面里的公告列表不再依赖后端转发全部由这段JS渲染。好处是其他页面后台管理界面、手机端页面复用公告数据时直接调用这个接口即可前后端的边界一次划清。6.2 验证方法变了不在页面上看效果先用curl和浏览器控制台接口写完不要直接刷新页面看效果先用命令行验证接口本身是否正确返回JSON这样能区分问题出在后端接口还是前端渲染。curl http://localhost:8080/hospital/msgApi返回内容类似这样就是接口通{status:0,data:[{id:1,title:春节门诊安排通知,createTime:2025-01-20 09:30:00}]}如果curl返回404查看Tomcat日志里有没有抛异常重点看Servlet注解是否生效。然后打开浏览器按F12进Console观察$.getJSON请求的Network状态码。状态200但页面没渲染多半是JSON拼接时字段名大小写错了后端的使用createTime前端却取createtime。顺手验证一下公告标题里如果包含引号或者换行符手动拼JSON会不会把JSON字符串撑破这是手动拼接JSON最大的坑。稳妥的做法是把标题里的双引号替换成中文引号或者用\uXXXX转义。6.3 一个收尾的检查习惯每次改动都不从页面开始验证项目跑通、接口改造完成之后我建议你保留多一个习惯每次拿到这种JspServlet老项目不管改动多小都强制走一遍先跑数据库脚本、再启动Tomcat、最后看日志的流程。数据库脚本对应表的字段和初始化数据是否和你预期一致Tomcat启动日志里有几条WARN分别来自哪个模块后台操作报错时去logs/catalina.out里翻Exception。我吃过一次亏在一个医生排班页面花了两个小时排查发现是SQL里字段别名大小写不一致导致ResultSet取不到值而那个错误信息被Tomcat吞掉了只返回了500页面。从那以后我每次拿到这类项目都先把日志级别调到DEBUG确认能跑通再谈改代码能省一大半排查时间希望帮到你。本文还有配套的精品资源点击获取
返回列表