简介:面向Java初学者的员工工资管理系统,采用Java Swing搭建桌面界面、MySQL负责数据持久化,实现了管理员与普通用户双角色体系,覆盖员工信息增删改查、部门维护、工资标准设置、工资查询与统计等业务模块,适合作为课程设计、毕业设计或自学项目参考。资源为zip压缩包,共163个文件,包括十六个Java源文件、三十个窗体定义、一百余个编译后的类文件、数据库脚本、可运行jar包及项目配置文件,压缩后大小仅944KB,结构清晰、导入方便。目前已有3332人学习下载。代码中界面与业务逻辑相对分开,初学者可通过窗体文件与类文件对照学习Swing组件的使用、事件监听以及Java与MySQL的交互过程;附带数据库脚本可快速还原环境,便于在此基础上修改完善,或扩展为其他信息管理系统。
1. Java 员工工资管理系统:一门课设凭什么能难住两届学生
每年到了课程设计季,我都能看到不少人在群里问同一类问题:Java 课设选题选了员工工资管理系统,代码写到一半发现角色权限乱成一团、数据库表结构改不动、Swing 界面和业务逻辑全塞在一个类里根本没法调试。这个选题看起来简单——无非是员工增删改查加工资设置,可真做起来,光是管理员和普通用户两套权限的切换,就能劝退一大半人。这份由 Java Swing 和 MySQL 实现的员工工资管理系统,恰好把这些问题都处理得比较干净:管理员能管部门、管员工、设工资、看统计,普通用户只能查自己的工资。代码复杂度对 Java 初学和做课程设计的在校生来说刚刚好,既不是 Hello World 级别的玩具,也不至于像企业级框架那样让人看不懂。这篇文章我就把它的结构、核心实现、部署步骤和容易踩的坑完整拆一遍。
2. 系统结构与技术选型:先搞懂这几个类分别干了什么
拿到一份源码,第一步不是急着双击运行,而是先把它的包结构和类职责理清楚。这个项目的类名起得很直白:AddEmployee、ModifyEmployee、DeleteEmployee 负责员工档案管理,SalarySetting 和 SalarySearch 管工资设置与查询,AddDepartment、DeleteDepartment、ModifyDepartment 管部门数据,Statistics 做统计,MainFrame 是主窗口。从命名就能看出来,它没有用什么 MVC 框架,而是典型的 Java Swing 事件驱动写法——每个业务动作对应一个窗口类或面板类,类内部直接处理按钮事件和数据库操作。
这种写法对初学者其实比 Spring Boot 那套更适合用来理解桌面应用的本质:界面是窗口,交互是监听器,数据是 JDBC 查询结果。我一般建议拿到项目后先按「实体-界面-数据库操作」三层去归类这些类。员工和部门是实体层,各种 Employee 和 Department 类是界面层,而数据库连接和 CRUD 操作分散在各自的事件处理逻辑里。这个项目没有单独抽出 DAO 层,这是它的简单之处,也是后期维护时最容易翻车的地方——改一个表名可能要动七八个文件。
2.1 MainFrame 主窗口:所有功能的入口和角色分流点
MainFrame 是整个系统的心脏,登录成功后所有操作都从这里开始。它的核心职责有两个:根据登录用户的角色动态加载可用菜单,以及维护各个业务窗口的单例或实例引用。管理员进来能看到部门管理、员工管理、工资设置、统计报表这几个入口,普通用户进来通常只能看到工资查询这一个入口。
从实现角度来看,MainFrame 里一般会有一个角色判断逻辑,用一个 int 或 String 类型的字段保存当前登录用户角色。Java Swing 做界面跳转最常见的做法是 setVisible(false) 隐藏当前窗口,再 new 出目标窗口。这里有一个值得注意的细节:如果每次点菜单都 new 一个新的窗口实例,内存里会堆积大量无引用对象,虽然 JVM 会回收,但快速切换时容易出现界面卡顿。更稳妥的做法是用单例模式或者提前在 MainFrame 里初始化好各个子窗口的引用,切换时只做 setVisible 控制。
2.2 数据库表设计:员工、部门、工资设置之间的关联关系
MySQL 数据库在这个项目里存了四类核心数据:管理员和用户账号、部门信息、员工档案、工资设置。员工表和部门表通常通过 dept_id 外键关联,工资设置表通过 employee_id 与员工表关联。这种设计是典型的第二范式结构——员工不直接存部门名称字符串,而是存部门 ID,查询时再 JOIN 出部门名。
我拿到数据库文件后第一件事就是看表结构定义,重点关注字段类型和是否允许为空。比如工资字段用 DECIMAL(10,2) 而不是 FLOAT,是因为浮点数在计算工资总和时会产生精度误差。员工入职日期用 DATE 类型,查询某个月入职的员工时直接 BETWEEN 比较就行。如果原表结构里这类细节做得好,说明作者有基本的数据库功底,反之则要自己动手调整。
2.3 JDBC 连接与驱动加载:为什么换台电脑就连不上数据库
这个项目用的是传统 JDBC 连接 MySQL,Class.forName 加载驱动、DriverManager.getConnection 拿连接、Statement 执行 SQL、ResultSet 遍历结果。这套写法在课程设计里出现频率最高,但也是环境问题的高发区。最常遇到的坑是驱动版本和 MySQL 版本不匹配:MySQL 5.x 用 com.mysql.jdbc.Driver,MySQL 8.x 必须换成 com.mysql.cj.jdbc.Driver,连接 URL 还要加上 useSSL=false 和 serverTimezone=Asia/Shanghai,否则直接报时区错误。
如果项目里用的是 DBUtil 之类的工具类,通常会把 url、username、password 写成静态常量。我个人的习惯是建议把数据库连接参数提取到 properties 配置文件里,虽然课程设计不要求这么干,但后续改库名、改密码会省很多事。另外每次操作完要关 Connection、Statement、ResultSet,顺序是从里到外,否则连接池会把连接耗尽,跑几次查询后程序就假死了。
3. 登录与前权限控制:管理员和普通用户的两套逻辑
登录模块是判断一个 Java 课程设计用心程度的试金石。很多项目只做了账号密码比对,登录成功后所有功能全部开放,角色区分形同虚设。这个系统的不同之处在于,登录动作不只是查一条用户表记录,后续整个主界面的菜单加载、操作按钮的可用状态、数据查询的范围,全部由当前登录者的角色决定。
管理员和普通用户的分工可以简单概括为:管理员管全局,能增删改查所有数据;普通用户只能看和自身相关的数据。用工位上的话说,这是垂直权限和水平权限的区别——垂直权限决定你能点哪些菜单,水平权限决定你查出来的数据是哪几条。
3.1 登录校验流程:从文本框到数据库比对再到角色分流
登录窗口的逻辑通常是这样:用户输入账号密码,点击登录按钮,事件监听器里取出 JTextField 和 JPasswordField 的内容,拼接 SQL 查询用户表。密码比对有两种常见做法,一种是直接 SELECT 出来然后字符串 equals 比较,另一种是把 WHERE 条件直接写进 SQL。前者更安全,至少密码不会出现在 SQL 日志里。
查出来的用户记录里有一个字段标识角色,假设是 role 字段,1 代表管理员,0 代表普通用户。校验通过后有两种处理方式:一种是在登录窗口里直接 new MainFrame(role),把角色传进去;另一种是做全局静态变量保存当前登录用户。第一种更推荐,显式传参比全局变量好理解,也方便后期扩展多角色。需要注意的是 JPasswordField 的 getPassword() 返回的是 char[] 而不是 String,很多初学者在这里直接用 toString() 拿到的是对象地址,导致密码永远比对失败。
3.2 菜单动态加载:如何让不同角色看到不同功能入口
菜单加载有两种实现路径。第一种是 JMenuBar 配合 JMenu 和 JMenuItem,登录时根据角色逐个 setVisible 控制显示隐藏;第二种是直接用 JButton 排列在主界面上,同样按角色条件判断是否 add 到容器。这个项目整体是窗口跳转风格,我估计用的是第二种居多,因为功能都以独立窗口类存在,主界面更像是功能选择台。
动态加载菜单有一个原则:不要只藏入口,后端也要做拦截。虽然桌面应用没有 Web 应用那种严格的拦截器概念,但至少在每个业务窗口的构造方法里可以再次校验角色。如果普通用户手工 new 一个管理员功能窗口出来,系统不应该允许操作。这种双重校验在课程设计里属于加分项,写进报告里也能体现你对权限控制有完整思考。
3.3 参数传递与窗口切换:为什么关闭子窗口后主界面也退了
Java Swing 开发里有一个非常经典的问题——点击子窗口的关闭按钮,整个程序都退出了。原因是子窗口调用了 System.exit(0) 或者 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)。正确做法是子窗口关闭时只 dispose() 自己,主窗口通过 setVisible(true) 重新显示。如果项目里出现点击返回主菜单没反应,多半是主窗口实例被垃圾回收了,或者主窗口引用在子窗口里没有保存。
窗口切换的另一种常见写法是用 CardLayout 做单窗口多面板切换,好处是避免了多窗口实例的管理问题,坏处是界面代码全部堆在一个类里,类体积迅速膨胀。对于这个项目这种功能较多的系统,保持多窗口结构反而更清晰,每个业务类只负责自己的窗口逻辑。关键点在于:主窗口统一管理子窗口实例,子窗口保存主窗口引用,关闭时通知主窗口刷新数据列表。
4. 核心模块实现细节:增删改查背后的边界条件处理
员工工资管理系统看起来功能不少,但归纳起来无非是部门管理、员工管理、工资设置、工资查询、统计报表这几块。每一块单独拎出来都不难,难的是把边界条件处理到位。举个例子,删除一个还在职且已有工资记录的员工,是直接 DELETE 还是软删除?删除一个还有员工的部门,是拒绝删除还是连员工一起删?这些细节决定了系统是能交作业的水平还是能实际用的水平。
从源码类的命名来看,AddEmployee、ModifyEmployee、DeleteEmployee 分了三个类,而不是合并成一个 EmployeeManager,这种方式类数量多一些,但每个类职责单一,事件监听器写在各自的类里,互相之间不干扰。对于初学者来说,这种拆分方式反而比一个大而全的 CRUD 类更容易读懂。
4.1 员工档案的增删改:必填校验和外部关联的影响
添加员工时,界面一般会有姓名、性别、年龄、部门、入职日期、基础工资等字段。点击确定按钮后,先做前端校验:姓名不能为空、工资必须是数字、部门必须已选择。如果在 JTextField 里输入了非数字内容直接入库,数据库会报 NumberFormatException 或 SQL 异常,对用户体验来说就是程序闪退。
删除员工的逻辑要复杂一些。如果工资设置表里有这个员工的记录,直接执行 DELETE FROM employee WHERE id = ? 会因为外键约束失败而报错。常见做法是先查工资表有没有关联记录,有的话先删除工资记录再删除员工,或者把删除操作包在事务里执行。该项目用的是 MySQL InnoDB 引擎,支持事务,所以用 connection.setAutoCommit(false) 包住多条删除语句是合理的做法。我个人在处理这类问题时习惯先查关联再做决定,如果是管理员删除员工操作,界面应弹出确认框告知该员工有关联工资记录。
修改员工信息的核心不是 UPDATE 语句怎么写,而是保存之前的数据回显。ModifyEmployee 类的关键逻辑是先从列表选中一行,把该行数据填充到各个输入框里,用户改完后再提交。这里有一个容易出错的地方:主键 ID 要单独保存,不能随着修改被覆盖,否则 UPDATE 的 WHERE 条件会失效,导致所有员工数据全被改成同一条。
4.2 工资设置与查询:一条 SQL 搞定还是分步查询
工资设置这个功能模块的工作流通常是:管理员选择一个员工,录入基本工资、岗位工资、绩效工资等,系统计算出应发工资并保存。有的项目会把五险一金、个税起征点也算进去,复杂度和业务要求直接相关。从类名 SalarySetting 来看,这个项目的工资条目应该是预设好的固定字段,不是自定义工资项。
工资查询模块相对简单,普通用户登录后输入工号或直接列出自己的工资记录。查询条件常见的有按月份、按姓名、按部门。SQL 写法从简单到复杂依次是:SELECT * FROM salary WHERE employee_id = ?、再加月份 BETWEEN、再加 JOIN employee 表做部门过滤。性能上这个量级的数据没有任何压力,重点是查询条件为空时的处理——如果用户一个条件都没填,应该默认查出所有记录,而不是拼接出一条语法错误的 SQL。
4.3 统计报表模块:用 Java 代码做聚合还是交给 SQL
Statistics 类通常负责两类统计:各部门员工人数、各部门工资总额。如果只做数字展示,用 SQL 的 GROUP BY 加聚合函数是最省事的。查询部门工资总额可以写成:
SELECT d.dept_name, SUM(s.salary) AS total_salary FROM salary s JOIN employee e ON s.employee_id = e.id JOIN department d ON e.dept_id = d.id GROUP BY d.dept_id这段 SQL 的作用是先把工资表、员工表、部门表三张表通过外键关联起来,再按部门分组,用 SUM 算出每个部门的工资总和。如果项目里没有单独做图表展示,结果通常会放在 JTable 里呈现。JTable 的填充方式是把 ResultSet 遍历出来的数据放进二维数组,再配合 String[] 类型的列名数组构造表格模型。
如果原项目需求里要求做柱状图或饼图,常见做法是引入 JFreeChart 依赖。课程设计里用 JFreeChart 是很讨巧的加分项——不用自己画坐标轴,几行代码就能生成一张图表。但要注意 JFreeChart 的版本兼容问题,较新版本要求 JDK 11 以上,如果你的环境是 JDK 8,要选择 1.5.x 的旧版本。
5. 部署运行与常见问题排查:从 JDK 环境到数据库连接的五个坑
课程设计项目拿到手能不能跑起来,很大程度取决于环境配置。这块我在帮人调试时见过太多翻车现场了,归纳下来高频问题集中在这五个方面,每一条都至少让我浪费过半小时。
5.1 现象一:双击运行没反应或报找不到主类
运行 Java Swing 程序前要确认 JDK 装好了、环境变量配对了。命令行输入 java -version 能输出版本号是基本前提。很多初学者在 IDE 里能跑,但导出成 jar 包后双击没反应,是因为 jar 包没有指定 Main-Class 清单属性。解决办法是在 IDE 的打包配置里设置主类为 MainFrame,或者用命令行打包时在 MANIFEST.MF 里写明 Main-Class。
5.2 现象二:JDBC 连接报 ClassNotFoundException
这个异常百分之九十是驱动 jar 包没有加入到项目的构建路径。如果用的是 IDE,右键项目 → Build Path → Add External JARs,选择对应版本的 mysql-connector-java 包。还有一种是 jar 包加进去了但版本不对,MySQL 8.x 的驱动类名和 5.x 不同,检查一下 Class.forName 里的字符串和 jar 包版本是否匹配。
5.3 现象三:SQLException: Unknown database
连接 URL 里写的数据库名在 MySQL 里不存在。比如 URL 写的是 jdbc:mysql://localhost:3306/salary_system,但导入的 SQL 脚本建的是 salary 库,那必然报错。解决方法是登录 MySQL 后用 SHOW DATABASES 查看实际库名,或者在 IDE 里先手动执行一遍 SQL 脚本确认建库语句执行成功。
5.4 现象四:中文乱码,界面和数据库全是问号
乱码问题根源在于字符集不一致。MySQL 建库时如果没指定 utf8mb4,默认可能用 latin1。正确的建库语句是 CREATE DATABASE IF NOT EXISTS xxx DEFAULT CHARSET utf8mb4。同时连接 URL 里追加 characterEncoding=utf8 参数,两边都设为 UTF-8,问题基本能解决。项目源码文件本身也可能因为编码不一致而乱码,IDE 右下角可以切换文件编码,GBK 和 UTF-8 之间转换一下就能看到正常中文。
5.5 现象五:连接超时或 Access denied for user
Access denied 是账号密码错误或权限不足。MySQL 8.x 默认的认证插件是 caching_sha2_password,老版本的 JDBC 驱动不认识这个插件会报错。解决办法是新建一个用户并指定 mysql_native_password 认证方式,或者把 root 用户的认证方式改掉。如果只是本机学习用,ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码' 就行。连接超时通常检查一下 MySQL 服务是否启动了,Windows 下在服务管理器里看 MySQL 的服务状态。
6. 进阶:给统计模块加一个按部门汇总的表格展示
基础功能跑通之后,如果想让这个项目在答辩时更有说服力,我建议把统计模块从单纯的数字展示升级成带汇总效果的表格。这个改造不需要改数据库结构,只加一个查询方法和一个展示面板。
第一步是在 Statistics 类里新增一个方法,查询各部门的员工人数与平均工资。SQL 可以用 GROUP BY 一次算出来:
SELECT d.dept_name, COUNT(e.id) AS emp_count, AVG(s.total_salary) AS avg_salary FROM department d LEFT JOIN employee e ON d.dept_id = e.dept_id LEFT JOIN salary s ON e.id = s.employee_id GROUP BY d.dept_id这里用 LEFT JOIN 而不是 INNER JOIN,是为了让没有员工的空部门也能显示出来,人数为 0。如果只统计有员工的部门,用 INNER JOIN 即可,两种写法在数据量小的时候性能没有差别,但从语义上讲,统计报表通常要把全部门都列出来才完整。
第二步是把查询结果填充到 JTable。JTable 的构造需要两个核心参数:列名数组和数据二维数组。数据数组的行数对应结果集的行数,列数对应 SELECT 出来的字段个数。遍历 ResultSet 时用 List 收集每一行的数据,最后转换成二维数组传给表格模型。
考虑到 JTable 默认不支持按列排序,而答辩时老师很可能会问「能不能按工资总额从高到低排」,可以直接在 SQL 末尾加 ORDER BY total_salary DESC,比在 Java 代码里写 Comparator 简单得多。如果后面想加图表展示,JFreeChart 的 BarChart 只需要把同样的统计结果转成 DefaultCategoryDataset 就行。
这个项目本身的代码结构是足够干净的,类名即功能,数据库表关系也清晰,花一个晚上通读源码、把五个坑对照排查一遍,基本就能运行起来。从那以后我每次拿到这类课设项目,都强制自己先走一遍「建库 → 配驱动 → 跑登录 → 逐模块点一遍」的流程,在 IDE 里把控制台的异常信息截图存下来,再动手改代码。这种习惯帮我省掉了大量重复排错的时间,希望也能帮到你。
本文还有配套的精品资源,点击获取