尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于JSP+Servlet+MySQL的员工信息管理系统:从环境搭建到权限隔离的完整实现

基于JSP+Servlet+MySQL的员工信息管理系统:从环境搭建到权限隔离的完整实现 简介这份资源是面向计算机专业学生与Java Web初学者的一份企业员工信息管理系统毕业设计文档围绕基于Java的B/S架构员工信息管理平台展开可用于课程设计、毕业设计参考或Java Web入门练手。压缩包内共1个docx文件约369KB内容涵盖摘要、目录、需求分析、系统概要设计、功能实现与系统测试等完整章节并附有中英文摘要与关键词。文档以MyEclipse为开发工具前台采用JSP后台数据库为MySQL服务器使用Tomcat系统划分为管理员与普通员工两大模块管理员可进行部门管理、员工信息管理、出勤管理、工资管理及请假审核普通员工仅能查看工资与提交请假兼顾了信息保密与系统安全性。目前已有500人学习下载适合需要完整论文结构、功能模块划分思路与开发技术选型参考的读者可帮助快速理清系统设计脉络并对照实现自己的项目。1. 从一份 JSP 毕设源码说起这套员工信息管理系统到底能跑通什么如果你手里正躺着一份基于Java企业员工信息管理系统的设计与实现.docx打开一看是需求分析、用例图、数据库表、测试用例一条龙那它大概率是一份典型的 JSP Servlet MySQL 的 B/S 架构毕设文档。这类资源在网上一搜一大把但真正能照着把环境搭起来、把管理员和普通员工两个角色的权限跑通、把部门/员工/出勤/工资/请假五条业务线串起来的其实没那么多。这套系统的核心价值在于它把「管理员全权限 普通员工只读工资和请假」这个权限模型讲清楚了而且给出了从数据库概念结构到逻辑结构、再到连接原理的完整链路。适合谁适合正在做 Java Web 课程设计、需要一份能改能跑的参考实现的人也适合刚入行想拿一个完整 CRUD 项目练手 JSP 和 JDBC 的开发者。它不花哨没有 Spring Boot 那套自动配置但胜在结构直白改起来不绕。2. 环境搭建与数据库落地MyEclipse Tomcat MySQL 的版本对齐2.1 为什么这套老技术栈反而更容易复现这套系统用的是 MyEclipse 8.5、Tomcat 6.0、MySQL 加 JSP看起来像是十年前的配置。但恰恰因为技术栈老依赖少反而比现在一堆 Maven 依赖冲突的 Spring Boot 项目更容易一次跑通。JSP 本质就是 Servlet页面里嵌 Java 代码Tomcat 负责编译成 Servlet 再执行整个链路没有额外的框架层。数据库连接走的是最原始的 JDBC没有连接池中间件出问题的时候报错信息直接指向 SQL 或者驱动排查路径短。我一般会建议先把 JDK 版本锁在 1.8因为 MyEclipse 8.5 对高版本 JDK 的兼容性有玄学问题编译能过但 Tomcat 启动时可能报UnsupportedClassVersionError。Tomcat 6.0 对应的是 Servlet 2.5 规范JSP 页面里用% page %指令和jsp:useBean都没问题但别用 Servlet 3.0 的注解式配置会不识别。2.2 数据库建表与连接配置的实操步骤先建库字符集用 utf8因为员工姓名和部门名称可能包含中文。下面这段 SQL 覆盖了部门、员工、出勤、工资、请假五张核心表字段名我按文档里的逻辑结构做了还原主键统一用自增 int外键关联员工编号。-- 创建数据库字符集必须指定否则中文乱码 CREATE DATABASE employee_mgr DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; USE employee_mgr; -- 部门表支持父子部门parent_id 为 0 表示顶级部门 CREATE TABLE department ( dept_id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL, parent_id INT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 员工表is_leader 标记是否领导role 区分管理员和普通员工 CREATE TABLE employee ( emp_id INT PRIMARY KEY AUTO_INCREMENT, emp_name VARCHAR(30) NOT NULL, gender VARCHAR(4), age INT, position VARCHAR(50), dept_id INT, is_leader TINYINT DEFAULT 0, username VARCHAR(30) UNIQUE NOT NULL, password VARCHAR(64) NOT NULL, role VARCHAR(10) DEFAULT employee, FOREIGN KEY (dept_id) REFERENCES department(dept_id) ); -- 出勤表按月记录出勤天数 CREATE TABLE attendance ( att_id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT, att_month VARCHAR(7), att_days INT DEFAULT 0, FOREIGN KEY (emp_id) REFERENCES employee(emp_id) ); -- 工资表按月记录工资金额 CREATE TABLE salary ( sal_id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT, sal_month VARCHAR(7), amount DECIMAL(10,2) DEFAULT 0.00, FOREIGN KEY (emp_id) REFERENCES employee(emp_id) ); -- 请假表status 为 0 待审核1 已批准2 已驳回 CREATE TABLE leave_apply ( leave_id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT, start_time DATETIME, end_time DATETIME, reason VARCHAR(255), status TINYINT DEFAULT 0, reply VARCHAR(255), FOREIGN KEY (emp_id) REFERENCES employee(emp_id) );建完表之后在 MyEclipse 里新建 Web Project把 MySQL 的 JDBC 驱动 jar 包丢进WebRoot/WEB-INF/lib目录。驱动版本选mysql-connector-java-5.1.x别用 8.0 的因为 8.0 的驱动类名变成了com.mysql.cj.jdbc.Driver而且默认要求 SSL 连接老代码里的连接 URL 不兼容。连接配置我习惯写一个DBUtil类把 URL、用户名、密码集中管理后面所有 DAO 都调这个类拿 Connection。// DBUtil.java统一数据库连接入口 public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/employee_mgr?useUnicodetruecharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 你的密码; static { try { // 老版本驱动类名5.1.x 系列用这个 Class.forName(com.mysql.jdbc.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(); } catch (SQLException e) {} try { if (stmt ! null) stmt.close(); } catch (SQLException e) {} try { if (conn ! null) conn.close(); } catch (SQLException e) {} } }这段代码的关键参数在 URL 里useUnicodetrue和characterEncodingutf8必须同时出现少一个中文就变问号。Class.forName放在 static 块里保证只加载一次驱动。关闭资源的顺序是 ResultSet → Statement → Connection反过来关在某些驱动实现下会抛异常。如果你在 Tomcat 启动时看到No suitable driver found九成是驱动 jar 没放进 lib 目录或者 URL 里端口写错了。2.3 登录模块与角色跳转的实现逻辑登录是整套系统的入口逻辑不复杂但坑不少。前端 JSP 提交用户名和密码到 ServletServlet 查 employee 表匹配成功后再根据 role 字段决定跳管理员首页还是员工首页。这里有个容易翻车的地方密码在数据库里是明文存储的文档里没提加密但实际部署时至少要做 MD5 摘要否则安全测试那一关过不了。下面是一个简化的登录 Servlet 核心逻辑。// LoginServlet.java处理登录请求并分发角色 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); // 实际项目中这里应该先对 password 做 MD5 再比对 String sql SELECT emp_id, emp_name, role FROM employee WHERE username? AND password?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); ResultSet rs ps.executeQuery(); if (rs.next()) { HttpSession session request.getSession(); session.setAttribute(empId, rs.getInt(emp_id)); session.setAttribute(empName, rs.getString(emp_name)); session.setAttribute(role, rs.getString(role)); // 根据角色跳不同页面 if (admin.equals(rs.getString(role))) { response.sendRedirect(admin/index.jsp); } else { response.sendRedirect(employee/index.jsp); } } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } catch (SQLException e) { e.printStackTrace(); } }用PreparedStatement而不是Statement拼接字符串是为了防 SQL 注入这是最基本的底线。session 里存了 empId、empName 和 role 三个属性后面所有页面都靠这三个值做权限判断和数据显示。跳转用sendRedirect而不是forward因为要改变浏览器地址栏避免刷新时重复提交表单。如果登录后页面报 404检查admin/index.jsp和employee/index.jsp的实际路径是否和 redirect 里的字符串一致大小写敏感。3. 管理员五大模块的 CRUD 实现从部门树到请假审核3.1 部门管理的父子结构与删除约束部门管理不是简单的平铺列表文档里明确提到了「添加部门以及添加子部门」说明部门表是自关联的树形结构。parent_id 为 0 的是顶级部门否则指向父部门的 dept_id。查询的时候要递归或者用自连接把层级拼出来。我一般会在 JSP 页面用递归标签或者 Java 代码里递归组装成树形结构再传给前端。删除部门的时候有个硬约束如果该部门下还有子部门或者还有员工不能直接删否则外键约束会报错。正确做法是先查子部门和员工数量确认都为空才执行 DELETE。// DepartmentDao.java删除前的安全检查 public boolean deleteDept(int deptId) { String checkChild SELECT COUNT(*) FROM department WHERE parent_id?; String checkEmp SELECT COUNT(*) FROM employee WHERE dept_id?; try (Connection conn DBUtil.getConnection()) { // 检查子部门 try (PreparedStatement ps conn.prepareStatement(checkChild)) { ps.setInt(1, deptId); ResultSet rs ps.executeQuery(); if (rs.next() rs.getInt(1) 0) { return false; // 有子部门拒绝删除 } } // 检查员工 try (PreparedStatement ps conn.prepareStatement(checkEmp)) { ps.setInt(1, deptId); ResultSet rs ps.executeQuery(); if (rs.next() rs.getInt(1) 0) { return false; // 有员工拒绝删除 } } // 两项检查都通过执行删除 try (PreparedStatement ps conn.prepareStatement(DELETE FROM department WHERE dept_id?)) { ps.setInt(1, deptId); return ps.executeUpdate() 0; } } catch (SQLException e) { e.printStackTrace(); return false; } }这段代码先查子部门再查员工任何一个不为零就返回 false前端收到 false 后弹提示「请先移除子部门或员工」。参数 deptId 从请求里拿注意做整数校验防止传入非数字导致NumberFormatException。如果数据库没有建外键约束这里的手动检查就是最后一道防线千万别省。3.2 员工信息管理与出勤工资的联动员工信息管理是管理员模块里最核心的部分涉及录入、修改、删除和按部门筛选。员工表里的 dept_id 关联部门表is_leader 标记是否领导role 决定登录后的权限。录入员工时用户名要唯一密码默认给一个初始值比如123456然后强制员工首次登录修改。出勤管理和工资管理都是按月录入管理员在页面上选择月份和员工填入天数和金额。这里有个联动逻辑工资计算通常参考出勤天数但文档里没有写自动计算规则所以实现上就是两个独立的录入表单管理员手动填。如果你要加自动计算可以在工资录入的 Servlet 里先查出该员工该月的出勤天数再乘以日薪基数但日薪基数需要额外配置。// EmployeeDao.java按部门筛选员工列表 public ListEmployee getEmployeesByDept(int deptId) { ListEmployee list new ArrayList(); String sql SELECT e.*, d.dept_name FROM employee e LEFT JOIN department d ON e.dept_id d.dept_id WHERE e.dept_id ? ORDER BY e.emp_id; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, deptId); ResultSet rs ps.executeQuery(); while (rs.next()) { Employee emp new Employee(); emp.setEmpId(rs.getInt(emp_id)); emp.setEmpName(rs.getString(emp_name)); emp.setGender(rs.getString(gender)); emp.setAge(rs.getInt(age)); emp.setPosition(rs.getString(position)); emp.setDeptName(rs.getString(dept_name)); emp.setLeader(rs.getInt(is_leader) 1); list.add(emp); } } catch (SQLException e) { e.printStackTrace(); } return list; }SQL 里用了 LEFT JOIN即使员工没有分配部门也能查出来dept_name 为 null。排序按 emp_id 升序保证列表稳定。参数 deptId 为 0 的时候可以约定为「查全部」在 Servlet 里做判断如果 deptId 为 0 就换一条不带 WHERE 的 SQL。返回的 List 在 JSP 里用c:forEach遍历注意引入 JSTL 标签库否则c:forEach不生效。3.3 请假审核的状态流转与回复机制请假审核是管理员和普通员工之间唯一的双向交互点。员工提交请假申请status 默认为 0待审核管理员在后台看到待审核列表点批准或驳回同时填写回复信息status 更新为 1 或 2。员工在前台查看自己的请假记录时能看到审核状态和回复内容。这里的状态流转必须用事务包起来先更新 leave_apply 表的 status 和 reply再插入一条操作日志如果有的话两步都成功才提交。如果只更新状态不写回复员工看到的回复栏就是空的体验很差。// LeaveDao.java管理员审核请假 public boolean auditLeave(int leaveId, int status, String reply) { String sql UPDATE leave_apply SET status?, reply? WHERE leave_id? AND status0; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, status); ps.setString(2, reply); ps.setInt(3, leaveId); // 加上 status0 条件防止重复审核已处理的申请 return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } }WHERE 条件里带了status0这是乐观锁的思路防止两个管理员同时审核同一条申请导致状态覆盖。如果返回 false说明该申请已经被处理过了前端提示「该申请已审核请刷新列表」。reply 字段长度在表里定义为 255前端 textarea 要限制字数否则超长会抛 SQL 异常。4. 普通员工模块与权限隔离只读工资和请假申请的边界4.1 员工端功能裁剪与 session 校验普通员工登录后只能看到三个功能请假申请、我的工资浏览、修改密码。出勤信息文档里说员工可以查看但摘要里只提了工资和请假我按摘要为准出勤查看可以保留但设为只读。权限隔离的核心是在每个 JSP 页面顶部加 session 校验如果 session 里的 role 不是 employee 或者根本没有登录直接重定向到登录页。这个校验不能只靠前端隐藏菜单因为用户可以直接敲 URL 访问管理员页面所以每个管理员页面的 Servlet 或 JSP 开头都要做角色判断。// 在管理员页面的 JSP 顶部加这段防止员工直接访问 % String role (String) session.getAttribute(role); if (role null || !admin.equals(role)) { response.sendRedirect(request.getContextPath() /login.jsp); return; } %这段脚本let 放在 JSP 最上面return之后不会再输出页面内容。request.getContextPath()拿到项目根路径避免相对路径跳转出错。如果员工通过 URL 直接访问admin/employeeList.jsp会被弹回登录页。注意 session 超时时间在web.xml里配置默认 30 分钟超时后 session 失效同样会被弹回。4.2 请假申请的时间校验与状态回显员工提交请假申请时开始时间和结束时间必须做校验结束时间不能早于开始时间开始时间不能早于当前时间。前端用 JavaScript 做第一层校验后端 Servlet 再做第二层防止绕过前端。提交成功后员工在「我的请假」列表里能看到自己所有申请的状态和回复。状态回显用不同的文字颜色区分待审核显示橙色已批准显示绿色已驳回显示红色。// leave_apply.jsp 前端时间校验 function validateTime() { var start document.getElementById(startTime).value; var end document.getElementById(endTime).value; if (!start || !end) { alert(请选择开始时间和结束时间); return false; } if (new Date(end) new Date(start)) { alert(结束时间必须晚于开始时间); return false; } if (new Date(start) new Date()) { alert(开始时间不能早于当前时间); return false; } return true; }new Date()解析yyyy-MM-dd HH:mm格式的字符串在大部分浏览器里没问题但 Safari 对带空格的格式解析有兼容性问题稳妥做法是把空格替换成T再解析。这个校验只是提升体验后端 Servlet 里还要用SimpleDateFormat再解析一次并比对防止有人用工具直接发请求绕过前端。4.3 工资浏览的按月查询与数据脱敏员工查看工资时只能看到自己的记录SQL 里必须带emp_id ?条件这个 emp_id 从 session 里取不能从请求参数里取否则员工可以改参数查别人的工资。按月查询用下拉框选择月份格式yyyy-MM查询结果只显示月份和金额不显示其他员工的任何信息。如果企业有工资保密要求还可以在页面上加水印或者禁止截图但那是另一个层面的事了。// SalaryDao.java员工查询自己的工资 public ListSalary getMySalary(int empId, String month) { ListSalary list new ArrayList(); String sql SELECT sal_month, amount FROM salary WHERE emp_id?; if (month ! null !month.isEmpty()) { sql AND sal_month?; } sql ORDER BY sal_month DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, empId); if (month ! null !month.isEmpty()) { ps.setString(2, month); } ResultSet rs ps.executeQuery(); while (rs.next()) { Salary s new Salary(); s.setMonth(rs.getString(sal_month)); s.setAmount(rs.getBigDecimal(amount)); list.add(s); } } catch (SQLException e) { e.printStackTrace(); } return list; }emp_id 从 session 取month 从请求参数取拼接 SQL 的时候注意参数顺序。金额用BigDecimal而不是double避免浮点精度问题。查询结果按月份降序最新的工资显示在最上面。5. 避坑与排查这套 JSP 项目最容易翻车的五个地方5.1 中文乱码从请求到响应全链路排查现象登录后员工姓名显示问号或者部门名称变成乱码。原因JSP 页面、Servlet 请求解析、数据库连接、数据库表字符集四个环节只要有一个没设 UTF-8 就会乱。解决JSP 页面顶部加% page contentTypetext/html;charsetUTF-8 %Servlet 里在doPost第一行加request.setCharacterEncoding(UTF-8)数据库连接 URL 带characterEncodingutf8建库建表时指定DEFAULT CHARACTER SET utf8。四个地方都对齐之后中文就不会出问题。如果还乱检查 Tomcat 的server.xml里 Connector 有没有加URIEncodingUTF-8。5.2 Tomcat 启动报 ClassNotFoundException 驱动类现象Tomcat 启动时控制台报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver。原因MySQL 驱动 jar 包没有放到WEB-INF/lib目录下或者放错了位置。解决确认mysql-connector-java-5.1.x.jar在WebRoot/WEB-INF/lib里然后在 MyEclipse 的 Build Path 里把它加到 Web App Libraries。如果用的是 Maven 项目检查 pom.xml 里依赖的 scope 是不是 providedprovided 的包不会打进 WEB-INF/lib。5.3 外键约束导致删除员工失败现象管理员删除员工时页面报 500控制台看到Cannot delete or update a parent row: a foreign key constraint fails。原因该员工在出勤表、工资表或请假表里还有关联记录外键约束阻止删除。解决先删关联表里的记录再删员工。或者在建表时把外键的ON DELETE行为设为CASCADE但级联删除风险高不推荐。我一般会在删除员工的 Servlet 里先查三张关联表的记录数有记录就提示「请先清理该员工的出勤、工资和请假记录」。5.4 session 失效后操作页面报空指针现象登录后放置一段时间不操作再点任何按钮都报NullPointerException。原因session 超时后session.getAttribute(empId)返回 null后面用这个 null 去查数据库或者做判断就炸了。解决在每个需要登录的页面顶部加 session 校验如果 empId 为 null 就重定向到登录页。另外可以在web.xml里把 session-timeout 设长一点比如 60 分钟但根本办法还是加校验。5.5 日期格式转换异常现象提交请假申请时报java.text.ParseException: Unparseable date。原因前端传过来的日期字符串格式和SimpleDateFormat的模式不匹配比如前端传2025-01-01T10:00后端用yyyy-MM-dd HH:mm解析就失败。解决统一前后端日期格式前端用yyyy-MM-dd HH:mm后端SimpleDateFormat也用同样的模式。如果前端用了input typedatetime-local浏览器会带一个T后端解析前先把T替换成空格。6. 从能跑到好用三个进阶改造与验证方法这套系统跑通之后如果想让它更像一个能拿得出手的项目有三个改造方向值得做。第一个是密码加密把明文存储改成 MD5 加盐登录时先对输入的密码做同样的摘要再比对。第二个是分页查询员工列表和请假列表数据量大了之后必须分页用 MySQL 的LIMIT offset, size配合COUNT(*)查总数前端显示页码。第三个是操作日志管理员每次增删改都往日志表里插一条记录记录操作人、操作类型、操作时间和影响的数据 ID出问题的时候能追溯。验证改造是否成功的方法很简单密码加密后用旧密码登录应该失败用新密码登录成功分页后第一页显示 10 条第二页显示接下来的 10 条总数和数据库里的记录数一致操作日志表里能看到对应的插入记录。我一般会在改造完之后写一个简单的测试用例用 JUnit 或者直接写个 main 方法跑一遍 DAO确认每个方法的返回值符合预期。// 密码 MD5 加密工具方法 public static String md5(String input) { try { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(input.getBytes(UTF-8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(MD5 加密失败, e); } }这个方法把任意字符串转成 32 位十六进制 MD5 值。注册和登录的时候都调这个方法数据库里存的就是密文。注意 MD5 本身已经不够安全了但比明文强如果要做真正的安全认证应该用 BCrypt 或者 SHA-256 加盐。改造完之后我习惯把数据库里的测试数据全部清掉重新走一遍从注册到登录到各模块操作的完整流程确认没有遗漏的明文密码残留。从那以后我每次交付这类 JSP 项目都会强制走一遍「清库重跑」的流程避免测试数据污染和权限漏洞。希望帮到你。本文还有配套的精品资源点击获取
返回列表