基于JSP的企业人事管理系统:源码实现与项目部署全解析

📅 发布时间:2026/9/15 2:53:37
基于JSP的企业人事管理系统:源码实现与项目部署全解析
简介基于JSP的企业人事管理系统毕业设计资源包为Java Web方向的毕设学生与初学者提供完整实战范本。系统覆盖用户管理、人事档案、考勤、薪酬、绩效、培训及报表等核心模块从功能需求到数据库设计均有代码与文档支撑。压缩包共277个文件主体为104个JSP页面和14个Java源文件辅以CSS、JavaScript、XML配置及class文件并含项目报告doc文档与数据库mdf文件整套容量仅3.18MB轻量但结构完整。已有51人学习下载适合用来对照理解ServletJSP的MVC分层、会话管理、权限控制及MySQL表关系设计等关键知识点。通过阅读项目报告与运行源码可快速掌握从环境搭建到功能实现的完整链路是毕业设计选题或Spring Boot过渡前打基础的实用参考资料。1. 基于 JSP 的企业人事管理系统毕业设计最常见的分层切入点一个基于 JSP 的企业人事管理系统是毕业设计里被选了很多年但仍然值得认真做的题目。很多人觉得 JSP 已经是老技术但正是因为它老Servlet 生命周期、请求转发、JDBC 事务边界、Filter 拦截这些概念都被完整保留着答辩老师一问就能展开而且人事系统的业务边界明确员工、部门、考勤、登录权限都能用清晰的表和字段表达不需要引入复杂概念。标题里的源代码和项目报告意味着交付物不是“能跑就行”而是必须有一份能解释设计过程的文档。这套系统要解决的是企业内部“统一管档案、管考勤、管权限”的后台需求。从技术上用 JSP 展示页面、Servlet 处理请求、JDBC 访问数据库三个环节串成一条完整的数据链路正好对应软件工程里的表现层、业务层、数据访问层。适合计算机专业做毕业设计或课程设计的在校生也适合想快速搭一个纯 Java Web 后台原型、又不想引入整套 Spring 生态的小团队。后面的内容会沿着技术选型、数据库建模、关键编码、Tomcat 部署四条线展开。2. 企业人事管理系统的 JSP 技术选型与数据库设计这套系统推荐使用 JSP Servlet JDBC 的三层结构而不是在 JSP 里写 Java 小脚本也不是一上来就套 Spring Boot。原因是标题点名了 JSP同时毕业设计与实现考察的核心是数据库设计和编程基础。既然使用 JDBC就必须把连接参数、SQL、结果集映射分开写后续每一个业务模块才能有统一的数据访问方式。JSP 负责渲染页面Servlet 负责接收请求和跳转DAO 负责所有 SQL 操作职责边界清楚以后项目报告里的架构图也容易画。2.1 选型JSP Servlet JDBC 的适用边界和实现原则JSP Servlet JDBC 的适用边界是业务量不大、没有复杂缓存和分布式需求。人事管理系统的主要操作是单表或两表以上的增删改查、权限控制、简单统计关系型数据库足够容纳。Tomcat 自带的 Servlet 容器能直接运行 JSP 和编译后的类文件不需要额外引入 Spring 上下文部署体积小这对毕业设计现场演示更友好。需要明确的是这里的 JSP 并不是指页面里直接写 Java而是用 JSP 负责展示Servlet 负责控制JavaBean 承载数据。实现原则有两条第一不在 JSP 里直接写数据库连接代码第二不在 Servlet 里写大段业务逻辑只做参数收集、调用 DAO、转发或重定向。这样答辩时才能说清楚表现层与控制层分离。2.2 数据库建模部门、员工、考勤、用户四张表的关系人事管理系统的核心实体可以收敛为四张表部门表、员工表、考勤表、用户表。部门与员工是一对多员工与考勤是一对多用户表负责登录账号与角色。如果想做得更完整可以加薪资表、招聘表但作为毕业设计四张表已经能覆盖“大而全”的功能列表并且数据关系清晰ER 图画起来也干净。字段类型上要注意员工编号用 varchar 而不是 int因为编号经常带前导零工资用 decimal(10,2) 而不是 float所有表都加 create_time 和 update_time项目报告里可以写成通用审计字段。表名核心字段关联关系用途departmentid, dept_name, manager_id与 employee 一对多部门层级employeeid, emp_no, emp_name, dept_id, hire_date, salary从属于部门员工档案attendanceid, emp_id, work_date, status, overtime_hours属于员工考勤记录sys_userid, username, password, role, emp_id对应员工档案登录权限建表 SQL 中先删除从表再删除主表避免外键约束报错。department 的 manager_id 先不建自引用外键这样删除部门时不会出现级联联动过于复杂的问题sys_user 的 password 字段长度给 64是为了容纳 MD5 或 SHA-256 摘要而不是明文。status 字段用 TINYINT 存 0、1、2分别表示未打卡、正常、异常比直接存文字更省空间也方便后台做统计。DROP TABLE IF EXISTS attendance; DROP TABLE IF EXISTS employee; DROP TABLE IF EXISTS department; DROP TABLE IF EXISTS sys_user; CREATE TABLE department ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL UNIQUE, manager_id INT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(20) NOT NULL UNIQUE, emp_name VARCHAR(50) NOT NULL, gender CHAR(2), dept_id INT, hire_date DATE, salary DECIMAL(10,2), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES department(id) ); CREATE TABLE attendance ( id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT, work_date DATE, status TINYINT DEFAULT 0, overtime_hours DECIMAL(4,1), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_att_emp FOREIGN KEY (emp_id) REFERENCES employee(id) ); CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role VARCHAR(20) DEFAULT USER, emp_id INT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );上面的建表顺序是先删子表再删主表SQL 脚本才能重复执行。emp_no 加了 UNIQUE 约束是用来校验员工编号不能重复dept_id 上的外键保证员工不能挂到一个不存在的部门。attendance 的 work_date 和 emp_id 组合起来应该加 UNIQUE 约束表示一个员工一天只能有一条考勤记录这个约束可以在项目报告里写成“业务规则落到数据库层面”。2.3 JDBC 连接参数的存放和 DAO 的基础写法一个常见的错误做法是把数据库地址、账号密码直接散落在每个类里遇到环境变化要改十几个文件。可交付的项目里应该有一个 db.properties 文件集中管理参数再用一个静态代码块加载。这样既能解释自己考虑了配置与代码分离也方便对方打开源码后只改一个文件就能跑起来。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hr_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456// DBUtil.java import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; public class DBUtil { private static String url; private static String username; private static String password; static { try (InputStream in DBUtil.class.getClassLoader().getResourceAsStream(db.properties)) { Properties props new Properties(); props.load(in); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); Class.forName(props.getProperty(jdbc.driver)); } catch (Exception e) { throw new ExceptionInInitializerError(数据库配置加载失败); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(url, username, password); } }逻辑说明db.properties 放在 classes 根目录下ClassLoader 的 getResourceAsStream 会从 classpath 读取。url 里的 characterEncodingutf8 与 useUnicodetrue 配套解决中文写入乱码serverTimezone 指定为 Asia/Shanghai避免 MySQL 8 的时区警告。Class.forName 在 JDBC 4.0 之后可以省略但保留对旧版本驱动兼容性更好。JDBC 连接每次调用都新建并发量不高时没问题如果以后想优化可以换 Druid 连接池但毕业设计里直接使用 DriverManager 更容易把原理讲清楚。相比连接池更重要的是连接不泄漏。2.3.1 资源释放的 finally 模板用 JDBC 时Connection、PreparedStatement、ResultSet 三个对象都需要释放。关闭顺序与创建顺序相反先关 ResultSet再关 PreparedStatement最后关 Connection。JDK 7 之后推荐用 try-with-resources代码更短且不容易漏。// EmployeeDAO 中的查询片段 String sql SELECT * FROM employee WHERE id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, employeeId); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { // 映射成 Employee 对象 } } } catch (Exception e) { e.printStackTrace(); }try-with-resources 会自动关闭括号里声明的资源不需要在 finally 里手动调用 close。注意参数占位符的下标从 1 开始ps.setInt(1, employeeId) 表示把 employeeId 放进第一条 SQL 参数的问号位置。这种写法在项目报告里可以写成“使用自动资源管理避免连接泄漏”比贴一大段 finally 代码更容易解释。3. 用 JSP Servlet JDBC 把员工模块从登录落到列表登录、员工列表、新增员工是人事管理系统最核心的三段链路这三块能跑通部门管理和考勤记录基本就是同一种写法的重复。这一章从权限过滤说起落到如何查询并渲染员工列表再到新增员工时最容易翻车的两个点。3.1 登录权限控制用 Filter 拦截未登录请求还是每个页面单独判断写 JSP 人事系统时最不专业的现象是用户不登录也能直接访问员工列表页。常见做法不是在每个 JSP 页面里判断 session而是用一个 Filter 拦截所有/pages/*请求。Filter 在 Servlet 容器处理请求之前执行只需要把未登录用户拦回 login.jsp这样登录页之外的受限页面就都有了统一控制。如果以后要把角色权限细分比如普通用户不能访问系统设置页也只需要在这个 Filter 里扩展判断。// AuthFilter.java import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; WebFilter(/pages/*) public class AuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); if (session null || session.getAttribute(loginUser) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }逻辑说明WebFilter(/pages/*)是 Servlet 3.0 的注解方式web.xml 里不用额外配置映射。getSession(false)表示当前没有 session 时不主动创建直接返回 null避免每个静态资源访问都生成新 session。sendRedirect拼接的是request.getContextPath()这样项目部署在带应用路径的 Tomcat 下也能正确跳转。注意 Filter 不拦截 login.jsp 本身所以登录页要放在/login.jsp而所有受保护页面统一放到/pages/目录下这是 JSP 后台项目常用的路径约定。3.2 员工列表Servlet 查数据、JSP 用 JSTL 渲染的完整链路点击“员工管理”菜单后浏览器请求/employee/list.do。Servlet 的 doGet 方法调用 EmployeeDAO 的 findAll 方法把 List 放到 request 作用域再用request.getRequestDispatcher(/pages/employee_list.jsp).forward()转发。JSP 页面通过 EL 表达式遍历 ListJSTL 标签负责循环和格式化。// EmployeeListServlet.java WebServlet(/employee/list.do) public class EmployeeListServlet extends HttpServlet { private EmployeeDAO employeeDao new EmployeeDAO(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { ListEmployee list employeeDao.findAll(); req.setAttribute(employeeList, list); req.getRequestDispatcher(/pages/employee_list.jsp).forward(req, resp); } catch (Exception e) { req.setAttribute(errorMsg, 查询员工列表失败); req.getRequestDispatcher(/pages/error.jsp).forward(req, resp); } } }参数说明request.setAttribute里的 keyemployeeList必须与 JSP 的 EL 变量名一致forward是服务端内部转发浏览器地址栏不会变化所以刷新页面会重复请求同一个 Servlet。这里不能改用sendRedirect因为重定向是二次请求request 作用域里的数据已经丢失。如果确实要重定向只能把 id 等参数拼到 URL 上再查一次。JSP 页面的循环渲染代码如下% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table classtable tr th员工编号/thth姓名/thth所属部门/thth入职日期/th /tr c:forEach items${employeeList} varemp tr td${emp.empNo}/td td${emp.empName}/td td${emp.deptName}/td td${emp.hireDate}/td /tr /c:forEach /table逻辑说明JSTL 标签库引入的 uri 在 Tomcat 9 里仍然使用http://java.sun.com/jsp/jstl/core不要因为网上有人提到 jakarta 前缀就乱改否则会报 TLD 加载错误。${emp.empName}会调用 Employee 类的 getEmpName 方法所以实体类属性命名必须规范。这里的deptName不是 employee 表的直接字段而是 DAO 中联表查询得到的虚拟属性。数据展示方式代码位置适用场景JSP Scriptlet页面内写 Java简单演示不推荐EL JSTL页面标签本项目推荐页面干净前端 JS 渲染 JSON浏览器端适合前后端分离偏题列表中EL JSTL 是 JSP 项目里维护成本最低的方案。如果页面出现null字符串先检查 getter 方法返回的字段名是否正确如果出现空列表先直接访问一次 Servlet 地址确认 EmployeeDAO 是否查到数据不要急着改页面。3.3 新增员工时最常踩的两个坑中文乱码和日期格式表单提交到/employee/add.dodoPost 里要做的第一件事是设置request.setCharacterEncoding(UTF-8)。这个设置必须放在读取任何request.getParameter之前否则 POST 请求的中文参数会变成问号。GET 请求的乱码要到 Tomcat 的 server.xml 中给 Connector 配置 URIEncoding而 POST 请求可以用一个过滤器统一处理。3.3.1 用一个字符编码过滤器统一处理请求与响应编码// EncodingFilter.java WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); chain.doFilter(request, response); } }逻辑说明这个过滤器拦截所有路径比在 Servlet 里逐个设置更省事。response.setCharacterEncoding 也顺手设置避免响应输出 ISO-8859-1。如果 JSP 页面中文正常但提交表单后数据库里乱码要检查三处JSP 页面的 pageEncoding、MySQL 表字符集、JDBC url 里的 characterEncoding三者必须一致。还有一个细节这个 Filter 放在 AuthFilter 之前还是之后会影响执行顺序编码过滤应该在最外层先执行。3.3.2 日期字符串与 java.sql.Date 的转换HTML 表单里的 input typedate 提交格式是 yyyy-MM-dd后端拿到的 String 需要转成 java.sql.Date 才能写入数据库。String hireDate req.getParameter(hireDate); SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd); java.util.Date parsedDate sdf.parse(hireDate); java.sql.Date sqlDate new java.sql.Date(parsedDate.getTime()); ps.setDate(4, sqlDate);逻辑说明SimpleDateFormat 的 pattern 必须与表单格式一致parse 后得到的是 java.util.Date不能直接传给 PreparedStatement 的 setDate 方法需要先转换成 java.sql.Date。如果用户没有填写日期getParameter 拿到的是空字符串parse 会抛 ParseException所以在 Servlet 里要 catch 住异常并把错误信息回传到表单页而不是让异常直接打到 Tomcat 错误页。处理新增员工时建议先获得 empNo 是否存在重复再执行 insert数据库唯一约束负责兜底这样能让用户看到业务提示而不是底层异常。4. 企业人事管理系统部署到 Tomcat 时的目录、报错与运行验证代码写完不是终点要能在干净 Tomcat 环境下运行这套 JSP 系统才算真正交付。部署阶段最常见的问题集中在目录结构缺文件、依赖 jar 包没放进 lib、JSP 编译缓冲未清理这三类。下面这些检查项每一步都能能对应到项目报告里的“运行环境与部署说明”。4.1 可交付的源代码压缩包建议采用这样的目录结构一个可交付的 JSP 项目压缩包不应只是 IDE 工作区还要让对方能直接提到 Tomcat 里跑。Eclipse 项目里WebContent 目录是部署根目录Maven 项目则对应 src/main/webapp。压缩包根部最好放一个说明文件写清 JDK、Tomcat、MySQL 版本以及初始化数据库的顺序。hr_system/ ├── db/ │ └── init.sql ├── src/ │ ├── com/hrsys/dao/ │ ├── com/hrsys/entity/ │ ├── com/hrsys/filter/ │ └── com/hrsys/servlet/ ├── WebContent/ │ ├── WEB-INF/ │ │ ├── lib/ │ │ └── web.xml │ ├── css/ │ ├── js/ │ ├── login.jsp │ └── pages/ │ ├── employee_list.jsp │ └── department_list.jsp └── 项目报告/ ├── 需求分析.docx ├── 数据库设计.docx └── 答辩演示.mddb/ 目录里放 init.sql项目报告里的数据库截图要和这里的 SQL 对应。WEB-INF/lib 需要放置 mysql-connector-java 和 jstl 两个关键 jar 包没有 jstl 会导致 JSP 页面停止渲染并报缺少标签库。lib 下的 jar 包文件名不要出现空格和中文否则 Tomcat 在解压时可能识别不到。4.2 在 Tomcat 上部署 JSP 项目的最小操作步骤在 Tomcat 上部署 JSP 项目有两种方式一种是直接用 IDE 导出的 war 包复制到 webapps 目录另一种是把 WebContent 整个复制成一个文件夹。对毕业设计演示场景war 包更合适因为迁移到新机器时不会遗漏单个文件。打包可以借助 IDE 的 export 功能也可以直接在命令行执行 jar 命令。cd hr_system jar cvf hr.war -C WebContent . cp hr.war /opt/tomcat/webapps/ cd /opt/tomcat/bin ./startup.sh tail -f /opt/tomcat/logs/catalina.out逻辑说明jar cvf hr.war -C WebContent .表示进入 WebContent 目录并把当前目录内容打包为 hr.war这样解压后的应用根路径就是登录页。Tomcat 启动时检测到 webapps 目录下的 war 包会自动解包。看到日志输出Deployment of web application archive hr.war has finished即表示部署成功如果端口被占用或数据库没启动catalina.out 最后会给出异常堆栈。如果不想每次启动都手动设环境变量也可以在 catalina.sh 中加上JAVA_OPTS-Xms256m -Xmx512m -Dfile.encodingUTF-8参数说明JVM 堆内存给 256MB 到 512MB对一个演示用的小型人事系统已经足够。-Dfile.encodingUTF-8用来防止 Windows 环境下控制台日志输出乱码。修改端口时Tomcat 的 conf/server.xml 中把 Connector 的 port 从 8080 改成 8088访问地址就变成http://localhost:8088/hr/pages/index.jsp。应用上下文路径是 hr因为 war 包名是 hr.war。4.3 启动后遇到 404、500、中文乱码时先查哪些位置404 的第一反应是看浏览器地址栏有没有包含应用路径/hr/因为 webapps 下解压出的目录名是 hrURL 没带应用路径就会打到 Tomcat 根应用。500 分为两类如果错误行指向 JSP 中某个属性不存在多半是实体类 getter 方法名写错如果报org.apache.jasper.JasperException且堆栈提到 JSTL先确认 WEB-INF/lib 里有没有 jstl 和 standard 包并将 jstl 换成 1.2 版本。中文乱码则优先看 JSP 页面第一行的 pageEncoding再看 db.properties 中的字符集参数。现象优先怀疑位置排错入口404应用上下文路径、文件防止位置localhost.log500Java 异常、JSP 编译异常、jar 包缺失catalina.out中文乱码JSP pageEncoding、请求字符集、MySQL 表字符集浏览器响应头与数据库日志4.3.1 JSP 编译后的 class 文件放在哪里经常有人问“jsp编译class文件保存在哪里”这个问题通常出现在修改 JSP 后页面不生效的场景。Tomcat 默认把 JSP 翻译后的 .java 和 .class 放到工作目录Linux 下位于/opt/tomcat/work/Catalina/localhost/hr/org/apache/jsp/Windows 下位于%CATALINA_BASE%\work\Catalina\localhost\项目名\org\apache\jsp。如果修改 JSP 后页面没有变化先删掉 work 目录下对应项目目录再重启 Tomcat强制触发重新编译。find /opt/tomcat/work -name *employee_list*.java这段命令用来快速定位编译后的 JSP 类。注意编译后的 .java 文件里大量使用 out.write 拼接 HTML不适合直接阅读业务逻辑它的主要价值是确认页面是否进入容器编译流程。JSP 的新代码没有被编译通常就是这个 work 缓存目录在作怪。5. 给 JSP 人事管理系统源码加一层能写进项目报告里的请求计时项目报告里最容易被追问的是“系统有多大工作量”和“哪个地方体现你的工程意识”。与其泛泛说“系统性能良好”不如在源码里加一个最轻量的请求计时过滤器把每次请求耗时打印到日志。这个技巧代码量很小但能证明你考虑到了可观测性答辩现场也有实际输出可展示。5.1 用 Filter 给每个请求计时并把日志输出到控制台// AccessLogFilter.java WebFilter(/*) public class AccessLogFilter implements Filter { private static final Logger LOG Logger.getLogger(AccessLogFilter.class.getName()); Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { long start System.nanoTime(); chain.doFilter(req, resp); long cost (System.nanoTime() - start) / 1_000_000; String path ((HttpServletRequest) req).getRequestURI(); LOG.info(path - cost ms); } }逻辑说明System.nanoTime 适合测量短时间间隔不能用 System.currentTimeMillis 代替。chain.doFilter 执行完后计算耗时再把请求路径和毫秒数写入日志。Filter 的执行顺序配置在 web.xml 或注解中通常编码过滤在最前面AccessLogFilter 放在业务 Filter 后面这样计时的才包含真实处理过程。如果把这段代码放进系统的 Tomcat 里访问登录页一次日志里会打印类似这样的内容INFO [AccessLogFilter] /hr/login.jsp - 3 ms INFO [AccessLogFilter] /hr/pages/employee_list.jsp - 15 ms这两个数字会随机器配置和数据库状态变化但是在项目报告里附上日志截图并写清测试环境为 Windows 11、Tomcat 9、MySQL 8就能作为“系统运行效率验证”的佐证。如果想让日志更完整还可以从 session 中取出当前登录用户的 username 拼进日志String username anonymous; HttpSession session ((HttpServletRequest) req).getSession(false); if (session ! null session.getAttribute(loginUser) ! null) { username (String) session.getAttribute(loginUser); } LOG.info(username - path - cost ms);注意getSession(false) 在这里不会创建新 session避免了所有静态资源请求都触发 session 创建。日志里同时保留用户名和请求耗时答辩时就能向老师说明源码里记录了操作痕迹后续可以配合 Filter 做简单的行为审计。本文还有配套的精品资源点击获取