☰
员工工资管理系统V3:Swing+MySQL桌面应用实战指南
2026/10/8 3:51:43 网站建设 项目流程

简介:基于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. 员工工资管理系统V3:Swing做桌面界面、MySQL存账本,课程设计绕不开的经典组合

临近期末,Java 课程设计群里最常见的求助就是“员工工资管理系统”。在 java+swing+mysql 这个组合下,V3 版本通常意味着从控制台数组版升级成了带 JFrame 界面、真正连接 MySQL 的完整桌面应用,覆盖登录鉴权、员工档案、按月算工资、查询打印这几个课程作业的标配功能。它适合正在做课程设计的学生、想复习 Swing 事件机制和 JDBC 编程的入门开发者,以及需要一套能双击运行、能上台演示的作品的人。这套东西门槛不高但坑不少:界面卡死、中文乱码、数据库连不上、金额对不上,每一条都能让演示当场翻车。最该担心的不是写不出代码,而是答辩时双击运行就报错。下文按环境、建表、连接、业务到避坑的顺序,把整个 V3 的落地路径完整讲一遍。

2. 先拆模块再写代码:工资管理系统的三层结构与三张表定稿

2.1 为什么 V3 选 Swing+MySQL 而不是做 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 只负责摆组件和转发用户操作,看到按钮点击就调用 service;service 放业务规则,比如工资怎么算、个税怎么扣、月结时事务怎么管;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' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 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 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 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) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

字段选型上有几个点值得注意。金额字段一律用 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" + "?useUnicode=true&characterEncoding=UTF-8" + "&useSSL=false&serverTimezone=Asia/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 里三个参数是血泪经验换来的。useUnicode=true 和 characterEncoding=UTF-8 解决中文乱码;serverTimezone=Asia/Shanghai 是 MySQL 8.0 驱动强制的时区参数,不加会报错;useSSL=false 避免本地开发时的 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 是单线程模型,所有界面绘制和事件处理都在 EDT(Event Dispatch Thread)上执行。我最初直接在按钮事件里调用 dao 查全部工资数据,数据量大时数据库查询占用 EDT 线程,界面重绘就全部排队。

解决:用 SwingWorker 把耗时的 JDBC 查询放到后台线程:

SwingWorker<List<Salary>, Void> worker = new SwingWorker<List<Salary>, Void>() { @Override protected List<Salary> doInBackground() throws Exception { return salaryDao.findByMonth(salaryMonth); } @Override protected void done() { try { List<Salary> 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 没带 characterEncoding=UTF-8;第三层是数据库表默认字符集不是 utf8mb4。

解决:按顺序三步排查。首先确保 IDE 里源文件编码全部是 UTF-8,IDEA 在 Settings 里改 File Encodings,Eclipse 在 Preferences 里设 Workspace 编码。其次看 JDBC URL,确认 DBUtil 里带了 characterEncoding=UTF-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-borrow=true 和 validation-query=SELECT 1,拿连接前先探活。网上常见的 URL 加 autoReconnect=true 对连接池里的死连接有时灵有时不灵,事务中途断线时它救不了你,最保险的还是用完即关。

5.4 金额精度和日期排序:两个不起眼的高频扣分点

现象:工资计算结果偶尔少一分钱,工资导出到 Excel 后对不上;工资表按月份查询,12 月排在 1 月前面。

原因:第一个问题是用 float/double 存金额,计算 0.1+0.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 的落地路径能帮到你,少走这些弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询