ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

员工工资管理系统V3:Swing+MySQL桌面应用实战指南

员工工资管理系统V3:Swing+MySQL桌面应用实战指南 简介基于Java Swing与MySQL的员工工资管理系统V3源码包面向正在完成Java课程设计或结课作业的学生解决桌面端员工工资管理中的登录、权限区分与工资数据维护问题。系统包含员工端与管理端两种角色员工可登录并查询个人工资管理员可对所有员工工资记录进行查询、添加、修改与删除整体结构清晰适合学习Swing界面与JDBC/MySQL的配合使用。整个RAR压缩包共46个文件大小约4.33MB除10个Java源文件和21个class编译文件外还包含5个xml配置、2个jar依赖库、1个sql数据库脚本以及project、classpath、iml等Eclipse/IDEA工程配置文件下载后导入开发环境并配置MySQL5.7以上版本即可运行。这套源码已有159人学习适合用于课程作业或结课设计的高分参考。下载者可直接对照源码梳理登录验证、员工模块和管理员模块的权限控制流程借助sql脚本快速初始化数据库并在此基础上做功能扩展或界面优化。1. 员工工资管理系统V3Swing做桌面界面、MySQL存账本课程设计绕不开的经典组合临近期末Java 课程设计群里最常见的求助就是“员工工资管理系统”。在 javaswingmysql 这个组合下V3 版本通常意味着从控制台数组版升级成了带 JFrame 界面、真正连接 MySQL 的完整桌面应用覆盖登录鉴权、员工档案、按月算工资、查询打印这几个课程作业的标配功能。它适合正在做课程设计的学生、想复习 Swing 事件机制和 JDBC 编程的入门开发者以及需要一套能双击运行、能上台演示的作品的人。这套东西门槛不高但坑不少界面卡死、中文乱码、数据库连不上、金额对不上每一条都能让演示当场翻车。最该担心的不是写不出代码而是答辩时双击运行就报错。下文按环境、建表、连接、业务到避坑的顺序把整个 V3 的落地路径完整讲一遍。2. 先拆模块再写代码工资管理系统的三层结构与三张表定稿2.1 为什么 V3 选 SwingMySQL 而不是做 Web选型理由与边界很多同学纠结要不要上 Spring Boot 做网页版我一般建议课程作业别冲动。Swing 是 JDK 自带的桌面组件库javax.swing 包里有 JFrame、JTable、JDialog 这些成熟控件不依赖浏览器、不搞跨域、不碰前端工程化。评分老师要看的是你懂不懂 Java 面向对象、事件监听、JDBC 和数据库设计Swing 恰好能把这几项都暴露在代码里演示时双击启动、填账号、点按钮整个流程都在本机比 Web 项目省掉一堆部署环节。MySQL 的作用是把数据持久化。V1、V2 版本常用数组或者文本文件存员工信息程序一关数据就丢到了 V3 用数据库员工档案、每月工资、操作记录都能长期留存而且可以用 SQL 直接查“某员工三个月工资趋势”“全公司月总支出”答辩时这些查询效果远比打印一堆 System.out 有说服力。MySQL 免费、网上 mysql 安装教程多老师机器上大概率也装着选它属于最稳的选择。需要注意边界Swing 适合单机或局域网小规模桌面应用不适合高并发在线访问。课程作业和内部工具用它没问题如果将来要多人同时登录、异地访问Java Web 才是正路。V3 的定位就是“能跑起来、能演示、代码结构清楚”不追求生产级分布式。2.2 三层结构界面层、业务层、数据访问层怎么分工Swing 项目最容易写成一坨事件监听里直接写 JDBC界面代码和 SQL 混在一起改一个字段要翻遍十个类。V3 我建议按三层结构分包这也是答辩时“模块划分清晰”的主要加分来源。src/ ├── view/ # 界面层LoginFrame、MainFrame、SalaryFrame ├── service/ # 业务层LoginService、SalaryService、EmployeeService ├── dao/ # 数据访问层UserDao、EmployeeDao、SalaryDao ├── model/ # 实体类User、Employee、Salary ├── util/ # 工具类DBUtil、Md5Util、DateUtil └── MainApp.java # 程序入口分工逻辑很直接view 只负责摆组件和转发用户操作看到按钮点击就调用 serviceservice 放业务规则比如工资怎么算、个税怎么扣、月结时事务怎么管dao 只写 SQL 和 JDBC 代码返回实体对象给 service。三层都按接口或类隔离后续要换数据库、换界面框架都不用推倒重来。2.3 三张核心表user、employee、salary 的字段设计与索引工资管理系统最核心的数据就是账号、员工、工资三张表。设计上一开始就定好避免写到一半发现字段缺了要删表重来。我常用的建表脚本如下CREATE DATABASE IF NOT EXISTS salary_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE salary_db; CREATE TABLE user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role VARCHAR(20) NOT NULL DEFAULT staff ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE employee ( emp_id INT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, department VARCHAR(50) NOT NULL, position VARCHAR(50) NOT NULL, base_salary DECIMAL(10,2) NOT NULL, hire_date DATE NOT NULL, status TINYINT NOT NULL DEFAULT 1 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE salary ( salary_id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL, month VARCHAR(7) NOT NULL, base DECIMAL(10,2) NOT NULL, bonus DECIMAL(10,2) NOT NULL DEFAULT 0, deduction DECIMAL(10,2) NOT NULL DEFAULT 0, insurance DECIMAL(10,2) NOT NULL DEFAULT 0, tax DECIMAL(10,2) NOT NULL DEFAULT 0, net_salary DECIMAL(10,2) NOT NULL, UNIQUE KEY uk_emp_month (emp_id, month), CONSTRAINT fk_salary_emp FOREIGN KEY (emp_id) REFERENCES employee(emp_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字段选型上有几个点值得注意。金额字段一律用 DECIMAL(10,2)不用 float/double否则月底对账会莫名多出两分钱。工资表里的 month 用 VARCHAR(7) 存“2025-05”这样的格式配合 UNIQUE KEY uk_emp_month 保证同一个员工同一个月只有一条工资记录月结重复执行时不会产生重复数据。外键约束不能省它能在删员工时挡住工资表的孤儿数据。索引方面employee 表的 emp_no 唯一索引、salary 表的 (emp_id, month) 联合唯一索引覆盖课程作业场景绰绰有余不要过度建索引增加 INSERT 开销。3. 环境搭建与数据库初始化JDBC 连接 MySQL 的完整准备3.1 JDK 版本、MySQL 版本与驱动 jar 的匹配V3 是 Swing 项目JDK 8 是最稳妥的选择。Swing 在 JDK 8 里已经非常成熟网上 java 环境变量配置详细教程也大多是针对 JDK 8 写的配置完 JAVA_HOME 和 PATH 基本不会遇到兼容性问题。MySQL 常见组合是 5.7 或 8.0比如常被提到的社区版 5.7.44也可以用 8.x关键不是版本多新而是驱动 jar 要和服务器版本对应。驱动方面MySQL 5.7 用 mysql-connector-java 5.1.49 可以跑MySQL 8.0 建议用 8.0.x 版本驱动类名是 com.mysql.cj.jdbc.Driver。常见翻车点是驱动类名不一致5.1 驱动有时写 com.mysql.jdbc.Driver 也行8.0 驱动写老类名会直接 ClassNotFoundException。处理方式是在 IDE 里把 jar 引入项目比如 IDEA 里放在项目根目录建一个 lib 文件夹右键 Add as Library如果用 Eclipse要 Build Path 手动加进来。环境变量也要注意网上 mysql 安装教程往往只说下一步下一步容易漏掉把 MySQL 的 bin 目录加入 PATH。不加 PATH 虽然能在 GUI 客户端里操作但命令行跑 mysql、mysqldump 时会很别扭。装完先打开命令行输入 mysql -u root -p 验证一下能进去再开始写代码。3.2 建库建表与初始化数据一份能反复执行的 SQL 脚本建表脚本要写成可以重复执行的版本方便开发中随时重置数据。把第 2 章的 SQL 存成 init.sql用命令行执行mysql -u root -p init.sql执行后用 mysql 数据库常用命令检查一下SHOW TABLES; 和 DESC employee;确认表和字段都建对了。再插入测试数据USE salary_db; INSERT INTO user (username, password, role) VALUES (admin, 0192023a7bbd73250516f069df18b500, admin); INSERT INTO employee (emp_no, name, department, position, base_salary, hire_date) VALUES (E001, 张伟, 技术部, Java工程师, 8500.00, 2023-04-01);密码字段存的是“admin123”的 MD5 值不要让明文密码出现在数据库里。测试员工数据至少准备三五个覆盖不同部门、不同基本工资档位后面演示工资计算时才好看。初始化数据的 INSERT 放在 SQL 脚本里还有一个好处万一开发中把数据改乱了重新执行一次脚本就能恢复原始状态算是一份后悔药。3.3 DBUtil 工具类连接参数、事务开关与字符集三件套V3 项目里每个 DAO 都需要拿到数据库连接公共的连接工具类必须最先写。我的 DBUtil 长这样package util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/salary_db ?useUnicodetruecharacterEncodingUTF-8 useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }URL 里三个参数是血泪经验换来的。useUnicodetrue 和 characterEncodingUTF-8 解决中文乱码serverTimezoneAsia/Shanghai 是 MySQL 8.0 驱动强制的时区参数不加会报错useSSLfalse 避免本地开发时的 SSL 握手警告。如果本地 MySQL 密码不是 123456把 PASSWORD 改掉即可。注意 DBUtil 里不主动开启事务。事务开关放在业务层因为工资月结涉及多次 INSERT 和 DELETE需要保证要么全成功要么全回滚普通单条查询保持默认自动提交就行。getConnection 每次调用都新建连接虽然比连接池开销大但课程作业的数据量根本感觉不到差别而且能避免连接池配置不当导致的连接泄漏。4. 登录、员工档案与工资结算三块核心功能的实现要点4.1 登录模块PreparedStatement 防注入是底线登录是所有功能的大门也是最容易被扣分的点。我第一次写这个系统时直接在 SQL 里拼字符串后来发现用户名输个单引号就能绕过去这是课程设计里最典型的“能跑但不敢演示”的代码。正确做法是全程用 PreparedStatement 的 ? 占位符public User login(String username, String password) { String sql SELECT user_id, username, password, role FROM user WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, Md5Util.encrypt(password)); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { return new User( rs.getInt(user_id), rs.getString(username), rs.getString(role) ); } } } catch (SQLException e) { e.printStackTrace(); } return null; }这里的参数说明很关键ps.setString(1, username) 会把传入值当作纯数据而不是 SQL 语句的一部分单引号、横杠、注释符都会被安全处理。MD5Util.encrypt 在比较前对用户输入做哈希数据库里存的也是哈希值这样即使数据库被人拿到也看不到明文密码。登录界面拿到返回的 User 对象后根据 role 字段决定是进管理员主界面还是普通员工查询界面。登录还有一个细节连续失败次数要不要做限制。课程作业可以不做复杂限制但至少要在界面上弹出“用户名或密码错误”的提示而不是直接没反应。JDialog 里放两个 JTextField 和一个 JButton按钮的 ActionListener 里调用 login 方法登录成功 dispose 当前窗口再 new MainFrame——这是 Swing 里最常见的窗口切换方式。4.2 员工档案管理JTable 展示、添加与刷新主界面中间的 JTable 是员工数据的展示核心。JDBC 查询返回 ResultSet但 JTable 需要的是 TableModel推荐用 DefaultTableModel 来承接String[] columns {工号, 姓名, 部门, 职位, 基本工资, 入职日期}; DefaultTableModel tableModel new DefaultTableModel(columns, 0); JTable table new JTable(tableModel); public void loadEmployeeData() { tableModel.setRowCount(0); String sql SELECT emp_no, name, department, position, base_salary, hire_date FROM employee WHERE status 1; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { tableModel.addRow(new Object[]{ rs.getString(emp_no), rs.getString(name), rs.getString(department), rs.getString(position), rs.getBigDecimal(base_salary), rs.getDate(hire_date) }); } } catch (SQLException e) { e.printStackTrace(); } }添加员工时用 insert 语句注意 employee 表和 salary 表没有直接关系所以新增员工不强制生成工资数据等到月结时统一按在册员工批量生成。这个设计比“添加员工就初始化一条工资记录”更贴近真实业务也更容易在答辩时解释清楚。刷新表格时先 setRowCount(0) 清空数据再 addRow这个顺序不能反否则表格会越点按钮行数越多。另一个容易忽略的坑是查询按钮点击后如果界面卡住说明查询阻塞了事件线程第 5 章会专门讲 SwingWorker 的解法。4.3 工资计算与月结BigDecimal 精度和事务回滚工资计算是 V3 的核心业务也是评分老师最爱提问的点。生活费和奖金可能有迟到扣款还要扣社保和个税。我把月结逻辑放到 SalaryService 里用事务包住整批写入public boolean calculateMonthlySalary(int year, int month) { String salaryMonth year - String.format(%02d, month); Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); String sql INSERT INTO salary(emp_id, month, base, bonus, deduction, insurance, tax, net_salary) VALUES (?, ?, ?, ?, ?, ?, ?, ?); try (PreparedStatement ps conn.prepareStatement(sql)) { for (Employee emp : employeeDao.findAllActive()) { BigDecimal base emp.getBaseSalary(); BigDecimal bonus bonusDao.findByMonth(emp.getId(), salaryMonth); BigDecimal deduction deductionDao.findByMonth(emp.getId(), salaryMonth); BigDecimal insurance base.multiply(BigDecimal.valueOf(0.10)); BigDecimal taxable base.add(bonus).subtract(deduction).subtract(insurance); BigDecimal tax taxOf(taxable); BigDecimal net taxable.subtract(tax); ps.setInt(1, emp.getId()); ps.setString(2, salaryMonth); ps.setBigDecimal(3, base); ps.setBigDecimal(4, bonus); ps.setBigDecimal(5, deduction); ps.setBigDecimal(6, insurance); ps.setBigDecimal(7, tax); ps.setBigDecimal(8, net); ps.addBatch(); } ps.executeBatch(); } conn.commit(); return true; } catch (Exception e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } return false; } finally { if (conn ! null) { try { conn.close(); } catch (SQLException ex) { ex.printStackTrace(); } } } }为什么这么写conn.setAutoCommit(false) 之后所有 INSERT 都在同一个数据库事务里中间任何一条失败都不会产生半截数据executeBatch 比逐条 executeUpdate 快而且能在同一条 PreparedStatement 上复用参数绑定。月结按钮点击后如果弹“计算成功”所有员工当月工资全部到位如果弹失败数据库里不会留下任何残留数据。taxOf 方法用 BigDecimal 做分段税率计算。基本工资 8500 元奖金 2000 元迟到扣款 300 元社保按基本工资 10% 算 850 元应税金额就是 9350 元按 3000 以内 3%、3000 到 12000 部分 10% 的简化税率计算结果是 90 加 635 共 725 元。BigDecimal 计算时乘法定律陷阱很多我用的是 valueOf(String) 而不是 new BigDecimal(double)这样不会引入二进制浮点误差答辩时可以主动讲这一句通常都是加分点。5. 避坑与排查Swing 卡死、中文乱码、连接失效、金额偏差5.1 界面假死把耗时查询放进了 EDT 线程现象点击“查询工资”按钮后整个窗口标题栏变成“无响应”过几秒才恢复期间窗口拖不动、按钮没反应。原因Swing 是单线程模型所有界面绘制和事件处理都在 EDTEvent Dispatch Thread上执行。我最初直接在按钮事件里调用 dao 查全部工资数据数据量大时数据库查询占用 EDT 线程界面重绘就全部排队。解决用 SwingWorker 把耗时的 JDBC 查询放到后台线程SwingWorkerListSalary, Void worker new SwingWorkerListSalary, Void() { Override protected ListSalary doInBackground() throws Exception { return salaryDao.findByMonth(salaryMonth); } Override protected void done() { try { ListSalary list get(); // 在这里刷新 JTable 模型 tableModel.setRowCount(0); for (Salary s : list) { tableModel.addRow(s.toRow()); } } catch (Exception ex) { JOptionPane.showMessageDialog(frame, 加载失败 ex.getMessage()); } } }; worker.execute();逻辑说明doInBackground 在后台线程执行返回值会交给 done 方法done 方法切回 EDT 执行直接更新组件是安全的。不要尝试在 doInBackground 里直接操作 JTable那是跨线程访问界面组件轻则不刷新重则抛异常中断。查询完给表格填充数据时再安全地操作组件。5.2 中文乱码的三层排查现象运行后界面显示“锟斤拷”或“”数据库表里查出来也是乱码。原因乱码几乎从来不是“玄学”而是三层编码不一致。第一层是 Java 源码文件的编译编码IDE 默认可能是 GBK而代码里写的 UTF-8 字面量就变成乱码第二层是 JDBC URL 没带 characterEncodingUTF-8第三层是数据库表默认字符集不是 utf8mb4。解决按顺序三步排查。首先确保 IDE 里源文件编码全部是 UTF-8IDEA 在 Settings 里改 File EncodingsEclipse 在 Preferences 里设 Workspace 编码。其次看 JDBC URL确认 DBUtil 里带了 characterEncodingUTF-8。最后在数据库执行SHOW VARIABLES LIKE character_set%;看到 character_set_client 和 character_set_connection 不是 utf8mb4 时建库语句里加 DEFAULT CHARACTER SET utf8mb4或者执行 SET NAMES utf8mb4。最稳的做法是第 3 章的建库脚本里写死 utf8mb4每次重建数据库都从这个脚本走。5.3 MySQL 连接隔夜失效现象程序跑了一晚上第二天早上第一次操作就报 Communications link failure 或 Connection is not available。原因MySQL 服务器默认 wait_timeout 是 8 小时空闲超过这个时间服务器会主动断开连接。我用的是 DriverManager 每次获取新连接本来不该有问题但如果有人图省事把 Connection 写成 static 成员复用就会踩到这个坑。解决jedis 那种“用完不关、下次还能用”的思维在 JDBC 里行不通。正确做法是每次用连接都在 finally 里 close确保连接不跨请求复用。如果项目里用了连接池比如 C3P0、Druid就在连接池配置里加 test-on-borrowtrue 和 validation-querySELECT 1拿连接前先探活。网上常见的 URL 加 autoReconnecttrue 对连接池里的死连接有时灵有时不灵事务中途断线时它救不了你最保险的还是用完即关。5.4 金额精度和日期排序两个不起眼的高频扣分点现象工资计算结果偶尔少一分钱工资导出到 Excel 后对不上工资表按月份查询12 月排在 1 月前面。原因第一个问题是用 float/double 存金额计算 0.10.2 结果是 0.30000000000000004第二个问题是用 VARCHAR 存日期后按字符串排序“12”排在“1”前面10 月也排在 1 月前面。解决金额统一用 BigDecimal 和 DECIMAL这个从建表阶段的第 2 章就定死了。日期字段在 employee 表里用 DATE 类型薪资月份用 month 字段配合 ORDER BY month 时一定要是“2025-01”这种补零格式或者直接 ORDER BY STR_TO_DATE(month, %Y-%m)。VARCHAR 存月份也不是不能用但生成月份字符串时 String.format(%02d, month) 这一步不能漏否则排序永远不对。6. 让课程作业在答辩时拿高分演示脚本与三个加分项6.1 答辩演示的分步脚本演示最怕临场手忙脚乱。我踩过的坑是提前没准备数据现场才想起来要造工资数据结果捏造的数据格式不对表格空白。现在我会在答辩前写好一张演示脚本表步骤操作亮点1启动 MainApp输入管理员账号登录演示登录鉴权和 MD5 密码校验2打开员工管理点击“查询”演示 JTable 展示和数据库读取3添加一名新员工刷新列表演示 INSERT 和表格刷新4进入工资月结选择 2025-05 生成演示事务批量写入和工资计算5勾选某员工查看工资明细演示关联查询和金额明细6导出当月工资单为 CSV演示简易文件导出功能演示前把测试数据恢复到初始状态用第 3 章的 init.sql 重置一下保证每一步都能复现。在答辩现场打开数据库客户端把 salary 表展示出来评委看到数据真进了 MySQL比口头吹架构可靠得多。6.2 三个低成本加分项第一个加分项是工资条导出。用 FileWriter 把 JTable 当前行的工资明细写成 CSV顺手用 JFileChooser 让用户选保存位置二十行代码就能加一个“导出”按钮演示效果很好。第二个加分项是加一个操作日志表。登录、添加员工、月结这三个动作都往 operation_log 表插一条记录字段是操作人、操作时间、操作描述。答辩时回答“怎么审计谁动了数据”这个问题就有话说了。第三个加分项是给删除操作加确认对话框并处理外键约束。员工名下有工资记录时删除要拦截并提示“该员工存在历史工资不能删除”而不是让 SQL 抛异常。这体现你对数据完整性的理解也是数据库课程的核心考点。我自己的经验是课程设计这东西代码写完只算完成一半另一半是让它演示起来不翻车。曾经见过有个同学项目做得挺全答辩当天 MySQL 服务没启动双击程序直接连接报错评委想看的全被黑匣子一样挡在弹窗后面。后来我习惯在每次动工前先用命令行确认 mysql -u root -p 能正常连上再启动 Swing 程序。希望这份从 0 到 V3 的落地路径能帮到你少走这些弯路。本文还有配套的精品资源点击获取
返回列表