简介:这份资源是一套基于 Java Swing 开发的桌面人事管理系统源码,面向正在学习 Java GUI 编程、需要完成课程设计或希望积累桌面应用实战经验的学生与开发者,可用于理解员工信息录入、查询、编辑及数据导入导出等基础人事管理流程的实现方式。压缩包共 36 个文件,以 java 源文件与编译后的 class 文件为主,另含工程配置与依赖 jar,整体约 771KB,体量轻便,便于直接导入 IDE 阅读与调试。目前已有 647 人学习下载,具备一定的参考基础。代码中可见 JFrame、JTable、JButton、JTextField、JComboBox、JFileChooser 等 Swing 组件的典型用法,并涉及事件监听、MVC 分层、数据持久化与异常提示等知识点,适合作为对照学习界面布局与业务逻辑分离的实例,帮助读者快速搭建自己的桌面管理应用雏形。
1. 从一份“能跑起来”的课设说起:Java Swing 人事管理系统到底要交付什么
每年到了课程设计季,总有人拿着一个能登录、能增删改查的 Java Swing 人事管理系统来问:这算不算做完了?我的判断标准很直接——把数据库断开、把窗口拉大、把两个用户同时登录,系统还能不能给出合理反馈。多数时候答案是不能。问题不在 Swing 本身,而在于一开始就没把“交付物”想清楚:它不是一个能点开的窗口,而是一套包含数据模型、权限边界、业务校验和可验收流程的完整方案。这套方案面向两类人:需要交毕设或课设的学生,以及要给内部小团队做一套轻量人事台账的开发者。下面按需求、设计、实现、避坑、验收的顺序,把一套可以直接照着搭的 Java Swing 人事管理系统讲透。
2. 需求与选型:为什么是 Swing 加 JDBC 而不是上框架
2.1 先把角色和用例钉死
人事管理系统的需求很容易写成一张大而全的功能清单,最后谁也做不完。我一般会先收敛到三个角色:普通员工、人事专员、系统管理员。普通员工只关心自己的档案和考勤记录,人事专员负责员工信息维护、部门调整、考勤汇总和薪资录入,管理员多一层账号与权限管理。用例层面,核心就是员工档案的增删改查、部门与岗位维护、考勤登记与月度统计、薪资条生成、登录与权限校验这五块。把这张角色—用例表先画出来,后面所有的表结构和窗口都能对得上号,不会出现“做着做着发现少了一个字段”的返工。
需要强调的是,课设和毕设的评分点往往不在功能数量,而在业务闭环是否完整。比如“新增员工”这个动作,合格的做法是同时校验身份证号唯一、部门必须存在、入职日期不能晚于当前日期,并在成功后写一条操作日志。这三件事写进去,方案的分量立刻不一样。
2.2 技术选型的取舍逻辑
Swing 常被吐槽“丑、老、没人用”,但在课程设计和内部小工具这个场景里,它的优势非常实在:JDK 自带、零额外依赖、打包成 jar 双击即用、不需要部署服务器。对比 JavaFX,后者需要额外引入模块,打包和运行环境配置对新手不够友好;对比 Web 方案,又要多学一套前端和后端框架,时间成本翻倍。所以这套方案的选型是 JDK 8 及以上 + Swing + JDBC + MySQL,构建工具用 Maven 管理驱动依赖,报表导出用 Apache POI 生成 Excel。这个组合的玄学之处在于:只要 JDBC 写规范,整个系统的稳定性就有底了。
数据库选 MySQL 而不是内嵌的 H2,是因为多数学校的实验环境都提供 MySQL,答辩时也方便演示数据持久化。如果确实没有数据库环境,把 JDBC 的连接串换成 H2 的内存模式即可,代码层几乎不用改,这一点在后面的配置里会体现。
2.3 非功能性需求别漏掉
很多人只写功能需求,结果答辩被问“两个人同时改同一个员工信息怎么办”就卡住。这套方案里我通常补三条非功能约束:一是所有数据库操作走预编译语句,防注入;二是关键写操作加事务,保证员工调部门和考勤记录要么一起成功要么一起回滚;三是界面操作不能在事件分发线程里做耗时查询,否则窗口会假死。这三条不需要多高深的技术,但写进方案里就是加分项。
3. 总体架构设计:三层结构与五个核心模块的职责划分
3.1 分层结构与包组织
系统按表现层、业务层、数据访问层三层组织,包结构如下:
com.example.hr ├── ui // Swing 窗口与面板 ├── service // 业务逻辑与事务边界 ├── dao // JDBC 数据访问 ├── model // 实体类 └── util // 数据库连接、日期工具、导出工具表现层只负责收集输入和展示结果,不写 SQL;业务层负责校验规则和事务;数据访问层只做增删改查。这样分层的好处是,答辩时被问到“业务规则写在哪”,你能明确指到 service 包,而不是在一堆 JButton 的监听器里翻。我见过太多把 SQL 直接写在按钮点击事件里的代码,改一个字段要翻五个文件,血泪经验。
3.2 五个核心模块的职责
| 模块 | 主要职责 | 关键类 |
|---|---|---|
| 登录与权限 | 账号认证、角色判定、菜单可见性控制 | LoginFrame、AuthService |
| 员工档案 | 员工信息增删改查、部门岗位关联 | EmployeePanel、EmployeeService |
| 考勤管理 | 考勤登记、月度汇总、异常标记 | AttendancePanel、AttendanceService |
| 薪资管理 | 薪资录入、按考勤计算、工资条导出 | SalaryPanel、SalaryService |
| 系统管理 | 账号维护、操作日志、数据备份 | AdminPanel、LogService |
模块之间通过 service 层调用,不跨层直接访问。比如薪资计算需要考勤数据,SalaryService 调用 AttendanceService 的查询方法,而不是自己去查考勤表。这条约束在项目初期看起来多余,等到要改考勤统计口径时,你会感谢自己当初守住了它。
3.3 数据库表设计
核心表五张,字段按人事场景的实际需要来定:
CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(20) NOT NULL UNIQUE, -- 工号,业务唯一键 name VARCHAR(50) NOT NULL, gender CHAR(1) DEFAULT 'M', id_card VARCHAR(18) UNIQUE, -- 身份证号,唯一校验 dept_id INT NOT NULL, position VARCHAR(50), hire_date DATE NOT NULL, status TINYINT DEFAULT 1, -- 1在职 0离职 FOREIGN KEY (dept_id) REFERENCES department(id) ); CREATE TABLE attendance ( id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL, work_date DATE NOT NULL, check_in TIME, check_out TIME, status VARCHAR(10) DEFAULT 'NORMAL', -- NORMAL/LATE/ABSENT UNIQUE KEY uk_emp_date (emp_id, work_date) );uk_emp_date这个唯一索引是关键设计点:它从数据库层面保证一个员工一天只有一条考勤记录,避免界面重复提交造成脏数据。status用状态位而不是物理删除,是为了保留历史档案,离职员工的数据在统计时仍可追溯。部门表、岗位表、账号表、日志表结构类似,这里不展开,原则是外键约束能加就加,让数据库帮你兜底。
4. 关键模块实现:从登录校验到考勤统计的落地代码
4.1 数据库连接与配置外置
连接管理用一个工具类封装,配置从 properties 文件读取,避免把密码硬编码进源码:
public class DBUtil { private static String url; private static String user; private static String password; static { try (InputStream in = DBUtil.class .getClassLoader().getResourceAsStream("db.properties")) { Properties p = new Properties(); p.load(in); url = p.getProperty("jdbc.url"); user = p.getProperty("jdbc.user"); password = p.getProperty("jdbc.password"); Class.forName(p.getProperty("jdbc.driver")); } catch (Exception e) { throw new ExceptionInInitializerError("数据库配置加载失败: " + e.getMessage()); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } }db.properties里写jdbc.url=jdbc:mysql://localhost:3306/hr?useSSL=false&serverTimezone=Asia/Shanghai,换成 H2 时只改这一行和驱动类名。把配置外置的另一个好处是答辩演示时可以直接改成本机数据库,不用重新编译。注意serverTimezone这个参数,漏掉它在新版驱动下会报时区错误,这是最常见的翻车点之一。
4.2 登录认证与角色控制
登录不是简单比对用户名密码,还要处理密码存储和角色分流:
public User login(String username, char[] rawPassword) { String sql = "SELECT id, username, password_hash, salt, role, status " + "FROM sys_user WHERE username = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); try (ResultSet rs = ps.executeQuery()) { if (!rs.next()) return null; if (rs.getInt("status") == 0) { throw new BizException("账号已停用,请联系管理员"); } String hash = sha256(new String(rawPassword) + rs.getString("salt")); if (!hash.equals(rs.getString("password_hash"))) return null; return new User(rs.getInt("id"), username, rs.getString("role")); } } catch (SQLException e) { throw new BizException("登录失败,请稍后重试", e); } }密码用“盐值 + SHA-256”存储,虽然比不上专门的密码哈希算法,但对课设场景足够,且能讲清楚“为什么不存明文”这个安全点。登录成功后根据role决定主窗口加载哪些菜单:普通员工只挂“我的档案”和“我的考勤”,人事专员挂业务菜单,管理员额外挂系统管理。菜单控制放在主窗口初始化时做,而不是每个按钮里判断角色,代码会干净很多。
4.3 员工档案的增删改查与事务
新增员工涉及档案写入和操作日志两条记录,必须放在一个事务里:
public void addEmployee(Employee emp, int operatorId) { String insertEmp = "INSERT INTO employee(emp_no, name, gender, id_card, " + "dept_id, position, hire_date, status) VALUES(?,?,?,?,?,?,?,1)"; String insertLog = "INSERT INTO operation_log(operator_id, action, target, created_at) " + "VALUES(?,?,?,NOW())"; Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 try (PreparedStatement ps = conn.prepareStatement(insertEmp)) { ps.setString(1, emp.getEmpNo()); ps.setString(2, emp.getName()); // ... 其余参数绑定 ps.executeUpdate(); } try (PreparedStatement ps = conn.prepareStatement(insertLog)) { ps.setInt(1, operatorId); ps.setString(2, "ADD_EMPLOYEE"); ps.setString(3, emp.getEmpNo()); ps.executeUpdate(); } conn.commit(); } catch (SQLException e) { rollbackQuietly(conn); if (e.getErrorCode() == 1062) { // 唯一键冲突 throw new BizException("工号或身份证号已存在"); } throw new BizException("保存失败", e); } finally { closeQuietly(conn); } }这里有两个设计说明。第一,setAutoCommit(false)之后所有写操作共享一个连接,日志和档案要么都成功要么都回滚,避免出现“档案存了但日志没记”的黑匣子状态。第二,捕获errorCode == 1062转成友好提示,比把数据库原始异常抛到界面上体面得多。查询列表则用JTable配合DefaultTableModel,分页参数通过LIMIT ?, ?传入,避免一次性加载上万条数据把界面拖死。
4.4 考勤登记与月度统计
考勤登记的核心是“一人一天一条”,靠前面设计的唯一索引兜底,代码里做幂等处理:
public void checkIn(int empId, LocalDate date, LocalTime time) { String sql = "INSERT INTO attendance(emp_id, work_date, check_in, status) " + "VALUES(?,?,?,?) " + "ON DUPLICATE KEY UPDATE check_in = VALUES(check_in)"; // 9:00 之后打卡标记为迟到 String status = time.isAfter(LocalTime.of(9, 0)) ? "LATE" : "NORMAL"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, empId); ps.setDate(2, java.sql.Date.valueOf(date)); ps.setTime(3, java.sql.Time.valueOf(time)); ps.setString(4, status); ps.executeUpdate(); } catch (SQLException e) { throw new BizException("打卡失败", e); } }ON DUPLICATE KEY UPDATE让重复打卡变成更新而不是报错,这是 MySQL 的写法,换 H2 时改成先查后插或MERGE INTO。月度统计用一条聚合 SQL 完成,按员工分组统计正常、迟到、缺勤天数,结果直接喂给JTable展示,导出时再用 POI 写进 Excel。统计口径要写进方案文档:迟到按打卡时间判定,缺勤按工作日减去有记录的天数计算,这样答辩时被追问也有据可依。
5. 避坑与排查:这套系统最容易翻车的五个地方
5.1 界面假死,点查询后窗口卡住
现象是点击查询按钮后窗口无响应,拖动一下才刷新。原因是数据库查询写在了按钮的actionPerformed里,而它运行在事件分发线程上。解决方式是把耗时操作放进SwingWorker的doInBackground,查询结果在done方法里回填表格。这个改动不大,但体验差别明显,也是答辩时能主动讲出来的亮点。
5.2 中文乱码,姓名存进去变成问号
现象是员工姓名入库后显示为乱码。原因通常是连接串没指定字符集,或者建库时用了非 utf8 的排序规则。解决方式是连接串加characterEncoding=utf8,建库语句显式写DEFAULT CHARSET=utf8mb4,同时确认 Swing 源文件编码为 UTF-8。三处对齐之后基本不会再出问题。
5.3 事务没生效,日志和档案对不上
现象是档案保存成功但操作日志缺失。原因多半是用了连接池或多次getConnection(),导致两条 SQL 不在同一个连接上,setAutoCommit(false)自然管不到。解决方式是像 4.3 那样在方法内持有同一个Connection对象,事务边界清晰。这一点在代码评审时最容易被忽略,建议写个单元测试专门验证回滚。
5.4 打包后运行报错,找不到数据库驱动
现象是 IDE 里正常,打成 jar 后启动报ClassNotFoundException。原因是驱动 jar 没一起打进去。解决方式是用 Maven 的 shade 插件打成包含依赖的 fat jar,或者用java -cp "app.jar:lib/*"的方式运行。打包这一步建议在项目中期就做一次,别等到答辩前一晚才发现。
5.5 权限只做在界面上,接口层没校验
现象是普通员工通过某种方式能调到管理员才该有的操作。原因是权限判断只写在菜单显示逻辑里,service 层没有二次校验。解决方式是在 service 方法入口校验当前登录用户的角色,界面控制只是体验优化,真正的边界在业务层。这条在课设里可能不扣分,但在真实项目里是硬伤,写进方案能体现工程意识。
6. 验收与答辩:怎么证明这套系统真的能用
验收不是把功能点一遍,而是按场景走完整流程。我一般会准备一份验收清单,按“数据准备—主流程—异常流程—非功能”四段来演示。数据准备阶段先造三个部门、十个员工、一个月的考勤记录,保证演示时有数据可看。主流程走一遍:管理员登录建账号,人事专员登录新增员工、登记考勤、生成工资条并导出 Excel,普通员工登录查看自己的档案和考勤。异常流程重点演示三个:重复工号被拦截、重复打卡被合并、离职员工不出现在在职列表。非功能部分演示并发修改同一条记录时的提示,以及断网或数据库停掉时的友好报错。
答辩陈述建议控制在八分钟,结构按“问题—方案—演示—难点”来。开头一句话说清系统解决什么问题,中间用一张架构图讲分层,演示环节只走主流程和两个异常,最后把 5.3 的事务处理和 5.5 的权限校验作为难点讲,这两个点最能体现你不是在堆界面。评委常问的问题集中在表结构设计理由、事务边界、密码存储方式,提前把这三块的答案写成两句话备着。
下面这张自查表可以在提交前过一遍:
| 检查项 | 合格标准 |
|---|---|
| 数据库脚本 | 能一键建库建表并插入初始管理员 |
| 登录 | 错误密码、停用账号均有明确提示 |
| 增删改查 | 唯一键冲突有友好提示,删除为逻辑删除 |
| 事务 | 档案与日志同成功同回滚 |
| 导出 | Excel 能正常打开且中文不乱码 |
| 打包 | 双击 jar 可运行,无需 IDE |
从那以后我每次带人做这类系统,都会要求先在纸上把角色用例表和五张核心表画完再动手写界面,界面可以慢慢调,数据模型错了就是推倒重来。希望这套结构能帮你少走几段弯路,把方案稳稳交出去。
本文还有配套的精品资源,点击获取