简介:面向Java Web初学者的宠物管理系统源码包,基于JSP+Servlet+MySQL实现,涵盖宠物分类查询、添加、编辑、删除等完整增删改查功能,适合用来理解传统Java Web分层结构与基础CRUD流程。包内共41个文件,核心包括7个Java源码、4个JSP页面、7个class编译文件及4个依赖Jar包,同时附带SQL初始化脚本、配置说明文档、功能演示视频和多张运行截图,便于对照学习与本地部署。压缩包整体6.37MB,内容组织清楚,源码、数据库、文档和视频分区放置,即使零基础也能按文档步骤完成环境配置并跑通项目。已有1580人学习下载,尤其适合课程设计、毕业设计入门参考或自学练手。
1. 宠物管理系统源码:一个能跑通全流程的 Servlet + JSP 练手项目
做 Java Web 入门的人最头疼的事就是:视频看了一堆,真要自己搭一个能增删改查的项目,半天连环境都起不来。这套 java+jsp+servlet+mysql 的宠物管理系统源码,走的是最经典的 JSP + Servlet + JDBC 三层结构,没有 Spring、没有 MyBatis,所有请求路径、参数获取、数据库连接都是最原始的手写方式。反而因为"原始",你一眼就能看明白一次 HTTP 请求是怎么从浏览器走到 Servlet,再走到 MySQL 的。
这套系统功能很纯粹:按分类查宠物、添加宠物、编辑宠物、删除宠物,四个操作凑齐了就是一个完整的 CRUD 闭环。原版只有查询和添加,没有编辑和删除,后来在源码基础上做了一次升级补全,现在交付的版本是完整的增删改查模块。它特别适合两类人:一类是刚学完 Servlet 和 JSP 语法、想找个完整项目对照的在校生,另一类是准备 Java 面试、想快速过一遍传统 Web 开发流程的求职者。接下来我把项目结构、核心代码、部署步骤和踩坑记录一条条拆给你看。
2. 项目结构与数据表设计:先读 SQL,再做增删改查
2.1 拿到压缩包先看什么
压缩包解开之后,目录结构是这样的:
petmgr # 项目根目录,可直接导入 Eclipse / IDEA │ db_petmgr.sql # 数据库初始化脚本,MySQL 直接执行 │ 详细配置.docx # 环境配置和部署说明,含 JDK / Tomcat 版本说明 │ 宠物管理系统.pdf # 系统功能说明文档 │ 006...mp4 # 演示视频,JSP 页面操作流程我拿到任何一套源码,第一步永远是先开 SQL 脚本,而不是先开代码。因为数据表能直接反映这个系统的业务边界——有几张表、字段怎么设计的、主键怎么生成的,看完这些你再回去读代码,思路会顺很多。这套系统的 SQL 脚本就一张宠物信息表,典型的单表 CRUD 结构,没有外键、没有关联查询,对新手的友好度拉满。
2.2 宠物表字段设计与类型选择
打开db_petmgr.sql,核心建表语句大致是这样:
CREATE TABLE pet ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT '宠物名称', category VARCHAR(20) NOT NULL COMMENT '分类:猫/狗/兔等', age INT DEFAULT 0 COMMENT '年龄(月)', gender VARCHAR(4) DEFAULT '公' COMMENT '性别', health VARCHAR(100) DEFAULT '健康' COMMENT '健康状况描述', detail TEXT COMMENT '详细描述' ) ENGINE=InnoDB DEFAULT CHARSET=utf8;字段设计逻辑很直白:id用自增主键,避免手动生成主键的麻烦;category单独拎出来做成 VARCHAR 而不是用数字字典,是因为这套系统没有设计分类表,直接用字符串存分类名可以让查询 SQL 少一次 JOIN,对入门项目来说这样做反而更好理解。age用 INT 存月份而不是年份,是因为宠物年龄普遍不满一岁,用月份表达更精确。detail用 TEXT 而不是 VARCHAR,是给描述字段留足空间,防止用户输入超长文本时插入失败。
2.3 数据库连接配置放在哪
JDBC 连接参数写在一个专门的工具类里,常见做法是放在src/com/util/DBUtil.java:
package com.util; import java.sql.*; public class DBUtil { private static final String DRIVER = "com.mysql.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/db_petmgr?useUnicode=true&characterEncoding=utf-8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }注意这里characterEncoding=utf-8必须加,否则 JSP 页面提交的中文宠物名存进 MySQL 会直接变问号。DRIVER用的是老驱动com.mysql.jdbc.Driver,这个类在 MySQL 5.x 及以下没问题;如果你本机装的是 MySQL 8.0+,要把驱动改成com.mysql.cj.jdbc.Driver,并且 URL 里加上serverTimezone=Asia/Shanghai,否则驱动加载会直接报错。这是整套源码里最容易被环境坑到的地方。
3. 核心代码走读:从列表查询到完整增删改查
3.1 按分类查询:Servlet 接收参数的过程
这套系统没用任何框架,请求入口全部是 Servlet。宠物列表页的查询逻辑是这样的流程:浏览器请求PetListServlet→ Servlet 里获取category参数 → 调用 DAO 层查数据库 → 把结果放进 request → forward 到pet_list.jsp。
package com.servlet; import com.dao.PetDao; import com.entity.Pet; import javax.servlet.ServletException; import javax.servlet.http.*; import java.io.IOException; import java.util.List; public class PetListServlet extends HttpServlet { private PetDao petDao = new PetDao(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); String category = request.getParameter("category"); List<Pet> petList; if (category != null && !"全部".equals(category)) { petList = petDao.findByCategory(category); request.setAttribute("currentCategory", category); } else { petList = petDao.findAll(); request.setAttribute("currentCategory", "全部"); } request.setAttribute("petList", petList); request.getRequestDispatcher("pet_list.jsp").forward(request, response); } }代码逻辑分三段看。第一段request.setCharacterEncoding("utf-8")是处理 POST 请求中文乱码的,放在 doGet 里不冲突,因为 GET 请求的参数在 URL 上,主要靠 Tomcat 的 URIEncoding 配置保证。第二段是核心分支:从 request 里拿category,如果为空或者等于"全部"就查全表,否则走条件查询。第三段是把查询结果塞进 request 作用域,forward 到 JSP 页面渲染。request.getRequestDispatcher(...).forward()是服务端跳转,地址栏不变,JSP 里可以直接用 EL 表达式取到petList这个属性。
这里有个细节值得留意:category参数在前端 JSP 里是通过超链接传的,比如pet_list.jsp?category=猫,中文参数会被浏览器自动 URL 编码,Tomcat 8.0+ 默认 UTF-8 解码没问题,但你的 Tomcat 如果版本很老(7.0 以下),需要手动改server.xml里的URIEncoding="UTF-8"。
3.2 DAO 层封装:PreparedStatement 防注入
业务逻辑没写在 Servlet 里,而是抽了一个PetDao类,这是传统分层里很标准的做法。数据访问层用PreparedStatement而不是Statement,参数用?占位符绑定:
package com.dao; import com.entity.Pet; import com.util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class PetDao { public List<Pet> findByCategory(String category) { String sql = "SELECT * FROM pet WHERE category = ?"; List<Pet> list = new ArrayList<>(); try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, category); ResultSet rs = ps.executeQuery(); while (rs.next()) { Pet pet = new Pet(); pet.setId(rs.getInt("id")); pet.setName(rs.getString("name")); pet.setCategory(rs.getString("category")); pet.setAge(rs.getInt("age")); pet.setGender(rs.getString("gender")); pet.setHealth(rs.getString("health")); pet.setDetail(rs.getString("detail")); list.add(pet); } } catch (SQLException e) { e.printStackTrace(); } return list; } }try-with-resources语法是关键,Connection 和 PreparedStatement 用完后自动关闭,不会把数据库连接资源耗尽。查出来的每一行记录手动 set 到 Pet 实体里,这个过程叫 ORM 映射,不过这是手写的。如果你对 MyBatis 里resultType="com.entity.Pet"的自动映射感到困惑,看看这段手动 set 的代码就明白了——框架做的其实就是你在这里做的事。
3.3 重点看升级部分:编辑和删除这两个模块
前面说了,原版没有编辑和删除功能,升级之后补全了。编辑功能是两个 Servlet 配合的:PetEditServlet负责回显数据,PetUpdateServlet负责接收表单提交更新数据库。删除是PetDeleteServlet,按 id 删一条记录。
// PetEditServlet - 编辑前先查出原数据回显到表单 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); int id = Integer.parseInt(request.getParameter("id")); Pet pet = new PetDao().findById(id); request.setAttribute("pet", pet); request.getRequestDispatcher("pet_edit.jsp").forward(request, response); } // PetUpdateServlet - 接收表单参数,执行 UPDATE protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); int id = Integer.parseInt(request.getParameter("id")); String name = request.getParameter("name"); String category = request.getParameter("category"); int age = Integer.parseInt(request.getParameter("age")); String gender = request.getParameter("gender"); String health = request.getParameter("health"); String detail = request.getParameter("detail"); Pet pet = new Pet(); pet.setId(id); pet.setName(name); pet.setCategory(category); pet.setAge(age); pet.setGender(gender); pet.setHealth(health); pet.setDetail(detail); new PetDao().update(pet); response.sendRedirect("PetListServlet"); }删改这类写操作,完成之后的重定向一定要用sendRedirect而不是forward。原因很朴素:forward是服务端跳转,浏览器地址栏还停在PetUpdateServlet,用户这时候按 F5 刷新,浏览器会再次提交上一次的 POST 请求,于是数据库里多了一条重复的宠物记录。sendRedirect会返回 302 让浏览器主动跳到PetListServlet,页面刷新变成普通的 GET 请求,再刷新一万次也只是重新查一次列表,安全得多。这个细节不知道坑了多少新手,项目里两次请求的跳转方式都用对了,你写代码的时候也要保持这个习惯。
3.4 JSP 页面的表单和列表渲染
JSP 页面用 JSTL 标签 + EL 表达式渲染数据,列表页的核心片段长这样:
<c:forEach items="${petList}" var="pet"> <tr> <td>${pet.id}</td> <td>${pet.name}</td> <td>${pet.category}</td> <td>${pet.age}</td> <td> <a href="PetEditServlet?id=${pet.id}">编辑</a> <a href="PetDeleteServlet?id=${pet.id}" onclick="return confirm('确定删除${pet.name}吗?')">删除</a> </td> </tr> </c:forEach>items="${petList}"对应 Servlet 里 set 进去的petList属性,var="pet"是循环变量。编辑和删除的链接拼了id参数,所以对应 Servlet 里用request.getParameter("id")能拿到值。onclick="return confirm(...)"是前端的最后一道确认屏障,防止误点删除按钮直接把数据清掉。
升级补全的增删改查都集中在这套模式里:Servlet 收参数、调 DAO、跳 JSP,结构非常扁平,每一环都看得见摸得着。
4. 本地部署与运行:三步把这个项目跑起来
4.1 环境版本怎么搭配
部署这套系统,环境搭错了会浪费大量时间,先说结论,你照着配就行:
| 组件 | 版本建议 |
|---|---|
| JDK | 8 或 11 |
| Tomcat | 8.5 或 9.0 |
| MySQL | 5.7(或 8.0,需改驱动) |
| IDE | Eclipse 或 IDEA,选 Java EE / Enterprise 模式 |
JDK 8 + Tomcat 8.5 + MySQL 5.7 是三件套里最稳的组合,和源码里配置的驱动、连接参数完全对齐。MySQL 8.0 也能跑,但要把DBUtil.java里的驱动类名换成com.mysql.cj.jdbc.Driver,同时 URL 上补时区参数,这个我在第 2 章已经提示过了。
4.2 建库导数据:SQL 脚本的执行方式
打开 MySQL 命令行或 Navicat,先建库再执行脚本:
mysql -u root -p # 输入密码后进入 mysql 命令行 CREATE DATABASE db_petmgr DEFAULT CHARACTER SET utf8; USE db_petmgr; SOURCE /你的目录/db_petmgr.sql;CREATE DATABASE ... DEFAULT CHARACTER SET utf8这里必须显式指定字符集,否则会继承 MySQL 默认的 latin1,中文字段值全变乱码。SOURCE是 MySQL 命令行执行外部 SQL 文件的标准方式,路径不能有中文,否则某些 Windows 版本的 MySQL 会报错。执行完之后用SHOW TABLES;确认 pet 表存在,再跑一条SELECT * FROM pet;看有没有初始数据。
4.3 导入项目到 IDEA 并配置 Tomcat
IDEA 里导入这套源码的操作是固定的:
File -> New -> Project from Existing Sources 选择 petmgr 目录 选 Import project from external model -> Maven 或 Eclipse (这套源码不带 Maven,选 Eclipse 导入方式即可)导入之后是一个普通的 Java Web 项目,需要在 Project Structure 里做两件事:把web目录(或WebContent)标记为 Web 资源目录,把lib下的 JDBC 驱动 jar 加进依赖。然后配置 Tomcat:
Run -> Edit Configurations -> 左上角 + -> Tomcat Server -> Local Application server 选你本地 Tomcat 的安装目录 Deployment 选项卡 -> + -> Artifact -> petmgr:war exploded Application context 填 /petmgrwar exploded是开发模式,IDEA 直接把编译后的 class 和 JSP 页面映射到 Tomcat,改代码热部署不用重新打 war 包。Application context填/petmgr,访问路径就是http://localhost:8080/petmgr/。
4.4 验证跑通:访问入口和功能检查
启动 Tomcat 之后,浏览器输入:
http://localhost:8080/petmgr/PetListServlet能看到宠物列表页说明部署成功。随后按四个顺序逐个点一遍:列表查询、添加宠物、编辑宠物、删除宠物。添加一条中文名字的宠物,再编辑改一下年龄,刷新列表确认数据变了,删除再刷新确认数据没了。四步走完,CRUD 闭环就通了。
5. 新手常见问题与排查:五个必踩的坑
5.1 404 错误:项目访问不到
现象:Tomcat 启动成功,浏览器访问http://localhost:8080/petmgr/却报 404。
原因:八成是 deployment 里的 Application context 和实际访问路径不一致,或者 Artifact 没部署上去。
解决:打开 Run Configuration 里的 Deployment 选项卡,确认 Artifact 已添加,Application context是/petmgr。改完重启 Tomcat,再用http://localhost:8080/petmgr/访问。注意 IDEA 里改配置后有时需要重新 Run,不能只点 Reload。
5.2 启动时报 ClassNotFoundException: com.mysql.jdbc.Driver
现象:Tomcat 启动时控制台直接抛ClassNotFoundException,或者运行到Class.forName(DRIVER)这段报错。
原因:MySQL 驱动 jar 没有打包进 WEB-INF/lib,或者驱动类名不对。MySQL 8.0 下用com.mysql.jdbc.Driver会报这个错,老驱动类名在 8.0 已移除。
解决:先确认驱动 jar 放到了WEB-INF/lib目录下并在 Project Structure 里加入了依赖。再确认 MySQL 版本,8.0 就把DBUtil.java里的驱动类名改成com.mysql.cj.jdbc.Driver。这两个问题单独出现都好查,最烦的是两个一起出现,排查顺序先看 jar 再看类名。
5.3 中文乱码:存进数据库全是问号
现象:JSP 页面表单提交加菲猫,数据库里存的是????,页面回显示乱码。
原因:三层乱码叠加——JSP 页面编码、Servlet 接收编码、JDBC 连接编码,任何一层不是 UTF-8 都会出问题。
解决:逐层排查。JSP 页面顶部确认有pageEncoding="UTF-8";Servlet 里request.setCharacterEncoding("utf-8")必须在getParameter之前执行;JDBC URL 必须有characterEncoding=utf-8。三层都改对了还乱码,检查 MySQL 表字段字符集是不是 UTF-8,SHOW CREATE TABLE pet;看字段的 charset,如果是 latin1 就把表删了重新执行 SQL 脚本。
5.4 删除功能点了没反应
现象:列表页点"删除"链接,页面没跳转,或者跳了但数据还在。
原因:最常见的是onclick="return confirm(...)"返回了 false。另外看数据库是不是自增主键,如果 id 类型不对,Integer.parseInt会抛 NumberFormatException。
解决:浏览器按 F12 开控制台,Network 面板刷新再点删除,看请求是没发出还是返回了错误。return confirm()返回 false 会阻断链接跳转,这是正常的保护机制。请求发出去且返回 500,看 Tomcat 控制台日志,十有八九是findById返回 null,或者id参数没传过来。单独测一个PetDeleteServlet?id=1,看这个 id 能不能删掉。
5.5 数据库连接超时:Too many connections
现象:连续点了几次查询或添加,报Too many connections异常。
原因:代码里有连接没关闭的地方。DBUtil.java里如果只关闭了 ResultSet,没有关闭 Connection,Tomcat 运行一段时间后连接池被打满。
解决:优先检查代码里是不是用了try-with-resources。这套源码里 DAO 层都是标准写法,但如果你自己往里面加方法,务必确保getConnection()创建的连接在 finally 里关闭。临时补救可以先重启 Tomcat 释放连接,治本还是靠规范关闭连接。
6. 从练手到实战:给这个项目打三个升级补丁
项目跑通之后,别急着换下一个教程,在它基础上加点东西收获更大。我通常建议新手按顺序做三件事。
第一件:把列表查询加上分页。现在的PetListServlet是一次性查全表,数据量超过 100 条页面就明显变卡。做法是给PetDao加一个findByPage(int pageNum, int pageSize)方法,SQL 用LIMIT ? OFFSET ?两个参数控制,前端 JSP 加"上一页/下一页"两个链接。这一步做完你就理解了 MySQL 分页和 MyBatis PageHelper 的原理。
第二件:把删除改成软删除。现在PetDeleteServlet是物理删除DELETE FROM pet WHERE id=?,数据没了就没了。改成软删除需要加一个status字段,默认 1,删除时执行UPDATE pet SET status=0 WHERE id=?,查询条件统一过滤status=1。这个改动对数据安全意识帮助极大,面试被问"生产环境怎么删数据"你就有话说了。
第三件:用 Session 做登录拦截。这步改动稍大,但很有价值。新建一个LoginServlet接收用户名密码,校验通过就session.setAttribute("user", username);在PetListServlet、PetEditServlet等每个 Servlet 的 doGet 最前面加一段判断:if (session.getAttribute("user") == null) { response.sendRedirect("login.jsp"); return; }。不用新建过滤器,先手动写一遍你会发现 Servlet 的重复代码有多烦人,这时候再回头学 Filter 过滤器,理解会深刻很多。
这三件事做完,这套宠物管理系统就从一个入门 CRUD 变成了带分页、软删除、登录校验的完整 Web 应用。我之前带过一个刚转行的朋友,他拿到源码第一周就只做这三件事,做完之后自己重新写了一遍,写出来就是能放在简历上的项目。
最后说一个我的个人习惯:每次部署完这套源码,我都会强行走一遍这个流程——关掉 Tomcat、清理数据库表数据、重新执行 SQL 脚本、重启 Tomcat、再走一遍增删改查。这套流程看起来机械,但能把环境依赖和代码逻辑彻底分开,排查问题的时候能帮你快速锁定到底是代码写错了还是环境改挂了。希望这份拆解能帮到你。
本文还有配套的精品资源,点击获取