
简介这是一份面向Java学习者和中小型公司的人事管理系统JSP项目源码包基于JSP/Servlet与Struts风格的分层架构覆盖人事管理常见业务场景可作毕业设计参考、技术研究素材或小型项目二次开发原型。压缩包共229个文件包含88个JSP页面和18个Java类及对应class文件构成前后台核心逻辑28个TLD标签描述符与XML、properties等配置支撑框架运行16个jar包提供依赖库另有32张JPG、11张GIF图片作为界面素材mdf/ldf数据库文件可直接附加至SQL Server还原测试数据并附带项目说明文档整包仅5.8MB轻量易部署。目前已有176人学习下载包内目录清晰、源码注释完整导入IDE即可运行便于对照理解从页面请求到数据存取的分层实现无论课程设计、个人钻研还是企业人事管理系统的改造复用都有较高的参考价值。1. JSP企业人事管理系统是什么毕业设计级Java Web项目的标准答案JSP企业人事管理系统是把企业人力资源的日常事务——员工档案、部门划分、考勤打卡、工资计算、公告发布——搬到Web上的一套Java Web应用也是JSPServletJDBC三件套最典型的综合练习项目。标题里的“源码资料打包下载”意味着你拿到的是一整个可直接导入Eclipse或IDEA的工程数据库脚本、说明文档都齐备不是零散的代码片段。这类项目最适合两类人正在做java课程设计的在校生以及想攒一个完整项目经验去应付java面试题的新手。它公认的难点从来不是业务逻辑而是环境配置和部署阶段的翻车下面从架构到踩坑逐层拆开讲。2. 项目拆解JSPServletMySQL的组合为什么长盛不衰2.1 三层架构与MVC人事管理系统的骨架打开这类源码包目录结构通常长这样HRMS/ ├── src/ │ ├── com/company/hrm/ │ │ ├── servlet/ # 控制器层处理请求和页面跳转 │ │ ├── dao/ # 数据访问层封装JDBC操作 │ │ ├── model/ # 实体类Employee, Dept, Admin │ │ ├── filter/ # 登录拦截过滤器 │ │ └── util/ # DBUtil、分页工具类 ├── WebContent/ │ ├── admin/ # 管理员后台页面JSP │ ├── employee/ # 员工端页面 │ ├── css/ js/ images/ # 静态资源 │ ├── WEB-INF/ │ │ ├── lib/ # mysql-connector.jar、jstl.jar │ │ └── web.xml # 配置Servlet、Filter映射 │ ├── login.jsp │ └── index.jsp └── sql/ └── hrm_db.sql # 建库建表初始化脚本这个结构就是典型的三层架构JSP负责展示ViewServlet负责接收请求和控制跳转ControllerDAO层负责数据库读写Model。实体类只承载字段和getter/setter不掺业务逻辑。之所以这套老技术到今天还有大量课程设计和外包项目在用是因为它把HTTP请求到数据库返回的完整路径暴露得清清楚楚——你在Spring MVC里只需要一个注解就完成的映射在Servlet里要手动写doGet/doPost、手动读参数、手动分发。把这些底层机制跑通一遍再去看Spring MVC的DispatcherServlet会顺畅得多。从选型角度看为什么不用Spring Boot起步你去应付java面试题时就会发现问题面试官问的是Servlet生命周期、Session原理、JDBC预处理你如果只会RestController和MyBatis-Plus追问一层就露馅。JSP项目虽然“土”但它是理解Java Web的底盘。而MySQL作为配套数据库是因为它部署简单、资料多、出问题一搜就有答案配合Navicat可视化操作连建表都不用敲命令适合新手在java环境配置刚折腾完、耐心所剩无几的阶段。2.2 模块划分与页面流转登录、部门、员工、考勤、工资的完整闭环企业人事管理系统的功能模块基本可以按“人”和“组织”两条线拆。组织线是部门管理增加部门、调整部门负责人、部门合并与解散。人员线是员工管理员工档案的新增、修改、离职、查询每个员工归属于某个部门。考勤和工资则是这两个维度的业务延伸考勤记录员工每日打卡状态工资按月根据基本工资、奖金、扣款算出实发金额。围绕这些系统还需要管理员账号体系和公告发布功能公告模块承担信息触达比如放假通知、工资条发放说明。页面流转遵循一个固定套路未登录用户访问任何业务页面都会被Filter踢回登录页登录成功后按角色分发到不同主页主页左侧是功能菜单点击后通过Servlet查询数据转发到对应的列表JSP在列表页点击“新增”或“编辑”进入表单页提交后由Servlet接收参数并调用DAO写入数据库最后重定向回列表页避免表单重复提交。以员工管理为例完整的链路是这样login.jsp → LoginServlet → index.jsp index.jsp → EmployeeServlet?actionlist → employee/list.jsp list.jsp → EmployeeServlet?actioneditid3 → employee/edit.jsp edit.jsp → EmployeeServlet?actionupdate → 重定向回 listaction参数是这类老项目的通用做法一个Servlet里用switch按action分发省去每个模块新建多个Servlet的麻烦。权限控制在最简单的实现里只分两层管理员能进/admin/目录下的页面普通员工只能访问自己的档案和考勤具体做法后面讲Filter时会落到代码。这套流转逻辑看懂了后续看任何JSP项目都能快速定位“哪个页面发起了什么请求、落在哪个Servlet”。3. 核心功能实现登录鉴权、员工CRUD与文件上传的落地代码3.1 登录与Session鉴权Filter拦截器的写法登录是整个人事系统的入口也是最容易被新手写漏的一环。常见做法是登录成功后把用户信息放进Session再写一个Filter拦截所有请求Session里没有用户就强制跳回登录页。下面这段是标准写法package com.company.hrm.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class LoginFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; String uri req.getRequestURI(); String ctx req.getContextPath(); // 放行登录页、登录接口和静态资源 if (uri.endsWith(login.jsp) || uri.contains(LoginServlet) || uri.endsWith(.css) || uri.endsWith(.js) || uri.endsWith(.png) || uri.endsWith(.jpg)) { chain.doFilter(request, response); return; } // 核心判断Session中是否存在登录标识 HttpSession session req.getSession(false); if (session null || session.getAttribute(currentUser) null) { resp.sendRedirect(ctx /login.jsp); return; } chain.doFilter(request, response); } }这段代码的关键点有两个第一getSession(false)表示不创建新Session这样能区分“从未登录”和“会话过期”两种状态避免给匿名用户无谓地发Cookie第二静态资源必须放行否则登录页的CSS和图片会被拦截页面样式全丢这是新手最容易困惑的“为什么我登录页这么丑”。Filter映射要在web.xml里配置注意url-pattern要写成/*而不是/后者只匹配根路径写错会导致过滤器完全不生效。参数说明chain.doFilter放行时必须原样传递request和response对象sendRedirect后面必须跟return否则放行代码会继续执行出现“已经重定向又继续渲染页面”的报错。另外如果项目要求记住登录状态常见做法是用Cookie存一个加密后的用户标识Filter里先验证Cookie再放行但这套代码里一般不会实现面试时能主动提出来反而是加分项。3.2 员工管理的CRUDJDBC封装与PreparedStatement防注入员工查询列表是所有人事系统里最核心的功能。DAO层代码通常会封装一个DBUtil工具类管理连接业务方法只关注SQL和结果集处理。下面这段代码展示了用PreparedStatement实现查询和新增package com.company.hrm.dao; import com.company.hrm.model.Employee; import com.company.hrm.util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class EmployeeDao { // 条件查询按部门ID和关键字过滤 public ListEmployee findByCondition(Integer deptId, String keyword) { ListEmployee list new ArrayList(); // 动态SQL先拼基础语句再加条件 StringBuilder sql new StringBuilder( SELECT e.id, e.emp_no, e.name, e.gender, e.dept_id, d.dept_name FROM t_employee e LEFT JOIN t_dept d ON e.dept_id d.id WHERE 11); if (deptId ! null) { sql.append( AND e.dept_id ?); } if (keyword ! null !keyword.trim().isEmpty()) { sql.append( AND (e.name LIKE ? OR e.emp_no LIKE ?)); } try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql.toString())) { int index 1; if (deptId ! null) { ps.setInt(index, deptId); } if (keyword ! null !keyword.trim().isEmpty()) { ps.setString(index, % keyword %); ps.setString(index, % keyword %); } try (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.setGender(rs.getString(gender)); emp.setDeptId(rs.getInt(dept_id)); emp.setDeptName(rs.getString(dept_name)); list.add(emp); } } } catch (SQLException e) { e.printStackTrace(); } return list; } }这段代码里有三个值得细看的点。第一WHERE 11看起来是废话实际是动态拼SQL时的惯用技巧——后续追加的每个条件都带AND不需要判断“是不是第一个条件”代码干净且不易漏空格。第二所有参数都用?占位并由PreparedStatement.setXxx()绑定绝不直接拼接字符串这是防SQL注入的底线。username参数如果直接拼进SQL传入 OR 11就能绕过密码校验这种坑在java面试题里几乎是必考题写在简历里的项目如果连预处理都没用会被面试官一眼看穿。第三try-with-resources语法自动关闭Connection、PreparedStatement、ResultSet老代码里常见的finally里关连接、关结果集的样板代码可以省掉前提是JDK7及以上。插入操作同理SQL写成INSERT INTO t_employee (emp_no, name, gender, dept_id) VALUES (?, ?, ?, ?)执行ps.executeUpdate()返回受影响行数大于0就是成功。这里有个实操细节数据库自增主键在插入后如果想立刻拿到可以用ps.getGeneratedKeys()在新增完员工后马上跳转到编辑页时会用到。这个API用的人少但很实用。3.3 资料包里的常见扩展Excel导入导出与图片上传很多人事系统资料包里会附带员工头像上传功能用Apache Commons FileUpload组件实现。这组件配置并不复杂但参数调不好就出问题// UploadServlet.java 核心片段 DiskFileItemFactory factory new DiskFileItemFactory(); // 缓冲区阈值文件小于1MB直接存内存超过则落临时磁盘 factory.setSizeThreshold(1024 * 1024); factory.setRepository(new File(System.getProperty(java.io.tmpdir))); ServletFileUpload upload new ServletFileUpload(factory); // 单个文件上限5MB整个请求上限10MB upload.setFileSizeMax(5 * 1024 * 1024); upload.setSizeMax(10 * 1024 * 1024); // 解析请求得到文件项列表 ListFileItem items upload.parseRequest(request); for (FileItem item : items) { if (!item.isFormField()) { String fileName new File(item.getName()).getName(); // 保存到项目上传目录文件名用时间戳避免重名 String savePath getServletContext().getRealPath(/uploads) File.separator System.currentTimeMillis() _ fileName; item.write(new File(savePath)); } }三点说明setSizeThreshold调太小大文件频繁读写临时文件性能差调太大多张图片同时上传时内存直接爆掉。FileItem.getName()在IE浏览器下会带完整路径必须用new File(name).getName()截取文件名否则存到服务器上的文件名会是一长串非法路径。还有一个经典坑Tomcat的getRealPath(/uploads)在项目重新部署后目录会被清空正规做法是把上传文件存到Tomcat之外的固定磁盘路径在web.xml或配置类里指定。这个细节能区分“会跑通”和“懂工程”的差别。4. 数据库设计与初始化核心表结构与SQL脚本的执行顺序4.1 表结构设计员工表、部门表、考勤表之间的关系数据库设计是这类项目的重头戏面试官通常会让你现场画表关系。人事管理系统的核心表可以压缩到六张关系清晰表名说明核心字段关联关系t_admin管理员id, username, password独立表t_dept部门id, dept_name, dept_manager被员工表引用t_employee员工id, emp_no, name, gender, age, dept_id, phone, entry_datedept_id → t_dept.idt_attendance考勤id, emp_id, att_date, statusemp_id → t_employee.idt_salary工资id, emp_id, month, base_salary, bonus, deduct, final_salaryemp_id → t_employee.idt_notice公告id, title, content, publish_time, publisher独立表设计时几个容易忽略的约束员工表的emp_no工号要加唯一索引人事系统最怕出现两个同工号员工数据一乱后面全乱考勤表建议加唯一约束(emp_id, att_date)同一个人同一天只能有一条打卡记录这是后期查重的重要防线工资表的month字段建议存VARCHAR(6)格式如“202506”不要存DATE类型因为工资月份是按账期算的不是自然月。外键约束在毕业设计里通常不加用逻辑关联代替理由是降低删除数据时的麻烦——你删一个部门外键会挡住你不让你删而业务上的做法是把该部门员工先移到别的部门再解散。字段类型选择也值得讲。密码存VARCHAR(32)配合MD5加密因为MD5输出固定32位十六进制手机号存VARCHAR(11)但数据库里不加数字校验因为用户可能填“转分机”之类的字符串工资相关字段用DECIMAL(10,2)绝对不用float/double——二进制浮点存0.1有误差计算工资会差出几分钱这是金融类数据的常识。建表SQL里还要统一ENGINEInnoDB DEFAULT CHARSETutf8mb4utf8mb4才能存emoji和生僻字老项目的utf8在插入“”这类字符时会直接报错。4.2 初始化脚本执行Navicat、命令行和编码设置拿到SQL脚本后执行顺序和方法有讲究。正确路径是先建库再建表脚本开头一般包含CREATE DATABASE IF NOT EXISTS hrm_db DEFAULT CHARACTER SET utf8mb4;和USE hrm_db;但如果你用Navicat连接后直接双击运行整个脚本这两句常会被跳过去导致表建到了默认的test库里。我的习惯是分两步先在Navicat里新建数据库字符集选utf8mb4然后右键“运行SQL文件”选择脚本这样建库语句不会造成干扰。命令行导入适合Linux服务器上操作命令注意编码参数mysql -uroot -p --default-character-setutf8mb4 hrm_db hrm_db.sql--default-character-setutf8mb4这个参数必须加否则脚本里的中文注释和初始数据在导入后会变成乱码。导入完成后验证一步查询数据是否完整。USE hrm_db; SELECT COUNT(*) FROM t_employee; SELECT * FROM t_admin LIMIT 1;初始管理员账号一般会写在脚本里常见是admin / 123456或admin / admin密码存的是MD5值直接用SQL查出来是密文。忘记初始密码时不需要重跑整个脚本单独执行一条UPDATE就能重置UPDATE t_admin SET password MD5(123456) WHERE username admin;JDBC连接串里的数据库名、用户名、密码必须和这里建库的信息一致任何一个对不上起Tomcat后一登录就报Access denied for user。这个报错后面避坑章还会细说。5. 部署避坑指南Tomcat、JDK、MySQL三大翻车现场5.1 Tomcat版本与JDK版本不匹配最常见的启动失败原因这类JSP项目最常见的部署失败不是代码问题而是Tomcat版本和JDK版本打架。Tomcat 8.5需要JDK 7Tomcat 9需要JDK 8Tomcat 10则把包名从javax.servlet换成了jakarta.servlet老项目里所有import javax.servlet.http.*的代码在Tomcat 10下直接编译报错。源码包里如果明确写了基于JDK 1.8你就老老实实用Tomcat 8.5或9.0不要去追新版本。现象点击Tomcat的startup.bat窗口一闪而过logs目录里的catalina.out或catalina.2025-xx-xx.log记录着java.lang.UnsupportedClassVersionError: Unsupported major.minor version 52.0。绿色中的“52.0”对应JDK 851.0对应JDK 749.0是JDK 5。解决方式要么降Tomcat版本要么调整IDE里的项目编译级别。还有一个隐蔽点在Eclipse里右键项目Properties → Java Compiler里如果选了1.8但Installed JREs指向的却是JDK 11启动时照样报错。改完两个地方要一起核对光改一处不生效。另一个高发问题是Tomcat用JRE而不是JDK。JSP在首次访问时需要编译成Servlet类底层依赖javax.tools和编译器接口纯JRE环境里编译直接抛Unable to compile class for JSP。所以Tomcat的JAVA_HOME环境变量一定要指向JDK安装目录而不是JRE目录。这也是java环境配置里最容易栽的点。5.2 数据库连接报错驱动、URL、时区三个玄学点项目跑起来后第一次登录最常见的就是数据库连不上报错五花八门ClassNotFoundException、Communications link failure、Unknown database、Access denied。逐个说。ClassNotFoundException: com.mysql.jdbc.Driver原因是mysql-connector-java的jar包没放进WEB-INF/lib目录。很多人把jar放进build path里Eclipse里编译不报错但Tomcat运行时用的是WEB-INF/lib下的包两者不一致最终运行时报ClassNotFound。解决把jar物理复制到WebContent/WEB-INF/lib/下然后刷新项目确认Deploy Assembly里包含这个目录。Communications link failure多半是连接串里的IP和端口问题。本机连数据库用localhost:3306没问题但如果你把项目部署到服务器上数据库IP要改成服务器的内网或公网地址端口要确认MySQL没有改过默认配置。ssl参数也常惹事MySQL 5.7以上默认要求SSL连接但本机开发环境没配证书连接串里要加useSSLfalse。Unknown database是连接串里库名写错注意大小写Linux下MySQL库名区分大小写。Access denied是用户名密码错误或者密码包含特殊字符没转义。最后还有一个时区问题连接串推荐写成jdbc:mysql://localhost:3306/hrm_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8serverTimezone不配MySQL 8.x驱动默认用UTC你明明存的早上9点查出来变下午5点工资考勤全错这是最常见的“数据没丢但看起来疯了”的坑。顺便说一句驱动类名在MySQL 8.x里变成了com.mysql.cj.jdbc.Driver老项目里写的com.mysql.jdbc.Driver虽然兼容但会打一行过时警告不碍事别被警告吓着。5.3 乱码问题页面、请求、数据库三层编码乱码是最折磨人的排错场景因为三个地方都可能出错JSP页面文件本身的编码、Tomcat接收请求的编码、MySQL存储的编码。先确认最小集JSP第一行pageEncodingUTF-8必须写同时页面本身的文件编码要在IDE里设置为UTF-8两个不一致时以文件实际编码为准pageEncoding写了UTF-8但文件是GBK中文直接乱。再改Tomcat的server.xml在Connector节点加URIEncodingUTF-8专门解决URL参数和表单GET提交的中文乱码。POST请求的中文乱码需要在Servlet里用request.setCharacterEncoding(UTF-8)而且必须在读取任何参数之前调用放在doPost第一行。有的项目做了Filter统一处理也行注意Filter顺序要在LoginFilter之前否则登录页表单提交时先被过滤器拦了参数已经被用ISO-8859-1读了一遍。数据库这边建库要utf8mb4连接串要characterEncodingutf8MySQL服务端的默认字符集也要核对SHOW VARIABLES LIKE character_set_server查出来如果是latin1改my.cnf后重启MySQL。排查乱码有个笨但有效的办法在JSP页面里写一行% page contentTypetext/html;charsetUTF-8 %然后浏览器按F12看响应头的Content-Type里charset是什么再用浏览器的“编码”手动切一次——如果手动切UTF-8后乱码恢复说明页面输出时没有声明正确编码如果手动切也没用说明数据在存储层就坏了删掉重来。按这个顺序排查乱码问题二十分钟内能定位。5.4 端口占用、启动超时与上传目录权限端口8080被占用是每个JSP学习者都踩过的坑。现象Tomcat启动日志报Port 8080 required by Tomcat v9.0 Server at localhost is already in use。排查用命令netstat -ano | findstr 8080Windows下看到占用进程的PID再去任务管理器结束它。如果这个是别的项目占用的更省事的改Tomcat端口打开conf/server.xml把Connector的port改成8081注意有多个Connector节点改的是HTTP/1.1那个AJP端口8009如果也被占也一起改。启动超时问题在Eclipse里表现为在Servers视图双击Tomcat或者用IDEA的Smart Tomcat插件时反映推出提示“Server Tomcat v9.0 Server at localhost was unable to start within 45 seconds”。这不是系统挂了而是启动慢把超时时间从45秒拉到120秒即可。但如果Tomcat一直卡在那不动先看日志有没有报错不要盲目加超时。上传功能里还有一个隐藏权限坑Linux服务器上Tomcat运行用户对/uploads目录没有写权限图片上传报FileNotFoundException: Permission denied。解决给上传目录chmod 777或者chown给Tomcat运行用户。Windows本机开发很少遇到但部署到云服务器或者虚拟机时必现。6. 进阶改造把毕业设计变成能写进简历的项目经验整个系统跑通只是起点真正让它成为简历上值得说的项目建议做四项低成本改造。第一Maven化新建一个Maven项目把jar依赖交给pom.xml管理去除WEB-INF/lib下堆着的jar包。这个动作看起来只是工程结构变化但面试官看到pom.xml会默认你有工程化意识还能顺带引导话题到“依赖冲突”、“scope”这类问题这些都有现成答案可背。第二密码加密升级把MD5换成BCrypt或SHA-256加盐代码改动量不大十几个引用点统一替换就行却能让面试官觉得你关注安全性。第三前端界面用Bootstrap重做老项目的CSS大多是十几年前的table布局换成Bootstrap后视觉焕然一新这块对评分的影响比功能还直接“jsp个人信息展示页面”这类搜索热词背后大家真正想要的就是一个不寒碜的页面。第四加一个导出功能用POI把员工列表导成Excel这是人事系统的高频需求代码量一百行左右但功能醒目。改造完成后做验证我习惯列一张功能测试清单逐项过分别用管理员和普通员工账号登录确认菜单权限不同新增员工后列表页能查到编辑员工时回显是否正确——注意编辑页回显是很多人的薄弱点经常忘了把查询出的对象set进request域删除部门时确认有员工归属的部门能否删除上传非图片文件和超大文件时页面是否有友好提示。光登录流程完整不算跑通建议把异常路径密码错误、账号锁定、Session过期也测一遍测试用例本身就是写简历的好素材。功能全部稳定后可以考虑做一个针对考勤打卡的压力验证用JMeter模拟50个并发登录和打卡请求观察Tomcat的响应时间是否还在可接受范围。JSP项目在50并发下通常会暴露连接池缺失的问题——每次请求都新建数据库连接是性能瓶颈的第一根源。如果简历里写“熟悉连接池”那就把Druid或C3P0整合进来替换裸的DBUtil这是性价比最高的性能优化。做完这一轮这个项目就不只是“能跑”而是“经得起问”了。这也是我这些年看下来JSP老项目最有价值的一条成长路径——技术虽老思路不会过时希望帮到你。本文还有配套的精品资源点击获取