☰
JSP企业人事管理系统毕业设计源码与论文实战指南
2026/10/8 16:47:38 网站建设 项目流程

简介:这份资源是面向高校计算机相关专业毕业生的人事管理系统毕业设计完整方案,包含可运行的JSP源代码与配套论文,适合需要完成企业级信息管理类课题的学生及Java Web初学者参考。压缩包共140个文件,约2.21MB,以52个jsp页面、22张jpg与20张gif界面素材、14个class与7个java源文件为核心,辅以8个css样式、4份doc文档及xml、js等配置脚本,覆盖从视图层到业务逻辑层的完整结构。论文部分从人事管理信息系统初步调查切入,系统阐述需求分析、数据流程分析、总体结构设计、数据结构与数据库设计以及输入输出设计,并记录基于JSP工具快速建立原型、迭代修正至可行系统的过程。目前已有82人学习,读者可借此获得一套可直接部署调试的赛题级项目,理解人事管理模块划分与数据流转思路,并对照论文掌握从需求到实现的完整写作框架。

1. 从一份 JSP 企业人事管理系统源码包说起:它到底能帮你解决什么

如果你正在为计算机毕业设计选题发愁,或者已经定了「企业人事管理系统」这个方向,却卡在代码写不出来、论文框架搭不起来的阶段,那这份基于 JSP 的企业人事管理系统设计(含源代码和论文)大概率就是你需要的参考。它本质上是一套用 JSP + Servlet + JDBC 技术栈实现的 Web 应用,覆盖员工信息管理、部门管理、考勤记录、薪资发放、用户权限等企业人事的核心业务模块,同时附带一份可参考的毕业论文。适合两类人:一是基础一般、需要一份能跑起来、能改、能讲清楚的全栈参考项目的本科生;二是想快速理解 JSP 三层架构在实际业务系统中怎么落地、怎么把数据库表映射成页面的开发者。它不解决高并发,也不涉及微服务,但它能让你在两周内拥有一套结构完整、逻辑自洽、答辩时经得起追问的系统。

2. 企业人事管理系统的功能拆解与技术选型:为什么是 JSP 而不是别的

2.1 人事系统到底需要哪些模块才算完整

很多同学一上来就写登录和员工增删改查,写到一半发现论文里「系统功能」那一章根本凑不够字数。问题出在前期没有把业务边界画清楚。一个能撑起毕业设计的企业人事管理系统,通常包含六个核心模块:用户与权限管理、部门与岗位管理、员工档案管理、考勤管理、薪资管理、统计报表。这六个模块之间存在明确的依赖关系——权限决定谁能操作哪些菜单,部门决定员工的归属,考勤和薪资依赖员工档案里的基本工资和岗位系数。

我一般会建议在动手写代码之前,先用一张 Excel 把每个模块的字段列出来。比如员工档案表至少要有:员工编号、姓名、性别、出生日期、身份证号、联系电话、所属部门、岗位、入职日期、基本工资、状态(在职/离职)。考勤表要有:考勤编号、员工编号、考勤日期、上班时间、下班时间、考勤状态(正常/迟到/早退/缺勤/请假)。薪资表要有:薪资编号、员工编号、发放月份、基本工资、考勤扣款、实发工资、发放状态。把这些字段确定下来,后面的建表和 JSP 页面就是体力活了。

提示:论文里「需求分析」章节的用例图,可以直接从这六个模块出发画,每个模块两到三个用例,足够撑起一章。

2.2 为什么 JSP 仍然是毕业设计的合理选择

从工程角度看,JSP 确实不是 2025 年新项目的首选。但在毕业设计这个场景里,它的优势非常明显。第一,学习曲线平缓。你不需要同时理解前端框架的响应式原理、后端 RESTful 设计、跨域配置和构建工具,只需要会 Java 基础、HTML 和 JDBC 就能跑通一个完整流程。第二,资料丰富。JSP 相关的教材、博客、视频教程存量极大,遇到问题搜索成本低。第三,答辩友好。评委老师对 JSP 的 MVC 模式非常熟悉,你讲 Servlet 接收请求、Service 处理业务、DAO 操作数据库、JSP 负责展示,逻辑清晰,不容易被问倒。

技术选型上,我建议采用 JSP + Servlet + JavaBean + JDBC + MySQL 的组合。不引入 Spring 或 MyBatis,不是因为它们不好,而是因为毕业设计的代码量本身不大,引入框架反而会增加配置复杂度和答辩时的解释成本。数据库用 MySQL 5.7 或 8.0 都可以,连接池可以用 Druid 也可以直接用 JDBC 原生连接,后者更简单,适合代码量在 3000 行以内的项目。

2.3 三层架构在 JSP 项目里的具体落地方式

三层架构不是口号,它决定了你的代码是「能改」还是「一改就崩」。在 JSP 企业人事管理系统里,三层分别是:表现层(JSP 页面 + Servlet)、业务逻辑层(Service 类)、数据访问层(DAO 类)。表现层只负责接收参数、调用 Service、把结果存到 request 或 session 里然后转发到 JSP;Service 层负责业务规则,比如「计算实发工资 = 基本工资 - 考勤扣款 - 社保扣款」;DAO 层只负责 SQL 执行和结果集映射。

一个常见的翻车场景是:把数据库查询直接写在 JSP 页面的 <% %> 脚本里。这样写一开始很快,但当你需要修改一个字段名时,要翻遍所有 JSP 文件。正确的做法是,JSP 页面里不出现任何 import java.sql.* 的代码,所有数据都通过 Servlet 提前准备好,用 EL 表达式和 JSTL 标签展示。

// EmployeeDAO.java 片段:分页查询员工列表 public List<Employee> findByPage(int page, int pageSize, String keyword) { List<Employee> list = new ArrayList<>(); // 使用 LIKE 实现模糊搜索,? 为占位符防止 SQL 注入 String sql = "SELECT * FROM employee WHERE name LIKE ? OR emp_no LIKE ? LIMIT ?, ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, "%" + keyword + "%"); ps.setString(2, "%" + keyword + "%"); ps.setInt(3, (page - 1) * pageSize); // 起始偏移量 ps.setInt(4, pageSize); // 每页条数 ResultSet rs = ps.executeQuery(); while (rs.next()) { Employee emp = new Employee(); emp.setId(rs.getInt("id")); emp.setEmpNo(rs.getString("emp_no")); emp.setName(rs.getString("name")); emp.setDeptId(rs.getInt("dept_id")); emp.setBaseSalary(rs.getBigDecimal("base_salary")); list.add(emp); } } catch (SQLException e) { e.printStackTrace(); } return list; }

这段代码的关键参数是 page 和 pageSize。page 从 1 开始,pageSize 一般设为 10 或 15,取决于你的表格宽度。LIMIT 的第一个参数是偏移量,计算公式是 (page - 1) * pageSize,这个公式写错会导致第一页数据重复或丢失。另外,模糊搜索的 keyword 两边要拼上 %,但不要拼在 SQL 字符串里,而是通过 PreparedStatement 的 setString 传入,否则会有 SQL 注入风险。

3. 从建库到跑通第一个页面:可复现的最小落地步骤

3.1 数据库建表与初始化数据的 SQL 脚本

拿到源码包后,第一件事不是打开 IDE,而是先把数据库建好。很多源码包里的 SQL 文件不完整,或者字符集不对导致中文乱码。我一般会自己写一份建表脚本,确保字段类型和长度符合业务需求。

-- 创建数据库,指定 utf8mb4 字符集以支持中文和特殊符号 CREATE DATABASE hrms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hrms; -- 部门表 CREATE TABLE department ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL COMMENT '部门名称', dept_desc VARCHAR(200) DEFAULT NULL COMMENT '部门描述' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 员工表,dept_id 外键关联部门表 CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(20) NOT NULL UNIQUE COMMENT '员工编号', name VARCHAR(50) NOT NULL COMMENT '姓名', gender CHAR(2) DEFAULT '男' COMMENT '性别', phone VARCHAR(20) DEFAULT NULL, dept_id INT DEFAULT NULL, position VARCHAR(50) DEFAULT NULL COMMENT '岗位', hire_date DATE DEFAULT NULL COMMENT '入职日期', base_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '基本工资', status TINYINT DEFAULT 1 COMMENT '1在职 0离职', FOREIGN KEY (dept_id) REFERENCES department(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 用户表,role 字段区分管理员和普通用户 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role VARCHAR(20) DEFAULT 'user' COMMENT 'admin 或 user', emp_id INT DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 初始化管理员账号,密码明文仅用于演示,实际应存 MD5 值 INSERT INTO sys_user (username, password, role) VALUES ('admin', '123456', 'admin'); INSERT INTO department (dept_name, dept_desc) VALUES ('技术部', '负责产品研发'), ('人事部', '负责招聘与考勤');

建表时最容易踩的坑是外键约束顺序。employee 表引用了 department 表,所以必须先建 department 再建 employee。另外,utf8mb4 和 utf8 的区别在于前者支持 emoji 和更多生僻字,虽然人事系统里不一定用得到,但统一用 utf8mb4 可以避免以后迁移时的麻烦。初始化管理员账号时,密码字段长度至少给 100,因为后面如果要改成 MD5 或 BCrypt,密文长度会超过 32 位。

3.2 在 IDEA 里配置项目并跑通登录页

把源码包解压后,用 IntelliJ IDEA 打开,选择「Import Project」而不是「Open」,因为老项目可能是 Eclipse 结构。导入后检查三处配置:一是 Project Structure 里的 JDK 版本,建议用 JDK 8 或 11,不要用 17 以上,否则 javax.servlet 包名会变成 jakarta.servlet,导致所有 Servlet 报错。二是 Web 目录的位置,确保 web.xml 在 WEB-INF 下,JSP 文件在 WebContent 或 webapp 目录下。三是 Tomcat 的配置,在 Run/Debug Configurations 里添加 Tomcat Server,Deployment 里加上 Artifact。

<!-- web.xml 中 Servlet 和过滤器的基础配置 --> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.hrms.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> <!-- 字符编码过滤器,解决 POST 请求中文乱码 --> <filter> <filter-name>EncodingFilter</filter-name> <filter-class>com.hrms.filter.EncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

字符编码过滤器是必须的,而且要在所有 Servlet 之前生效。如果只配了 Servlet 里的 request.setCharacterEncoding,GET 请求的中文参数仍然会乱码,因为 GET 参数的编码由 Tomcat 的 server.xml 决定。最稳妥的做法是在 Tomcat 的 Connector 标签里加上 URIEncoding="UTF-8",同时在 web.xml 里配过滤器。数据库连接信息一般放在 db.properties 文件里,不要硬编码在 Java 类中,否则换一台电脑就要改代码。

3.3 员工信息增删改查的完整链路与参数传递

以「新增员工」为例,完整链路是:employee_add.jsp 表单提交到 EmployeeAddServlet,Servlet 用 request.getParameter 获取字段,封装成 Employee 对象,调用 EmployeeService.add(emp),Service 调用 EmployeeDAO.insert(emp),DAO 执行 INSERT 语句,最后 Servlet 重定向到员工列表页。这个链路里,任何一环的参数名不一致都会导致数据丢失。

// EmployeeAddServlet.java 核心代码 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取表单参数,参数名必须与 JSP 中 input 的 name 属性一致 String empNo = request.getParameter("empNo"); String name = request.getParameter("name"); String gender = request.getParameter("gender"); String phone = request.getParameter("phone"); String deptIdStr = request.getParameter("deptId"); String position = request.getParameter("position"); String hireDate = request.getParameter("hireDate"); String baseSalaryStr = request.getParameter("baseSalary"); Employee emp = new Employee(); emp.setEmpNo(empNo); emp.setName(name); emp.setGender(gender); emp.setPhone(phone); // 部门 ID 和薪资需要做空值和数字格式校验 emp.setDeptId(deptIdStr == null || deptIdStr.isEmpty() ? 0 : Integer.parseInt(deptIdStr)); emp.setPosition(position); emp.setHireDate(java.sql.Date.valueOf(hireDate)); emp.setBaseSalary(new BigDecimal(baseSalaryStr == null ? "0" : baseSalaryStr)); EmployeeService service = new EmployeeService(); boolean success = service.add(emp); if (success) { response.sendRedirect("employee_list"); // 重定向到列表页 } else { request.setAttribute("msg", "新增失败,员工编号可能已存在"); request.getRequestDispatcher("employee_add.jsp").forward(request, response); } }

这里有几个参数细节值得注意。hireDate 从前端传来的是字符串,格式必须是 yyyy-MM-dd,否则 java.sql.Date.valueOf 会抛异常。baseSalary 用 BigDecimal 而不是 double,因为工资计算涉及金额,double 会有精度丢失。deptId 如果前端传空字符串,直接 parseInt 会报 NumberFormatException,所以要先判断空值。重定向用 sendRedirect 而不是 forward,是为了防止用户刷新页面时重复提交表单。

4. 论文部分怎么搭框架:让代码和文字对得上

4.1 论文目录的常见结构与各章字数分配

毕业论文一般要求 8000 到 15000 字,章节结构通常是:绪论、相关技术介绍、需求分析、系统设计、系统实现、系统测试、结论。很多同学写到最后发现「系统实现」章节无话可说,是因为前面「系统设计」写得太空,没有把数据库表结构和模块流程图画清楚。我的建议是:绪论 1500 字,技术介绍 1500 字,需求分析 2000 字,系统设计 2500 字,系统实现 3000 字,测试 1000 字,结论 500 字。其中系统设计和实现两章要占一半以上篇幅,因为这是你自己做的部分,也最容易写出细节。

注意:论文里的截图不要直接截整个屏幕,用截图工具只截浏览器内容区域,去掉桌面和任务栏。截图里的数据要真实,不要出现「测试1」「aaa」这种明显凑数的内容。

4.2 把数据库表结构和 ER 图写进论文的正确方式

数据库设计是论文里最容易拿分也最容易翻车的部分。拿分是因为评委一看表结构就知道你有没有认真做;翻车是因为很多同学直接从 Navicat 里导出建表语句贴进论文,格式混乱,字段注释也没有。正确的做法是:先用 Visio 或 draw.io 画一张 ER 图,实体用矩形,属性用椭圆,关系用菱形,主键加下划线。然后在论文里放一张「主要数据表清单」表格,列出表名、中文名、说明。最后每个核心表单独一小节,用表格列出字段名、类型、长度、是否为空、说明。

字段名类型长度允许空说明
idINT11否主键,自增
emp_noVARCHAR20否员工编号,唯一
nameVARCHAR50否员工姓名
dept_idINT11是外键,关联部门表
base_salaryDECIMAL10,2是基本工资,默认 0

这张表看起来简单,但它能体现你对字段类型和约束的理解。比如 DECIMAL(10,2) 表示总共 10 位数字,其中 2 位小数,最大能存 99999999.99,足够覆盖普通企业的薪资范围。如果写成 FLOAT 或 DOUBLE,评委可能会问「为什么不用 DECIMAL」,这时候你要能答出精度问题。

4.3 系统测试章节怎么写才不像凑字数

测试章节不是把每个页面点一遍然后截图。有效的写法是:先写测试环境(操作系统、浏览器版本、数据库版本、Tomcat 版本),再写功能测试用例表,最后写测试结论。功能测试用例表至少覆盖登录、新增员工、修改员工、删除员工、考勤录入、薪资计算这六个场景。每个用例写清楚前置条件、操作步骤、预期结果、实际结果。如果实际结果和预期一致,就写「通过」;如果不一致,就写你发现了什么问题、怎么修复的。评委看到你记录了 bug 和修复过程,反而会觉得你确实动手做了。

5. 避坑与排查:JSP 人事系统最常见的五个翻车现场

5.1 中文乱码:现象是页面显示问号,原因是编码链路没统一

现象:登录页输入中文用户名后,数据库里存的是乱码,或者列表页显示的姓名是「??」。原因:JSP 页面、Servlet、数据库连接、Tomcat 四个环节的编码不一致。解决:JSP 文件头部加 <%@ page contentType="text/html;charset=UTF-8" %>,web.xml 配 EncodingFilter,数据库连接 URL 加 useUnicode=true&characterEncoding=utf8,Tomcat 的 server.xml 里 Connector 加 URIEncoding="UTF-8"。四个地方都改完,重启 Tomcat,清空浏览器缓存再试。

5.2 数据库连接报 ClassNotFoundException:驱动包没放进 WEB-INF/lib

现象:启动 Tomcat 后访问登录页,控制台报 java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因:MySQL 驱动 jar 包没有放到项目的 WEB-INF/lib 目录下,或者放进去后没有重新部署。解决:下载 mysql-connector-java-8.0.x.jar,复制到 WEB-INF/lib,在 IDEA 的 Project Structure 里确认该 jar 被标记为「Compile」范围,然后重新 build artifact 并重启 Tomcat。注意 MySQL 8.0 的驱动类名是 com.mysql.cj.jdbc.Driver,5.x 是 com.mysql.jdbc.Driver,写错也会报同样的错。

5.3 表单提交后数据没进库:参数名不匹配或事务没提交

现象:点击保存按钮后页面跳转正常,但数据库里没有新记录。原因:JSP 里 input 的 name 属性和 Servlet 里 request.getParameter 的参数名不一致,或者 DAO 里执行了 INSERT 但 Connection 没有 commit。解决:先在 Servlet 里加一行 System.out.println(request.getParameterMap()),看控制台有没有打印出参数;如果没有,就是 name 写错了。如果有参数但没进库,检查 JDBC 是否设置了 setAutoCommit(false),如果是,必须在 finally 里 commit,否则连接关闭时事务回滚。

5.4 删除部门时外键约束报错:先删子表再删主表

现象:在部门管理页面点击删除,控制台报 Cannot delete or update a parent row: a foreign key constraint fails。原因:employee 表里有员工还关联着这个部门,直接删 department 会违反外键约束。解决:两种方案。一是先检查该部门下是否有员工,如果有就提示「请先转移或删除该部门下的员工」;二是把外键的删除策略改成 ON DELETE SET NULL,这样删除部门后员工的 dept_id 变成 NULL。毕业设计里推荐第一种,因为业务逻辑更清晰,也更容易在论文里写「系统做了数据完整性校验」。

5.5 分页查询第一页数据重复:偏移量公式写反了

现象:员工列表第一页显示的是第 11 到 20 条,第二页显示的是第 21 到 30 条,第一页的 1 到 10 条不见了。原因:LIMIT 的偏移量写成了 page * pageSize,而不是 (page - 1) * pageSize。解决:把 SQL 里的偏移量改成 (page - 1) * pageSize。如果 page 从 0 开始,公式就是 page * pageSize,但前端传参一般从 1 开始,所以后端要减 1。这个坑很小,但排查起来很费时间,建议在 DAO 里加一行日志打印 page 和偏移量。

6. 进阶技巧:让这份源码包变成你自己的项目

6.1 把明文密码改成 MD5 加密的完整步骤

源码包里如果密码是明文存储,答辩时被问到「安全性」会很被动。改造步骤很简单:在注册和登录的 Service 里,把用户输入的密码用 MD5 加密后再存库或比对。Java 自带的 MessageDigest 就能做,不需要额外 jar 包。

// MD5Util.java public class MD5Util { public static String encrypt(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); for (byte b : digest) { // 每个字节转成两位十六进制,不足补零 sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5 算法不可用", e); } } }

改造时要注意:数据库里已有的明文密码需要手动更新成 MD5 值,否则老用户登录不了。登录逻辑改成 MD5Util.encrypt(inputPassword).equals(dbPassword)。MD5 虽然现在被认为不够安全,但在毕业设计里足够体现「密码不存明文」的意识。如果想让评委更满意,可以加一句「生产环境建议使用 BCrypt 或 Argon2」。

6.2 用过滤器实现登录拦截和权限控制

没有登录拦截的系统,直接在浏览器地址栏输入 employee_list 就能看到数据,这是答辩时的硬伤。加一个 LoginFilter,在 doFilter 里判断 session 里有没有 user 对象,没有就重定向到登录页。再进一步,根据 user 的 role 字段判断是否有权限访问某个路径,比如只有 admin 能访问 salary_list。

// LoginFilter.java 核心逻辑 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("login.jsp") || uri.endsWith("/login") || uri.contains("/css/") || uri.contains("/js/")) { chain.doFilter(req, resp); return; } HttpSession session = request.getSession(); Object user = session.getAttribute("user"); if (user == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); } else { chain.doFilter(req, resp); } }

这个过滤器要在 web.xml 里配置对所有路径生效,但要注意放行登录相关的 URL,否则会陷入死循环。权限控制可以在过滤器里继续加判断,也可以在每个 Servlet 里单独判断。毕业设计里用过滤器做统一登录拦截就够了,权限细分可以在论文里作为「未来改进方向」提一句。

6.3 答辩前必须自己走一遍的检查清单

最后分享一个我自己的习惯:答辩前一天,把系统从头到尾走一遍,重点检查五个地方。第一,数据库里有没有测试数据,至少要有三个部门、十个员工、一个月的考勤记录,否则演示时页面空空荡荡。第二,所有增删改查操作是否都有成功和失败的提示信息,不要点了按钮没反应。第三,退出登录后直接访问内部页面,看是否被拦截。第四,把浏览器窗口缩小到 1024 宽度,看表格有没有错位。第五,提前想好三个可能被问到的问题:为什么用 JSP 不用 Spring Boot、数据库表之间的关联关系是什么、如果数据量大了怎么优化。这三个问题答得上来,答辩基本就稳了。

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询