ARTICLE DETAIL

资讯详情

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

JSP+Servlet+MySQL宠物管理系统:从建表到部署的全流程解析

JSP+Servlet+MySQL宠物管理系统:从建表到部署的全流程解析 简介基于Java JSPServletMySQL的简单宠物管理系统源码面向刚接触Java Web开发的学习者以宠物信息管理为业务场景完整演示分类查询、添加、编辑、删除等核心操作适合作为增删改查模块的入门练习。资源共41个文件压缩包6.37MB包含Java源文件、JSP页面、class编译文件、Jar依赖包、SQL数据库脚本、配置说明文档、PDF说明手册以及MP4演示视频代码目录petmgr、数据库脚本和文档分层清晰便于对照学习。目前已有1580人学习下载。通过这套资源读者可获得可直接运行的宠物管理项目源码结合数据库脚本和配置文档快速搭建环境再配合演示视频理解从页面发起请求到Servlet处理后返回结果的完整流程从而掌握JSPServletMySQL整合开发的增删改查实现思路为后续更复杂的Java Web项目打下基础。1. 一个“简答”的宠物管理系统值不值得你花一下午跑一遍每年毕业设计季和期末课设都会有一批人拿到 javajspservletmysql 的宠物管理系统源码。标题里的“简答”我按业内习惯理解成“简化版”功能不多流程完整正好用来做 Java Web 的入门样本。它的核心价值不在宠物业务本身而在于把一条最经典的链路完整串了起来浏览器发请求给 ServletServlet 调 DAODAO 用 JDBC 访问 MySQL再把数据交给 JSP 渲染成页面。适合三类人要交课程设计的学生、刚学完 Java 语法想第一次接触 Web 的开发者、实习前想补传统 Web 底子的新人。跑通这一遍你再看 Spring MVC、MyBatis 这类技术会明显少一层黑匣子。2. 先拆业务边界一个简化版宠物管理系统的模块与数据库表2.1 模块怎么划登录、宠物信息、领养记录三个闭环拿到源码第一步不是急着点运行而是先看业务边界。一个简化版宠物管理系统通常只围绕三件事转谁能进来、有什么宠物、宠物被谁领走了。对应到页面上就是这么几组功能。登录和退出区分普通用户和管理员宠物信息浏览支持列表、按类别筛选、按名称搜索管理员维护宠物也就是新增、修改、删除领养流程普通用户发起申请管理员审核通过或拒绝有些版本还会加一个按类别统计宠物数量的小功能用来撑起首页的数据面板。你发现没有这套功能和 Java 课设最常见的“学生管理系统”“图书管理系统”是同构的只是换了个业务皮。它覆盖了 Java Web 最基础的两大考点会话管理和增删改查再加一个分页搜索就够了。为什么不建议在这个阶段加购物车、在线支付、聊天室因为业务一旦复杂学习重心就会从“Servlet 怎么接收请求、JDBC 怎么防注入”漂移到“这个字段该放哪张表”反而把主线冲淡。一个合格的课设系统核心是把 CRUD 和登录做到位剩下的都是加分项。我见过不少同学把宠物系统硬生生做成电商网站最后答辩时连自己写的 SQL 都解释不清楚这就本末倒置了。代码组织上常见做法是严格分三层JSP 只负责展示不写 Java 业务逻辑Servlet 只收参数、调服务、转发页面DAO 层放所有 SQL返回实体对象或集合。实体类对应表结构一个实体一张表。这套分层就是后面学 Spring MVC 时 Controller、Service、Mapper 的原型能把三层边界守住这个源码才算真正读懂了。2.2 四张表怎么建字段类型、默认值、索引与插入语句简化版的表一般不会超过五张最核心的是四张类别表、宠物表、用户表、领养记录表。表名和字段名不同源码里可能叫 pet、pets、tb_pet但设计逻辑基本一致。下面这份建表 SQL 是这类项目最通用的形态MySQL 5.7 和 8.0 都能直接跑。DROP DATABASE IF EXISTS pet_sys; CREATE DATABASE pet_sys DEFAULT CHARACTER SET utf8mb4; USE pet_sys; CREATE TABLE category ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE pet ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, breed VARCHAR(50) COMMENT 品种, category_id INT NOT NULL COMMENT 关联 category.id, age INT DEFAULT 0, sex TINYINT DEFAULT 1 COMMENT 1公 2母, health_status VARCHAR(20) DEFAULT 健康, photo VARCHAR(200) COMMENT 图片路径不是图片本身, description TEXT, status TINYINT DEFAULT 0 COMMENT 0可领养 1已领养, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_category (category_id), KEY idx_create_time (create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, phone VARCHAR(20), role TINYINT DEFAULT 0 COMMENT 0普通用户 1管理员 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE adoption ( id INT AUTO_INCREMENT PRIMARY KEY, pet_id INT NOT NULL COMMENT 关联 pet.id, user_id INT NOT NULL COMMENT 关联 user.id, adopt_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 0 COMMENT 0申请中 1已通过 2已拒绝 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO user (username, password, role) VALUES (admin, MD5(123456), 1); INSERT INTO category (name) VALUES (猫), (狗), (兔); INSERT INTO pet (name, breed, category_id, age, sex, description) VALUES (团子, 英短, 1, 2, 1, 性格温和已打疫苗);几个字段设计上的选型理由值得你对着源码逐条对一遍。status 这类状态字段用 TINYINT 而不是 VARCHAR原因是状态是枚举语义用 0、1、2 表示一套离散取值比存“可领养”“已领养”字符串省空间也不容易因为中英文不一致产生脏数据。age 用 INT 而不用 DATETIME因为这个系统只在列表里展示“几岁”不需要拿生日去算年龄图省事。create_time 设置 DEFAULT CURRENT_TIMESTAMP 之后INSERT 的时候不用手动传时间数据库自己写当前时间代码里少一行也避免应用服务器和数据库服务器时间不一致。索引的加法要和查询路径对应。category_id 建索引因为按类别筛选是列表页的高频操作create_time 建索引因为列表默认按时间倒序。如果将来要按名称搜索还可以给 name 加一个普通索引但别在这类小表上建太多索引写多读少反而拖慢插入。外键这块课设有两种流派一种是老老实实建 FOREIGN KEY靠数据库保证 pet.category_id 不会指向不存在的类别另一种是什么都不建在 DAO 层写代码先查一眼 category 存不存在。我见过的大多数源码走的都是后者因为做增删改查的时候每次外键校验都是一次额外查询很多人嫌麻烦直接省了。两种做法在面试时都能自圆其说关键是要说清楚你选的方案到底是哪一边。还有两处细节值得注意。photo 字段存的是图片路径而不是图片本身图片文件放 WebContent/upload 或 WEB-INF 下由 Servlet 上传落盘这是 JSP 传统项目的通行做法避免把二进制塞进数据库导致表体积失控。password 字段在演示项目里常见的是 MD5 或者明文但你要知道真实项目里必须用 bcrypt 这类加盐哈希MD5 在今天的算力下已经不安全。这里用 MD5(123456) 只是为了让你登录时少走一步弯路别把它当成标准答案。2.3 为什么选 JDBC 直连而不是直接上 MyBatis很多同学拿到这种源码会有一个疑问现在新项目都用 Spring Boot MyBatis这套 JSP Servlet JDBC 是不是过时了我的看法是作为教学样本它反而更合适。这份源码的核心教学点是 Servlet 和 JSP数据访问层用 JDBC 直连能把“连接、执行、关闭”这条链路完全暴露在你面前一旦换成 MyBatisSQL 被 XML 和动态标签包起来你就很难看清一条数据到底是怎么从 MySQL 流到 JSP 页面的。JDBC 不管怎么封装底层永远是六个固定动作加载驱动、获取连接、创建 PreparedStatement、执行 SQL、遍历 ResultSet、倒序关闭资源。你可以打开源码里的任何一个 DAO基本都能拆出这六步。理解这六步还有个额外的好处以后看 JdbcTemplate 或 MyBatis你会发现它只是在帮你省掉模板代码核心语义没有变。很多人在面试时被问“MyBatis 底层是怎么连数据库的”如果提前把 JDBC 这六步吃透答起来会顺很多。3. 把源码跑起来JDK、Tomcat、MySQL 的版本选型与首启步骤3.1 先解决最容易翻车的版本组合这类源码跑不起来十有七八是版本不对而不是代码有问题。最典型的一坑是 Tomcat 版本Tomcat 9 及之前用的是 javax.servlet 包名Tomcat 10 起把包名全改成了 jakarta.servlet老源码放进 Tomcat 10 直接编译不过报的错还是天书一样的 ClassNotFoundException。所以我一般建议用 JDK 8 Tomcat 9.0.x MySQL 5.7这个组合对老源码兼容性最好。JDK 8 在今天依然是课设和很多公司老项目的底线的确很老但它和 Servlet 4.0 配合最省事没必要为了追新上 JDK 17那会多出一堆模块化和版本兼容的问题。MySQL 用 5.7 还是 8.0取决于你机器上现成装的是哪个。全新安装的话5.7 系列选 5.7.44 这个比较靠后的小版本稳定性没问题如果机器上已经装了 8.0也不用卸载重装只要求两件事驱动 jar 换成 mysql-connector-java 8.0.x连接串里按第 5 章的参数配好。源码包里如果自带 lib 目录先看里面驱动 jar 的文件名——看到 mysql-connector-java-5.1.x 配 MySQL 8.0这就是一个经典的隐形雷。3.2 导入项目前先认目录结构压缩包解压之后不要急着双击打开先扫一眼目录结构。老式 JSP 项目的典型布局长这样pet_sys/ ├── src/ │ ├── com/pet/entity/ # Pet、User 等实体类 │ ├── com/pet/dao/ # PetDao、UserDaoJDBC 操作全在这 │ ├── com/pet/web/ # LoginServlet、PetServlet、EncodingFilter │ └── com/pet/util/ # JDBCUtil、分页工具 ├── WebContent/ # 有的项目叫 webapp │ ├── login.jsp │ ├── pet_list.jsp │ ├── css/ │ ├── upload/ │ └── WEB-INF/ │ ├── web.xml │ └── lib/ # 驱动和 JSTL 的 jar 在这 └── sql/ └── pet_sys.sql看到 src 下全是 .class 而没有 .java说明发包里只带了编译产物这种基本没法改代码建议直接换一份如果只有一个 .war 包扔进 Tomcat 的 webapp 目录也能解压运行适合快速演示但不适合二次开发。IDEA 导入选 New → Project from Existing Sources语言级别选 JDK 8然后把 Tomcat 9 配成项目的 Application ServerEclipse 用户则是 File → Import → Existing Projects into Workspace需要确认项目里有 .project 文件才能被识别。3.3 建库导数据、改连接配置SQL 脚本导入这一步很机械但位置不能搞错。先打开 mysql 命令行执行导入再用一条查询确认表和数据都已经就位mysql -uroot -p pet_sys.sql mysql -uroot -p -e SELECT id, name, status FROM pet_sys.pet LIMIT 5;看到返回 5 条数据表就算建好了。接下来改连接配置。大多数源码会把数据库连接信息放在 src 下的 jdbc.properties 或 db.properties 里少数直接写在 JDBCUtil.java 的静态常量中——搜“getConnection”就能定位。改成一个能连上你自己 MySQL 的配置jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/pet_sys?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password你的密码这串 URL 里每个参数都是血泪经验。useSSLfalse 表示本地开发关掉 SSL 握手不然 MySQL 8.0 下经常在建立连接阶段就报错characterEncodingutf8 保证应用和数据库之间传输中文用的是 UTF-8这行不写第 4 章的乱码问题迟早找上门serverTimezoneAsia/Shanghai 是 MySQL 8.0 驱动强制要求的老驱动 5.1.x 不需要但不加就会出现 “The server time zone value” 的报错allowPublicKeyRetrievaltrue 专门解决 8.0 默认认证插件 caching_sha2_password 下的连接失败。这些参数如果你在 5.7 上跑后两个可以省但留着也不影响。3.4 部署到 Tomcat 并完成第一次启动环境配完剩下的就是部署。IDEA 里打开 Run/Debug Configurations加一个 Tomcat Server LocalDeployment 标签页把当前项目的 Artifact 加进去选择 exploded 模式Application context 填 /pet_sys或者填源码里原本的上下文路径。启动后不要光看页面先看控制台日志出现 “Deployment of web application archive ... has finished” 才算部署成功。第一次自测路径建议从登录页开始浏览器访问 http://localhost:8080/pet_sys/login.jsp用 SQL 里预置的 admin / 123456 登录。如果 404先查 Application context 是不是打错了再看访问的路径有没有带上上下文如果 500大概率是数据库连接没配好切到 Tomcat 的 localhost 日志文件看完整堆栈。这里有一个经验不要把错误信息只看最后一行JDBC 的报错关键在 Caused by 那一段前面的异常往往只是包装。4. 登录与宠物列表链路Servlet、DAO、JSP 三层代码怎么配合4.1 web.xml 里的映射规则请求从 URL 到 Servlet老式 JSP 项目里Servlet 的映射大多写在 WEB-INF/web.xml 里。打开这个文件你会看到一堆servlet和servlet-mapping配对的结构前者把类名注册成逻辑名后者把 URL 路径绑定到逻辑名上。核心片段长这样servlet servlet-namePetServlet/servlet-name servlet-classcom.pet.web.PetServlet/servlet-class /servlet servlet-mapping servlet-namePetServlet/servlet-name url-pattern/pet/list/url-pattern /servlet-mapping这样的好处是一个 Servlet 类可以挂多个 URL。比如 PetServlet 里同时处理/pet/list、/pet/add、/pet/delete就可以写三组 mapping 都指向同一个逻辑名然后在 doGet 或 doPost 里用 request.getServletPath() 判断到底调哪个方法。新版一点的源码可能直接用 WebServlet(/pet/list) 注解效果等价但课设代码里用 web.xml 的更多因为导师更习惯看这种显式配置。Servlet 的生命周期在这套源码里是能直接观察到的容器启动时不实例化除非配了 load-on-startup第一次收到请求才创建实例并调用 init之后每次请求进 service按请求方法分发给 doGet 或 doPost关 Tomcat 时调 destroy。java 面试题里这道题几乎年年出现死记硬背不如在代码里打断点看一遍。把断点打在 doGet 第一行刷新页面你会看到调用栈从 service 进到 doGet比任何教程都直观。4.2 JDBCUtil连接获取与关闭的顺序为什么不能反几乎每份源码都会有一个 JDBC 工具类名字可能是 JDBCUtil、DBUtil、BaseDao。它的作用只有一个把“拿连接”和“关资源”这两件重复事收拢到一个类里。典型的实现长这样public class JDBCUtil { private static final String URL jdbc:mysql://localhost:3306/pet_sys?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/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); } public static void close(ResultSet rs, Statement stmt, Connection conn) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }两个容易被忽略的细节。第一驱动注册写在 static 块里保证类加载时只执行一次如果你看到的是老写法 Class.forName(com.mysql.jdbc.Driver)在 MySQL 8.0 驱动下也能跑但会打出一行 Deprecation 警告顺手改成 com.mysql.cj.jdbc.Driver 即可。第二close 方法里的顺序必须是 ResultSet → Statement → Connection 倒着来如果先关闭连接再去关 Statement某些 MySQL 版本下会抛异常。JDK 7 之后的 try-with-resources 能自动完成这个顺序但很多老源码没这么写你在 DAO 里看到的还是手动关三件套。4.3 宠物列表 分页 关键字搜索的完整实现把列表页做完整是这份源码最有含金量的部分因为它把 DAO、Servlet、JSP 三层的配合全串起来了。先看 DAO 层的查询方法。这里采用的是动态拼接 SQL 的方案根据前端是否传了 keyword 和 categoryId 决定 WHERE 条件分页用 LIMIT 控制排序用 create_timepublic ListPet list(int page, int pageSize, String keyword, String categoryId) throws SQLException { StringBuilder sql new StringBuilder(SELECT * FROM pet WHERE status 0); ListObject params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql.append( AND name LIKE ?); params.add(% keyword.trim() %); } if (categoryId ! null !categoryId.trim().isEmpty()) { sql.append( AND category_id ?); params.add(Integer.parseInt(categoryId)); } sql.append( ORDER BY create_time DESC, id DESC LIMIT ?, ?); params.add((page - 1) * pageSize); params.add(pageSize); try (Connection conn JDBCUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql.toString())) { for (int i 0; i params.size(); i) { ps.setObject(i 1, params.get(i)); } try (ResultSet rs ps.executeQuery()) { // 每行封装成一个 Pet 对象add 到 ListPet } } }这里有个关键点LIKE 的百分号是拼进参数里的而不是拼进 SQL 字符串里目的是让 PreparedStatement 把整段内容当参数处理从根上防掉 SQL 注入。LIMIT 的参数同样走占位符。ORDER BY create_time DESC, id DESC 这个写法是在 MySQL 排序上做的一层保险当两条记录 create_time 完全相同只按时间排序会导致分页时顺序跳动加上 id 作为第二排序键顺序就稳定了。Servlet 层负责收页号、调 DAO、把结果放进 request 域然后转发。注意转发和重定向的选择查询类操作用 forward地址栏不变用户刷新页面看到的是同一份数据表单提交成功后的新增、修改、删除用 sendRedirect 跳回列表否则用户一按 F5 就把同一条数据重复插入一遍。这是一个非常暴露基本功的细节很多课设源码在这里直接翻车。分页链接在 JSP 里生成时要注意把 keyword 和 categoryId 一起带回。范例是这样的逻辑c:if test${currentPage 1} a href${pageContext.request.contextPath}/pet/list?page${currentPage - 1}keyword${param.keyword}上一页/a /c:if c:forEach items${petList} varpet div classcard h4${pet.name}/h4 p${pet.breed} / ${pet.age}岁 / ${pet.status 0 ? 可领养 : 已领养}/p /div /c:forEach如果你在页面里用了${pet.name}这种 EL 表达式运行时报出 JSP 解析相关的异常十有八九是 WEB-INF/lib 下少了 jstl.jar 和 standard.jar。这两个 jar 是 JSTL 标签库的载体老项目里经常和驱动 jar 一起躺在 lib 目录下缺了就去补别写一堆 scriptlet 硬顶。JSP 里的 EL 三元表达式${pet.status 0 ? 可领养 : 已领养}在 Servlet 2.4 之后就支持读到这边代码时不用惊讶。4.4 中文乱码的三处固定检查点宠物系统最绕不开的业务数据就是中文所以乱码几乎是必踩的坑。处理乱码不需要玄学只需要按三个固定位置检查。第一处是 JSP 页面的头部必须有% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %同时文件本身保存成 UTF-8 编码第二处是 Servlet 接收请求参数时要设置编码POST 请求必须在读参数前执行 request.setCharacterEncoding(UTF-8)第三处是数据库连接串里的 characterEncodingutf8 和表字段的 utf8mb4这两层不一致就会写入乱码。为什么只说 POST因为 Tomcat 8 起GET 请求的 URI 参数已经默认用 UTF-8 解码而 POST 请求体默认按 ISO-8859-1 读不显式设置必乱。所以最常见的乱码场景集中在表单提交那一步而不是列表展示。如果你看到的是页面显示正常、写进数据库变成问号说明问题出在连接串或表字符集看到的是数据库正常、页面上全是问号问题出在 JSP 编码或文件保存格式。按这两条方向去查比盲目改配置高效得多。5. 排查手册MySQL 连接失败、Tomcat 启动异常、中文乱码的避坑清单下面这几条是帮人调这类源码时最常碰到的高发坑每一条都按现象、原因、解决三个步骤说清楚。遇到问题先对号入座不要一上来就重装 MySQL。5.1 MySQL 8.0 下的连接报错SSL 和 Public Key Retrieval现象项目能编译、能部署但一访问页面就抛异常控制台出现 “Communications link failure” 或 “Public Key Retrieval is not allowed”。原因MySQL 8.0 默认开的是 caching_sha2_password 认证插件首次连接时客户端需要向服务端要公钥来做 RSA 加密而连接串里没允许这个行为如果驱动还是 5.1.x 老版本服务端的新认证方式它根本不认识同样连不上。解决在 jdbc.url 后面追加useSSLfalseallowPublicKeyRetrievaltrue并把驱动 jar 换成 mysql-connector-java 8.0.x。这是本地开发环境的合法配置如果将来部署到生产SSL 要反过来开启allowPublicKeyRetrieval 也要评估是否保留。5.2 Access denied for user rootlocalhost5.7 的临时密码机制现象mysql -uroot -p 回车后直接被拒密码怎么试都不对或者用 Navicat、IDEA Database 面板连本地库时提示 Access denied。原因MySQL 5.7 从初始化开始就不允许空密码它会生成一串随机临时密码写在日志里很多人跟着老教程一路下一步最后卡在 root 密码这关。MySQL 8.0 则是老的图形客户端不认新认证插件也会出现同样的表象。解决5.7 先查日志拿临时密码登录后立即跑一遍 mysql_secure_installation 重置密码。查询命令是 grep temporary password /var/log/mysqld.logWindows 上在安装目录的 data 文件夹里找 .err 文件。8.0 则优先升级客户端和驱动版本而不是急着把 root 的认证插件改回 mysql_native_password。实在要绕过认证重置密码mysqld --skip-grant-tables 可以启动一个临时免密实例但这是最后手段生产环境千万别这么干。5.3 Tomcat 启动失败端口被占用和 IDEA 残留进程现象点启动 Tomcat 后控制台报 SEVERE 级错误核心信息是Failed to start connector [Connector[HTTP/1.1-8080]]或者提示 Address already in use: JVM_Bind。原因8080 被其他程序占用最常见的是前一次 Tomcat 没有正常关闭就杀掉了 IDE或者机器上装了别的 Web 服务。解决先定位再处理别盲改端口。Windows 上执行netstat -ano | findstr 8080看到 LISTENING 的 PID 后taskkill /PID pid /FmacOS 和 Linux 用lsof -i:8080找到进程名再处理。如果确实不想动占用进程就改 Tomcat 的 conf/server.xml把Connector port8080改成 8081但页面里如果硬编码了 8080 的跳转地址也要同步改这一步很多人会漏。5.4 页面中文全是问号三处字符集必须对齐现象列表页中文全部显示成 ?或者录入中文后到 MySQL 里看是乱码再读回来页面也乱。原因字符集链路某一段断了。JSP 文件保存编码、JSP 声明编码、Servlet 读取编码、连接串编码、数据库表字符集这五层必须统一为 UTF-8 或兼容集任何一层不一致都会在某一端暴露。解决按顺序排查。先看 JSP 首行是否声明 UTF-8并确认 IDEA 右下角文件编码是 UTF-8再看连接串有没有 characterEncodingutf8最后进 MySQL 执行SHOW CREATE TABLE pet\G如果看到 CHARSETlatin1用ALTER TABLE pet CONVERT TO CHARACTER SET utf8mb4;转一次。已经写入的乱码数据转了字符集也救不回来直接重导 sql 脚本最省事。像“换台电脑就乱码”“本地好、打包给别人就跑起来乱码”这类看似古怪的现象九成是上面某一层没对齐。6. 让源码“不像课设”加登录拦截 Filter、接连接池、做端到端验证6.1 用几行 Filter 把登录校验抽出来现在的源码多半在每个 Servlet 里重复判断 session最直接的改进就是加一个登录过滤器。把校验逻辑收进 Filter所有受保护路径一次搞定新增页面也不用再贴一遍判断代码WebFilter(/pet/manage/*) public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest r (HttpServletRequest) req; HttpSession session r.getSession(false); if (session null || session.getAttribute(loginUser) null) { ((HttpServletResponse) resp).sendRedirect(r.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }注意拦截路径要选对别把登录页自己也拦了。把公开路径和受保护路径分开是这一步最容易被忽略的边界。6.2 把 DriverManager 换成连接池只改 JDBCUtil 这一处JDBC 直连在并发上来后会有明显瓶颈每次请求都新建物理连接用完又销毁。用一个连接池替换后连接被反复复用改动却很小——只动 JDBCUtil 的 getConnectionDAO 层代码一行都不用碰。课设常见用 C3P0实际开发我更推荐 HikariCP配置项就几个initialSize、maximumPoolSize、maxLifetime。连接池参数一旦调错现象会很玄学一会儿正常一会儿超时。我的习惯是把连接等待时间设短一点让它快速失败比所有请求都挂起好排查得多。6.3 端到端验证清单改完 Filter 和连接池做一轮从入口到数据库的完整验证别只点几下页面就说完成。参考这张清单对照着测操作预期结果失败时查哪里未登录直接访问宠物管理页被弹回登录页Filter 的拦截路径和 session 判断用 admin 登录进入宠物列表页SQL 中预置用户是否还在密码哈希是否一致新增一只宠物列表出现新数据pet 表的 category_id 是否有效普通用户提交领养申请application 出现一条状态为 0 的记录adoption 表插入语句管理员通过申请pet.status 变成 1adoption.status 变成 1两个表的更新是否在一个事务里最后一行往往被忽略。如果管理员通过申请时只改了 pet 表、没改 adoption 表数据就裂了。跨表的状态流转正确做法是用一个事务包起来或者至少保证这两条 SQL 要么都成功、要么都失败。我当年做课设时把 SQL 直接写在 JSP 里能跑但自己都说不清每个请求是怎么走的实习的时候被带我的工程师点了一句“你这是在造黑匣子”才老老实实把 Servlet 和 DAO 拆开。现在每回拿到这种 JSP Servlet MySQL 的源码我都会先按三个问题去读映射在哪、连接在哪、SQL 在哪。这三个问题能答清楚你就真正吃透了这套技术栈后面再看什么框架都不会虚。希望帮到你。本文还有配套的精品资源点击获取
返回列表