资讯动态

JSP+MySQL毕设实战:人事管理系统完整开发与避坑指南

发布时间:2026/10/6 16:43:44 来源:尧图企业网站定制
简介这套人事管理系统是基于JSPServletMySQL的JavaWeb毕业设计项目也是ERP中的人力资源管理模块实现。系统围绕企业人事信息化场景提供管理员登录注册、登录校验、员工信息新增等核心功能并将数据访问、业务处理与页面显示分离非常适合作为计算机相关专业毕设选题或课程设计参考。资源包共108个文件压缩后仅2.04MB内容涵盖12个JSP页面、11个Java源文件及对应class文件、HTML/CSS/JS静态资源、SQL数据库脚本和项目配置同时附带27张JPG界面截图便于快速比对页面实现效果个别DS_Store类垃圾文件不影响使用。项目数据库文件位于lib包下导入IDE后即可初始化数据并运行配合源码可完整梳理从前端表单到Servlet控制层、Service业务层和DAO持久层的调用链。目前已有1447人学习/下载适合需要快速理解JavaWeb分层架构、参考人事管理表设计或进行二次开发的开发者。1. 人事管理系统毕设选题JSPMySQL 这条路还走不走得通人事管理系统几乎是 JavaWeb 课程设计和毕业设计里最经典的选题没有之一。它的大小恰好卡在一个舒服的位置数据表三五张、页面十来页、增删改查齐全既能完整展示 JSPServletMySQL 这套传统技术栈又不会在答辩前把自己逼到通宵。选题本身不新鲜但很多人真正卡住的地方不是「会不会写」而是「装不上、连不通、跑起来全是乱码」。这篇文章我把从 MySQL 安装配置到 JSP 页面联调的完整路径拆开写清楚参数、命令、坑都放在对应位置顺着走一遍就能交出一套能演示、能答辩、老师愿意往下问的项目。2. 为什么 2025 年还有人用 JSP 做毕设这套技术栈的真实收益与边界2.1 JSP 不是旧是知识覆盖面大很多人在选题时会纠结一个很现实的问题现在校招都在聊 Spring Boot、微服务我做一个 JSP 项目是不是等于自断前程这个焦虑我能理解但你要把它拆开看。毕设和简历项目是两码事。毕设的核心目标是让答辩老师快速确认三件事你懂 HTTP 请求是怎么被处理的、你懂数据库建模和 SQL、你懂前端页面和后端数据是怎么对接的。JSP 技术栈恰好把这三件事全部暴露在明面上没有任何封装可以藏住你的不懂。JSP 页面本质上是一个 Servlet。容器会把.jsp文件翻译成一个 Java 类再编译成 servlet 执行。这意味着你在 JSP 里写的% %脚本片段、${}EL 表达式、c:forEach标签最终都会变成 Java 代码在服务器上跑。理解了这一层你在排查「为什么页面显示空白」「为什么变量取不到值」的时候脑子里就会有一个清晰的模型先看 JSP 翻译出来的 Java 源码再看编译后的 class 文件最后看运行时数据。这个排查路径在你以后写任何服务端渲染页面时都通用。相比之下Spring Boot 的自动装配把 Tomcat 容器、DispatcherServlet、视图解析器全替你配好了你写一个return login页面就能跳转但真要问你「这个字符串是怎么变成/login.jsp的」很多人答不上来。答辩老师往深里问三句就露馅。JSP 项目没有这个问题因为每一步跳转、每一次转发、每一个数据绑定都是你手写出来的你能讲清楚的东西远远多于框架帮你代劳的东西。2.2 JSPServlet 与 Spring Boot 的选型界限在哪我不反对你用 Spring Boot 做毕设但我反对你在 JSP 和 Spring Boot 之间犹豫不决时选了一个自己说不清的。先把两种方案的差异摆在一张表里你就知道边界了对比维度JSP Servlet JDBCSpring Boot Thymeleaf/模板学习曲线平缓知识全透明陡峭需要理解 IoC/自动装配答辩深度适合讲清原理适合讲工程化实践代码量偏多DAO 层要手写偏少MyBatis/JPA 接管排错难度异常栈直观代理对象、切面导致栈很深环境要求JDK 8 Tomcat 9 MySQLJDK 17 Maven 大量依赖适合场景课程设计、本科毕设有实习经验、能讲清框架的人我的建议很直接如果你现在还在问「JSP 和 MySQL 怎么连起来」那就选 JSP因为你现在最重要的不是项目有多新而是把 JavaWeb 这条主链路彻底走通一次。如果你已经独立写过三五个成套项目再拿 JSP 做毕设确实有点浪费时间那就去写 Spring Boot 加 Vue 分离式。毕设题目从来不是越新越好是越你能讲清楚越好。从技术演进的角度看JSP 确实已经不是主流方案但它在教学体系里依然是理解 MVC 最直接的载体。SUN 公司当年设计 JSP 的初衷就是让页面开发和业务逻辑分离——JSP 管展示Servlet 管控制JavaBean 管数据。这个 MVC 思想不会过时你日后写任何后端框架都能感受到它的影子。你做 JSP 毕设买的不是未来三五年能用在生产环境的技术而是这套 MVC 思维和一次完整的开发演练这个账要算清楚。3. 把环境立住MySQL 8.0 安装配置与人事系统三张表设计3.1 MySQL 8.0 安装的四个关键参数省得后面返工环境问题占了 JSP 毕设排障的至少一半比例。我接手过好几个卡在「MySQL 装好了但项目连不上」的求助追下去全是安装阶段埋的雷。先从下载说起不要下.msi图形化安装版本去官网下 ZIP 压缩包版本。ZIP 包干净、可重复、卸载时删文件夹就行对毕设这种一次性的环境足够了。你搜「mysql 安装教程」能看到各种版本认准 8.0.x 的 ZIP 包。解压之后在根目录新建my.ini这是我每次都会用的一套最小配置[mysqld] # 端口号3306 被占用时再改这里 port3306 # 安装目录改成你自己的实际路径 basedirD:/mysql-8.0.40-winx64 # 数据目录不要放在 C 盘避免权限问题 datadirD:/mysql-8.0.40-winx64/data # 字符集必须整套指定否则建表默认 latin1 character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci # 时区问题JDBC 连接串里也要保持一致 default-time-zone08:00 # 本地开发关闭严格模式避免 date 字段默认值报错 sql_modeSTRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION max_connections200 [client] default-character-setutf8mb4这段配置里character-set-server和collation-server是最容易踩坑的地方。很多人装完 MySQL 不去动字符集结果建表默认latin1JSP 页面往数据库里插中文变成???然后到处找「中文乱码」的解决办法。其实根就在这个 ini 文件里。default-time-zone08:00也是个大坑如果你不写MySQL 8.0 默认用 UTC 时区你now()出来的时间比北京时间少 8 小时而你 JDBC 连接串里如果再传一个serverTimezoneUTC两边一致倒是没问题但一旦混用就全乱了。接下来是初始化和管理命令用管理员身份打开 CMD顺序执行# 进入 MySQL 的 bin 目录 cd /d D:\mysql-8.0.40-winx64\bin # 初始化数据目录8.0 必须做这一步否则服务起不来 mysqld --initialize-insecure # 安装 Windows 服务服务名建议用 mysql80避免和你以后装的版本冲突 mysqld --install mysql80 # 启动服务 net start mysql80 # 登录--initialize-insecure 生成了空密码 root 账号 mysql -uroot -p--initialize-insecure的含义是初始化数据目录并且 root 账号为空密码。千万别用--initialize那个会生成一个随机密码第一次登录还要去data目录下的.err日志里翻对毕设来说没必要。执行完net start mysql80如果报「服务无法启动」先去data目录下的主机名.err文件看错误最常见的两个原因一个是my.ini路径写错导致 basedir 找不到另一个是data目录已经被初始化过删掉data文件夹重新执行初始化即可。连接上以后记得改密码和允许远程访问这一步平时没人提等你写完项目发现在另一台电脑上连不上自己的库就晚了-- 把 root 密码改成你项目里要用的比如 123456 ALTER USER rootlocalhost IDENTIFIED BY 123456; -- 创建远程访问账号方便你从别的机器连数据库调试 CREATE USER root% IDENTIFIED BY 123456; GRANT ALL PRIVILEGES ON *.* TO root% WITH GRANT OPTION; FLUSH PRIVILEGES;3.2 人事系统的表设计三张表建好后面少写一半重复代码人事管理系统的业务其实就两块管人和管部门。绝大多数增删改查都围绕这两块展开。所以核心表设计我建议控制在三张表部门表、员工表、用户表。CREATE DATABASE hrms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE hrms; -- 部门表 CREATE TABLE dept ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 部门ID, name VARCHAR(50) NOT NULL UNIQUE COMMENT 部门名称, manager VARCHAR(20) DEFAULT NULL COMMENT 负责人, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB COMMENT部门表; -- 员工表 CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 员工ID, emp_no VARCHAR(20) NOT NULL UNIQUE COMMENT 工号, name VARCHAR(20) NOT NULL COMMENT 姓名, gender CHAR(1) DEFAULT 男 COMMENT 性别, birthday DATE DEFAULT NULL COMMENT 出生日期, dept_id INT COMMENT 所属部门ID, position VARCHAR(30) DEFAULT NULL COMMENT 职位, salary DECIMAL(10,2) DEFAULT 0.00 COMMENT 薪资, entry_date DATE DEFAULT NULL COMMENT 入职日期, status TINYINT DEFAULT 1 COMMENT 1在职 0离职, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id) ON DELETE SET NULL ) ENGINEInnoDB COMMENT员工表; -- 用户表登录功能用的 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE COMMENT 登录名, password CHAR(32) NOT NULL COMMENT MD5密码, real_name VARCHAR(20) DEFAULT NULL COMMENT 真实姓名, role TINYINT DEFAULT 1 COMMENT 1管理员 2普通操作员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT系统用户表;三张表的设计是有讲究的。employee.dept_id外键指向dept.id并且ON DELETE SET NULL意思是删除一个部门后该部门下的员工dept_id变为 NULL员工数据不丢失。这个设计在答辩时是个加分点老师问「删部门时员工怎么办」你能答出SET NULL而不是「删除部门前手动清员工」这体现的是你对引用完整性的理解。常用的排序和查询场景我也提前给你铺好。员工表查询最常用的两个条件是按部门筛选和按入职日期排序以及查「某部门薪资最高的员工」。这些语句建议现在就在纳品里练熟后面写 DAO 层时不用现想-- 按部门分组查平均薪资 SELECT dept_id, AVG(salary) AS avg_salary FROM employee GROUP BY dept_id; -- 查薪资最高的前 5 名员工 SELECT * FROM employee ORDER BY salary DESC LIMIT 5;MySQL 的ORDER BY遇到 NULL 值要特别注意。默认排序规则里ASC时 NULL 排最前DESC时 NULL 排最后。这个规则在 MySQL 8.0 里是固定的如果你需要把 NULL 强制排到最后用ORDER BY ISNULL(salary), salary ASC这种写法。答辩的时候冷不丁问一句排序边界你能答出这个细节会非常加分。表建完之后往里插几条测试数据。注意员工表的emp_no是唯一键测试数据别用重复工号。我一般会插两条中文姓名、三个部门、两个用户确保登录、查询、删除都有数据可演示。不要插太多造数据两三行足够你在页面上看到效果了。4. 把这套骨架跑起来登录拦截、JDBC 连接池与分页查询的完整代码4.1 从登录页到 LoginServlet一次请求的完整生命周期技术栈选定之后代码从哪下手我建议先做登录功能因为它串起了浏览器、Servlet、JDBC、数据库四层是整条主链路的微缩版。登录页用一个简洁的 JSP注意表单提交方式必须是 POSTaction 指向 Servlet 的 URL 映射% page contentTypetext/html;charsetUTF-8 languagejava % html head title人事管理系统登录/title /head body h2人事管理系统/h2 form action${pageContext.request.contextPath}/login methodpost 用户名input typetext nameusernamebr 密 码input typepassword namepasswordbr span stylecolor:red${msg}/spanbr input typesubmit value登录 /form /body /html这里action路径用${pageContext.request.contextPath}拼接项目上下文路径用 EL 表达式取出来这样项目部署后改名也不会把表单请求路径写死。${msg}是后面 Servlet 里request.setAttribute(msg, ...)存进去的错误提示。POST提交保证了密码不在地址栏明文暴露——虽然这个项目密码用了 MD5 存储但传输层没有加密毕设层面够用就行。对应的 LoginServlet 处理逻辑如下WebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); String username request.getParameter(username); String password request.getParameter(password); // 用 MD5 加密后再查库避免明文密码直接参与 SQL 拼接 String md5Pwd MD5Util.md5(password); // 这里使用连接池Servlet 里不手动打开/关闭 Connection try (Connection conn DBUtil.getConnection()) { String sql SELECT * FROM sys_user WHERE username? AND password?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, md5Pwd); ResultSet rs ps.executeQuery(); if (rs.next()) { // 登录成功把用户信息放进 session后续 Filter 靠它判断是否放行 request.getSession().setAttribute(loginUser, username); request.getSession().setAttribute(realName, rs.getString(real_name)); response.sendRedirect(request.getContextPath() /employee/list); } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); } } catch (Exception e) { e.printStackTrace(); request.setAttribute(msg, 系统异常请稍后重试); request.getRequestDispatcher(/login.jsp).forward(request, response); } } }这套代码的核心是PreparedStatement的使用。用占位符?传参数而不是把用户输入直接拼进 SQL 字符串这是最基础的 SQL 注入防线。很多毕设项目死在答辩老师随口一句「你能防 SQL 注入吗」你写出PreparedStatement就能答上来。另外try-with-resources语法保证了Connection、PreparedStatement、ResultSet三层资源自动关闭不写finally手动关代码少四五行且不会漏关连接。连接池这一层我强烈建议你用 Druid 或者 C3P0不要再用DriverManager每次手动创建连接。别小看这个选择Druid 自带监控页面答辩时打开监控页展示 SQL 执行次数和慢查询统计这属于「超出毕设预期」的亮点老师会眼前一亮。最小配置用druid-1.2.8.jar加一个druid.properties文件driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/hrms?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue usernameroot password123456 initialSize5 maxActive20 maxWait3000注意连接串里的每个参数都有用。useUnicodetruecharacterEncodingutf8让 JDBC 传输层用 UTF-8 编码配合 MySQL 服务端utf8mb4才是完整链路。serverTimezoneAsia/Shanghai对应之前 my.ini 里的default-time-zone两边必须一致差一点就会出现日期字段偏移 8 小时。useSSLfalse和allowPublicKeyRetrievaltrue是用来规避 MySQL 8.0 的 SSL 连接报错的——你搜「mysql ssl连接错误」能搜出几百个帖子本质就是本地没配 SSL 证书关掉就行。maxWait3000是拿连接的超时时间如果设成 0连接池耗尽的瞬间线程会无限期阻塞页面卡死。设一个明确的超时时间失败时会给异常而不是死等。4.2 用 Filter 把登录拦截和字符集统一一起解决登录做完之后紧接着要解决的是「没登录能不能直接访问页面」。这个场景天然属于 Filter 的职责范围。同时把请求和响应的字符集编码统一放在 Filter 里做比在每个 Servlet 里重复写两行要干净得多WebFilter(/*) public class CharsetAndAuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; // 统一字符集放在 Filter 里就不用每个 Servlet 再重复 set 了 request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); // 放行登录页、登录接口和静态资源 String uri request.getRequestURI(); if (uri.endsWith(/login.jsp) || uri.endsWith(/login) || uri.contains(/static/) || uri.endsWith(.css) || uri.endsWith(.js)) { chain.doFilter(request, response); return; } // 检查 session 里有没有登录标记 Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { // 未登录跳回登录页 response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(request, response); } }Filter 的拦截逻辑里放行条件一定要写全。常见翻车现场是把.css和.js也拦了导致登录页样式全丢只剩纯 HTML 元素排版。判断路径时用endsWith而不是contains因为contains会把/login和/login.jsp都拦下来而且如果项目里还有其他含login字样的路径也会被误伤。这个细节虽然小但反映的是你对 URL 语义的理解。Filter 还有个容易被忽略的坑如果你在 Filter 里做了response.sendRedirect那之后就不要继续调用chain.doFilter了因为响应已经提交继续调用会导致IllegalStateException。很多新手在这上面翻车现象是「有时能跳转有时报错」排查半天发现是 if-else 结构没写对return 被吞了。4.3 员工列表分页查询和搜索条件的 SQL 拼接员工列表页是人事管理系统的主界面也是 DAO 层最值得做厚的地方。分页查询的 SQL 用LIMIT语法但要注意LIMIT的两个参数含义第一个是偏移量第二个是条数。页码从 1 开始的话偏移量计算公式是(pageNum - 1) * pageSize。public ListEmployee selectByPage(int pageNum, int pageSize, String keyword) { String sql SELECT e.*, d.name AS dept_name FROM employee e LEFT JOIN dept d ON e.dept_id d.id WHERE e.name LIKE ? OR e.emp_no LIKE ? ORDER BY e.entry_date DESC, e.id DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { String like % keyword %; ps.setString(1, like); ps.setString(2, like); ps.setInt(3, (pageNum - 1) * pageSize); ps.setInt(4, pageSize); ResultSet rs ps.executeQuery(); ListEmployee list new ArrayList(); while (rs.next()) { Employee emp new Employee(); emp.setId(rs.getInt(id)); emp.setName(rs.getString(name)); emp.setEmpNo(rs.getString(emp_no)); emp.setDeptName(rs.getString(dept_name)); emp.setSalary(rs.getBigDecimal(salary)); emp.setEntryDate(rs.getDate(entry_date)); list.add(emp); } return list; } catch (Exception e) { throw new RuntimeException(分页查询失败, e); } }这个 SQL 里有两个细节值得在答辩时主动讲。第一个是LEFT JOIN的选择——用LEFT JOIN而不是INNER JOIN是因为员工表里可能有dept_id为 NULL 的离职员工内连接会把这些人过滤掉页面数据不完整。第二个是排序字段的选择——ORDER BY e.entry_date DESC, e.id DESC用了两个字段因为同一天入职的人可能很多只按日期排无法保证顺序稳定追加id DESC才保证分页时每页数据不重不漏。LIKE 查询的模糊匹配走不了索引这点也是答辩高频题。你的关键词搜索只在员工表和工号里做数据量撑死几百条全表扫描完全没压力但如果数据量到了几万条就要考虑用全文索引或改前缀匹配。你主动说出来这段话老师会认为你不只写了代码还想过性能边界。5. 避坑指南JSPMySQL 项目里高频踩到的 6 个具体问题5.1 JDBC 驱动类名与版本错位导致 ClassNotFoundException现象项目在别人电脑上能跑自己电脑上启动 Tomcat 后访问页面直接报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver。原因你用的 MySQL 驱动 JAR 包是 8.x 版本但代码里还写着 5.x 时代的驱动类名com.mysql.jdbc.Driver。MySQL 官方在 Connector/J 8.0 里把驱动类改名成了com.mysql.cj.jdbc.Driver老名字在 8.x 里默认不加载。还有一种情况是驱动 JAR 没放进WEB-INF/lib目录而是放在了 Tomcat 的 lib 下。Tomcat 的类加载机制是「应用优先加载自己 lib 下的类」不会去找全局 lib所以放在全局目录里应用一样报类找不到。解决优先检查两处——第一WEB-INF/lib下有没有 mysql-connector-java 的 jar 包第二驱动类名和驱动 JAR 版本是否匹配。8.x 连接串写法里的com.mysql.cj.jdbc.Driver且连接串必须有serverTimezone参数。5.x 时代可以不写时区8.x 不写给报错The server time zone value is unrecognized。5.2 中文乱码三层要一起设少一层都是问号现象JSP 页面显示正常插入数据库后中文变???。原因乱码从来不是单一环节的问题而是请求参数编码、数据库表字符集、JDBC 连接串编码三层中某一层没对齐。浏览器提交表单时用页面编码Tomcat 接收请求参数时默认用 ISO-8859-1MySQL 存储时看表和库的字符集JDBC 传输时看连接串的characterEncoding。任何一层不一致中文就出问题。解决三层全部统一到 UTF-8。第一层JSP 页面头部写contentTypetext/html;charsetUTF-8POST请求在 Filter 或 Servlet 里执行request.setCharacterEncoding(UTF-8)。第二层建库语句指定DEFAULT CHARACTER SET utf8mb4检查现有表用SHOW CREATE TABLE employee看默认字符集。第三层JDBC 连接串里加useUnicodetruecharacterEncodingutf8。排查顺序从数据库开始先在命令行用INSERT中文查出来如果是问号说明是 MySQL 侧问题命令行正常但 JSP 插入是问号就是 JDBC 连接串问题。有的朋友会在这时候去改my.ini加上init_connectSET NAMES utf8mb4对于本地单应用来说没有必要固定连接串参数就够改 init_connect 影响的是所有连接反而容易造成时区和字符集的隐性不一致。5.3 Tomcat 10 的 jakarta 包名与老代码的水土不服现象把一套之前在 Tomcat 9 下能跑的项目放进 Tomcat 10启动后报错java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet之类。原因Tomcat 10 开始把 Servlet API 从javax.servlet迁移到了jakarta.servlet旧代码里import javax.servlet.*的类全部找不到。这是 Oracle 把 Java EE 捐给 Eclipse 基金会后的品牌切割代码层面是包名整体更名底层类和 API 接口几乎没有区别。解决毕设阶段最省事的办法是不要碰 Tomcat 10。下载 Tomcat 9 版本保持在javax.servlet包名体系下和你的教材、网上大部分 JSP 教程保持一致。如果你已经装了 Tomcat 10不想卸重装那就要把项目里所有import javax.servlet改成import jakarta.servlet同时 Tomcat 的web.xml还有你依赖的第三方库比如 JSTL也要换成jakarta命名空间版本。对毕设来说这个纯属浪费时间直接换 Tomcat 9 是正经答案。下载时留意官网目录里的Tomcat 9和Tomcat 10标签别一眼晃过去下错。5.4 MySQL 排序 NULL 值你以为按薪资降序结果 NULL 排最前现象员工列表按薪资排序离职员工或薪资字段没填的记录 NULL 全堆在最上面把正常数据挤下去了。原因MySQL 的ORDER BY默认把 NULL 当作最小值。ASC时 NULL 排最前DESC时 NULL 排最后。这个规则其实和大多数人的直觉相反——直觉上 NULL 是「空」应该排最后但 MySQL 的定义是 NULL 最小所以降序时 NULL 反而在末尾。区分一下DESC时 NULL 排最后正合你意ASC时 NULL 排最前往往不合你意。解决如果你要强制 NULL 到最底部不改变其他字段排序规则可以这样写ORDER BY (salary IS NULL), salary DESC。salary IS NULL这个表达式的结果是 0非空或 1NULL0会排在1前面这就是「把 NULL 强制沉底」的底层逻辑。同理要把 NULL 强制放最上面用ORDER BY (salary IS NOT NULL), salary DESC。这种写法在 Oracle、PostgreSQL 里同样适用SQL 标准里你还可以用NULLS FIRST/LAST但 MySQL 直到 8.0.30 还不支持这个子句所以只能用上面的表达式写法。5.5 JSP 图片坐标定位相对路径在转发表单里失效现象JSP 页面里img srcimages/logo.png在登录页显示正常跳转到子目录页面或者经过 Servlet 转发后的页面上图片裂了。原因srcimages/logo.png是相对路径浏览器解析时是以当前访问的 URL 路径为基准去拼的。如果当前 URL 是/employee/list浏览器会把它解析成/employee/images/logo.png而你的图片实际在项目根目录的/images/logo.png。登录页通常直接注册为根路径所以正好对上经过 Servlet 转发或 Filter 拦截的 URL 路径一变相对路径就错位。解决JSP 页面里不要写相对路径全部使用${pageContext.request.contextPath}拼绝对路径img src${pageContext.request.contextPath}/images/logo.png。涉及表单提交后的重定向跳转也一样。另外要注意如果你的页面中有base标签它能改变所有相对路径的基准但用得不熟容易把已经正确的绝对路径也带歪风险更大我一般建议直接用 EL 前缀不引入 base 标签。图片坐标定位这个需求在人事系统里常见于「部门架构图」或「员工照片墙」页面HTML 里对图片定位可以用 CSSposition: relative加top/left偏移但前提是图片路径先解决否则定位好了也显示不出来。5.6 事务忘了提交删除部门后员工表出现悬挂引用现象删掉一个部门部门列表没了但员工列表里员工还挂在已删除部门的名下部门名称显示为空。原因删除部门的外键策略是ON DELETE SET NULL这个约束在 MySQL InnoDB 里只要执行 DELETE 就会自动生效。如果伸出问题多半是你自定义的删除逻辑里先删部门再更新员工或者用多条 SQL 在一个事务里处理了删除和回填但没有COMMIT导致约束层面的自动动作没有持久化。另一种更隐蔽的情况是 JDBC 连接默认是自动提交但你在代码里开启了事务conn.setAutoCommit(false)后只做了 DELETE忘了commit()直接关了连接事务回滚部门删除了但外键动作没生效。解决多表操作的业务一律显式控制事务三行代码不能少conn.setAutoCommit(false); try { // 执行删除部门等操作 conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); }判断是不是这个原因有个笨办法在 MySQL 命令行手动执行DELETE FROM dept WHERE id1然后查询employee.dept_id如果外键生效关联员工会自动变 NULL。命令行下正常而程序里不正常那就去代码里检查setAutoCommit(false)和commit()之间的逻辑是否有异常分支把 commit 跳过了。MySQL 事务这一块答好了在答辩时跟老师聊锁分类——共享锁、排他锁、意向锁的区别——都是加分项至少说明你读过 InnoDB 的锁粒度。6. 从「能跑」到「能答辩」压测、索引与事务的验证方法项目联调完之后不要急着打包交差。用这三个方法把项目从「能跑」推到「能讲」的层次同时也帮你自己在答辩前把系统真实情况摸清楚。第一个是压测登录接口。用 JMeter 建一个线程组10 个线程循环 20 次打/login这个 POST 接口观察平均响应时间和错误率。本地单机 MySQL 不做任何优化时这个接口的响应时间正常应该在 50ms 到 150ms 之间。如果你的结果超过 500ms 甚至上千毫秒说明 SQL 有问题检查是否全表扫描、连接池是否没生效、是否存在 N1 查询。你带着一份压测报告去答辩比任何口头描述都有说服力。JMeter 的聚合报告可以截图插到毕设论文的测试章节这是很多论文里「性能测试」一节的真实来源。第二个是检查慢查询日志。执行下面两条命令把 MySQL 的慢查询阈值设为 1 秒跑一遍核心操作然后看日志里有没有超过阈值的 SQLSET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1; SHOW VARIABLES LIKE slow_query_log_file;如果你发现某条 SQL 进了慢查询日志最常规的优化是在employee.name和employee.emp_no上建普通索引。注意LIKE %关键字%这种模糊匹配不加索引但WHERE emp_no EMP001这种等值查询可以走索引。面试时聊 MySQL 排序和索引能说出「等值走索引、模糊前导通吃」的人比死背八股文的印象分高不少。如果你按标题搜过「mysql创建索引」和「mysql排序」相关的内容正好可以把这些积累整理成答辩素材。第三个是事务演示。故意做一个操作性演示在员工管理页面同时提交两个操作——新插入一条员工记录同时更新一个部门的负责人。把 SESSION A 里的插入操作开启事务但不要提交然后在命令行里到 SESSION B 查这条数据是否存在让老师亲眼看到「读不到未提交数据」提交后再查数据可见。这个演示直接对应 ACID 的隔离性造价低、效果直观。你给老师讲什么是「不可重复读」和「幻读」用这个现场实验比背定义强得多。回到最初的问题JSPMySQL 做人事管理系统到底值不值得。我的结论是值得且性价比很高。这套题能覆盖 JavaWeb 的全部主干知识排坑过程本身就是在训练你排查问题的方法论——先把环境立对再把链路走通最后才是功能堆积。我当年做这套题时在 MySQL 8.0 的时区参数上栽过跟头页面里全乱码的日子现在想起来也谈不上愉快但正是这些坑让我后来用 Spring Boot 时格外注意连接串里的每一个参数。把这些经验留在你的毕设记录里答辩时你会讲得有底气做的过程也能真的学到东西。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑