简介:这份PDF面向计算机专业学生与Java Web开发者,围绕“基于Java Web的企业人力资源管理系统”这一毕业设计课题,完整呈现从选题背景到系统落地的全过程。内容涵盖人力资源管理理论、B/S与C/S架构比较、Jsp/Servlet技术基础,以及可行性分析、需求分析、总体设计与数据库设计等关键环节,并给出管理员登录、员工信息、培训、招聘、奖惩、薪金、合同、考勤等模块的具体实现思路,可作为课程设计、毕设选题与项目练手的参考范本。资源包共1个PDF文件,约2.84MB,结构按绪论、基础理论、系统分析、总体设计、系统实现等章节组织,目录清晰,便于按模块查阅与对照学习。目前已有154人学习,适合希望理解Java Web项目完整开发流程、需要借鉴系统分析与设计文档写法的读者参考。
1. 从一份 Jsp/Servlet 毕设拆起:这套 Java Web 人力资源系统到底能跑通什么
很多同学拿到《基于Java Web的企业人力资源管理系统的设计与实现》这类毕设 PDF 时,第一反应是“又是烂大街的增删改查”。但真正翻到第 5 章实现部分你会发现,它把考勤、奖惩、工资三条业务线用员工编号串成了一条闭环,这个设计思路比单纯堆 CRUD 页面值钱得多。这套系统基于 B/S 三层结构,用 Jsp/Servlet 做核心、MySQL 5.1 做后台、MyEclipse 8.6 做开发平台,覆盖员工信息、招聘、培训、奖惩、薪金、合同、考勤七个模块。它适合两类人:一是要交 Java Web 毕设、需要一份能跑通全流程参考实现的学生;二是刚转 Java Web、想找一个业务闭环完整的小型管理系统练手的初级开发。下面我按“资源是什么 → 怎么用 → 坑在哪”的顺序,把这份 PDF 里的设计拆成能直接抄作业的落地笔记。
2. 技术选型与架构拆解:为什么是 Jsp/Servlet + B/S 三层,而不是 Spring Boot
2.1 B/S 与 C/S 的取舍:这套系统为什么必须走浏览器端
PDF 第 2.3 节花了整整两页对比 B/S 和 C/S,核心结论就一句话:人力资源管理的使用对象是管理员,办公地点不固定,客户端越轻越好。C/S 两层结构把业务逻辑压在客户端,每次改一个工资计算公式就得挨个工位更新 exe;B/S 三层结构把表示逻辑、业务逻辑、数据逻辑全放在服务器端,管理员换台电脑打开浏览器就能用,升级只动服务器。
具体到这套系统的三层划分是这样的:
| 层次 | 职责 | 本系统对应实现 |
|---|---|---|
| 表示层 | 接收请求、展示结果 | JSP 页面 + HTML/CSS/JavaScript |
| 业务逻辑层 | 处理业务规则、事务 | Servlet + JavaBean |
| 数据逻辑层 | 数据持久化、查询 | MySQL 5.1 + JDBC |
这个划分的直接好处是:工资管理里“自动计算个人所得税”这种规则,只写在 Servlet 里一处,JSP 只负责把结果渲染成表格。改税率不用碰页面,改页面不用碰税率。
2.2 Jsp/Servlet 的分工边界:哪些逻辑放 JSP,哪些必须抽到 Servlet
PDF 第 2.4 节讲 Jsp/Servlet 时提到一个关键点:JSP 不增加任何 Servlet 实现不了的功能,它的价值在于把静态 HTML 和动态内容分离。落到这套系统里,我的建议是——
- JSP 只做三件事:接收表单参数回显、遍历 Servlet 传过来的 List 渲染表格、引入公共头尾。
- Servlet 做四件事:校验登录态、调用 DAO 查库、组装业务数据、转发或重定向到 JSP。
- 数据库连接、事务提交、异常回滚,全部封在 DAO 层,Servlet 不直接写 JDBC。
常见做法是建一个 BaseServlet,把doGet和doPost统一转发到execute方法,子类只重写execute。这样登录校验、编码设置这些重复代码只写一次。
2.3 数据库选型与员工编号关联设计
PDF 第 4.4 节明确选了 MySQL 5.1,理由是免费、轻量、和 JDBC 配合成熟。真正值得抄的是它的关联设计:工资表、考勤表、奖惩表都带emp_id字段,通过员工编号把三条业务线串起来。这意味着——
- 算某员工当月工资时,先按
emp_id查考勤汇总,再查奖惩汇总,最后合并进工资记录。 - 奖惩管理里涉及金钱的记录,统计当月金额后直接插入工资表对应字段。
- 考勤异常(迟到、缺勤)触发的扣款,也走奖惩表再汇入工资。
这个设计的边界在于:它假设一个员工在同一月份只有一条工资记录、一条考勤汇总记录。如果企业有多次发薪或分段考勤,这套模型需要加时间维度字段,否则会覆盖。
2.4 建库脚本与核心表结构落地
PDF 附录二给了 SQL 建库语句,但正文里没有完整贴出。按这套系统的业务描述,我一般会先建员工主表,再建各业务从表,外键统一指向emp_id。下面是一段可直接执行的建库骨架:
-- 员工主表:所有业务表的关联中心 CREATE TABLE employee ( emp_id INT PRIMARY KEY AUTO_INCREMENT, emp_name VARCHAR(50) NOT NULL, gender CHAR(2), birth_date DATE, dept_id INT, position VARCHAR(50), hire_date DATE, status TINYINT DEFAULT 1 -- 1在职 0离职 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 考勤表:按员工+月份汇总 CREATE TABLE attendance ( att_id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL, att_month CHAR(7), -- 格式 2024-06 late_count INT DEFAULT 0, absent_days INT DEFAULT 0, leave_days INT DEFAULT 0, FOREIGN KEY (emp_id) REFERENCES employee(emp_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 奖惩表:金额字段会汇入工资 CREATE TABLE reward_punish ( rp_id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL, rp_type TINYINT, -- 1奖励 2惩罚 rp_amount DECIMAL(10,2), rp_date DATE, rp_reason VARCHAR(200), FOREIGN KEY (emp_id) REFERENCES employee(emp_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 工资表:汇总考勤扣款和奖惩金额 CREATE TABLE salary ( sal_id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL, sal_month CHAR(7), base_salary DECIMAL(10,2), att_deduct DECIMAL(10,2) DEFAULT 0, rp_amount DECIMAL(10,2) DEFAULT 0, tax DECIMAL(10,2) DEFAULT 0, real_salary DECIMAL(10,2), FOREIGN KEY (emp_id) REFERENCES employee(emp_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;逻辑说明:employee是主表,attendance、reward_punish、salary都通过emp_id外键关联。参数上,att_month和sal_month用CHAR(7)存YYYY-MM,比存日期类型更省事,查询时直接字符串匹配。DECIMAL(10,2)保证金额精度,别用FLOAT,否则个税计算会出现分位误差。status字段做软删除,员工离职不物理删行,否则历史工资记录的外键会断。
3. 七个功能模块的实现路径:从登录到工资汇总的代码骨架
3.1 登录模块:Session 校验与过滤器
PDF 第 5.1.1 节只说了“管理员登录的实现”,没展开细节。按 Jsp/Servlet 的常规做法,登录流程是:JSP 表单提交用户名密码 → LoginServlet 查库比对 → 成功则session.setAttribute("admin", admin)→ 重定向到主页面。关键是要加一个登录过滤器,防止未登录直接访问内部 JSP。
// LoginFilter.java:拦截所有内部页面请求 public class LoginFilter implements Filter { 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("LoginServlet") || uri.contains("/css/") || uri.contains("/js/")) { chain.doFilter(req, resp); return; } // 校验 Session 中是否有管理员 Object admin = request.getSession().getAttribute("admin"); if (admin == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, resp); } }逻辑说明:过滤器在web.xml里配置/*拦截,放行名单用 URI 后缀判断。参数上,request.getContextPath()保证部署路径变化时重定向地址正确。注意 Session 超时时间要在web.xml里设,默认 30 分钟,毕设演示够用,生产环境按需调。
3.2 员工信息管理:分页查询与模糊搜索
员工信息管理是其他模块的基础。PDF 说“收集录入、编辑、删除、查看”,实际落地时最容易被忽略的是分页——员工上百条后,一次性SELECT *渲染表格会卡。常见做法是用LIMIT做物理分页:
// EmployeeDAO.java:分页 + 按姓名模糊查询 public List<Employee> findByPage(String keyword, int page, int size) { List<Employee> list = new ArrayList<>(); String sql = "SELECT * FROM employee WHERE status=1 AND emp_name LIKE ? " + "ORDER BY emp_id DESC LIMIT ?, ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, "%" + (keyword == null ? "" : keyword) + "%"); ps.setInt(2, (page - 1) * size); // 偏移量 ps.setInt(3, size); // 每页条数 ResultSet rs = ps.executeQuery(); while (rs.next()) { list.add(mapRow(rs)); } } catch (SQLException e) { e.printStackTrace(); } return list; }逻辑说明:page从 1 开始,偏移量是(page-1)*size。keyword为空时LIKE '%%'匹配全部。参数上,size建议设 10 或 15,太大失去分页意义,太小翻页频繁。ORDER BY emp_id DESC让新员工排前面,符合管理习惯。注意PreparedStatement的?占位符不能用在LIMIT后面做字符串拼接,必须用setInt,否则有注入风险。
3.3 考勤与奖惩联动:员工编号作为关联键
这是整套系统最核心的业务逻辑。PDF 第 1.1.2 节明确说“工资表、考勤表、奖惩表都有员工编号字段,通过员工编号关联”。落地时的顺序是:
- 考勤管理员每天录入考勤,系统按月汇总迟到、缺勤、请假次数。
- 奖惩管理员录入奖惩记录,涉及金钱的标记金额。
- 工资管理员生成当月工资时,按
emp_id和月份查考勤汇总和奖惩汇总,计算扣款和奖惩金额。
// SalaryService.java:生成某员工某月工资 public Salary generateSalary(int empId, String month) { Salary sal = new Salary(); sal.setEmpId(empId); sal.setSalMonth(month); // 1. 取基本工资 double base = salaryDAO.getBaseSalary(empId); sal.setBaseSalary(base); // 2. 考勤扣款:迟到一次扣 50,缺勤一天扣 200 Attendance att = attendanceDAO.getByEmpAndMonth(empId, month); double attDeduct = 0; if (att != null) { attDeduct = att.getLateCount() * 50 + att.getAbsentDays() * 200; } sal.setAttDeduct(attDeduct); // 3. 奖惩金额:奖励为正,惩罚为负 double rpAmount = rewardPunishDAO.sumByEmpAndMonth(empId, month); sal.setRpAmount(rpAmount); // 4. 个税:起征点 5000,简化按 3% 算 double taxable = base - attDeduct + rpAmount - 5000; double tax = taxable > 0 ? taxable * 0.03 : 0; sal.setTax(tax); // 5. 实发 sal.setRealSalary(base - attDeduct + rpAmount - tax); salaryDAO.insert(sal); return sal; }逻辑说明:扣款标准(迟到 50、缺勤 200)和个税起征点(5000)是参数,实际企业不同,建议抽到配置文件或常量类。rewardPunishDAO.sumByEmpAndMonth用SUM(rp_amount)聚合,奖励存正数、惩罚存负数,这样加减逻辑统一。注意生成工资前要判断当月是否已生成,避免重复插入——加一个UNIQUE(emp_id, sal_month)约束最省事。
3.4 招聘、培训、合同模块的轻量实现
这三个模块业务相对独立,PDF 里描述也偏简单。招聘管理因为“面向社会,所有人可浏览”,所以招聘列表页不能放在登录过滤器后面,要在过滤器放行名单里单独开一个/recruit/list.jsp。培训管理和合同管理就是标准的增删改查,复用员工管理的分页和搜索模板即可。
一个容易翻车的地方:招聘信息录入后,应聘者投递简历如果也要入库,需要单独建application表,用recruit_id关联招聘岗位。PDF 没提这个表,但如果要做完整闭环,这是必须补的。
4. 避坑与排查:这套 Jsp/Servlet 系统最容易翻车的五个点
4.1 中文乱码:POST 和 GET 要分开处理
现象:表单提交的中文姓名存进数据库变成???,或者页面显示乱码。
原因:JSP 页面pageEncoding和 Servlet 的request.setCharacterEncoding不一致,GET 请求的编码还受 Tomcat 的URIEncoding影响。
解决:JSP 头部统一写<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>;Servlet 里在取参数前加request.setCharacterEncoding("UTF-8");Tomcat 的server.xml里 Connector 加URIEncoding="UTF-8"。三处都对齐,乱码基本绝迹。
4.2 数据库连接泄漏:Connection 没关导致 Tomcat 卡死
现象:系统跑一段时间后,所有请求都超时,重启 Tomcat 才恢复。
原因:DAO 里Connection、PreparedStatement、ResultSet没有在finally里关闭,连接池耗尽。
解决:用 try-with-resources 语法,把资源声明在 try 的括号里,JVM 自动关闭。如果项目用的是老版本 JDK,就在finally里按ResultSet → PreparedStatement → Connection的顺序逐个关,每个关之前判空。
4.3 工资重复生成:没有唯一约束导致数据翻倍
现象:工资管理员手抖点了两次“生成工资”,同一个月出现两条记录,实发金额对不上。
原因:salary表没有(emp_id, sal_month)唯一约束,INSERT不报错。
解决:建表时加UNIQUE KEY uk_emp_month (emp_id, sal_month),生成前先SELECT COUNT(*)判断,已存在则走UPDATE而不是INSERT。
4.4 过滤器拦截了静态资源:CSS 和 JS 加载失败
现象:登录后页面样式全丢,浏览器控制台报 404。
原因:LoginFilter拦截/*,把/css/style.css和/js/validate.js也拦了,未登录态下直接重定向到登录页。
解决:过滤器里放行.css、.js、.png、.jpg等后缀,或者把静态资源统一放到/static/目录下,过滤器只拦/admin/路径。
4.5 MySQL 5.1 驱动类名写错:com.mysql.jdbc.Driver还是com.mysql.cj.jdbc.Driver
现象:启动报ClassNotFoundException,数据库连不上。
原因:MySQL 5.1 用com.mysql.jdbc.Driver,MySQL 8.x 用com.mysql.cj.jdbc.Driver,混用就翻车。
解决:确认WEB-INF/lib下的驱动 jar 版本,5.1 的 jar 配com.mysql.jdbc.Driver,URL 写jdbc:mysql://localhost:3306/hr_db?useUnicode=true&characterEncoding=utf8。别抄网上 8.x 的配置,版本对不上。
5. 从 PDF 到可运行项目:我的复现习惯与一个验证技巧
把这份 PDF 变成能跑的项目,我的习惯是分四步走,每步都有验证点,不跑通不往下走。
第一步,先建库。把第 2.4 节的建库脚本执行一遍,用SHOW TABLES确认四张核心表都在,再手动插一条员工数据,验证外键约束生效。这一步不跑通,后面所有代码都是空中楼阁。
第二步,搭骨架。在 MyEclipse 里建 Dynamic Web Project,把 MySQL 驱动 jar 丢进WEB-INF/lib,写一个DBUtil测试类,main方法里getConnection()打印连接对象。连接成功再写 DAO,失败就查驱动类名、URL、端口、密码这四项。
第三步,按模块递进。先做登录和过滤器,跑通“未登录跳转、登录后放行”;再做员工管理,验证分页和模糊搜索;最后做考勤→奖惩→工资的联动,这是整套系统的业务核心,也是答辩时最容易被问的地方。
第四步,验证数据一致性。生成一条工资记录后,手动用 SQL 核对:
-- 核对工资表的实发金额是否等于各项加减结果 SELECT s.emp_id, s.sal_month, s.base_salary, s.att_deduct, s.rp_amount, s.tax, s.real_salary, (s.base_salary - s.att_deduct + s.rp_amount - s.tax) AS expected FROM salary s WHERE s.emp_id = 1 AND s.sal_month = '2024-06';如果real_salary和expected对不上,说明 Java 端的计算逻辑和数据库存储之间有精度丢失或顺序错误。这个核对习惯是我从一次答辩前夜发现的金额差 0.01 元开始养成的——当时FLOAT改DECIMAL才解决。从那以后我每次做完金额相关模块,都强制走一遍 SQL 核对,不信任 Java 端的double运算。
还有一个技巧:PDF 附录三给了“系统主要实现代码”,但页码从 34 到 52,说明代码量不小。复现时不要试图一次全抄,按模块拆开,每个模块跑通再合并。Jsp/Servlet 项目最容易出的问题不是单个功能写不出来,而是模块之间的 Session 传递和页面跳转乱套。先保证一条业务线端到端跑通,再横向复制到其他模块,比七个模块同时开工效率高得多。
希望帮到你。
本文还有配套的精品资源,点击获取