ARTICLE DETAIL

资讯详情

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

Java Swing + MySQL 员工工资管理系统:JDBC连接与功能实现详解

Java Swing + MySQL 员工工资管理系统:JDBC连接与功能实现详解 简介基于Java Swing与MySQL开发的员工工资管理系统V3是一套面向Java课程设计、毕业设计及数据库初学者的完整可运行源码。系统实现了员工与管理员双角色登录机制员工登录后仅可查询个人工资信息管理员登录后能查询全部员工工资记录并可执行添加、修改、删除等维护操作权限划分明确符合实际工资管理场景。压缩包共46个文件体积仅4.33MB包含10个Java源文件、21个class编译后文件、5个XML配置、2个JAR依赖库以及salary.sql数据库脚本同时保留.idea、.settings等工程设置导入IDEA或Eclipse即可直接运行MySQL要求5.7及以上版本。目前已有159人学习浏览适合作为高分课程作业的参考范例。借助这些源码初学者能深入理解Swing窗体布局、事件监听和JDBC数据库增删改查并能直接利用SQL脚本快速建表在现有模块基础上进行二次开发扩展。1. Java Swing MySQL 员工工资管理系统课程作业里的高频需求长什么样如果 Java 课程设计里有一道几乎人人都撞上过的题员工工资管理系统当属其中之一。我在答辩现场也见过不少翻车现场界面画了一半、按钮没有事件、数据写不进 MySQL最后只能对着空窗口讲代码。直到看到这份 V3 的完整源码包的实现才明白这种项目真正难的不是 Swing 布局而是数据链路——登录权限、员工表、工资表、月度统计这一条线要闭环。V3 版的价值就在这它把一套真实可跑的 Swing JDBC MySQL 工程整理成了课程作业最需要的骨架适合正在赶 Java 课设、需要演示完整功能且必须讲清楚每一步的同学直接参照。2. 数据库表设计与 JDBC 连接层先定四张表再写界面2.1 四张表怎么拆才不冗余工资管理系统的核心是“员工——部门——工资——账号”四个维度。见过不少同学把部门名字直接塞进员工表工资表里也重复存一遍员工姓名结果改一个部门要同步两张表。V3 采用三范式思路拆成四张独立表表之间用外键关联代码里再用 JOIN 查询组装结果这套结构在答辩时也最容易讲清楚。表名关键字段说明deptdept_id, dept_name, dept_desc独立部门表员工表通过 dept_id 关联employeeemp_id, emp_no, emp_name, gender, birthday, dept_id, position, base_salary, hire_date, statusemp_no 唯一索引status 用于软删除salarysalary_id, emp_id, salary_month, base_salary, overtime_pay, bonus, deduction, actual_salary, create_timesalary_month 存 YYYY-MM 字符串sys_useruser_id, username, password, rolerole 区分管理员与普通员工建表 SQL 里两个地方值得留意salary_month 不要用 DATETIME直接用 CHAR(7) 存储“2025-05”这种月份格式按月度统计时用字符串比较和 GROUP BY 都更干净employee.status 用 TINYINT1 表示在职、0 表示离职工资表里只统计在职员工。这样设计的直接好处是月度汇总 SQL 写起来不需要大量的时间格式化转换。CREATE DATABASE wage_db DEFAULT CHARACTER SET utf8mb4; USE wage_db; CREATE TABLE dept ( dept_id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL, dept_desc VARCHAR(200) ); CREATE TABLE employee ( emp_id INT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(20) NOT NULL UNIQUE, emp_name VARCHAR(50) NOT NULL, gender CHAR(2), birthday DATE, dept_id INT, position VARCHAR(50), base_salary DECIMAL(10,2), hire_date DATE, status TINYINT DEFAULT 1, FOREIGN KEY (dept_id) REFERENCES dept(dept_id) );这段 SQL 里的 DECIMAL(10,2) 是工资字段的推荐类型10 位总长度、2 位小数避免用 FLOAT 出现 0.10.20.30000000000000004 的精度问题。外键约束在课程作业里建议保留它能防止录入工资时填一个不存在的员工编号虽然 Java 层也会校验但数据库层面的约束是最后一道防线。2.2 JDBC 连接串与驱动选型MySQL 5.7 和 8.0 写法不一样V3 源码里最容易被忽略但最容易翻车的文件就是数据库连接工具类。MySQL 8.0 之后驱动类名从 com.mysql.jdbc.Driver 改成了 com.mysql.cj.jdbc.Driver如果你电脑装的是 MySQL 8.0却把网上老教程里的驱动类名直接粘过来加载驱动那一步就会抛 ClassNotFoundException。以下是我会采用的 DBUtil 写法package com.wage.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { // MySQL 8.0 必须用 cj 路径5.7 用 com.mysql.jdbc.Driver private static final String DRIVER com.mysql.cj.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/wage_db ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf-8; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }连接串里三个参数要记住serverTimezoneAsia/Shanghai 解决 MySQL 8 的时区报错characterEncodingutf-8 解决中文乱码useSSLfalse 跳过本地开发时的证书校验。密码 123456 只是示例实际使用时要改成自己的 root 密码。如果你用的 MySQL 是 5.7 且驱动也是 5.x把 DRIVER 改回 com.mysql.jdbc.Driver 就好URL 里的时区参数可以留也可以不留。2.3 PreparedStatement 不让用户输入直接拼进 SQLV3 里的 DAO 层统一用了 PreparedStatement 的 ? 占位符这一点在课程设计中属于明显的加分项。网上能搜到不少用 String 直接拼接 SQL 的老代码比如 SELECT * FROM employee WHERE emp_no empNo 这种写法在答辩演示时一旦输入框里填个 OR 11整张表的数据就直接查出来了场面会很难收场。public Employee findByEmpNo(String empNo) { String sql SELECT * FROM employee WHERE emp_no ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, empNo); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { return mapToEmployee(rs); } } } catch (SQLException e) { e.printStackTrace(); } return null; }这段代码同时用到了 try-with-resourcesJava 7 之后的语法Connection、PreparedStatement、ResultSet 三个资源会自动关闭不用再写一堆 finally 里判断 null。mapToEmployee 方法负责把 ResultSet 的每一列赋值给 Employee 实体对象里面要处理 rs.getInt(emp_id)、rs.getString(emp_name) 这类取列操作注意列名一定要与数据库字段名完全一致不然取出来的全是 0 或 null。3. 功能模块怎么落登录、员工、工资录入到月度统计的代码链路3.1 登录验证不要在密码框用 getText登录模块是 V3 的第一个入口也是最容易被老师打开的第一个界面。登录框的密码组件用的是 JPasswordField取值时必须用new String(pwdPassword.getPassword())而不是.getText()。getText() 在 JPasswordField 上已经被标记废弃而且它会把密码以 String 形式留在内存中对课程作业来说还有一层隐藏含义面试官看到你能区分 char[] 和 String会认为你具备基本的密码处理意识。private void btnLoginActionPerformed(ActionEvent e) { String username txtUsername.getText().trim(); String password new String(pwdPassword.getPassword()); if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, 账号和密码不能为空); return; } User user userDao.findByUsernameAndPassword(username, password); if (user ! null) { this.dispose(); new MainFrame(user).setVisible(true); } else { JOptionPane.showMessageDialog(this, 账号或密码错误); } }验证逻辑通过后要做两件事dispose 掉登录窗体释放资源把当前登录的 User 对象传给主窗体。MainFrame 里根据user.getRole()判断菜单项是否可用角色是 ADMIN 的能看到“工资录入”“员工管理”菜单普通员工只能看“个人工资查询”。这里把登录后的权限判断前置到界面渲染阶段比在每个按钮事件里二次判断要省事得多。3.2 员工 CRUD一张表单同时承担新增和编辑员工管理的界面通常是左边一个 JTable 显示列表右边一排文本框加一个“保存”按钮。V3 的做法是点表格行时回填文本框再点保存时判断 emp_id 是否为空来决定走 INSERT 还是 UPDATE这样一个面板就承担了增改两件事。DAO 层对应方法如下public int saveOrUpdate(Employee emp) { if (emp.getEmpId() null) { String sql INSERT INTO employee(emp_no, emp_name, gender, birthday, dept_id, position, base_salary, hire_date, status) VALUES(?,?,?,?,?,?,?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, emp.getEmpNo()); ps.setString(2, emp.getEmpName()); ps.setString(3, emp.getGender()); ps.setDate(4, java.sql.Date.valueOf(emp.getBirthday())); // 其余字段按顺序 set 到位 return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } else { String sql UPDATE employee SET emp_no?, emp_name?, gender?, birthday?, dept_id?, position?, base_salary?, hire_date?, status? WHERE emp_id?; // 同样按顺序 set 参数 } return 0; }setDate 的参数类型是 java.sql.Date而界面表单里日期文本是 String所以要先通过java.sql.Date.valueOf(LocalDate.parse(...))转换。这里最容易踩的坑是月份少补零输入“2025-5-1”会导致 DateTimeParseException我一般会在 jTextField 的失焦事件里自动补成“2025-05-01”。删除操作我习惯用 UPDATE 把 status 置为 0 而不是 DELETE因为工资表里还要关联离职员工的历史记录物理删除会让以前的月度报表对不上账号。3.3 工资计算与月度统计别把所有数据拉到 Java 内存里算工资模块的关键点在于“实发工资”不是用户填的而是根据基本工资、加班费、奖金、扣款计算出来的actual_salary base_salary overtime_pay bonus - deduction。V3 在保存工资记录时用 SQL 表达式直接算而不是在 Java 里算完再存这样即使以后从数据库端直接插入数据实发工资也不会错。月度汇总则用一条聚合 SQL 完成SELECT d.dept_name, COUNT(*) AS emp_count, SUM(s.base_salary s.overtime_pay s.bonus - s.deduction) AS total_pay, AVG(s.base_salary s.overtime_pay s.bonus - s.deduction) AS avg_pay FROM salary s JOIN employee e ON s.emp_id e.emp_id JOIN dept d ON e.dept_id d.dept_id WHERE s.salary_month ? GROUP BY d.dept_name ORDER BY total_pay DESC;传给这条 SQL 的参数是一个字符串“2025-05”比传 Date 对象再 DATE_FORMAT 要稳得多。因为 salary_month 在表里本来就是 CHAR(7)字符串相等比较能直接走索引数据量大一点也能扛住。统计结果用一个 DefaultTableModel 填充到 JTable 展示SUM 和 AVG 返回的 BigDecimal 类型不能直接 toString要用setScale(2, RoundingMode.HALF_UP)保留两位小数再显示。4. 本地运行与打包JDK 配置、MySQL 8 初始化、驱动路径三个硬门槛4.1 JDK 环境变量配置与版本选择把源码包从压缩文件里解压出来后第一件事不是打开 IDE而是确认命令行里java -version和javac -version能正常输出。V3 是基于 Java 8 语法写的JDK 8 和 JDK 17 都能编译运行但如果课程实验室的机器装的还是 JDK 8就别为了尝鲜装 17Swing 在 JDK 8 上表现最稳。如果命令提示符提示“javac 不是内部或外部命令”说明 PATH 里没有 Java 的 bin 目录。java -version javac -version环境变量配置就两件事新建 JAVA_HOME 指向 JDK 安装目录比如C:\Program Files\Java\jdk1.8.0_202在 Path 里追加%JAVA_HOME%\bin。配置完必须新开一个命令行窗口再验证因为旧窗口的环境变量不会刷新。我见过在同一个黑窗口里配完直接敲 java -version 报错的那不是配置错了是窗口没重开。4.2 MySQL 8 初始化与建库脚本导入V3 源码包里有数据库脚本文件里面是完整的建库建表语句和几条测试数据。在命令行导入之前先确认 MySQL 服务处于启动状态。Windows 上 MySQL 8 的安装流程要记住两个关键命令初始化数据目录用mysqld --initialize-insecure生成了一个无密码的 root 用户适合本地开发注册 Windows 服务用mysqld --install这两个命令都要在管理员权限的命令行里执行。mysqld --install net start mysql mysql -u root -p进入 mysql 客户端后先创建数据库再导入脚本或者直接用重定向方式一步完成mysql -u root -p db/wage_db.sql如果你本机之前装过 MySQL 5.7再用 8.0 的客户端连接会出现认证插件不兼容问题这个在第五章排查里细说。导入完成后用USE wage_db; SHOW TABLES;验证四张表都在再执行一条SELECT * FROM employee;看中文是否正常这一步排掉字符集问题后面连 Java 才不会一脸懵。4.3 驱动 jar 的放置位置与编译运行mysql-connector-java 的 jar 包是连接 MySQL 的桥梁源码包里通常放在 lib 目录下。用 IDE 打开项目后把 lib 目录标记为 Library如果用命令行编译得手动把 jar 加进 classpath。Windows 下 classpath 分隔符是分号Linux 下是冒号别搞混了。javac -encoding UTF-8 -cp .;lib/* com/wage/Main.java java -cp .;lib/* com.wage.Main-encoding UTF-8不仅让中文注释正常更重要的是 Java 源文件里的中文字符串字面量比如“员工工资管理系统”在编译后能被正确转成 UTF-8 字节。有些人用记事本写代码保存成了 GBK再用 UTF-8 编译就会发现界面标题全是乱码。.-cp .;lib/*里的点代表当前目录lib/*是通配符加载该目录下所有 jar这种写法比逐个写全路径省事也是 Maven 出现之前的老 Java 人惯用手法。5. 常见问题排查五个高频坑位的现象、原因与修复5.1 驱动加载失败与 MySQL 8 认证插件冲突现象程序启动后控制台抛出ClassNotFoundException: com.mysql.jdbc.Driver或者连接时抛Access denied for user rootlocalhost偶尔会出现Public Key Retrieval is not allowed。原因前者是驱动类名用错——MySQL 8 的驱动必须写com.mysql.cj.jdbc.Driver写 5.x 的类名当然找不到。后者是 MySQL 8 默认的认证插件是 caching_sha2_password旧版本的驱动不认识URL 里又没有 allowPublicKeyRetrievaltrue所以连接被拒绝。解决先用 SHOW VARIABLES LIKE default_authentication_plugin; 查你的 MySQL 认证方式。如果是 caching_sha2_password两条路选一条在连接串追加上allowPublicKeyRetrievaltrue或者把 root 用户改回兼容模式。ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456; FLUSH PRIVILEGES;5.2 中文乱码字符集、连接串、源文件三个环节缺一不可现象JTable 里显示的员工姓名为“???”或者问号数据库里直接用命令行查询却是正常中文。原因中文乱码有三次转码机会任何一环出错都会乱。数据库本身不是 utf8mb4JDBC 连接串没带 characterEncodingutf-8Java 源文件编译时没指定 -encoding。解决数据库层面执行SHOW VARIABLES LIKE character_set%;查看当前字符集。建库时就用 utf8mb4 而不是 utf8这是最省事的方式连接串的 URL 一定要有characterEncodingutf-8编译命令加-encoding UTF-8。三处都改完再重启程序基本就不会再出乱码了。5.3 日期类型转换与金额精度翻车现象birthday 字段插入时抛java.sql.SQLException: Cannot convert value 2025-05-01 from column 4 to TIMESTAMP或者实发工资显示成 3200.400000001。原因日期类型不匹配界面输入的是 String数据库字段是 DATE中间必须经过java.sql.Date.valueOf()转换工资字段如果建表时用了 DOUBLE 或 FLOAT浮点运算会出现精度漂移。解决日期统一走LocalDate.parse()解析后再转 sql.Date金额字段建表用 DECIMAL(10,2)Java 端对应 BigDecimal显示前用setScale(2, RoundingMode.HALF_UP)四舍五入。如果业务上需要保留两位小数加减乘除都用 BigDecimal 的 add/subtract/multiply 方法不要用 Java 原生运算符。5.4 界面假死耗时查询别占用 EDT 线程现象点“统计”按钮后整个窗口变成白屏无响应Windows 会显示“未响应”点了两次后干脆卡死。原因Swing 的界面刷新和事件回调都跑在 EDTEvent Dispatch Thread上。统计查询、聚合计算这类耗时操作如果直接写在按钮事件里EDT 被阻塞界面自然假死。数据库连接慢、数据量大时尤其明显。解决把耗时查询丢到 SwingWorker 后台线程完成后通过 done() 方法在 EDT 上安全更新界面模型。这是 Swing 并发编程的标准姿势也是面试官喜欢追问的点。SwingWorkerListString[], Void worker new SwingWorkerListString[], Void() { Override protected ListString[] doInBackground() { return reportDao.findMonthlyReport(month); } Override protected void done() { try { DefaultTableModel model buildModel(get()); tblReport.setModel(model); } catch (Exception e) { e.printStackTrace(); } } }; worker.execute();5.5 服务起不来与数据库备份还原现象net start mysql提示“服务名无效”或者 3306 端口被占用本地跑着 MariaDB 导致 MySQL 8 起不来。原因MySQL 解压版需要先执行mysqld --install注册 Windows 服务没注册直接 net start 当然无效。端口被占用通常是你机器上有另一个 MySQL 实例或 MariaDB 在运行。解决管理员命令行执行mysqld --install后再 net start查端口占用用netstat -ano | findstr 3306确认 PID 后考虑改 my.ini 里的 port3307。数据库备份还原用 mysqldump 和 mysql 命令组合mysqldump -u root -p wage_db wage_db_backup.sql mysql -u root -p wage_db wage_db_backup.sql6. 给 V3 加第三个迭代外置配置、Excel 导出与密码加固V3 的代码结构已经足够跑通课程设计但如果你想在答辩时多说三分钟我建议做三个小改造每个改动量都不大讲起来却很有料。第一把数据库连接参数从 DBUtil 类里抽到 db.properties 文件。DBUtil 里的 URL、用户名、密码硬编码在 Java 类中换一台机器演示就要改代码重编译。改成外置配置后只需在类加载时读取属性文件答辩老师问“如果数据库密码换了怎么办”你就能理直气壮地说修改配置文件即可。第二给工资报表加一个“导出 CSV”按钮把 JTable 当前模型的数据循环写入文本文件用 GBK 编码导出方便 Excel 直接打开不乱码。private void btnExportCsv() { ListString lines new ArrayList(); lines.add(部门,人数,总工资,平均工资); for (int r 0; r tblReport.getRowCount(); r) { lines.add(tblReport.getValueAt(r, 0) , tblReport.getValueAt(r, 1) , tblReport.getValueAt(r, 2) , tblReport.getValueAt(r, 3)); } Files.write(Paths.get(salary_report.csv), lines, Charset.forName(GBK)); }第三sys_user 表的密码字段不要明文存储。课程作业里一般没人较真但稍微写上一点 MD5 加盐的说明就能显出工程素养注册时把password salt做哈希再入库登录时先哈希再查库。不需要引入第三方库java.security.MessageDigest 就够用。这三个改动加起来不超过一百行代码但让 V3 从“能运行”升级成“有工程意识”。这套源码包我拆过之后的一个习惯是不管拿到什么课程设计先跑通数据库脚本再打开入口类最后才看界面代码。从那以后我每次做类似项目都会强制自己先建表后写 DAO再渲染界面倒过来做十有八九要返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表