简介:这份资源是一套基于Java+JSP+Servlet+MySQL开发的简易宠物管理系统源码,面向Java Web初学者与课程设计入门者,帮助其理解Servlet请求处理、JSP页面展示与数据库增删改查的完整协作流程。压缩包共41个文件,约6.37MB,包含7个Java源文件与7个class编译文件、4个JSP页面、4个jar依赖包,以及SQL建库脚本、配置说明文档、项目配置文件和演示视频等,覆盖从代码到部署的完整环节。系统实现宠物按分类查询、添加、编辑与删除功能,在原有基础上补齐了编辑与删除模块,形成完整的增删改查闭环,适合小白对照源码理解MVC分层与数据库操作。目前已有1580人学习下载,可作为课程作业、实训项目或自学练手的参考模板,配合文档与录屏快速跑通项目并掌握基础开发思路。
1. 从一份「java+jsp+servlet+mysql简答宠物管理系统源码」说起:它到底能解决什么
如果你正在搜「java+jsp+servlet+mysql简答宠物管理系统源码」,大概率不是想听 JSP 的历史,而是想找一份能跑起来、能改、能写进简历或课程设计的 JavaWeb 项目。这套技术栈是典型的「老派但扎实」组合:JSP 负责页面渲染,Servlet 接请求做控制,MySQL 存宠物、主人、领养记录这些数据。它解决的核心问题是——让你在没有 Spring Boot 全家桶的情况下,把 MVC 三层结构亲手串一遍,理解请求从浏览器到数据库再回到页面的完整链路。
适合谁?一是刚学完 Java 基础、想找个完整案例练手的在校生;二是需要交课程设计、毕业设计但不想从零搭框架的人;三是面试前想补一补 Servlet 生命周期、Session 管理、JDBC 事务这些「java面试题」高频考点的求职者。不适合谁?如果你已经熟练 Spring Boot + MyBatis-Plus,这套东西对你来说是降维,但拿来当「mysql项目实战」的底层原理复习材料依然有价值。下面我按「能跑通、能改、能避坑」的路径拆开讲。
2. 环境搭建与数据库设计:把 MySQL 和 Tomcat 先伺候好
2.1 JDK、Tomcat、MySQL 的版本选择与安装顺序
这套技术栈对环境版本不挑剔,但版本错配是新手翻车重灾区。我一般推荐 JDK 8(兼容性最好,JSP/Servlet 老项目几乎不会出幺蛾子)、Tomcat 8.5 或 9.0、MySQL 5.7 或 8.0。注意 MySQL 8.0 的驱动类名和连接串跟 5.7 不一样,这是后面踩坑章节要重点说的。
安装顺序建议:JDK → MySQL → Tomcat → IDE(Eclipse 或 IDEA)。JDK 装完配JAVA_HOME和Path,命令行敲java -version能出版本号才算过。MySQL 在 Windows 上安装时,记住你设的 root 密码,字符集选utf8mb4,不然中文宠物名会变问号。Tomcat 解压即用,但要在 IDE 里绑定好,否则启动报 404 你都不知道找谁。
# 验证 JDK 是否配置成功 java -version # 预期输出类似:java version "1.8.0_xxx" # 验证 MySQL 服务是否启动(Windows) net start mysql # 如果提示服务已启动,说明没问题;提示服务名无效,说明没装成服务 # 登录 MySQL 验证 mysql -u root -p # 输入密码后进入 mysql> 提示符即成功上面三条命令是环境自检的最小集合。java -version看 JDK,net start mysql看服务,mysql -u root -p看账号密码。任何一条失败,先别急着往下走,环境问题拖到后面会伪装成代码 bug,排查成本翻倍。
2.2 宠物管理系统的表结构设计:四张表撑起核心业务
一个「简答」版宠物管理系统,不需要几十张表。核心就四张:用户表、宠物表、领养记录表、分类表。设计原则是——字段够用就行,别过度设计,但主外键和索引要留好,这是后面写 SQL 和排查慢查询的基础。
| 表名 | 关键字段 | 说明 |
|---|---|---|
user | id, username, password, role | role 区分管理员和普通用户 |
pet | id, name, category_id, age, status, img_url | status 标记可领养/已领养 |
category | id, name | 宠物分类,如猫、狗、鸟 |
adoption | id, user_id, pet_id, adopt_time | 领养记录,关联用户和宠物 |
建表 SQL 我习惯直接写在一个init.sql里,导入一次到位。注意pet表的status用 tinyint(0 可领养,1 已领养),比字符串省空间也好判断。img_url存相对路径,别存绝对路径,否则换台机器图片全挂。
CREATE DATABASE pet_db DEFAULT CHARACTER SET utf8mb4; USE pet_db; CREATE TABLE `user` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL UNIQUE, `password` VARCHAR(100) NOT NULL, `role` TINYINT DEFAULT 0 COMMENT '0普通用户 1管理员' ); CREATE TABLE `pet` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL, `category_id` INT, `age` INT, `status` TINYINT DEFAULT 0 COMMENT '0可领养 1已领养', `img_url` VARCHAR(200), FOREIGN KEY (`category_id`) REFERENCES `category`(`id`) ); CREATE TABLE `category` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(30) NOT NULL ); CREATE TABLE `adoption` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `user_id` INT, `pet_id` INT, `adopt_time` DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (`user_id`) REFERENCES `user`(`id`), FOREIGN KEY (`pet_id`) REFERENCES `pet`(`id`) );建表时把category放在pet前面,因为pet有外键指向它,顺序反了会报错。adopt_time用DEFAULT CURRENT_TIMESTAMP省得代码里手动塞时间。字符集统一utf8mb4,支持 emoji,宠物名里带个表情也不会炸。
2.3 在 IDEA 里跑通第一个 Servlet:从 web.xml 到注解
老项目用web.xml配置 Servlet 映射,新一点用@WebServlet注解。两种都要会看,因为「servlet demo」类面试题经常问这个。最小可运行 Servlet 长这样:
@WebServlet("/hello") public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8"); resp.getWriter().write("宠物管理系统启动成功"); } }@WebServlet("/hello")把 URL 映射到类上,浏览器访问http://localhost:8080/项目名/hello就能看到输出。setContentType里必须带charset=utf-8,否则中文乱码。这一步跑通,说明 Tomcat 和项目结构没问题,后面写业务才有底气。
3. 核心功能实现:JSP 页面、Servlet 控制与 JDBC 数据访问
3.1 JSP 页面如何展示宠物列表并处理中文乱码
JSP 本质是「写起来像 HTML 的 Servlet」,页面顶部加<%@ page contentType="text/html;charset=utf-8" %>是铁律。展示宠物列表用 JSTL 的c:forEach比脚本片段<% %>干净得多,也更容易维护。
<%@ page contentType="text/html;charset=utf-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <table> <c:forEach items="${petList}" var="pet"> <tr> <td>${pet.name}</td> <td>${pet.age}</td> <td>${pet.status == 0 ? '可领养' : '已领养'}</td> </tr> </c:forEach> </table>${petList}是 Servlet 里request.setAttribute("petList", list)塞进去的。c:forEach遍历集合,var是循环变量。三元表达式直接判断状态,省得写<c:if>。注意 JSTL 需要引入jstl.jar和standard.jar,漏了会报NoClassDefFoundError,这是新手最常见的翻车点之一。
3.2 Servlet 接收表单、调用 Service、转发到 JSP 的完整链路
一个「添加宠物」的流程最能体现 MVC:JSP 表单提交 → Servlet 接收参数 → Service 调 DAO → 数据库插入 → 重定向回列表页。Servlet 里不要写 SQL,这是分层的基本纪律。
@WebServlet("/pet/add") public class PetAddServlet extends HttpServlet { private PetService petService = new PetServiceImpl(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); // 必须在取参数前设置 String name = req.getParameter("name"); int age = Integer.parseInt(req.getParameter("age")); int categoryId = Integer.parseInt(req.getParameter("categoryId")); Pet pet = new Pet(name, age, categoryId, 0); petService.addPet(pet); resp.sendRedirect(req.getContextPath() + "/pet/list"); } }setCharacterEncoding("utf-8")必须放在getParameter之前,放后面等于没设,中文参数照样乱码。sendRedirect用req.getContextPath()拼路径,避免项目名硬编码。重定向而不是转发,是为了防止用户刷新页面重复提交表单,这是「后悔药」级别的习惯。
3.3 JDBC 工具类与 DAO 层:把连接、查询、关闭封装好
JDBC 代码写多了全是重复的try-catch-finally,封装一个DBUtil是必修课。连接参数放配置文件,别硬编码在 Java 里。
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/pet_db?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); // MySQL 8.0 驱动 } catch (ClassNotFoundException e) { throw new RuntimeException("驱动加载失败", e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { // 依次关闭,每个都判空,异常吞掉但不影响主流程 } }com.mysql.cj.jdbc.Driver是 MySQL 8.0 的驱动类,5.7 是com.mysql.jdbc.Driver,写错直接ClassNotFoundException。连接串里serverTimezone=UTC不加,MySQL 8.0 可能报时区错误。characterEncoding=utf8保证存取中文不乱码。DAO 层用PreparedStatement而不是Statement,防 SQL 注入,这是安全底线。
4. 避坑与排查:这套老技术栈最容易翻车的 5 个地方
4.1 现象:页面中文全是问号或乱码
原因:三个环节任一没设 UTF-8 都会乱码——数据库字符集、连接串编码、Servlet/JSP 响应编码。解决:数据库建库时DEFAULT CHARACTER SET utf8mb4;连接串加characterEncoding=utf8;Servlet 里req.setCharacterEncoding("utf-8")和resp.setContentType("text/html;charset=utf-8")都写上。三处对齐,乱码必消。
4.2 现象:启动报 404,Servlet 访问不到
原因:@WebServlet路径和访问 URL 不匹配,或者web.xml里metadata-complete="true"导致注解被忽略。解决:先看 Tomcat 启动日志有没有Servlet [xxx] registered;再检查项目web.xml的metadata-complete属性,设成false或删掉;最后确认访问路径带上了项目 contextPath。
4.3 现象:MySQL 8.0 连接报「Public Key Retrieval is not allowed」
原因:MySQL 8.0 默认用caching_sha2_password认证插件,老驱动或连接串没配好就报这个。解决:连接串加allowPublicKeyRetrieval=true,或者把用户认证插件改成mysql_native_password。前者改配置更省事,后者改数据库更彻底,看你的环境权限。
4.4 现象:JSP 里用 JSTL 报NoClassDefFoundError
原因:只放了jstl.jar没放standard.jar,或者两个 jar 版本不匹配。解决:JSTL 1.2 只需要一个jstl-1.2.jar,放进WEB-INF/lib目录,别放 Tomcat 的 lib 里。放错位置会导致项目能编译但运行时报类找不到,这个坑我踩过不止一次。
4.5 现象:领养功能并发下同一只宠物被领养两次
原因:先查状态再更新,两步之间没有锁,两个请求同时通过检查。解决:用一条UPDATE pet SET status=1 WHERE id=? AND status=0,判断影响行数是否为 1,为 0 说明已被领养。把判断和更新合并成原子操作,比加 synchronized 更可靠,也更符合数据库层面的并发控制思路。
5. 进阶技巧:把这份源码改造成能写进简历的项目
5.1 用过滤器统一处理编码和登录校验
每个 Servlet 都写setCharacterEncoding太啰嗦,用一个Filter统一处理。登录校验也放过滤器里,判断 Session 里有没有用户,没有就跳登录页。这样业务代码只管业务,横切逻辑抽干净。
@WebFilter("/*") public class EncodingFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding("utf-8"); resp.setContentType("text/html;charset=utf-8"); chain.doFilter(req, resp); // 放行,别忘写 } }@WebFilter("/*")拦截所有请求,chain.doFilter是放行,漏写这行整个项目所有页面白屏,这是过滤器最经典的翻车点。登录校验过滤器里判断session.getAttribute("user") == null就重定向到登录页,注意放行登录页和静态资源,否则死循环。
5.2 分页查询:从「全查再截取」到 SQL LIMIT
新手写分页容易SELECT *全查出来再用List.subList截取,数据量一大就内存爆炸。正确做法是 SQL 层LIMIT,配合COUNT(*)算总页数。
-- 第 page 页,每页 size 条,offset 从 0 开始 SELECT * FROM pet LIMIT #{offset}, #{size}; SELECT COUNT(*) FROM pet;offset = (page - 1) * size,这个公式记牢。LIMIT在数据量大时性能优势明显,因为数据库只返回需要的行。分页参数从前端传,Servlet 里做边界校验,page 小于 1 归 1,超过总页数归总页数,防止越界。
5.3 用事务保证领养操作的原子性
领养宠物要同时做两件事:更新宠物状态、插入领养记录。两步必须同成功或同失败,否则会出现「宠物标记已领养但没有领养记录」的脏数据。JDBC 手动事务:
Connection conn = DBUtil.getConnection(); try { conn.setAutoCommit(false); // 关闭自动提交 petDao.updateStatus(conn, petId, 1); adoptionDao.insert(conn, userId, petId); conn.commit(); // 两步都成功才提交 } catch (Exception e) { conn.rollback(); // 任一步失败全回滚 throw new RuntimeException("领养失败", e); } finally { conn.setAutoCommit(true); DBUtil.close(conn, null, null); }关键点是 DAO 方法要接收同一个Connection,不能各自去getConnection,否则事务管不到两个连接。setAutoCommit(false)开启事务,commit提交,rollback回滚。这套手动事务是理解 Spring@Transactional的基础,面试问「mysql锁的分类」和事务隔离级别时,你能从这段代码讲起,比背概念有说服力。
5.4 验证改造是否成功:三个自测清单
改完别急着交,按这三条自测:第一,中文输入输出全链路无乱码,包括宠物名、分类名、提示信息;第二,并发领养同一只宠物,只有一个请求成功,另一个提示「已被领养」;第三,分页边界测试,page=0、page=999、size=0 都不报 500 错误。这三条过了,这份源码才算真正「能打」。
我自己带人做课程设计时,最常见的后悔药就是——功能跑通就交,结果答辩时老师一问并发和事务就露馅。把上面这几点补上,你的项目从「能跑」变成「能讲」,面试官问起 Servlet 生命周期、Session 与 Cookie 区别、JDBC 事务这些「java基础」高频题,你都有代码可以对应。希望帮到你。
本文还有配套的精品资源,点击获取