實(shí)戰(zhàn)教程)
簡(jiǎn)介本資源是一套基于Java Swing與MySQL開發(fā)的員工工資管理系統(tǒng)完整實(shí)現(xiàn)面向計(jì)算機(jī)專業(yè)本科生及Java初學(xué)者適用于Java課程設(shè)計(jì)、期末大作業(yè)等實(shí)踐教學(xué)場(chǎng)景。系統(tǒng)功能覆蓋員工信息管理、部門維護(hù)、工資核算、查詢統(tǒng)計(jì)與密碼修改等核心模塊代碼結(jié)構(gòu)清晰、注釋規(guī)范已通過本地編譯驗(yàn)證并獲98分高分評(píng)審具備良好的學(xué)習(xí)參考價(jià)值與工程可復(fù)現(xiàn)性。壓縮包共47個(gè)文件含12個(gè)Java業(yè)務(wù)邏輯類、10個(gè)XML配置與界面布局文件、5個(gè)SQL建表與數(shù)據(jù)初始化腳本、13張操作界面截圖如add.png、query.png、modify.png等以及jar依賴庫(kù)和項(xiàng)目配置文件整體大小為2.99MB。目前已有153人下載學(xué)習(xí)配套文檔說明詳實(shí)目錄組織合理便于理解MVC分層思想、Swing事件驅(qū)動(dòng)機(jī)制及JDBC數(shù)據(jù)庫(kù)交互全流程。1. 這不是又一個(gè)“Hello World”Swing界面它是一套能跑通完整工資核算閉環(huán)的Java桌面系統(tǒng)你可能已經(jīng)點(diǎn)開過十幾個(gè)標(biāo)著“Java課程設(shè)計(jì)”的Swing項(xiàng)目雙擊jar包后彈出一個(gè)帶登錄框的窗口輸完賬號(hào)密碼——然后卡在“查詢中…”再無下文。而這個(gè)Goat_SalaryManageSystem-SMS不同它在本地 MySQL 8.0.26 環(huán)境下實(shí)測(cè)可完成「部門錄入 → 員工建檔 → 基礎(chǔ)薪資配置 → 考勤扣減計(jì)算 → 工資條生成 → Excel導(dǎo)出」全鏈路評(píng)審分98分不是虛的。它沒用 Spring Boot 做后端也沒上 Web 前端框架而是用純 Swing 組件JDBC 直連 MySQL把 JDBC 的事務(wù)控制、ResultSet 映射、Swing 事件驅(qū)動(dòng)與 UI 狀態(tài)同步這些 Java SE 核心能力扎扎實(shí)實(shí)串成一條線。適合正在啃《Java核心技術(shù)卷I》第10章Swing和第12章JDBC的本科生也適合想快速?gòu)?fù)現(xiàn)一個(gè)“非玩具級(jí)”桌面應(yīng)用的轉(zhuǎn)行者——它不炫技但每個(gè)按鈕背后都有 try-catch 包裹的 PreparedStatement 執(zhí)行邏輯每張表格都對(duì)應(yīng)著真實(shí)字段映射的 TableModel 實(shí)現(xiàn)。2. 為什么選 Swing MySQL 8.0.26 而不是 JavaFX 或 SQLite2.1 技術(shù)棧選擇背后的教學(xué)剛性約束課程設(shè)計(jì)明確要求“使用 Java 原生 GUI 庫(kù)與關(guān)系型數(shù)據(jù)庫(kù)”這直接排除了 JavaFX需額外模塊聲明、WebView依賴瀏覽器內(nèi)核和嵌入式數(shù)據(jù)庫(kù)如 HSQLDB。Swing 是 JDK 8~17 自帶、零依賴、組件生命周期清晰的唯一選項(xiàng)MySQL 則因?qū)W校機(jī)房統(tǒng)一部署、助教熟悉其權(quán)限管理與備份機(jī)制成為事實(shí)標(biāo)準(zhǔn)。特別注意本項(xiàng)目強(qiáng)制綁定mysql-connector-java-8.0.26.jar而非最新版 8.3.x原因在于后者默認(rèn)啟用sslModeREQUIRED而本地開發(fā)環(huán)境常未配置 SSL 證書會(huì)導(dǎo)致Communications link failure。這是學(xué)生最容易卡住的第一道墻。提示若啟動(dòng)時(shí)報(bào)java.sql.SQLException: Unknown system variable caching_sha2_password說明 MySQL 8.0 默認(rèn)認(rèn)證插件不兼容舊驅(qū)動(dòng)。解決方案不是降級(jí) MySQL而是執(zhí)行以下 SQL 切換用戶認(rèn)證方式ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_password; FLUSH PRIVILEGES;此命令將 root 用戶認(rèn)證方式從caching_sha2_password切回mysql_native_password與 8.0.26 驅(qū)動(dòng)完全兼容。2.2 數(shù)據(jù)庫(kù)初始化腳本的三層結(jié)構(gòu)解析項(xiàng)目中init.sql、gongzi.sql、gongzi_beifen.sql并非簡(jiǎn)單重復(fù)而是承擔(dān)不同職責(zé)腳本名作用關(guān)鍵內(nèi)容使用時(shí)機(jī)init.sql創(chuàng)建基礎(chǔ)庫(kù)表結(jié)構(gòu)與初始數(shù)據(jù)CREATE DATABASE IF NOT EXISTS salary_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;CREATE TABLE department(...)首次部署時(shí)執(zhí)行建立空庫(kù)gongzi.sql插入典型業(yè)務(wù)數(shù)據(jù)樣本INSERT INTO employee (id, name, dept_id, base_salary) VALUES (1001, 張三, 1, 8500.00);初始化后立即執(zhí)行填充測(cè)試數(shù)據(jù)gongzi_beifen.sql全量數(shù)據(jù)備份含自增主鍵值INSERT INTO employee VALUES (1001,張三,1,8500.00,...), (1002,李四,2,7200.00,...);用于恢復(fù)誤刪數(shù)據(jù)保留原始 ID 序列執(zhí)行順序必須是先運(yùn)行init.sql創(chuàng)建庫(kù)表 → 再運(yùn)行g(shù)ongzi.sql插入數(shù)據(jù)。若跳過init.sql直接執(zhí)行g(shù)ongzi.sql會(huì)因表不存在報(bào)錯(cuò)若用gongzi_beifen.sql替代gongzi.sql則因包含INSERT ... VALUES (...)全量語(yǔ)句在已有數(shù)據(jù)時(shí)觸發(fā)主鍵沖突。2.3 Swing UI 層的組件化拆解與狀態(tài)管理整個(gè)系統(tǒng) UI 由src/sdms/ui/下 12 個(gè).java文件構(gòu)成每個(gè)文件對(duì)應(yīng)一個(gè)功能面板如AddEmployeePanel.java、SalaryQueryPanel.java。關(guān)鍵設(shè)計(jì)在于MainForm.java中的卡片布局CardLayout管理// MainForm.java 片段 private CardLayout cardLayout; private JPanel contentPanel; public MainForm() { contentPanel new JPanel(); cardLayout new CardLayout(); contentPanel.setLayout(cardLayout); // 添加所有功能面板以字符串為標(biāo)識(shí)符 contentPanel.add(new LoginPanel(this), login); contentPanel.add(new MainDashboardPanel(this), dashboard); contentPanel.add(new AddEmployeePanel(this), addEmployee); contentPanel.add(new QuerySalaryPanel(this), querySalary); // ... 其他面板 }所有面板通過this引用傳遞MainForm實(shí)例實(shí)現(xiàn)跨面板通信。例如點(diǎn)擊“查詢工資”按鈕時(shí)MainDashboardPanel調(diào)用// MainDashboardPanel.java private void onQueryClick() { mainForm.showCard(querySalary); // 切換到查詢面板 queryPanel.loadAllEmployees(); // 觸發(fā)數(shù)據(jù)加載 }這種設(shè)計(jì)避免了全局靜態(tài)變量符合 Swing 單線程規(guī)則所有 UI 更新必須在 Event Dispatch Thread 中執(zhí)行也便于單元測(cè)試——每個(gè)面板可獨(dú)立實(shí)例化并注入 mock 的MainForm。3. 從零部署JDK、MySQL、IDE 三環(huán)境聯(lián)調(diào)實(shí)操指南3.1 JDK 8u2XX 環(huán)境驗(yàn)證與 CLASSPATH 構(gòu)建本項(xiàng)目編譯目標(biāo)為1.8需確認(rèn) JDK 版本java -version # 輸出應(yīng)為java version 1.8.0_381 或類似 javac -version # 輸出應(yīng)為javac 1.8.0_381若版本不符需設(shè)置JAVA_HOME并更新PATH。重點(diǎn)檢查CLASSPATH是否包含所有依賴 JAR# Linux/macOS 檢查方式Windows 類似 echo $CLASSPATH # 正確輸出應(yīng)包含項(xiàng)目根目錄下的 lib/ 路徑例如 # /path/to/Goat_SalaryManageSystem-SMS/lib/jxl.jar:/path/to/.../lib/mysql-connector-java-8.0.26.jar若缺失需在 IDE 或命令行中顯式添加。IntelliJ IDEA 中操作路徑File → Project Structure → Modules → Dependencies → → JARs or directories選擇lib/文件夾。3.2 MySQL 8.0.26 本地服務(wù)配置要點(diǎn)僅安裝 MySQL 不夠必須完成三項(xiàng)配置3.2.1 創(chuàng)建專用數(shù)據(jù)庫(kù)用戶非 root直接用 root 連接存在安全風(fēng)險(xiǎn)且部分學(xué)校機(jī)房禁用 root 遠(yuǎn)程登錄。創(chuàng)建最小權(quán)限用戶-- 登錄 MySQL 后執(zhí)行 CREATE USER sms_userlocalhost IDENTIFIED BY StrongPass123!; CREATE DATABASE salary_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; GRANT SELECT, INSERT, UPDATE, DELETE ON salary_db.* TO sms_userlocalhost; FLUSH PRIVILEGES;此用戶僅對(duì)salary_db擁有 CRUD 權(quán)限無法創(chuàng)建庫(kù)或查看其他數(shù)據(jù)庫(kù)。3.2.2 修改 MySQL 配置文件啟用遠(yuǎn)程訪問可選若需從其他機(jī)器訪問如宿舍電腦連實(shí)驗(yàn)室服務(wù)器編輯my.cnfLinux或my.iniWindows[mysqld] bind-address 0.0.0.0 # 允許所有 IP 訪問 port 3306 # 確保 skip-networking 未啟用重啟 MySQL 服務(wù)后用sms_user賬號(hào)從外部連接測(cè)試mysql -h 192.168.1.100 -P 3306 -u sms_user -p salary_db3.2.3 驗(yàn)證 JDBC 連接字符串有效性項(xiàng)目中數(shù)據(jù)庫(kù)連接配置位于src/sdms/db/DatabaseConnection.javaprivate static final String URL jdbc:mysql://localhost:3306/salary_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue; private static final String USER sms_user; private static final String PASSWORD StrongPass123!;參數(shù)含義useSSLfalse禁用 SSL解決 8.0.26 驅(qū)動(dòng)兼容性問題serverTimezoneAsia/Shanghai避免The server time zone value ... is unrecognized錯(cuò)誤allowPublicKeyRetrievaltrue配合mysql_native_password認(rèn)證方式注意若修改了 MySQL 端口如改為 3307必須同步修改URL中的:3306部分否則連接超時(shí)。3.3 IntelliJ IDEA 導(dǎo)入與運(yùn)行配置導(dǎo)入步驟不能直接Open項(xiàng)目根目錄必須按 Maven 結(jié)構(gòu)識(shí)別啟動(dòng) IDEA →Open→ 選擇Goat_SalaryManageSystem-SMS文件夾當(dāng)提示 “Import project from external model” 時(shí)勾選 “Create project from existing sources”因本項(xiàng)目無 pom.xml非 Maven 項(xiàng)目在Project SDK步驟中選擇已安裝的 JDK 1.8完成后右鍵src/sdms/GoatSalaryManageSystem.java→Run GoatSalaryManageSystem.main()若首次運(yùn)行報(bào)ClassNotFoundException: com.mysql.cj.jdbc.Driver說明mysql-connector-java-8.0.26.jar未加入模塊依賴右鍵項(xiàng)目 →Open Module Settings→Modules→Dependencies→→JARs or directories→ 選擇lib/mysql-connector-java-8.0.26.jar4. 核心業(yè)務(wù)邏輯深度解析工資計(jì)算引擎如何規(guī)避浮點(diǎn)數(shù)精度陷阱4.1 工資條生成中的 BigDecimal 實(shí)戰(zhàn)應(yīng)用員工月工資 基本工資 績(jī)效獎(jiǎng)金 補(bǔ)貼 - 社??劭?- 個(gè)稅 - 缺勤扣款。若用double計(jì)算8500.50 2000.00 - 1200.35可能產(chǎn)生9299.149999999999導(dǎo)致銀行代發(fā)失敗。本項(xiàng)目在src/sdms/model/SalaryCalculator.java中全程使用BigDecimalpublic class SalaryCalculator { private static final MathContext MC new MathContext(2, RoundingMode.HALF_UP); public BigDecimal calculateTotalSalary(Employee emp, AttendanceRecord att) { BigDecimal base new BigDecimal(emp.getBaseSalary().toString()); BigDecimal bonus new BigDecimal(emp.getBonus().toString()); BigDecimal allowance new BigDecimal(emp.getAllowance().toString()); BigDecimal deduction calculateDeductions(emp, att); return base.add(bonus).add(allowance).subtract(deduction, MC); } private BigDecimal calculateDeductions(Employee emp, AttendanceRecord att) { BigDecimal socialSecurity new BigDecimal(emp.getSocialSecurity().toString()); BigDecimal tax calculateTax(base.add(bonus).add(allowance)); BigDecimal absence calculateAbsenceDeduction(att); return socialSecurity.add(tax).add(absence); } }關(guān)鍵點(diǎn)所有double字段如baseSalary在參與計(jì)算前必須通過toString()轉(zhuǎn)為BigDecimal避免new BigDecimal(8500.50)因二進(jìn)制浮點(diǎn)表示誤差引入8500.499999999999MathContext(2, RoundingMode.HALF_UP)確保所有中間結(jié)果保留 2 位小數(shù)并四舍五入如1200.345→1200.35subtract(deduction, MC)顯式傳入 MathContext保證減法也遵循相同精度規(guī)則4.2 Excel 導(dǎo)出模塊的 jxl.jar 兼容性處理項(xiàng)目使用jxl.jarJExcelAPI導(dǎo)出工資條為.xls格式因其輕量且無需 Office 環(huán)境。但該庫(kù)已停止維護(hù)與 JDK 9 的模塊化存在沖突。解決方案是在src/sdms/util/ExcelExporter.java中添加異常兜底public void exportToExcel(ListSalaryRecord records, String filePath) { try (WritableWorkbook workbook Workbook.createWorkbook(new File(filePath))) { WritableSheet sheet workbook.createSheet(工資明細(xì), 0); // 寫入表頭與數(shù)據(jù)... workbook.write(); } catch (IOException e) { // JDK 9 模塊化導(dǎo)致的 AccessControlException 兜底 if (e instanceof java.security.AccessControlException) { JOptionPane.showMessageDialog(null, 導(dǎo)出失敗JDK 版本過高請(qǐng)使用 JDK 8 運(yùn)行本程序, Excel 導(dǎo)出錯(cuò)誤, JOptionPane.ERROR_MESSAGE); } throw new RuntimeException(Excel 導(dǎo)出異常, e); } }提示若需支持.xlsx格式可替換為 Apache POI但會(huì)增加 5MB 以上 JAR 體積違背課程設(shè)計(jì)“輕量可控”原則。4.3 Swing 表格數(shù)據(jù)刷新的線程安全實(shí)踐當(dāng)用戶在QuerySalaryPanel中點(diǎn)擊“刷新”按鈕時(shí)后臺(tái)查詢可能耗時(shí) 200ms。若直接在 EDTEvent Dispatch Thread中執(zhí)行 JDBC 查詢UI 將凍結(jié)。項(xiàng)目采用SwingWorker實(shí)現(xiàn)異步加載private void refreshTable() { // 禁用按鈕防止重復(fù)提交 refreshButton.setEnabled(false); statusLabel.setText(加載中...); SwingWorkerListSalaryRecord, Void worker new SwingWorker() { Override protected ListSalaryRecord doInBackground() throws Exception { // 在后臺(tái)線程執(zhí)行耗時(shí)的 JDBC 查詢 return salaryService.queryAllSalaries(); } Override protected void done() { try { ListSalaryRecord data get(); // 獲取 doInBackground 返回值 tableModel.setData(data); // 更新 TableModel statusLabel.setText(共 data.size() 條記錄); } catch (Exception e) { JOptionPane.showMessageDialog(null, 查詢失敗 e.getMessage()); } finally { refreshButton.setEnabled(true); } } }; worker.execute(); // 啟動(dòng)異步任務(wù) }此模式確保 UI 響應(yīng)性且done()方法自動(dòng)在 EDT 中執(zhí)行可安全更新tableModel和statusLabel。5. 故障排查手冊(cè)5 類高頻報(bào)錯(cuò)的定位與修復(fù)路徑5.1 連接 MySQL 失敗的逐層診斷表當(dāng)DatabaseConnection.getConnection()拋出異常時(shí)按此順序排查報(bào)錯(cuò)信息關(guān)鍵詞可能原因驗(yàn)證命令修復(fù)動(dòng)作Communications link failureMySQL 服務(wù)未啟動(dòng)或端口被占用netstat -an | grep 3306Linuxtelnet localhost 3306Windows啟動(dòng) MySQL 服務(wù)sudo systemctl start mysqldCentOSbrew services start mysqlmacOSUnknown database salary_dbinit.sql未執(zhí)行或執(zhí)行失敗mysql -u root -p -e SHOW DATABASES;手動(dòng)執(zhí)行mysql -u root -p init.sqlAccess denied for user sms_userlocalhost用戶密碼錯(cuò)誤或權(quán)限不足mysql -u root -p -e SELECT User,Host FROM mysql.user;重置密碼ALTER USER sms_userlocalhost IDENTIFIED BY newpass;No suitable driver found for jdbc:mysql://...mysql-connector-java-8.0.26.jar未加載查看 IDEAProject Structure → Libraries將 JAR 拖入lib/文件夾并重新添加為依賴The server time zone value ... is unrecognizedMySQL 時(shí)區(qū)配置與 JDBC 參數(shù)不匹配mysql -u root -p -e SELECT global.time_zone, session.time_zone;在 JDBC URL 中添加serverTimezoneAsia/Shanghai5.2 Swing 界面亂碼與圖標(biāo)缺失的根因分析中文顯示為方塊、按鈕圖標(biāo)不出現(xiàn)本質(zhì)是資源路徑與類加載器問題5.2.1 中文亂碼字體渲染異常根本原因是 JVM 啟動(dòng)參數(shù)未指定字體配置。在 IDEA 運(yùn)行配置中添加 VM options-Dfile.encodingUTF-8 -Dsun.jnu.encodingUTF-8 -Dawt.useSystemAAFontSettingslcd若仍無效在MainForm.java構(gòu)造函數(shù)中強(qiáng)制設(shè)置系統(tǒng)字體UIManager.put(Label.font, new Font(Microsoft YaHei, Font.PLAIN, 14)); UIManager.put(Button.font, new Font(Microsoft YaHei, Font.BOLD, 14)); SwingUtilities.updateComponentTreeUI(this); // 刷新所有組件5.2.2 圖標(biāo)文件PNG加載失敗項(xiàng)目中image/文件夾存放 15 個(gè) PNG 按鈕圖標(biāo)加載代碼為ImageIcon icon new ImageIcon(getClass().getResource(/image/add.png));常見失敗原因image/文件夾未被 IDEA 識(shí)別為資源根目錄 → 右鍵image→Mark Directory as → Resources Root圖標(biāo)文件名大小寫錯(cuò)誤Linux 區(qū)分大小寫→ 檢查add.png是否實(shí)際為Add.pnggetResource()返回 null → 在加載處添加判空URL iconUrl getClass().getResource(/image/add.png); if (iconUrl null) { System.err.println(圖標(biāo)未找到/image/add.png請(qǐng)檢查 resources 目錄結(jié)構(gòu)); icon new ImageIcon(); // 返回空?qǐng)D標(biāo)避免 NPE } else { icon new ImageIcon(iconUrl); }5.3 編譯報(bào)錯(cuò)package javax.swing does not exist的終極解法此錯(cuò)誤表明編譯器未識(shí)別 Swing API通常因 JDK 配置錯(cuò)誤IDEA 中File → Project Structure → Project → Project SDK必須指向 JDK非 JRE安裝路徑如/Library/Java/JavaVirtualMachines/jdk1.8.0_381.jdk/Contents/Home命令行中確認(rèn)javac與java指向同一 JDKwhich javac which java ls -la $(dirname $(dirname $(which javac)))/jre # 輸出應(yīng)顯示 jre 目錄存在證明是完整 JDK若javac指向 OpenJDK 而java指向 Oracle JDK需統(tǒng)一環(huán)境變量export JAVA_HOME/path/to/jdk1.8.0_381 export PATH$JAVA_HOME/bin:$PATH注意不要嘗試手動(dòng)復(fù)制rt.jar或修改ext/目錄——這是反模式且 JDK 9 已移除rt.jar。5.4 工資計(jì)算結(jié)果偏差 0.01 元的調(diào)試技巧當(dāng)calculateTotalSalary()返回值比預(yù)期少 0.01大概率是MathContext未全局生效。在SalaryCalculator.java中插入調(diào)試日志public BigDecimal calculateTotalSalary(...) { System.out.println(Base: base , Bonus: bonus , Allowance: allowance); BigDecimal sum base.add(bonus).add(allowance); System.out.println(Sum before deduction: sum); BigDecimal result sum.subtract(deduction, MC); System.out.println(Final result: result); return result; }觀察輸出是否出現(xiàn)9299.149999999999類似值。若存在說明某處BigDecimal構(gòu)造未用toString()需定位到具體字段如emp.getBonus()返回double并修正為// 錯(cuò)誤寫法引入浮點(diǎn)誤差 new BigDecimal(emp.getBonus()) // 正確寫法精確構(gòu)造 new BigDecimal(Double.toString(emp.getBonus()))本文還有配套的精品資源點(diǎn)擊獲取