简介这是一套基于Java Swing开发的桌面端人事管理系统源码面向Java初学者与GUI编程实践者解决企业员工信息录入、查询、编辑及数据批量导入导出等基础人事管理需求。资源包共36个文件含16个.java源文件涵盖Person、EmployeeDAO、MainFrame等核心业务类、16个.class编译文件、1个Eclipse项目配置文件.project、1个JDBC驱动jar包mysql-connector-java-5.1.15-bin.jar及配套.classpath和.settings/prefs配置完整呈现MVC分层结构与Swing组件集成逻辑包体仅771KB轻量易读。已有647人学习下载适合通过真实项目理解JTable动态绑定、JFileChooser文件交互、JDBC连接MySQL、事件监听机制及异常友好提示等关键实践点代码结构清晰、注释充分可直接导入Eclipse运行调试是Java GUI开发入门与课程设计的优质参考范例。1. Java Swing人事管理系统不是“过时技术”的怀旧玩具而是中小团队快速落地的轻量级桌面管理方案很多人看到“Java Swing”四个字第一反应是“这玩意儿2010年就该进博物馆了”。但去年我帮某高校后勤处做一套内部人员排班与考勤登记工具时客户明确提了三条硬需求不连外网、单机可运行、三天内交付原型、运维人员只会双击exe。最后上线的正是基于Swing的纯本地人事管理系统——没有Spring Boot的启动耗时没有浏览器兼容性扯皮没有权限体系改造成本所有操作响应都在毫秒级。它解决的不是高并发或微服务治理问题而是“今天下午三点前让三位行政老师能录入37位临时工的入职信息并导出Excel”这种具体到分钟的任务。如果你面对的是局域网封闭环境、IT支持薄弱、业务逻辑清晰但迭代节奏慢的内部管理场景Swing不是退路而是一条被低估的务实路径。它不追求炫酷界面但胜在稳定、可控、零依赖——JRE装好就能跑连数据库都可选嵌入式HSQLDB整个安装包压完不到12MB。2. 从零搭起骨架用MavenSwingHSQLDB构建可运行最小系统2.1 项目结构设计为什么坚持“UI层-业务层-数据层”三分离Swing项目最容易陷入的泥潭就是把事件监听、SQL拼接、表格渲染全塞进一个JFrame里。某次我接手一个维护了五年的老系统主类文件长达2800行新增一个“按部门导出”功能花了两天——光是定位数据源初始化位置就看了三遍initComponents()。所以这次我强制采用三层结构ui/只放继承自JPanel/JFrame的视图组件禁止出现任何new Connection()、executeUpdate()、业务校验逻辑service/封装EmployeeService、DepartmentService等调用DAO处理事务边界和参数校验dao/仅含EmployeeDao、DepartmentDao负责SQL执行与ResultSet映射不持有Connection对象由Service层统一管理提示Swing本身不提供IoC容器所以Service层实例通过静态工厂获取如EmployeeService.getInstance()避免全局变量污染又比Spring轻量十倍。2.2 Maven依赖配置精简到只剩4个核心坐标Swing本身是JDK内置模块无需额外引入。真正需要声明依赖的只有四类dependencies !-- 嵌入式数据库免安装、单文件存储、支持ACID -- dependency groupIdorg.hsqldb/groupId artifactIdhsqldb/artifactId version2.7.2/version /dependency !-- 日志SLF4J桥接Log4j2避免System.out.println满天飞 -- dependency groupIdorg.slf4j/groupId artifactIdslf4j-api/artifactId version2.0.12/version /dependency dependency groupIdorg.apache.logging.log4j/groupId artifactIdlog4j-slf4j2-impl/artifactId version2.21.1/version /dependency dependency groupIdorg.apache.logging.log4j/groupId artifactIdlog4j-core/artifactId version2.21.1/version /dependency /dependencies注意不要引入JavaFX、SwingX、JGoodies等第三方UI库。它们会显著增加jar包体积且多数组件在高DPI屏幕如4K笔记本上渲染异常。原生Swing的JTableDefaultTableModelTableCellRenderer组合配合UIManager.setLookAndFeel()调用系统原生LF反而更稳。2.3 初始化HSQLDB用内存模式快速验证再切到文件模式落地开发阶段用内存数据库提速避免每次启动都重建表// DatabaseUtil.java public class DatabaseUtil { private static final String URL_MEM jdbc:hsqldb:mem:mymemdb;sql.syntax_pgstrue; private static final String URL_FILE jdbc:hsqldb:file:./data/hrdb;shutdowntrue; public static Connection getConnection() throws SQLException { // 开发时注释掉下一行用内存库上线前取消注释启用文件库 // return DriverManager.getConnection(URL_MEM, SA, ); return DriverManager.getConnection(URL_FILE, SA, ); } }关键点说明sql.syntax_pgstrue启用PostgreSQL风格语法如LIMIT、ILIKE降低后期迁移到PG的成本shutdowntrue确保JVM退出时自动保存数据到磁盘避免断电丢数据./data/hrdb路径为相对路径打包成exe后仍有效Inno Setup默认解压到%APPDATA%\YourApp3. 核心功能实现员工信息CRUD与部门树联动的Swing写法3.1 主窗口布局用GroupLayout替代GridBagLayout的实操理由GridBagLayout曾是Swing布局“标配”但它参数多、调试难、缩放行为不可控。现代Swing开发中GroupLayout已成为更可靠的选择——它由IDEIntelliJ IDEA / NetBeans自动生成且天然支持组件对齐、间距约束和动态重排。主窗口MainForm.java关键片段// 使用IDE生成的GroupLayout代码手动编写极容易出错 GroupLayout layout new GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 760, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addComponent(btnAdd) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnEdit) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnDelete) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 400, Short.MAX_VALUE) .addComponent(txtSearch) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnSearch))) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(btnAdd) .addComponent(btnEdit) .addComponent(btnDelete) .addComponent(txtSearch) .addComponent(btnSearch)) .addContainerGap()) );逻辑说明scrollPane包裹JTable高度设为400像素确保内容区固定可视区域避免滚动条跳动按钮组水平排列搜索框与按钮右对齐符合Windows/macOS原生应用习惯addContainerGap()添加10px边距避免组件贴边难点击3.2 JTable数据绑定用ObservableList替代DefaultTableModel的必要性DefaultTableModel简单直接但存在两个硬伤① 数据变更无法通知其他组件如修改姓名后右侧详情面板不同步② 不支持懒加载万级数据直接addRow()必然卡死我们改用ObservableListEmployee 自定义AbstractTableModel// EmployeeTableModel.java public class EmployeeTableModel extends AbstractTableModel { private final ObservableListEmployee data; public EmployeeTableModel(ObservableListEmployee data) { this.data data; // 监听列表变化自动触发表格刷新 data.addListener((ListChangeListener.Change? extends Employee c) - { while (c.next()) { if (c.wasAdded()) fireTableRowsInserted(c.getFrom(), c.getTo() - 1); if (c.wasRemoved()) fireTableRowsDeleted(c.getFrom(), c.getTo() - 1); if (c.wasUpdated()) fireTableRowsUpdated(c.getFrom(), c.getTo() - 1); } }); } Override public int getRowCount() { return data.size(); } Override public int getColumnCount() { return 6; } // ID, 姓名, 部门, 入职日期, 状态, 操作 Override public Object getValueAt(int row, int col) { Employee e data.get(row); return switch (col) { case 0 - e.getId(); case 1 - e.getName(); case 2 - e.getDeptName(); // 非deptId而是关联查询后的名称 case 3 - e.getHireDate(); case 4 - e.getStatus() 1 ? 在职 : 离职; case 5 - 编辑; // 实际为JButton渲染器此处仅占位 default - ; }; } }参数说明ObservableList来自javafx.collections需在module-info.java中声明requires javafx.base;即使不用JavaFX UI其集合工具链也远超AWTfireTableRowsUpdated()比fireTableDataChanged()更精准只刷新变更行避免整表闪烁第5列“操作”实际由TableCellRenderer绘制为JButton点击事件委托给JTable的MouseListener避免在模型中混入UI逻辑3.3 部门树与员工表联动用JTreeTreeSelectionListener实现双向过滤人事系统中“查看某部门下所有员工”是高频操作。Swing的JTree天然适合部门层级展示但需解决两个问题① 树节点点击后员工表如何只显示该部门数据② 员工表双击某人时树如何自动展开并选中其所在部门解决方案用TreeSelectionListenerTableRowSorter// 在初始化树时绑定 tree.addTreeSelectionListener(e - { TreePath path tree.getSelectionPath(); if (path ! null) { DepartmentNode node (DepartmentNode) path.getLastPathComponent(); // 获取部门ID设置表格行过滤器 sorter.setRowFilter(RowFilter.regexFilter(^ node.getId() $, 2)); // 列2为department_id字段隐藏列非部门名称列 } }); // 员工表双击事件中反向定位树节点 table.addMouseListener(new MouseAdapter() { Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() 2) { int row table.rowAtPoint(e.getPoint()); if (row 0) { int modelRow table.convertRowIndexToModel(row); Employee emp model.getData().get(modelRow); // 递归查找树中对应部门节点并展开路径 TreePath path findDepartmentPath(tree.getModel(), emp.getDeptId()); if (path ! null) { tree.setSelectionPath(path); tree.scrollPathToVisible(path); // 确保可见 } } } } });关键细节RowFilter.regexFilter()比RowFilter.generalFilter()性能高3倍以上因正则编译一次复用部门ID存为隐藏列JTable列宽设为0避免用户看到冗余数字findDepartmentPath()需自行实现递归查找返回TreePath而非TreeNode因TreePath包含完整路径信息支持scrollPathToVisible()4. 避坑指南Swing人事系统开发中踩过的5个真实深坑4.1 现象JTable在Windows 11高DPI屏幕上文字模糊、行高异常原因JVM默认未启用DPI感知Swing组件按96 DPI渲染被系统放大后出现插值模糊解决启动时添加JVM参数-Dsun.java2d.uiScale1.0并在main方法开头强制设置public static void main(String[] args) { // 必须在SwingUtilities.invokeLater之前调用 System.setProperty(sun.java2d.uiScale, 1.0); // Windows平台额外启用GDI渲染 if (System.getProperty(os.name).toLowerCase().contains(win)) { System.setProperty(sun.java2d.dpiaware, true); } SwingUtilities.invokeLater(MainForm::new); }4.2 现象HSQLDB文件模式下程序异常退出后数据丢失原因未正确关闭ConnectionHSQLDB的shutdowntrue参数未生效解决用try-with-resources包装Connection并在JFramewindowClosing事件中显式关闭// 在JFrame子类中重写 Override protected void processWindowEvent(WindowEvent e) { if (e.getID() WindowEvent.WINDOW_CLOSING) { try (Connection conn DatabaseUtil.getConnection()) { conn.createStatement().execute(SHUTDOWN); } catch (SQLException ex) { logger.error(数据库关闭失败, ex); } System.exit(0); } super.processWindowEvent(e); }4.3 现象JComboBox下拉选项中文乱码显示为方块原因Swing默认字体不支持中文且未在UIManager中统一设置解决在main方法最开头注入系统字体UIManager.put(Label.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); UIManager.put(Button.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); UIManager.put(TextField.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); // 必须在SwingUtilities.invokeLater之前执行4.4 现象导出Excel时日期列在WPS中显示为数字如44926原因Apache POI写入java.util.Date时未指定CellStyle数据格式解决创建专用日期单元格样式CellStyle dateStyle workbook.createCellStyle(); CreationHelper createHelper workbook.getCreationHelper(); dateStyle.setDataFormat(createHelper.createDataFormat().getFormat(yyyy-mm-dd)); // 写入单元格时显式设置 cell.setCellStyle(dateStyle); cell.setCellValue(employee.getHireDate());4.5 现象多线程更新JTable时抛IllegalStateException: EventQueue is not initialized原因在非EDT线程中直接调用model.fireTableRowsInserted()解决所有Swing组件更新必须包裹SwingUtilities.invokeLater()// 错误写法后台线程中 model.fireTableRowsInserted(0, list.size()-1); // 正确写法 SwingUtilities.invokeLater(() - { model.fireTableRowsInserted(0, list.size()-1); table.scrollRectToVisible(table.getCellRect(list.size()-1, 0, true)); });5. 进阶技巧让Swing人事系统具备“伪现代化”体验的3个关键改造5.1 用JLayer装饰登录框实现毛玻璃效果与输入校验反馈Swing原生不支持毛玻璃但JLayer可叠加渲染层。我们利用JLayer的paint()钩子在登录面板上绘制半透明遮罩并叠加实时校验图标// LoginPanel.java 中 private JLayerJPanel createLoginLayer() { JPanel panel new JPanel(new BorderLayout()); panel.add(new JLabel(用户名), BorderLayout.WEST); JTextField userField new JTextField(15); panel.add(userField, BorderLayout.CENTER); // 创建JLayer传入自定义LayerUI JLayerJPanel layer new JLayer(panel, new LoginLayerUI(userField)); return layer; } // LoginLayerUI.java public class LoginLayerUI extends LayerUIJPanel { private final JTextField field; private BufferedImage glassImage; public LoginLayerUI(JTextField field) { this.field field; } Override public void paint(Graphics g, JComponent c) { super.paint(g, c); Graphics2D g2 (Graphics2D) g.create(); // 绘制毛玻璃背景取父容器快照高斯模糊叠加半透明 if (glassImage null) { glassImage createGlassImage(c); } g2.drawImage(glassImage, 0, 0, null); // 输入校验用户名为空时在右侧绘制红色感叹号 if (field.getText().trim().isEmpty()) { g2.setColor(Color.RED); g2.setFont(new Font(Dialog, Font.BOLD, 14)); g2.drawString(!, field.getWidth() - 20, 20); } g2.dispose(); } }这个技巧的价值在于用户感知到的是“现代UI”而底层仍是Swing原生组件无需引入JavaFX或Web技术栈。毛玻璃仅用于登录页不影响主业务界面性能。5.2 用SystemTray实现后台常驻与消息提醒人事系统常需“后台运行有新审批时弹窗提醒”。Swing的SystemTrayAPI在Windows/macOS/Linux均可用public class TrayManager { private static final SystemTray tray SystemTray.getSystemTray(); private static final PopupMenu popup new PopupMenu(); public static void init() throws AWTException { // 创建托盘图标需准备ico/png文件 Image image Toolkit.getDefaultToolkit().getImage(icon.png); TrayIcon icon new TrayIcon(image, 人事系统); icon.setPopupMenu(popup); // 添加左键双击事件恢复主窗口 icon.addActionListener(e - SwingUtilities.invokeLater(MainForm::new)); // 添加右键菜单项 MenuItem exitItem new MenuItem(退出); exitItem.addActionListener(e - System.exit(0)); popup.add(exitItem); tray.add(icon); } // 外部调用此方法发送提醒 public static void showNotification(String title, String message) { if (tray.isSupported()) { tray.displayMessage(title, message, TrayIcon.MessageType.INFO); } } }使用时机当检测到数据库中approval_status1的新记录时调用TrayManager.showNotification()。比轮询数据库更轻量且不干扰用户当前工作流。5.3 打包为Windows原生EXE用jpackage替代exe4j的实操配置JDK 14内置jpackage比exe4j更干净且支持Windows应用商店签名# 构建命令Windows PowerShell中执行 jpackage ^ --input target/ ^ --name 人事管理系统 ^ --main-class com.example.hr.MainForm ^ --main-jar hr-system-1.0.jar ^ --type exe ^ --win-shortcut ^ --win-menu ^ --icon src/main/resources/icon.ico ^ --vendor 某实验室 ^ --description 内部人事信息管理工具 ^ --app-version 1.0.0生成目录结构hr-system/ ├──人事管理系统.exe ← 双击即运行自动检测JRE ├──runtime/ ← 内置JRE 17免用户安装 ├──app/hr-system-1.0.jar ← 你的代码jar └──lib/ ← HSQLDB等依赖jar关键优势用户无需安装JRE程序自带精简版JRE约120MB.exe文件可通过微软EV证书签名绕过Windows SmartScreen警告--win-shortcut自动生成开始菜单和桌面快捷方式符合Windows用户直觉我坚持在每个Swing项目里做三件事第一用UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())强制走系统原生主题不搞自绘皮肤第二所有数据库操作加try-catch(SQLException)并记录完整堆栈而不是吞掉异常第三导出功能必带进度条JProgressBar哪怕只有100条数据——因为用户永远不知道你后台在查几张表。这些不是“高级技巧”而是让系统在真实办公室里活过三个月的基本功。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?