从Servlet到JDBC:宠物医院管理系统JavaWeb实战解析
简介这是一套面向计算机相关专业毕业设计、课程设计与期末大作业的 JavaWeb 宠物医院管理系统完整项目。代码经导师指导并获评审 99 分功能覆盖宠物档案、医生排班、用户登录等典型业务模块前端以 JSP 页面呈现后端由 Servlet 与 Java 类支撑并配有可直接导入的 SQL 数据库脚本可帮助初学者快速跑通前后端联调流程适合动手实践与二次开发。压缩包共含 113 个文件主要包括 40 个 JSP 页面、39 个 Java 源码、11 个 XML 配置、SQL 脚本及少量 CSS/JS 与图片素材整体仅 750KB轻量易部署目录结构按实体类、控制层、页面文件划分便于对照学习。目前已有 102 人学习下载。从内容预览看PetServlet、userServlet、DoctorServlet 等关键代码一应俱全配合数据库脚本即可本地还原完整系统无论用于毕设答辩还是项目实战演练都有较高参考价值。1. 宠物医院管理系统为什么从 Servlet 而不是 Spring Boot 起步白天刚帮人挑完一份毕设代码压缩包解压后只有几个.java文件和一份.sql脚本拿起来就能跑。这是一套 javaweb 的宠物医院管理系统源码PetServlet、userServlet、DoctorServlet 三个控制器加上数据库脚本就是整套系统的骨架。它没有 Spring Boot 的自动配置也没有 MyBatis 的动态 SQL所有请求转发、JDBC 查询都摆在明面上反而更适合用来回答“什么请求走到了哪个方法”这类答辩问题。系统覆盖宠物档案管理、用户登录、医生信息维护、预约接诊这几条核心业务线代码量不大数据库脚本一次导入即可完成初始化。适合正在做 JavaWeb 课程设计、又不想靠 Maven 拉几百兆依赖的计算机相关专业学生有几年经验的开发也可以拿它当“拆解入门项目”的样本看老式 Servlet 工程如何组织业务。拿到这个源码第一件事不是点运行而是先看懂它的请求入口和数据流。标题里说的“高分毕设、代码完整确保可以运行”指的是编译级完整但每次换电脑都可能遇到 JDK、MySQL、Tomcat 版本不一致的问题下面章节按可直接复现的方式写。2. 系统代码拆解PetServlet、userServlet、DoctorServlet 的请求流转与数据层实现2.1 从文件结构先读懂请求入口一个 Servlet 3.0 规范下的 JavaWeb 工程不一定需要 web.xml 配置所有路径。源码里 PetServlet.java、userServlet.java、DoctorServlet.java 三个控制器文件名首字母小写后就是 URL 前缀也就是/pet、/user、/doctor。这种命名方式不是巧合它把宠物医院系统的三类角色直接映射成了路由前缀宠物档案、平台用户、医生信息。看下面这张映射表Servlet映射路径核心业务PetServlet/pet宠物档案新增、修改、查询、删除userServlet/user用户注册、登录、注销与权限判断DoctorServlet/doctor医生信息维护、排班与接诊状态这张表能直接在答辩 PPT 里用。导师问“路由怎么设计的”答案是通过 URL 前缀 action 参数实现资源定位例如/pet?actionsave。换成 RESTful 风格会变成POST /pet和DELETE /pet/{id}但课设项目用 action 参数更直观模型层代码几乎不用动。2.2 PetServlet 的 doGet/doPost 调度模型看 Servlet 源码时大部分时间不用逐行读完重点看 doGet 和 doPost 怎么处理 action 就行。常见的实现是把逻辑集中到同一个方法这份源码里 PetServlet 的结构基本是下面这种写法WebServlet(/pet) public class PetServlet extends HttpServlet { Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action request.getParameter(action); if (list.equals(action)) { // 调用 PetDao 查询宠物列表转发到 pet_list.jsp } else if (add.equals(action)) { // 跳转到新增宠物表单页 pet_add.jsp } else if (save.equals(action)) { // 获取表单参数组装 Pet 对象调用 PetDao.insert() } else if (delete.equals(action)) { // 获取 petId调用 PetDao.deleteById(petId) } // 默认 action 也按 list 处理避免空指针 } Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 必须放在最前面否则中文表单参数会乱码 request.setCharacterEncoding(UTF-8); doGet(request, response); } }这个写法的优势是逻辑集中。doPost里设置编码后转发给doGetGET 和 POST 共用同一套分支。需要加新功能时只要在 action 判断里加一个else if不需要新建类也没有 Spring MVC 的 HandlerMapping 概念。劣处是方法会变长当 action 超过六七个就要考虑抽 service 层了。从实用角度给三个参数级建议。第一action 名不要带特殊字符特别是和?会直接截断参数。第二petId这类数值参数必须在进入 DAO 前做一次Integer.parseInt异常处理否则用户手输/pet?idabc会直接 500。第三重定向用response.sendRedirect(pet?actionlist)转发用request.getRequestDispatcher(pet_list.jsp).forward(request, response)两者混用会导致浏览器地址栏刷新时重复提交表单。2.3 userServlet 与 DoctorServlet 的角色区分userServlet 和 DoctorServlet 的区别本质是权限模型的区别。用户端登录逻辑是userServlet 接收用户名密码查 user 表后把 userId 和 role 放进 session再根据 role 跳转到用户首页或管理员页。医生端则不做登录通常只做信息展示与排班查询所以 DoctorServlet 一般只提供 list 和 detail 两个 action。这种拆分让改动边界很清晰以后要加“医生登录”就在 DoctorServlet 中加 login action不影响用户端逻辑。数据层沿用 JDBC用最传统的 DBUtil 类注册驱动和获取连接。源码里不一定把 DBUtil 单独列出来但只要是纯 Servlet JDBC 工程必然有类似下面的工具类public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/pet_hospital ?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }代码逻辑说明Class.forName(com.mysql.cj.jdbc.Driver)是注册 MySQL 驱动。8.x 版本驱动类名必须带cj5.x 版本是com.mysql.jdbc.Driver用错会直接 ClassNotFound。getConnection()每次调用都新建物理连接在课设并发量下没问题生产环境要换成连接池见第 5 章。密码不建议硬编码在类里但课设源码常见做法确实如此。如果想让代码更好看抽一个jdbc.properties并用静态代码块读取。再往下就是 DAO 层。PetDao 查列表通常是SELECT * FROM pet WHERE del_flag0新增用PreparedStatement预编译setString和setInt按查询参数顺序赋值。这里容易踩的坑是 PreparedStatement 参数从 1 开始计数写 0 会抛IndexOutOfBoundsException。3. 数据库脚本设计宠物、医生、用户表结构与导入实践3.1 核心表结构的闭环设计高分毕设数据库脚本讲究的是“够用且闭合”。宠物医院管理系统中user 保存登录用户和角色pet 表通过owner_id指向 userdoctor 表独立维护appointment 表再把用户、宠物、医生关联起来。常见表结构如下表名职责关键字段pet宠物档案id, pet_name, pet_type, owner_id, age, sex, del_flaguser登录用户id, username, password, real_name, phone, roledoctor医生信息id, doctor_name, department, title, work_timeappointment预约接诊记录id, pet_id, doctor_id, user_id, appoint_time, status这套设计能回答两个答辩必问问题。为什么 pet 不直接存 owner_name因为关系数据库讲究范式owner_name 会随 user 表变化而重复存储改一次用户名要同步多行。为什么 appointment 要单独建表因为 pet 和 doctor 是多对多关系拆出中间表才能保存“谁在什么时间约了哪位医生”的完整上下文。字段里保留del_flag是因为宠物档案通常需要留存不做物理删除只做逻辑删除。3.2 用命令行导入数据库脚本拿到脚本后不需要手工建表直接导入即可。打开命令行或终端进入脚本所在目录执行mysql -u root -p --default-character-setutf8mb4 pet_hospital pet_hospital.sql如果还没有pet_hospital数据库先登录 MySQL 执行CREATE DATABASE pet_hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;命令参数说明--default-character-setutf8mb4控制客户端读取脚本文件时使用的编码。很多人直接双击导入遇到中文乱码就是因为脚本文件是 UTF-8而连接默认用了 latin1。pet_hospital.sql如果包含CREATE DATABASE目标库名可以省略但建议保持与 DBUtil 里 URL 配置的库名一致。导入后检查表SHOW TABLES;和SHOW CREATE TABLE pet\G不要只看图形化工具的左侧列表。如果数据库是 MySQL 8.0老项目驱动的认证插件可能会报Unable to load authentication plugin caching_sha2_password。那是因为 8.0 默认认证插件是 caching_sha2_password而旧驱动不认识。临时解决方案是把账号改回 mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456; FLUSH PRIVILEGES;这条命令只影响 root 账号的认证方式不动表结构和数据适合让老 JDBC 驱动立刻跑通。3.3 初始化测试账号高分毕设的脚本通常会带测试数据例如管理员 admin / admin123、医生 d01 / 123456。这些初始数据放在 user 表的 role 字段里。如果脚本没有初始化手动插入时注意代码里是否用了 MD5 工具类INSERT INTO user (username, password, real_name, phone, role) VALUES (admin, MD5(admin123), 系统管理员, 13800000000, admin);说明MD5 固定加密在安全上不够强但课设级别能跑通。答辩时可以说“密码经过 MD5 摘要后入库后续可替换为 BCrypt 或 SHA-256 加盐”这句话比老实存明文要高一个档次。插入完成后重启 Tomcat重新登录避免 session 里还留着旧的登录状态。4. IDEA Tomcat MySQL 环境下的部署与配置4.1 环境版本组合这套源码没有 Maven 坐标依赖 jar 都在WEB-INF/lib下所以版本匹配比依赖管理更重要。推荐组合如下组件推荐版本说明JDK1.8大部分课设源码编译目标为 Java 8更高版本可能触发 Illegal reflective access 警告Tomcat8.5 / 9.0支持 Servlet 3.1 / 4.0注解扫描正常MySQL5.7 / 8.08.0 需要改 JDBC URL 时区参数IDEA2020.3 以上社区版即可运行 Web 项目Tomcat 不建议用 10.x因为它默认走 Jakarta EEServlet 包名从javax.servlet变成了jakarta.servlet老代码的 import 全部失效。JDK 也建议锁 1.8避免在jsp-api.jar和servlet-api.jar上踩版本坑。4.2 导入源码并配置 Artifact打开 IDEA 后按下面步骤走选择File - New - Project from Existing Sources定位到解压后的项目根目录项目类型选 Eclipse 或普通 Java 模块均可。右键src目录Mark Directory as - Sources Root右键web目录或WebContentMark Directory as - Web Resources Directory。打开Project Structure - Artifacts点 - Web Application: Exploded把项目模块添加进去。确认WEB-INF/lib下有mysql-connector-java驱动包没有就从本地 Maven 仓库拷贝一份jar 文件放进 lib 后需要重新构建 Artifact。配置 TomcatRun - Edit Configurations - - Tomcat Server - Local指定 Tomcat 目录Deployment 里添加第 3 步的 ArtifactApplication context 建议改成/。启动前确认 DBUtil 或 jdbc.properties 里的数据库账号密码与本地一致启动 Tomcat浏览器访问http://localhost:8080/pet?actionlist。整个流程不联网也可以完成因为项目不依赖 Maven 中央仓库。如果启动后页面报The requested resource [/pet] is not available优先检查 Artifact 名称和访问路径。IDEA 默认会把 Application context 设置成模块名比如/宠物系统_war_exploded/pet改成/后访问/pet就正常了。4.3 启动时的常见报错与定位Tomcat 日志是最直接的诊断入口catalina.out里出现关键字时按下面表格对号入座报错关键字可能原因解决方式ClassNotFoundException: com.mysql.jdbc.Driver驱动 jar 没在 WEB-INF/lib拷贝 mysql-connector-java-8.0.x.jar 并重建 ArtifactCannot create PoolableConnectionFactory数据库服务没启动检查 MySQL 服务和 3306 端口Access denied for user rootlocalhost账号密码错误核对 DBUtil 里的 USER 和 PASSWORDPort 8080 was already in use端口被其他进程占用改 server.xml 的 Connector 端口或结束占用进程The superclass javax.servlet.http.HttpServlet was not found缺少 Servlet API项目里添加 Tomcat 依赖库java.sql.SQLException: The server time zone valueMySQL 8.0 时区问题URL 加 serverTimezoneAsia/Shanghai其中时区错误最常见。MySQL 8.0 默认时区是 UTC老代码 URL 没带serverTimezone连接建立就会抛异常。改成jdbc:mysql://localhost:3306/pet_hospital?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai重新编译后即可。这里有个技巧如果项目里同时有多个链接位置先用全局搜索找到jdbc:mysql://然后把所有连接串统一替换不要只改 DBUtil 一个类。5. 上线前检查与三个常见改造点5.1 中文乱码的全链路处理很多同学把项目跑起来后发现页面显示正常但插入宠物名字变成问号。原因不是代码写错而是请求、数据库、响应三层里有某层编码丢失。除了 DBUtil 里加characterEncodingUTF-8还要在web.xml配置一个编码过滤器filter filter-nameencodingFilter/filter-name filter-classcom.pet.filter.EncodingFilter/filter-class /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mappingFilter 对应的 Java 类如下public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }提示只写req.setCharacterEncoding不够响应也要设置ContentType。同时数据库连接要带useUnicodetrue三者缺一都可能出现中文乱码。5.2 把 JDBC 连接替换成 HikariCP如果答辩被问“系统有什么不足”最容易被攻击的点就是每次请求都新建数据库连接。改造方式很简单不用换框架只要引入 HikariCP jar 和 slf4j-api.jar然后把 DBUtil 改成连接池public class DBUtil { private static final HikariDataSource DATA_SOURCE; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/pet_hospital ?serverTimezoneAsia/Shanghai); config.setUsername(root); config.setPassword(123456); config.setDriverClassName(com.mysql.cj.jdbc.Driver); config.setMaximumPoolSize(10); config.setMinimumIdle(2); DATA_SOURCE new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return DATA_SOURCE.getConnection(); } }这段代码替换原 DBUtil 后所有 DAO 里的getConnection()调用不用改。MaximumPoolSize和MinimumIdle是连接池核心参数课设环境 10 和 2 就够不需要像生产环境那样做压测调优。改造完成后启动日志里会出现 HikariPool 的初始化记录答辩时可以拿这个作为性能优化点。5.3 给宠物列表加一个分页参数没有分页的列表页在数据量超过几十条后就会很卡。加分页不需要额外插件MySQL 的LIMIT就能解决。PetDao 里加一个方法public ListPet findPage(int page, int pageSize) throws SQLException { String sql SELECT * FROM pet WHERE del_flag 0 ORDER BY id DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, (page - 1) * pageSize); ps.setInt(2, pageSize); // 执行查询并封装 ListPet } }PetServlet 的 list 分支中读取分页参数int page 1; int pageSize 10; String pageParam request.getParameter(page); if (pageParam ! null) { page Integer.parseInt(pageParam); } request.setAttribute(page, page); request.setAttribute(pets, petDao.findPage(page, pageSize));分页参数说明LIMIT ?, ?第一个?是偏移量第二个是每页条数。第 1 页从 0 开始第 2 页从 10 开始所以(page - 1) * pageSize正好对应当前页起点。接口保持/pet?actionlistpage2的格式页面里只加“上一页”“下一页”两个链接就够了。这段逻辑复制到现有list分支里列表页浏览体验会明显提升。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →