
简介面向Java毕业设计、课程设计及期末大作业人群基于JSPMySQL技术搭建的B/S结构人事人力资源管理系统覆盖管理员后台、部门信息、员工信息、合同管理、绩效管理工资与奖惩和考勤管理模块适合初学者对照学习Web项目开发与数据库交互。压缩包共116个文件大小3.02MB以69个JSP页面为主构成业务界面配合CSS、JavaScript完善前端效果jar包提供运行依赖SQL脚本用于还原MySQL数据库docx为开题报告文档目录结构清晰便于定位。目前已有64人学习下载。通过完整源码可以查阅各模块后台管理逻辑与页面跳转关系数据库文件能让系统快速恢复演示环境开题报告则补充了选题背景和设计思路对完成毕业设计、期末大作业或理解传统JSP项目开发流程都有实用价值。1. 这套人事系统的价值不在页面而在数据库的耦合设计一个基于 JSP 的毕业设计人事系统放在今天去拆很多人第一反应是“技术栈太老”。但实际上这类系统最适合用来建立对 Java Web 全流程的完整认知尤其是数据库表之间的状态耦合关系这套系统里几乎每一张表都在互相约束。管理员、部门、员工、合同、绩效、考勤六个模块看起来独立实际上员工表靠部门表兜底合同表靠员工表联动考勤结果又直接灌进绩效计算里。对于正在做 Java 期末大作业或毕业设计的开发者这套代码的参考价值不在某个页面做得多漂亮而在于它把 MVC 的边界和 JDBC 事务处理完整地串了起来对于有五年以上经验的工程师反而值得花时间看的是这套系统的表设计和权限控制思路很多新项目在这方面的设计未必比它严谨。适合两类人一类是需要交付计算机期末大作业但不知道模块边界怎么划的学生另一类是面试前想快速复盘 Servlet 生命周期、JDBC 预编译和 Session 管理细节的 Java 开发者。2. 架构分层与 JDBC 数据访问2.1 表示层、业务层、数据层的边界到底怎么切这套系统采用的是经典 JSP Servlet JDBC 三层结构虽然表面上看页面文件直接放在 WebRoot 下实际运行时的调用链路是清晰的浏览器请求页面后JSP 负责渲染Servlet 承接请求参数并调用具体的业务处理数据访问统一通过 JDBC 完成。这里的核心是业务层不能直接拼 SQL而是通过 DAO 类把数据操作封装起来。这个项目里常见的做法是每个表对应一个 DAO 类比如 EmployeeDao、DeptDao、AttendanceDao每个 DAO 类的方法名直接反映业务意图例如findEmployeeByDeptId(String deptId)和updateSalaryByEmpId(String empId, BigDecimal salary)。从分层合理性上评估这套系统的边界划分基本符合 JSP 时代生产项目的主流写法。有一点会被新手忽略JSP 页面里存在大量% %脚本片段这是模型 1 向模型 2 过渡时期的设计方式责任划分不像 Spring MVC 那样有强制约束。所以在写大作业时如果要改造优先把 JSP 里的 Java 代码抽到 Servlet 或独立的业务类里页面只保留 JSTL 标签和 EL 表达式。这样做的直接收益是后续加功能时不用在页面里猜数据是从哪来的。分层结构对应的职责如下| 层 | 载体 | 核心职责 | |------|------|-----------| | 表示层 | JSP / CSS / JavaScript | 接收用户输入、渲染列表和表单 | | 业务层 | Servlet JavaBean | 参数校验、业务规则计算、调用 DAO | | 数据层 | DAO 类 JDBC | 封装对员工、部门等表的增删改查 | | 数据库 | MySQL | 存储数据、维护外键和唯一约束 |2.2 JDBC 连接与预编译这套系统最值得读的一段代码JDBC 连接这块建议优先找这个项目里的数据库连接工具类通常是DBUtil或者BaseDao。代码逻辑一般是读取属性文件中的驱动、URL、用户名和密码然后通过Class.forName加载驱动用DriverManager.getConnection创建连接。对于一个毕业设计的完整度来说这个类还承担了连接关闭的回收工作。下面的示例结构是这套系统里比较常见的写法可以直接对照源码看package com.hr.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBUtil { private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/hr_db?characterEncodingutf-8; private static final String USER root; private static final String PASSWORD root; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); if (stmt ! null) stmt.close(); if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这段代码在博文和技术博客里被反复引用原因在于它展示了 JDBC 使用的三个关键动作加载驱动、获取连接、释放资源。资源释放的顺序必须是从 ResultSet 到 Statement 再到 Connection 反向关闭实操中经常有人只关 Connection导致数据库连接池被占满。这套系统的数据库连接没有使用连接池是直连方式所以每执行一次查询都会新建物理连接。开发时感受不到问题并发量上来后就会出现响应变慢、数据库连接数被打满的现象这也是面试时经常被问到的一个考点。2.3 查询参数绑定与 SQL 注入的边界在员工信息查询、考勤记录筛选这类模块中项目普遍使用PreparedStatement做参数绑定。典型实现方式是调用conn.prepareStatement(sql)获取预编译对象再用setString、setInt等方法给占位符赋值。相比直接拼接字符串PreparedStatement会在数据库端把 SQL 结构固定下来参数内容作为纯数据处理从机制上切断注入路径。public Employee findByEmpNo(String empNo) { String sql SELECT * FROM employee WHERE emp_no ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, empNo); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { Employee emp new Employee(); emp.setEmpId(rs.getString(emp_id)); emp.setEmpName(rs.getString(emp_name)); emp.setDeptId(rs.getString(dept_id)); emp.setSalary(rs.getBigDecimal(salary)); return emp; } } } catch (SQLException e) { e.printStackTrace(); } return null; }这里有一个容易踩的坑try-with-resources写法在 JDK 7 之后才支持而 Tomcat 和 MyEclipse 组合的老环境默认编译级别可能是 JDK 1.6。如果导入源码后编译报语法错误要先去项目的编译级别设置里确认。另外一个细节是com.mysql.jdbc.Driver在 MySQL 5.x 时代是标准写法但到了 MySQL 8.x驱动类变成了com.mysql.cj.jdbc.DriverURL 还需要追加serverTimezoneAsia/Shanghai否则会报时区错误。这是五年以上工程师也会碰到的兼容性问题不是基础概念没掌握而是技术版本演进导致的认知断层。3. 人事系统的数据库设计与模块联动重点是那张员工表3.1 六张核心表的结构说明这套系统的数据库文件里主要包含管理员表、部门表、员工表、合同表、考勤表、绩效表。部门表和员工表通过dept_id关联员工表和合同表通过emp_id关联考勤表和员工表通过emp_id关联绩效表里的工资和奖惩记录又指向员工。这个设计的核心是员工表被多张业务表引用因此它的字段设计决定了整个系统能跑多远。表名核心字段关联字段deptdept_id, dept_name, manager无employeeemp_id, emp_name, dept_id, salary, hire_datedept_id - deptcontractcontract_id, emp_id, start_date, end_date, typeemp_id - employeeattendanceatt_id, emp_id, work_date, statusemp_id - employeeperformanceperf_id, emp_id, score, bonus, penalty, monthemp_id - employeeadmin_useruser_id, username, password无从建表角度来看这套系统的设计思路符合第三范式的基础要求业务数据不冗余员工姓名不会在考勤表和绩效表里重复存储使用时通过JOIN查询获取。面试时如果被问到“为什么合同信息不直接存在员工表里”答案就是合同可能经历多次续签一个员工对应多条合同记录拆成独立表才能保留历史状态。3.2 建表脚本里的关键约束写法打开数据库文件中的 SQL 脚本建表语句里值得关注的是字段类型的选择和默认值的设置。工资字段通常使用DECIMAL(10,2)而不是FLOAT因为金额需要精确计算入职日期使用DATE类型而不是字符串性别和合同类型使用TINYINT或CHAR(1)加注释避免魔法数扩散。CREATE TABLE employee ( emp_id VARCHAR(32) PRIMARY KEY COMMENT 员工编号, emp_name VARCHAR(50) NOT NULL COMMENT 姓名, dept_id VARCHAR(32) NOT NULL COMMENT 所属部门, gender CHAR(1) DEFAULT 1 COMMENT 1-男 2-女, salary DECIMAL(10,2) DEFAULT 0.00 COMMENT 基本工资, hire_date DATE COMMENT 入职日期, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8;主键采用VARCHAR而不是自增INT是因为员工编号往往带有业务含义比如部门前缀加流水号。外键约束的建立保证了删除部门时如果部门下还有员工数据库会直接拒绝避免出现孤儿数据。这里有一个实际开发中的取舍点外键约束在高并发写入场景下会影响性能所以互联网公司普遍不建物理外键但在毕业设计和期末大作业里保留外键反而能体现对数据一致性的理解深度不用刻意去掉。3.3 考勤结果怎么流向绩效计算考勤模块在人事系统里的通常逻辑是管理员按照月份录入员工考勤状态状态字段包括正常、迟到、早退、旷工和请假。绩效模块再根据当月考勤统计结果自动调整工资项。这套系统里的实现方式是考勤表记录每天的状态绩效表按月汇总工资等于基本工资加奖金减罚款。SELECT e.emp_id, e.emp_name, d.dept_name, e.salary, IFNULL(p.bonus, 0) AS bonus, IFNULL(p.penalty, 0) AS penalty, (e.salary IFNULL(p.bonus, 0) - IFNULL(p.penalty, 0)) AS final_salary FROM employee e LEFT JOIN dept d ON e.dept_id d.dept_id LEFT JOIN performance p ON e.emp_id p.emp_id AND p.month 2025-06这个查询逻辑展示了一个关键设计绩效表按月存储一个员工一个月只对应一条绩效记录主键可以设计为emp_id month的联合唯一索引。如果当月没有绩效记录LEFT JOIN配合IFNULL能把工资计算结果兜住不会因为缺少绩效行而查不到员工。这是一个在 JSP 大作业里体现业务完整度的重要细节企面上很多学生写的查询直接使用INNER JOIN一旦绩效表没有当月记录整行数据就从列表里消失管理员会误以为员工被删除了。4. 从源码到可运行Tomcat 部署与调试细节4.1 环境版本匹配是第一道门槛这套系统的开发环境是 MyEclipse服务端是 Tomcat数据库是 MySQL运行前必须先确认版本匹配。常见的版本组合是 JDK 1.7 或 1.8、Tomcat 7 或 8、MySQL 5.5 到 5.7。如果本机装的是 MySQL 8.x需要同时替换 JDBC 驱动包为mysql-connector-java-8.0.x.jar并修改DBUtil里的驱动类和 URL 参数。注意修改后要重新编译整个项目只替换 JAR 文件不够因为加载驱动的代码还指向旧类名。导入 MyEclipse 时有一个高频坑WebRoot 目录没有被识别为 Web 根目录导致部署后访问 404。手动处理路径的方式是右键项目 - Properties - MyEclipse - Web将 Web Content 目录和部署上下文都检查一遍确保 WebRoot 是被标记为可发布的。还有一种情况是项目的.classpath文件里引用了本机不存在的 JAR 路径导入后如果大量类报红直接在 Libraries 里把失效引用移除重新添加 Tomcat 运行环境和 JDK 库。4.2 数据库导入与账号配置数据库文件通常是.sql格式。导入时建议用命令行而不是图形化工具避免编码问题导致的中文乱码mysql -u root -p hr_db.sql导入完成后查看是否成功mysql -u root -p -e SHOW TABLES FROM hr_db;如果导入过程中报Unknown database需要手动先建库再导入CREATE DATABASE hr_db DEFAULT CHARACTER SET utf8;数据库层面确认后再去源码里核对连接账号和密码。DBUtil 中如果没有使用外部配置文件连接参数是硬编码的那就要和本地数据库账号保持一致否则启动后查询页面会直接报Access denied for user。注意检查数据库连接地址中的localhost:3306是否和本地端口一致如果有多个 MySQL 实例或修改过默认端口这里经常对不上。4.3 部署到 Tomcat 的完整路径如果不想用 MyEclipse 自带部署可以手动把工程编译后的结构放到 Tomcat 的 webapps 下。这个过程有利于理解 JSP 项目在容器中的真实运行方式。首先确认项目的classes目录中包含所有编译后的 class 文件和配置文件lib目录中包含 JDBC 驱动 JAR。然后用压缩工具将 WebRoot 打成 WAR 包有两个注意点一是 WAR 包的目录结构必须保持 WEB-INF 在根路径下二是压缩时不能把外层文件夹本身包含进去。cd WebRoot jar cvf hr_system.war .把生成的hr_system.war复制到 Tomcat 的webapps目录下cp hr_system.war /usr/local/tomcat/webapps/ cd /usr/local/tomcat/bin ./startup.shTomcat 启动时会自动解压 WAR 包然后通过浏览器访问http://localhost:8080/hr_system/即可看到登录页面。如果访问仍然 404检查 Tomcat 启动日志catalina.out常见错误无非是端口占用、Servlet 映射路径写错、JDBC 驱动类找不到三种。4.4 常见运行时报错对照表| 报错现象 | 直接原因 | 处理方式 | |-----------------------------|-------------------------------|-------------------------------| | 404 页面不存在 | 部署路径与访问路径不一致 | 检查 context path 是否为 /hr_system | | 无法连接数据库 | URL、用户名或密码不匹配 | 核对 DBUtil 中数据库配置 | | ClassNotFoundException: JDBC | 驱动 JAR 未放入 WEB-INF/lib | 复制 mysql-connector JAR | | 中文乱码 | 页面、连接、数据库编码不一致 | 统一使用 UTF-8并加 characterEncoding | | Tomcat 端口冲突 | 8080 被其他进程占用 | 修改 server.xml 或结束占用进程 |这些报错里乱码问题比较隐蔽。要确认三层因素JSP 页面头部的contentType是否设置了 UTF-8、JDBC URL 中是否添加了characterEncodingutf-8、MySQL 表结构本身的字符集是否为 utf8。三层中任何一层缺了页面展示就可能出现问号。有一个快速验证排查思路先用命令行直接插入一条中文数据再通过系统页面查询如果命令行插入的也是乱码说明是数据库创建时字符集设置的问题而不是系统代码的问题。5. 把演示视频里的手动流程转成可重复的验证脚本演示视频展示的是管理员登录后逐个操作菜单的过程操作步骤多且有顺序依赖。项目的加分做法是把这套手动验收流程固化成 SQL 验证脚本无论哪个环境部署跑一遍脚本就能确认系统是否可用。-- 1. 验证管理员登录账号是否可用 SELECT * FROM admin_user WHERE username admin; -- 2. 验证部门-员工-考勤-绩效链路是否完整 SELECT COUNT(*) AS emp_cnt FROM employee WHERE dept_id D001; SELECT a.emp_id, COUNT(CASE WHEN a.status 正常 THEN 1 END) AS normal_days, COUNT(CASE WHEN a.status 迟到 THEN 1 END) AS late_days FROM attendance a WHERE DATE_FORMAT(a.work_date, %Y-%m) 2025-06 GROUP BY a.emp_id; -- 3. 验证绩效计算结果 SELECT e.emp_id, e.salary, p.bonus, p.penalty, (e.salary IFNULL(p.bonus, 0) - IFNULL(p.penalty, 0)) AS pay FROM employee e LEFT JOIN performance p ON e.emp_id p.emp_id WHERE p.month 2025-06;这个方法对后续做 JSP 期末大作业答辩非常实用。很多毕业生在演示时卡在“数据不足导致页面空白”的场景提前用脚本构造出部门和员工数据再准备几条跨月考勤记录和绩效记录页面就能表现出完整效果。熟练的工程师还可以进一步把数据库初始数据抽出来做成data.sql单独文件每次部署后自动执行避免手工造数据破坏后台业务数据的前后关联。考勤模块的进阶改造可以围绕统计口径做一个小扩展现有设计只统计每个员工的迟到次数但没有把迟到时长折算成扣款。如果想在毕业设计中展示业务分析能力可以在考勤表增加late_minutes INT字段然后在绩效查询中把迟到扣款和罚款合并计算。这个改造天然适合用 SQL 去描述逻辑比在 JSP 页面里写 Java 循环更清晰也能支撑后续接入报表工具做可视化展示。本文还有配套的精品资源点击获取