简介:本资源是一套完整的JavaWeb企业级员工信息管理系统毕业设计实战资料,面向计算机专业本科生、Java初学者及Web开发入门者,解决中小企业员工信息化管理的实际需求。系统采用B/S架构,涵盖管理员(部门/员工/考勤/工资/请假审核)与普通员工(查看工资、提交请假)双角色功能,代码结构清晰,数据库设计规范,具备完整业务闭环。资源共8个文件,包含2份Word文档(中期检查表、毕业论文)、1份PPT答辩材料、1个SQL数据库脚本、1个ZIP源码包、2段MP4项目讲解视频(含部署、模块演示与密码修改等关键操作),以及1份任务书,总大小29.64MB,内容组织逻辑严密,便于分模块学习与复现。已有178人下载学习,提供从环境搭建、数据库创建、功能实现到测试验证的全流程支撑,特别适合课程设计、毕设参考与JavaWeb综合能力提升。
1. 这不是又一个“学生作业式”JavaWeb项目:它真能跑通MySQL 8.0 + Tomcat 9 + IDEA 2023,带完整请假审批流、角色权限隔离和可部署war包
你搜“JavaWeb员工管理系统”,页面刷出几百个“含论文+PPT+代码”的压缩包——点开一看,数据库脚本用的是MySQL 5.6语法,web.xml里还写着<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>,Tomcat启动报java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet,连登录页都加载不出来。这个资源不一样:它基于Servlet 4.0 + JSP 2.3 + JDBC 8.0构建,所有模块(员工档案、部门管理、岗位设置、请假申请、审批历史、统计报表)全部走真实HTTP请求链路,数据库字段命名遵循snake_case规范,SQL语句明确标注事务边界,连webapp/WEB-INF/web.xml里<welcome-file-list>都配了index.jsp而非login.html。它不教你怎么写HelloWorld,而是直接给你一套能改、能扩、能上线的最小可行系统——适合刚学完JDBC想练手的真实业务场景,也适合外包团队快速搭后台原型。如果你正卡在IDEA里配不好Tomcat运行时、搞不定MySQL中文乱码、或者被NoClassDefFoundError: javax/xml/bind/DatatypeConverter这种玄学错误折磨到凌晨三点,这份资料就是为你准备的。
2. 从零启动:IDEA 2023.2 + Tomcat 9.0.83 + MySQL 8.0.33 环境搭建与项目导入实操
2.1 环境版本对齐:为什么必须锁死这三组版本号?
很多翻车就栽在环境错配上。这份资料明确要求:
- IDEA:2023.2及以上(低版本不支持Servlet 4.0的
@WebServlet注解自动注册) - Tomcat:9.0.83(非9.0.x任意版!9.0.80之前存在JNDI资源注入漏洞补丁未合入,且该版本已验证兼容JDK 17)
- MySQL:8.0.33(关键!8.0.32起默认启用
caching_sha2_password认证插件,旧JDBC驱动会报Access denied for user)
提示:不要用XAMPP/MAMP这类集成包——它们自带的Tomcat和MySQL版本不可控,且端口、路径、配置文件位置全被封装,排查问题时像在黑匣子摸开关。
2.2 IDEA中导入项目:三步绕过90%的编译失败
项目结构是标准Maven Webapp骨架,但pom.xml里藏着几个关键配置:
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- MySQL 8.0专用驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- Servlet API必须provided,否则打包后Tomcat冲突 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> </dependencies>操作步骤:
- IDEA →
File → Open→ 选择解压后的根目录(含pom.xml和src/main/webapp) - 弹窗选"Open as Project"(不是"Import Project"!后者会忽略webapp结构)
- 右下角弹出"Maven projects need to be imported" → 点
Enable Auto-Import,等待依赖下载完成
注意:若IDEA提示"Project SDK is not configured",必须手动指定JDK 17(不是JRE!),路径为
C:\Program Files\Java\jdk-17.0.1(Windows)或/Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home(macOS)。JDK 11或JDK 21都会触发Unsupported class file major version错误。
2.3 Tomcat服务器配置:两个致命参数必须手动改
IDEA内置Tomcat配置界面会漏掉关键项,必须进Run → Edit Configurations → Tomcat Server → Configuration面板手动设置:
| 配置项 | 值 | 说明 |
|---|---|---|
| Application context | /emp | 不能留空!否则访问地址变成http://localhost:8080/,而项目登录页实际路径是/emp/login.jsp |
| VM options | -Dfile.encoding=UTF-8 -Duser.timezone=GMT+8 | 解决中文日志乱码和数据库时间戳偏移(MySQL 8.0默认时区为SYSTEM,需与JVM对齐) |
| Deployment → Artifact | emp:war exploded | 必须选这个!war格式无法热更新JSP,exploded才能实时看到页面修改 |
提示:首次启动前,先在
src/main/resources/db.properties里改数据库连接参数:jdbc.url=jdbc:mysql://localhost:3306/emp_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true jdbc.username=root jdbc.password=your_password_here
allowPublicKeyRetrieval=true是MySQL 8.0.33必需参数,漏写会报Could not retrieve public key。
2.4 数据库初始化:执行emp_system.sql前必须做的三件事
提供的emp_system.sql是完整建库脚本,但直接执行会失败:
先创建数据库并指定字符集(不能依赖脚本里的
CREATE DATABASE):CREATE DATABASE emp_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;确认MySQL用户有
CREATE ROUTINE权限(脚本含存储过程):GRANT CREATE ROUTINE ON emp_system.* TO 'root'@'localhost'; FLUSH PRIVILEGES;关闭MySQL严格模式(否则
INSERT INTO emp (name, age) VALUES ('张三', NULL)会报错):SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES',''));
执行完emp_system.sql后,检查emp表是否含10条测试数据,user_role表是否含admin和employee两条记录——这是登录校验的起点。
3. 核心功能拆解:员工请假审批流如何用原生Servlet+JSP实现闭环
3.1 请假流程状态机:从提交到归档的5个状态及触发条件
系统不依赖Spring Workflow或Activiti,而是用纯数据库字段+Servlet逻辑控制状态流转:
| 状态码 | 状态名 | 触发动作 | 数据库字段 |
|---|---|---|---|
0 | 待提交 | 员工填写表单点击“保存草稿” | status = 0,submit_time IS NULL |
1 | 待审批 | 员工点击“提交申请”,触发INSERT INTO leave_apply | status = 1,submit_time = NOW() |
2 | 审批中 | 部门经理登录后,在/approve/list.jsp点击“处理” | status = 2,approver_id = ?,approve_time = NULL |
3 | 已通过 | 经理点击“同意”,更新status=3并写入approve_time | status = 3,approve_time = NOW(),result = 'pass' |
4 | 已拒绝 | 经理点击“拒绝”,同上但result = 'reject' | status = 4,approve_time = NOW(),result = 'reject' |
关键设计:所有状态变更都通过
LeaveApplyServlet的doPost方法统一处理,避免前端JS直接调用不同URL导致状态不一致。例如拒绝操作不是发/reject?id=123,而是POST到/leave/apply,body里带action=reject&applyId=123。
3.2 权限控制粒度:URL拦截器如何区分“谁能看到谁的数据”
系统没用Shiro或Spring Security,而是自研AuthFilter,拦截规则写在web.xml:
<filter> <filter-name>AuthFilter</filter-name> <filter-class>com.emp.filter.AuthFilter</filter-class> </filter> <filter-mapping> <filter-name>AuthFilter</filter-name> <url-pattern>/admin/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>AuthFilter</filter-name> <url-pattern>/approve/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>AuthFilter</filter-name> <url-pattern>/leave/*</url-pattern> </filter-mapping>AuthFilter.doFilter()核心逻辑:
// 获取当前用户角色 String role = (String) request.getSession().getAttribute("role"); String requestURI = request.getRequestURI(); // /admin/开头的URL只允许admin访问 if (requestURI.startsWith("/admin/") && !"admin".equals(role)) { response.sendRedirect(request.getContextPath() + "/error/403.jsp"); return; } // /approve/开头的URL只允许manager访问(部门经理) if (requestURI.startsWith("/approve/") && !"manager".equals(role)) { response.sendRedirect(request.getContextPath() + "/error/403.jsp"); return; } // /leave/apply.jsp允许所有登录用户访问,但数据查询SQL加WHERE条件 if (requestURI.contains("/leave/apply.jsp")) { // 在DAO层动态拼接:AND emp_id = ?(当前用户ID) }血泪经验:千万别在JSP里用
<c:if test="${sessionScope.role == 'admin'}">做按钮显示控制!前端隐藏不等于后端拦截,攻击者直接POST到/admin/delete照样能删库。真正的权限必须在Filter或Servlet入口处校验。
3.3 数据库设计关键约束:为什么leave_apply表要冗余dept_name字段?
leave_apply表结构片段:
CREATE TABLE leave_apply ( id BIGINT PRIMARY KEY AUTO_INCREMENT, emp_id BIGINT NOT NULL, dept_id BIGINT NOT NULL, dept_name VARCHAR(50) NOT NULL, -- 冗余字段! start_date DATE NOT NULL, end_date DATE NOT NULL, reason TEXT, status TINYINT DEFAULT 0, submit_time DATETIME, approver_id BIGINT, approve_time DATETIME, result VARCHAR(10) );冗余dept_name的原因:
- 避免联表查询性能损耗:审批列表页需显示“张三(技术部)请假3天”,若每次查
leave_apply JOIN department ON dept_id=department.id,QPS高时MySQL连接数暴涨 - 保证历史数据准确性:员工A原属“技术部”,后调岗到“产品部”,其历史请假记录仍应显示“技术部”,而非随部门表更新而改变
- 简化JSP取值逻辑:
<td>${apply.dept_name}</td>比<td>${apply.department.name}</td>少一层对象导航,降低NPE风险
注意:冗余字段的维护由
LeaveApplyService.addApply()方法保证——插入前先查DepartmentDAO.findById(apply.getDeptId()),再把department.getName()赋值给apply.setDeptName()。
4. 避坑指南:IDEA运行JavaWeb项目最常踩的7个坑及现场急救方案
4.1 现象:启动Tomcat后浏览器打开http://localhost:8080/emp显示404,但IDEA控制台无报错
原因:webapp/WEB-INF/web.xml中<welcome-file-list>未配置,或配置了index.html但项目根目录下没有该文件
解决:
- 检查
webapp/WEB-INF/web.xml是否存在,内容是否含:<welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list> - 确认
webapp/login.jsp文件真实存在(注意大小写!Linux下Login.jsp≠login.jsp) - 若用Maven构建,检查
target/emp/目录下是否有login.jsp——没有说明资源未拷贝,需在IDEAProject Structure → Artifacts中确认WEB-INF和webapp目录已添加到输出路径
4.2 现象:登录时用户名密码正确,但跳转到/error/500.jsp,控制台报java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter
原因:JDK 17移除了Java EE模块,javax.xml.bind包已废弃,但项目代码中仍有DatatypeConverter.printBase64Binary()调用(常见于密码加密工具类)
解决:
- 在
pom.xml添加兼容依赖:<dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>4.0.3</version> </dependency> - 将代码中
javax.xml.bind.DatatypeConverter替换为jakarta.xml.bind.DatatypeConverter - 清理IDEA缓存:
File → Invalidate Caches and Restart → Invalidate and Restart
4.3 现象:MySQL连接成功,但查询中文字段返回??乱码
原因:JDBC URL未指定characterEncoding=utf8mb4,或MySQL服务端my.cnf未配置collation-server=utf8mb4_unicode_ci
解决:
- 修改
db.properties:jdbc.url=jdbc:mysql://localhost:3306/emp_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=utf8mb4 - 检查MySQL服务端配置(Windows在
C:\ProgramData\MySQL\MySQL Server 8.0\my.ini,Linux在/etc/my.cnf):[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci - 重启MySQL服务后,执行SQL验证:
确保SHOW VARIABLES LIKE 'character_set%'; SHOW VARIABLES LIKE 'collation%';character_set_server和collation_server均为utf8mb4
4.4 现象:修改JSP页面后刷新浏览器无变化,必须重启Tomcat才生效
原因:IDEA的On Update action设置为Update classes and resources,但未勾选Update resources,或Tomcat配置中On frame deactivation未设为Update resources
解决:
Run → Edit Configurations → Tomcat Server → On Update action→ 选Update classes and resources- 同页面下拉到底 →
On frame deactivation→ 选Update resources - 确认
Settings → Build → Compiler → Build project automatically已勾选 - 若仍无效,在
Help → Find Action输入Registry,搜索compiler.automake.allow.when.app.running,勾选此项
4.5 现象:请假审批页点击“同意”后,数据库status字段仍为1,未变更为3
原因:ApproveServlet中SQL更新语句未加WHERE id = ?条件,导致全表status=3
解决:
- 检查
ApproveDAO.updateStatus()方法:String sql = "UPDATE leave_apply SET status = ?, approve_time = NOW(), result = ? WHERE id = ?"; // 必须有WHERE! PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, status); // 3 or 4 ps.setString(2, result); // "pass" or "reject" ps.setLong(3, applyId); // 关键!漏掉这行就全表更新 - 在开发环境开启MySQL慢查询日志,执行
SHOW PROCESSLIST观察是否有UPDATE leave_apply SET status=3无WHERE的语句正在运行
5. 进阶技巧:如何把这套系统快速改成“员工考勤系统”或“报销审批系统”
5.1 模块替换法:用3个文件撬动整个业务线迁移
系统采用“配置驱动业务”设计,核心业务逻辑集中在以下三个文件,改它们就能切换系统类型:
| 文件路径 | 作用 | 替换示例(考勤系统) | 替换示例(报销系统) |
|---|---|---|---|
src/main/webapp/WEB-INF/web.xml | URL路由映射 | <servlet-mapping><url-pattern>/attendance/*</url-pattern></servlet-mapping> | <servlet-mapping><url-pattern>/reimburse/*</url-pattern></servlet-mapping> |
src/main/resources/db.properties | 数据库连接 | jdbc.url=.../attendance_system | jdbc.url=.../reimburse_system |
src/main/java/com/emp/dao/LeaveApplyDAO.java | 业务实体DAO | 改名为AttendanceRecordDAO.java,SQL全换为INSERT INTO attendance_record | 改名为ReimburseApplyDAO.java,SQL换为INSERT INTO reimburse_apply |
关键技巧:DAO层方法名保持
addXXX()、listXXX()、updateXXX()不变,仅改SQL内容。这样Service层和Servlet层代码完全不用动——LeaveApplyService可直接重命名为AttendanceService,内部调用的leaveApplyDAO.add()自动适配新表。
5.2 字段复用表:一张sys_config表撑起多系统通用配置
系统预留sys_config表用于存放跨系统参数,避免硬编码:
CREATE TABLE sys_config ( id BIGINT PRIMARY KEY AUTO_INCREMENT, config_key VARCHAR(100) UNIQUE NOT NULL, -- 如 'overtime_rate', 'reimburse_limit' config_value VARCHAR(500) NOT NULL, description VARCHAR(200), updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );在ConfigUtil工具类中封装读取:
public class ConfigUtil { public static String getOvertimeRate() { return getConfigValue("overtime_rate"); // 返回"1.5"字符串 } public static Double getReimburseLimit() { return Double.parseDouble(getConfigValue("reimburse_limit")); // 返回10000.0 } private static String getConfigValue(String key) { // 从数据库查config_value,加本地缓存避免频繁查询 } }这样考勤系统要调整加班费倍率,只需在MySQL里执行:
INSERT INTO sys_config (config_key, config_value, description) VALUES ('overtime_rate', '2.0', '周末加班费倍率');报销系统要提高差旅报销上限,执行:
REPLACE INTO sys_config (config_key, config_value, description) VALUES ('reimburse_limit', '20000', '年度差旅报销总额上限');5.3 报表导出增强:用Apache POI 5.2.4生成带样式的Excel
原系统报表页只有HTML表格,升级为Excel导出只需两步:
在
pom.xml添加POI依赖:<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.4</version> </dependency>新增
ExportExcelServlet,核心代码:XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet("员工请假统计"); // 设置表头样式 XSSFCellStyle headerStyle = workbook.createCellStyle(); Font font = workbook.createFont(); font.setBold(true); headerStyle.setFont(font); Row headerRow = sheet.createRow(0); String[] headers = {"姓名", "部门", "请假天数", "事由", "状态"}; for (int i = 0; i < headers.length; i++) { Cell cell = headerRow.createCell(i); cell.setCellValue(headers[i]); cell.setCellStyle(headerStyle); } // 填充数据(从DAO查出List<LeaveApply>) List<LeaveApply> list = leaveApplyDAO.listAll(); for (int i = 0; i < list.size(); i++) { Row row = sheet.createRow(i + 1); LeaveApply apply = list.get(i); row.createCell(0).setCellValue(apply.getEmpName()); row.createCell(1).setCellValue(apply.getDeptName()); row.createCell(2).setCellValue(apply.getDays()); row.createCell(3).setCellValue(apply.getReason()); row.createCell(4).setCellValue(apply.getStatusDesc()); // status=3→"已通过" } // 自动列宽 for (int i = 0; i < headers.length; i++) { sheet.autoSizeColumn(i); } response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment; filename=leave_report.xlsx"); workbook.write(response.getOutputStream());
从那以后我每次接到新需求,第一件事就是打开
sys_config表看有没有现成参数能复用,第二件事是查web.xml里URL映射是否要新增路径——而不是从零建工程。这套系统的真正价值不在代码量,而在它强迫你把“变与不变”提前想清楚:哪些是业务本质(状态机、权限模型),哪些只是表名和字段名(leave_apply → attendance_record)。希望帮到你。
本文还有配套的精品资源,点击获取