JSP+Servlet+MySQL宠物管理系统源码解析:从环境搭建到避坑实践

发布时间:2026/10/7 5:52:55
JSP+Servlet+MySQL宠物管理系统源码解析:从环境搭建到避坑实践 简介这套基于jspservletmysql的简易宠物管理系统源码覆盖宠物分类查询、添加、编辑与删除完整增删改查流程操作逻辑直观、代码结构简明适合Java Web入门者作为首个实战项目也便于课设参考与功能扩展。资源包共41个文件压缩后仅6.37MB包含7个java源文件及对应class文件、4个jsp页面、4个jar依赖包同时提供数据库脚本db_petmgr.sql、详细配置说明docx、项目文档pdf、界面预览png及操作演示mp4从环境搭建到最终运行均有配套材料便于对照学习和排错。已有1580人学习浏览足见其对新手具备较高参考价值。通过阅读源码与观看演示视频可快速理解Servlet、JSP与MySQL三者的协作方式掌握基础增删改查模块的开发思路为独立完成同类管理系统打下扎实基础。1. 简答宠物管理系统源码JSPServletMySQL 这套老组合在 2025 年还能做什么想想一个场景你在贴吧或 GitHub 上搜“简答宠物管理系统源码”找到的可能是一个十年前传下来的压缩包里面是 JSPServletMySQL 的代码。打开一看全是 jsp 页面里嵌着 Java 代码Servlet 被当作控制器用数据库是个 MySQL 的 sql 文件——但这就是一个能跑起来、能看到完整增删改查、能作为毕设或二次开发起点的项目。它最大的价值不是技术有多新而是这套东西在你自己的电脑上能跑通你能看明白每一行代码在干什么。本文按“环境搭好→数据建好→代码读懂→坑避开”的顺序把这份源码从头到尾拆清楚适合正准备做毕设的本科生、刚开始接触 Java Web 的初学者以及想快速做个内部小工具的开发者。2. 搭建可运行环境JDK、Tomcat、MySQL 的版本搭配与数据库初始化2.1 版本选型别在 JDK 和 Tomcat 的搭配上翻车很多人在“简答宠物管理系统源码”上卡住的第一步不是代码本身而是环境。网上搜 MySQL 安装教程能搜到一大堆但教程对应的版本——5.7、8.0 还是 5.5——和源码需要的版本经常对不上。宠物管理系统是典型的 Java Web 老项目它依赖的组件包括 JDK、Tomcat、MySQL 和 JDBC 驱动。常见的一个匹配组合是JDK 8 Tomcat 8.5 MySQL 5.7 mysql-connector-java 5.1.49。为什么因为老源码里的 jsp 编译方式、Servlet API 的导入路径javax.servlet.*都是按 JDK 8 和 Tomcat 8 的生态写的。如果你图新直接上 JDK 17 Tomcat 10大概率会遇到一个异常编译源码时找不到 javax.servlet因为 Tomcat 10 已经把所有 API 换成了 jakarta.servlet 包名。这不是源码的问题是版本选型的问题。提示拿到源码后第一件事先看项目的 web.xml 头和 import 语句。只要你看到 javax.servlet就把 JDK 定在 8、Tomcat 定在 9 以下看到 jakarta.servlet才考虑 Tomcat 10。我的习惯做法是直接用 Eclipse 的 Dynamic Web Project 方式导入源码容器选 Tomcat 8.5执行环境选 JavaSE 1.8。IDEA 用户则是在 Project Structure 里把 SDK 和语言级别都锁到 8。这里有一个很多人忽略的参数Tomcat 的运行内存。老项目往往没有调过 JVM 参数图片上传、分页查询一上来容易在 IDE 里卡死一般建议在 catalina.bat 的 CATALINA_OPTS 里加 -Xms512m -Xmx1024m。简单说环境不是越新越好匹配才是第一优先级。2.2 MySQL 建库脚本字符集、排序规则和初始数据源码包里通常会带一个 pet.sql 或 db_pet.sql 文件内容是 CREATE DATABASE、CREATE TABLE 和 INSERT。如果你在 MySQL 5.7 里直接 source 导入大概率能跑通但如果在 MySQL 8.0 里导入可能碰上“Unknown collation: utf8mb4_0900_ai_ci”这类报错。这是 MySQL 8 默认排序规则和旧库不一致导致的。解决的办法是把脚本里的排序规则统一改成 utf8mb4_general_ci或者干脆在库创建语句里显式指定。CREATE DATABASE pet_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE pet_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT DEFAULT 0 ) ENGINEInnoDB COMMENT 用户表; CREATE TABLE t_pet ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 宠物名, species VARCHAR(20) COMMENT 品种比如英短、金毛, age INT COMMENT 年龄按月计, price DECIMAL(10, 2), image_path VARCHAR(255) COMMENT 图片相对路径, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 宠物表;这段脚本做了一个核心动作把字符集和排序规则在库级别固定下来。t_pet 表里的字段名、注释和 JDBC 代码里的 getString(name) 是对应的不建议重命名。这里有两个参数你需要关注DECIMAL(10, 2) 表示价格最多 8 位整数加 2 位小数ENGINEInnoDB 保证事务和行锁老源码里如果写成 MyISAM建议手动改掉。导入后用一个简单的 SELECT 验证数据有没有进来。mysql -u root -p pet_db pet.sql mysql -u root -p -e USE pet_db; SELECT COUNT(*) FROM t_pet;上面第二行命令的 -e 参数表示执行完语句就退出交互模式适合快速确认数据量。如果 COUNT 返回 0检查脚本里 INSERT 之前有没有 DROP TABLE很多源码为了可重复执行会在建表前 DROP导致你之前导入的数据被清了这不是 bug是脚本的正常行为。除此之外MySQL 8 的驱动类和 URL 参数也和旧版不同这个放在第 5 章避坑清单里细说。3. 把增删改查跑起来Servlet 控制层、JSP 页面与 JDBC 的三层写透3.1 写一个 PetServlet请求分发和参数校验的写法在“简答宠物管理系统”里最常见的架构是JSP 负责展示和收集数据Servlet 当控制器DAO 类用 JDBC 操作 MySQL。以宠物管理为例一个 PetServlet 通常在 doPost 方法里先拿 action 参数再根据 action 的值决定调用 add、update、delete、query 里的哪个方法这是 Java Web 基础三层分层的标准姿势。你要是刚学完 Java 基础正好能在这里看到面向对象、方法调用是怎么落到真实项目里的。这里有一个很关键的写法就是不要在一个 doPost 里把所有逻辑堆完而是把每个动作拆成一个私有方法这样出错了看堆栈能一眼定位。Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String action req.getParameter(action); if (add.equals(action)) { addPet(req, resp); } else if (update.equals(action)) { updatePet(req, resp); } else if (delete.equals(action)) { deletePet(req, resp); } else { resp.sendRedirect(req.getContextPath() /petList.jsp); } }这段代码第一行的 req.setCharacterEncoding(UTF-8) 写的很清楚POST 请求的中文参数靠它保证不乱码这个放在 doPost 的最前面执行顺序不能乱。action 是一个约定好的字符串JSP 表单里 hidden 字段叫 action 值为 addServlet 这边就能正确派发。resp.sendRedirect 用了 getContextPath()这个值得强调如果你的项目部署名是 PetSystem那么 contextPath 是 /PetSystem如果你写死 /petList.jsp项目换个部署名就 404。PetServlet 里的 addPet 方法接着调用 DAO 层DAO 返回 boolean 后决定跳转到成功页还是失败页。常见错误是直接在 Servlet 里写 Connection、写 PreparedStatement看起来能跑但维护性差而且每来一个请求就新建一个连接数据库连接数会被打满。源码里如果看到这种写法建议把 JDBC 操作统一收口到一个 PetDao 类这是后面第 6 章要讲的重构方向。3.2 JSP 页面上的数据回显JSTL 标签和 jsp 的 forEach 循环JSP 在整个系统里扮演的是视图角色但很多老源码里的 jsp 页面直接嵌了 % % Java 代码块。这种写法能跑但页面一旦复杂、JSP 被编译成 Servlet 时就容易埋雷。比较稳妥的方案是引入 JSTL 标签库用 c:forEach、c:if 这类标签替代脚本片段。% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table tr th宠物名/thth品种/thth年龄/thth价格/thth操作/th /tr c:forEach items${petList} varpet tr td${pet.name}/td td${pet.species}/td td${pet.age}/td td${pet.price}/td td a hrefpet?actioneditid${pet.id}编辑/a a hrefpet?actiondeleteid${pet.id} onclickreturn confirm(确认删除?)删除/a /td /tr /c:forEach /table这段 JSP 有几个细节items 里的 petList 是 Servlet 里 req.setAttribute(petList, list) 传过来的集合变量名两者必须完全一致varpet 定义了循环里每次取出的对象名后面取字段用 ${pet.name}对应 Pet 类的 getName() 方法。操作列里的链接第一个用 GET 请求带 id第二个 delete 同样走 GET但通过 onclick 弹了个 confirm 提示。这里有个明显的坑GET 请求做删除不安全会被爬虫或预加载触发后面第 4 章会给出 Filter 拦截的方案。新手改这些页面时最容易犯的错是改了 JSP 后没重新部署或者部署了但浏览器缓存还在页面没变化。排查顺序是Tomcat 的 work 目录下有没有对应的 class浏览器按 CtrlF5 强刷再不行重启 Tomcat。JSP 第一次访问会被翻译成 Servlet 并编译如果 JSP 页面上有语法错误报错信息往往会指向生成的 Java 文件而不是你的 JSP 原文这时候去 Tomcat 的 work 目录里找那个名字长得像 jsp_000005.class 的文件能帮你定位真实行号。3.3 写一个 PetDaoPreparedStatement 是底线Statement 是雷区DAO 层是宠物管理系统里 Java 代码和 MySQL 打交道的地方。老源码里不少用的是 Statement也就是把字符串拼进 SQL 里再执行。这是 SQL 注入的重灾区比如登录框里输入一个 1 OR 11 就能绕过校验。简答系统虽然大多是课堂作业或毕设但养成 PreparedStatement 习惯对未来做正式项目也是有利的。下面这段是一个标准的 findByPage 方法public ListPet findByPage(int page, int pageSize) { ListPet list new ArrayList(); String sql SELECT id, name, species, age, price, image_path FROM t_pet 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); ResultSet rs ps.executeQuery(); while (rs.next()) { Pet p new Pet(); p.setId(rs.getInt(id)); p.setName(rs.getString(name)); p.setSpecies(rs.getString(species)); p.setAge(rs.getInt(age)); p.setPrice(rs.getBigDecimal(price)); list.add(p); } } catch (SQLException e) { e.printStackTrace(); } return list; }这段代码的要点在 LIMIT ?, ? 两个占位符上。第一个 ? 是起始偏移量计算方式是(当前页码-1) * 每页条数第二个 ? 拉出来的是 pageSize。ORDER BY id DESC 放在 LIMIT 前面语义是“按 id 倒序后再取偏移量”。如果顺序搞反MySQL 会报语法错误。try-with-resources 的写法try 后面直接跟括号能省去手动关闭 Connection 的步骤但前提是 DBUtil.getConnection() 返回的 Connection 没被代理掉如果用了连接池池里的连接归还逻辑是连接池自己处理不会因为 try 块结束真的断开物理连接。这块看着简单但很多项目翻车的原因恰恰是 try 块结束后用了一个已经关闭的 Connection。4. 让系统更像一个产品分页查询、登录拦截器、宠物图片上传4.1 登录 Filter该拦截的请求一个都不能漏只在页面上放一个 if 判断不是真正的权限控制用户直接访问 /pet?actiondeleteid1 一样能删。Servlet 规范里的 Filter 是干这个的。一个登录校验过滤器逻辑很简单检查 session 里有没有 user有就放行没有就重定向到 login.jsp。但常被忽视的是两个细节——放行静态资源和放行登录接口本身。public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); if (uri.endsWith(.css) || uri.endsWith(.js) || uri.endsWith(.jpg) || uri.endsWith(login.jsp) || uri.endsWith(/login)) { chain.doFilter(req, resp); return; } if (request.getSession().getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); }这段 Filter 代码里第一个 if 的放行条件是靠后缀判断的缺点是如果项目里还有一些公共页面像 register.jsp、index.jsp你得继续往这个 if 里加条件。更工程化的做法是维护一张白名单列表用 ArrayList 装起来然后 contains 判断。第二个 if 里 getAttribute(user) 的返回值是 Object你不能拿它直接等于 null 以外的形式去比较因为比较之前类型强转会搞出 ClassCastException。sendRedirect 的目标是 contextPath /login.jsp同样不能丢 contextPath。配置上老项目的 web.xml 里需要手动注册这个 Filter并且在它的 里写 /pet 或者 /这直接决定了拦截范围。如果写 /pet那么 PetListServlet 之外的请求就都不会被拦如果写 /和前面的静态资源放行条件配合起来才完整。很多源码在这个环节为了省事只在某个 Servlet 里做了权限判断效果远不如 Filter。4.2 分页查询不仅要会写 SQL还要算清楚总页数宠物管理系统的列表页如果只有几十条数据不分页也行但一旦录入几百条宠物页面直接拉出几百行浏览器渲染慢、MySQL 查询也慢。前面 3.3 节已经给了 LIMIT ?, ? 的 SQL这一步要补的是总页数从哪来。总页数 ceil(总记录数 / 每页条数)而总记录数必须单独查一次 COUNT(*)不能靠 List.size() 来猜因为当前页的数据量不一定等于每页条数。public int countAll() { String sql SELECT COUNT(*) FROM t_pet; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { if (rs.next()) { return rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } return 0; }一个容易踩的坑是 COUNT(*) 返回的列名在不同 JDBC 驱动里不一样按列名取容易报不存在用 rs.getInt(1) 取第一列最稳。还有一点分页的 page 参数是从 JSP 的 request 里拿的用户可以在地址栏输入 ?page-1此时 (page - 1) * pageSize 变成负数MySQL 的 LIMIT 会报错或返回空集。所以拿到 page 参数后要做一次 Math.max(1, page) 的钳制这是很多源码没考虑到的边界输入。4.3 宠物图片上传路径存数据库文件存磁盘这两者要分清宠物管理系统通常需要上传宠物照片。常见的做法是用 commons-fileupload 组件解析 multipart 表单把二进制流写到项目目录下的 upload 文件夹里数据库 t_pet 表的 image_path 只存相对路径页面渲染时拼成完整 URL。DiskFileItemFactory factory new DiskFileItemFactory(); ServletFileUpload upload new ServletFileUpload(factory); ListFileItem items upload.parseRequest(request); String fileName null; for (FileItem item : items) { if (item.isFormField()) { if (name.equals(item.getFieldName())) { petName item.getString(UTF-8); } } else { fileName System.currentTimeMillis() _ item.getName(); File saved new File(uploadDir, fileName); item.write(saved); } }这里有一个很实际的坑item.getName() 返回的是客户端原始文件名不同浏览器的格式还不一样有的带完整路径 C:\fakepath\dog.jpg有的只有 dog.jpg你需要截取最后一个 \ 或 / 后面的部分。文件名用 System.currentTimeMillis() 加时间戳再拼原始名是为了避免多人同时传同名文件导致互相覆盖。uploadDir 必须是磁盘上的绝对路径不要相对路径因为 Servlet 的工作目录在 Tomcat 启动后可能会变。部署到服务器后upload 目录一般放在 Tomcat 的 webapps 下和项目平级这样页面里访问 /upload/xxx.jpg 时Tomcat 能直接命中静态资源。5. 常见问题排查数据库连接失败、请求 404、中文乱码的避坑清单5.1 数据库连接失败MySQL 8 驱动与 5.7 驱动的差异现象源码直接跑报 ClassNotFoundException: com.mysql.jdbc.Driver。原因项目里导入的 mysql-connector-java 是 5.x而本地 MySQL 是 8.08.0 的驱动类全被改到了 com.mysql.cj.jdbc.Driver旧驱动在连接 MySQL 8 时虽然有时能连上但会在 SSL 和字符集上报一堆警告甚至直接失败。解决换 mysql-connector-java 8.0.x 驱动同时把 JDBC URL 改成驱动要求的形式加上 serverTimezone 参数。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/pet_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8 jdbc.usernameroot jdbc.password123456注意 URL 里 useSSLfalse 一定要显式写因为 MySQL 8 默认开启 SSL 握手本地开发环境没有证书就容易报 SSL 连接错误serverTimezoneAsia/Shanghai 解决驱动在解析日期类型时默认取 UTC 时区导致的时间差 8 小时问题。characterEncodingUTF-8 解决中文写入的编码问题。这四个参数是 MySQL 8 下跑老项目的标配缺一个都有可能出现那种“时好时坏”的诡异问题。5.2 请求 404部署名、web.xml 映射、静态资源路径三种来源现象打开浏览器输入 http://localhost:8080/pet/login.jsp 提示 404。原因404 在宠物管理系统里基本有三个来源。第一个是项目部署名Tomcat 访问路径是 http://localhost:8080/部署名/资源不是 http://localhost:8080/资源很多源码的 README 里没写部署名你自己建的项目名和源码 web.xml 里配的不一致URL 就对不上。第二个是 Servlet 映射web.xml 里配置的 写的是 /pet你访问 /petServlet 就 404。第三个是 JSP 页面上引用 CSS/图片的路径用相对路径写成了 css/style.css但当前页面在 /pet/list.jsp浏览器解析成 /pet/css/style.css样式文件是真存在的但路径错了。解决第一个把项目部署名固定下来或者用 IDE 的 run configuration 里设置 Application context 为 /就能通过根路径访问。第二个去 web.xml 里核对映射名。第三个建议在 JSP 头部写 c:set varctx value${pageContext.request.contextPath}/然后用 ${ctx}/css/style.css 引用所有静态资源一劳永逸。5.3 中文乱码请求编码、响应编码、数据库字符集三条线现象页面新增宠物后回显名字变成一个“????”或者一排方框。中文乱码是 Java Web 项目里被问烂的问题但根因其实只有三条线请求编码、响应编码、数据库字符集。请求侧POST 参数由 req.setCharacterEncoding(UTF-8) 解决这行必须在读参数之前执行GET 参数的编码由 Tomcat 处理默认使用 ISO-8859-1需要改 Tomcat 的 conf/server.xml 里 Connector 的 URIEncodingUTF-8或者用 String new String(name.getBytes(ISO-8859-1), UTF-8) 强行转码。响应侧resp.setContentType(text/html;charsetUTF-8) 和 % page contentTypetext/html;charsetUTF-8 % 二选一即可但一定确认没有重复设置冲突。数据库侧建库时字符集如果是 latin1 或 utf8不是 utf8mb4在存放某些特殊字符时也会出问题重建库比较直接。排查顺序建议是从响应开始查浏览器 F12 看响应头的 Content-Type 是不是 UTF-8再看数据库表结构的 CHARSET最后看代码里请求编码设置时机这三条线按我给的顺序排查基本十分钟就能定位到是哪条线断的。5.4 端口冲突与 Tomcat 内存不足的临时处置现象Tomcat 启动时单纯 show in browser 页面转圈或者报 Address already in use: JVM_Bind 8080。后者是因为 8080 端口已经被别的进程占了。解决可以用 netstat -ano | findstr 8080 查一下是哪个进程占的端口是另一个 Tomcat 还是别的服务IDE 里出现端口占用直接改 server.xml 里 Connector 的 port 为 8081 最快。内存不足则是在启动日志里看到 OutOfMemoryError: PermGen spaceJDK 8 已经移除了 PermGen但老 Tomcat 下仍有 Metaspace 问题调大 CATALINA_OPTS 即可具体配置在第 2 章提过不再赘述。6. 把源码吃透后的进阶验证给自己写一份接口测试脚本当你能独立把宠物管理系统跑起来、增删改查都顺了建议下一步不是急着改页面样式而是给这套系统补一层接口验证。理由很简单手工在浏览器里点只能覆盖“happy path”漏掉边界场景。用 curl 能快速测出参数缺失、非法 id、越权操作这些隐患。# 新增宠物 curl -X POST http://localhost:8080/pet?actionadd \ -d nametestDogspeciesLabradorage6price800.00 \ -c cookies.txt # 访问需要登录的列表页 curl -b cookies.txt http://localhost:8080/pet?actionlist上面 -c 保存 session 的 cookie-b 带上 cookie 访问受保护页面。如果你的 Filter 写得对不带 cookie 的请求会被重定向到 login.jsp带上了才能拿到列表。这个验证方式直接检验了 Filter 的有效性同时也暴露了一个新手常犯的错误把 actionadd 写成 actionaddPet结果 Servlet 里 else 分支把请求重定向到列表页。curl 的返回值会用 302 还是 200 给你答案。另一个值得做的进阶是把 DAO 里的 JDBC 模板代码抽成一个通用 BaseDao把 findById、findAll、update 这类重复代码收敛掉。这个操作完成之后你再回看这份“简答宠物管理系统源码”就会发现它的业务逻辑其实很薄大部分复杂度是 JDBC 样板代码和陈旧写法带来的。我现在自己做小项目时仍然会参考这类系统的分层方式但要说个人习惯我反而更推荐把这些样板代码逐步替换成 Spring Boot 里的 JdbcTemplate——不是因为它新而是因为它让数据访问代码短了一半可读性提升很多。当然毕设交付时如果想稳一点维持原样、把接口测试脚本连同 README 一起交上去拿到高分的机会更大。希望这份拆解能帮你在“简答宠物管理系统源码”这个起点上少走几步弯路。真正有价值的不是跑通它而是跑通之后你知道了哪一步为什么难、错在哪个变量、该查哪份日志。做 Java Web 老项目就是这样被旧坑绊倒一次以后见到类似现象就能一眼认出这也是你从“照着源码改”走向“自己设计系统”最快的路径。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询