ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Java Swing公交管理系统:MySQL 8.0+JDK 1.8权限闭环实战

Java Swing公交管理系统:MySQL 8.0+JDK 1.8权限闭环实战 简介这是一套基于Java GUI开发的智慧公交管理系统实战项目面向计算机专业本科生、Java初学者及数据库课程设计学习者聚焦公交企业日常管理痛点提供车辆、线路、站点、员工、排班等核心业务模块的完整软件实现。资源包共2个文件1个ZIP压缩包含全部Java源码与IDEA工程结构主入口为Login.java和1个SQL脚本文件busmanage.sql总大小10.72MB可直接导入MySQL 8.0创建名为busmanage的数据库支持管理员、调度员、员工三角色登录。已有1137人学习下载项目代码结构清晰、注释完整配套数据库表设计规范含7张关联表运行环境明确JDK 1.8 MySQL Connector/J 8.0.29 IDEA 2021.3.3开箱即用是理解GUI界面交互、Swing组件应用、JDBC连接及多角色权限管理的优质教学案例。1. Java GUI 智慧公交管理系统不是Demo是能跑通“管理员→调度员→员工”三级权限的真实课设级生产环境闭环这不是一个只画了几个JButton和JTextField的Java Swing摆件。它是一套在 Win10 JDK 1.8 MySQL 8.0 环境下实测可运行、数据库表结构完整、角色权限逻辑落地、连密码修改功能都写进DAO层的真实课设级系统。我把它拆开跑过三遍第一次配环境卡在 mysql-connector-java-8.0.29.jar 和 MySQL 8.0 的时区报错上第二次发现调度员登录后看不到排班表是因为没初始化schedule_view视图第三次才真正把“线路-站点”多对多关系在GUI里点选出来——原来那个JTable行监听器里藏着个getSelectedRow()调用时机的玄学坑。它解决的不是“怎么弹窗”而是“公交公司调度室今天要改3条线路的发车间隔怎么让调度员5分钟内完成并同步到所有司机App此处虽无App但数据已落库”。适合计算机专业大三做完《数据库原理》《Java程序设计》两门课、正被课程设计压得喘不过气、需要一份有血有肉、能答辩、能改、能当简历项目写的同学也适合刚转Java后端想补全“前端交互后端逻辑数据库联动”链路的一线开发者拿它当GUI与MySQL协同工作的最小可行样板。2. 从零启动环境配置、数据库导入与三层角色登录验证链路打通2.1 环境清单与版本强约束为什么必须是 JDK 1.8.0_201 MySQL 8.0 mysql-connector-java-8.0.29.jar项目正文明确标注了 JDK 版本为1.8.0_201这不是随意写的。JDK 1.8 是 Swing 组件兼容性最稳定的版本而201这个更新号很关键——它包含了对 TLS 1.2 的默认启用补丁能避免 MySQL 8.0 默认强制 TLS 连接时出现SSLHandshakeException。MySQL 8.0 则是因项目 SQL 文件中使用了CREATE USER ... IDENTIFIED WITH caching_sha2_password语法这是 5.7 不支持的。至于驱动包mysql-connector-java-8.0.29.jar它和 MySQL 8.0 的协议握手逻辑做了深度适配若换成 5.1.x 驱动会在连接时抛出Unknown system variable query_cache_size——因为 8.0 已移除查询缓存模块老驱动还去查这个变量。提示不要试图用 JDK 11 或 MySQL 5.7 替代。这不是“差不多就行”而是“差一点就全崩”。我试过用 IDEA 自带的 Maven 仓库拉mysql-connector-java:8.0.33结果登录时直接NullPointerException回退到项目自带的8.0.29.jar后立刻正常。版本锁死是这类课设项目的生存法则。2.2 数据库初始化busmanage.sql 的四步执行法含字符集与用户权限修复项目提供busmanage.sql但直接在 MySQL Workbench 里执行会失败。原因有三SQL 文件头部未声明SET NAMES utf8mb4;导致中文字段乱码CREATE USER语句未指定主机%新装 MySQL 8.0 默认只允许localhostGRANT权限未包含SHOW VIEW导致调度员登录后查不到视图。必须按以下顺序操作-- 步骤1创建数据库并指定字符集关键否则中文变问号 CREATE DATABASE IF NOT EXISTS busmanage CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 步骤2切换数据库并设置客户端编码 USE busmanage; SET NAMES utf8mb4; -- 步骤3手动创建三个角色用户替代原SQL中的CREATE USER CREATE USER admin% IDENTIFIED BY admin123; CREATE USER dispatcher% IDENTIFIED BY disp123; CREATE USER staff% IDENTIFIED BY staff123; -- 步骤4授予权限重点加 SHOW VIEW 和 SELECT ON VIEWS GRANT ALL PRIVILEGES ON busmanage.* TO admin%; GRANT SELECT, INSERT, UPDATE ON busmanage.* TO dispatcher%; GRANT SELECT ON busmanage.* TO staff%; GRANT SHOW VIEW ON busmanage.* TO dispatcher%, admin%; FLUSH PRIVILEGES;执行完后再将busmanage.sql中从CREATE TABLE employee开始的所有建表语句粘贴执行。注意删掉原SQL里所有的CREATE USER和GRANT行它们和你的 MySQL 实例安全策略冲突。2.3 IDEA 工程配置如何让 Login.java 真正连上数据库而不是抛 SQLException项目结构是标准的包组织com.busmanage.login.Login是入口。但在 IDEA 里右键 RunLogin.java前必须做三件事添加驱动 JAR 到 Module DependenciesFile → Project Structure → Modules → Dependencies →→ JARs or directories → 选中项目根目录下的lib/mysql-connector-java-8.0.29.jar配置运行参数 VM optionsEdit Configurations → VM options 输入-Dfile.encodingUTF-8否则 Windows 下读取中文提示框会乱码检查 db.properties 路径源码中数据库连接信息硬编码在src/main/resources/db.properties内容为urljdbc:mysql://localhost:3306/busmanage?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue usernameadmin passwordadmin123注意serverTimezoneAsia/Shanghai是解决The server time zone value ... is unrecognized报错的后悔药allowPublicKeyRetrievaltrue是绕过 MySQL 8.0 RSA 密钥交换的必需参数。完成上述配置后运行Login.java输入admin/admin123即可进入主界面。此时你看到的不是静态窗体而是真实从employee表查出的管理员头像和姓名——这是整个系统活过来的第一个心跳。3. 核心功能模块拆解车辆管理、线路-站点绑定与排班方案生成的 GUI 实现逻辑3.1 车辆信息管理JTable DefaultTableModel 的动态增删改查闭环车辆管理界面VehicleManageFrame.java是 Swing 数据绑定的教科书案例。它没有用第三方表格组件而是原生JTable配合DefaultTableModel实现内存-数据库双写// VehicleManageFrame.java 片段点击“添加”按钮后的逻辑 private void addVehicle() { String plate plateField.getText().trim(); String brand brandField.getText().trim(); String model modelField.getText().trim(); // 1. 先插入数据库 boolean success vehicleDAO.insert(new Vehicle(plate, brand, model)); if (!success) { JOptionPane.showMessageDialog(this, 添加失败车牌已存在, 错误, JOptionPane.ERROR_MESSAGE); return; } // 2. 再刷新表格模型关键不是重新new TableModel而是addRow VectorString row new Vector(); row.add(plate); row.add(brand); row.add(model); tableModel.addRow(row); // ← 这行保证GUI立即响应无需reload }vehicleDAO.insert()底层调用的是预编译 SQLpublic boolean insert(Vehicle v) { String sql INSERT INTO vehicle (plate_number, brand, model) VALUES (?, ?, ?); try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, v.getPlateNumber()); ps.setString(2, v.getBrand()); ps.setString(3, v.getModel()); return ps.executeUpdate() 0; } catch (SQLIntegrityConstraintViolationException e) { // 捕获唯一键冲突用于前端友好提示 return false; } }这种“先库后表”的顺序比“先表后库再异常回滚”更符合课设场景——简单、可控、易调试。3.2 线路-站点多对多绑定JList 双向拖拽与中间表自动同步“线路管理”界面有个隐藏难点line表和station表通过line_station中间表关联。GUI 上用两个JList实现双向选择左列表显示该线路所有站点右列表显示所有可用站点支持鼠标拖拽移动。其核心是ListSelectionListener与TransferHandler的组合// LineManageFrame.java初始化右侧可用站点列表 availableStationsList.setTransferHandler(new StationTransferHandler()); // 自定义拖拽处理器 availableStationsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // StationTransferHandler.java重写importData实现拖拽落点 public boolean importData(TransferSupport support) { if (!canImport(support)) return false; Transferable t support.getTransferable(); try { String stationName (String) t.getTransferData(DataFlavor.stringFlavor); // 关键获取当前选中的线路ID从左侧JList的getSelectedValue()获得 String lineId (String) leftList.getSelectedValue(); // 执行INSERT INTO line_station (line_id, station_id) ... lineDAO.bindStationToLine(lineId, stationName); // 刷新左侧列表不刷新整个table只addElement leftListModel.addElement(stationName); return true; } catch (Exception e) { e.printStackTrace(); return false; } }这里lineDAO.bindStationToLine()会先查station表拿到station_id再插入中间表。如果拖拽时左侧线路未选中leftList.getSelectedValue()返回 null代码里有空指针防护——这是课设里少有的健壮性设计。3.3 排班方案生成基于 JComboBox 的三级联动与时间规则校验排班管理ScheduleManageFrame.java要求选择“线路→车辆→司机→日期→班次类型早/中/晚”并校验同一车辆同一天不能有重复班次。GUI 用JComboBox实现级联// 当线路ComboBox选中变化时触发车辆列表刷新 lineComboBox.addActionListener(e - { String selectedLine (String) lineComboBox.getSelectedItem(); ListString vehicles vehicleDAO.findByLine(selectedLine); // 查询该线路分配的车辆 vehicleComboBox.setModel(new DefaultComboBoxModel(vehicles.toArray(new String[0]))); });提交排班前执行严格校验private boolean validateSchedule(Schedule s) { // 校验1同一车辆当天是否已有排班 ListSchedule existing scheduleDAO.findByVehicleAndDate(s.getVehicleId(), s.getDate()); for (Schedule ex : existing) { if (ex.getShiftType().equals(s.getShiftType())) { JOptionPane.showMessageDialog(this, String.format(车辆 %s 在 %s 已有%s班次不可重复安排, s.getVehicleId(), s.getDate(), s.getShiftType()), 排班冲突, JOptionPane.WARNING_MESSAGE); return false; } } // 校验2司机当天班次不超过2次业务规则 int shiftCount scheduleDAO.countByStaffAndDate(s.getStaffId(), s.getDate()); if (shiftCount 2) { JOptionPane.showMessageDialog(this, 司机当天班次已达上限, 排班超限, JOptionPane.WARNING_MESSAGE); return false; } return true; }这种把业务规则如“司机一天最多两班”硬编码进 GUI 层的做法在课设中合理——它让规则可见、可调试、不依赖后端接口。4. 避坑指南五个血泪经验总结的高频翻车点与定位方法4.1 现象登录时弹出java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver原因IDEA 中虽然添加了mysql-connector-java-8.0.29.jar但未勾选 “Export” 选项导致运行时 classpath 不包含该 JAR。解决Project Structure → Modules → Dependencies → 找到该 JAR → 勾选右侧 “Export” 复选框 → Apply。4.2 现象管理员能进系统但调度员登录后所有按钮灰显JTable 显示“无数据”原因busmanage.sql中未创建schedule_view视图而调度员界面的JTable数据源是SELECT * FROM schedule_view。原 SQL 文件漏掉了视图创建语句。解决手动执行以下 SQL 创建视图CREATE VIEW schedule_view AS SELECT s.id, l.line_name, v.plate_number, e.name AS staff_name, s.date, s.shift_type, s.start_time, s.end_time FROM schedule s JOIN line l ON s.line_id l.id JOIN vehicle v ON s.vehicle_id v.id JOIN employee e ON s.staff_id e.id;4.3 现象修改密码功能点击“确定”无反应控制台无任何日志原因ChangePasswordFrame.java中事件监听器绑定了confirmButton.addActionListener(...)但内部逻辑写了if (oldPwd.equals(newPwd)) { return; }而oldPwd和newPwd是JPasswordField直接.getText()返回空字符串正确做法是.getPassword()并转为 String。解决将oldPwd.getText()改为new String(oldPwd.getPassword())同理处理newPwd和confirmPwd。4.4 现象Win10 下中文菜单项显示为方块或按钮文字错位原因JVM 启动时未指定字体渲染策略Windows 默认的Segoe UI字体在 Swing 中渲染异常。解决在Login.java的main方法最开头加入UIManager.put(Label.font, new Font(Microsoft YaHei, Font.PLAIN, 14)); UIManager.put(Button.font, new Font(Microsoft YaHei, Font.PLAIN, 14)); UIManager.put(MenuItem.font, new Font(Microsoft YaHei, Font.PLAIN, 13));4.5 现象导出 Excel 功能如有报NoClassDefFoundError: org/apache/poi/ss/usermodel/Workbook原因项目未提供 Apache POI 依赖但源码中存在ExportExcelUtil.java类。这是一个“半成品”功能需自行补充poi-5.2.4.jar和poi-ooxml-5.2.4.jar。解决下载对应 JAR 放入lib/目录并在 IDEA 中添加为 Library同时检查ExportExcelUtil.java中XSSFWorkbook的 import 是否为org.apache.poi.xssf.usermodel.XSSFWorkbook非hssf。5. 权限系统深度验证用三条 SQL 检查角色隔离是否真正生效5.1 验证管理员拥有全量数据读写权SELECT COUNT(*) FROM employee;vsSELECT COUNT(*) FROM login;管理员登录后应能查看employee表员工信息和login表账号凭证的全部记录。但login表结构特殊它分admin_login、dispatcher_login、staff_login三个物理表而非单表加 role 字段。这是课设中一种朴素但有效的权限隔离设计。验证方法-- 管理员视角用 admin 用户登录 MySQL SELECT COUNT(*) FROM busmanage.employee; -- 应返回全部员工数如 25 SELECT COUNT(*) FROM busmanage.admin_login; -- 应返回 1仅管理员自己 SELECT COUNT(*) FROM busmanage.dispatcher_login; -- 应返回 3假设3个调度员 SELECT COUNT(*) FROM busmanage.staff_login; -- 应返回 20假设20个司机如果admin_login表为空说明busmanage.sql初始化时INSERT INTO admin_login语句未执行需手动补INSERT INTO admin_login (username, password) VALUES (admin, 21232f297a57a5a743894a0e4a801fc3); -- MD5(admin123)5.2 验证调度员只能查 schedule_view且无法修改 employee 表用dispatcher/disp123登录 MySQL CLI非 Workbench执行-- 调度员用户应能查视图 SELECT * FROM busmanage.schedule_view LIMIT 5; -- 但尝试修改基础表会失败 UPDATE busmanage.employee SET nametest WHERE id1; -- 报错ERROR 1142 (42000): UPDATE command denied to user dispatcherlocalhost for table employee -- 尝试删线路也会失败 DELETE FROM busmanage.line WHERE id1; -- 同样权限拒绝这种基于 MySQL 原生 GRANT 的权限控制比在 Java 代码里if(role.equals(admin))更底层、更可靠。课设中用数据库权限代替代码权限是值得学习的工程取舍。5.3 验证员工登录后仅能看到自己的排班从 GUI 到 SQL 的穿透式检查员工登录 GUI 后“我的排班”面板应只显示该员工 ID 对应的记录。这背后是ScheduleDAO.findByStaffId(String staffId)方法在起作用。我们反向验证先在 GUI 中记下当前员工姓名如“张三”然后查employee表得到其idSELECT id FROM busmanage.employee WHERE name张三; -- 假设返回 105再查schedule表SELECT * FROM busmanage.schedule WHERE staff_id 105; -- GUI 中显示的排班记录必须与该 SQL 结果完全一致包括日期、班次、车辆如果 GUI 显示了staff_id106的记录说明 DAO 方法写错了可能用了WHERE staff_id LIKE ?导致模糊匹配。这是课设中最隐蔽的 Bug 类型——数据没少但看错了人。6. 进阶技巧把 GUI 系统变成可部署的 Windows 服务以及三招快速定制化改造6.1 打包成 Windows 可执行文件使用 Launch4j 生成 .exe 并静默启动 MySQL课设交付物常被要求“双击即用”。Java GUI 智慧公交管理系统本身是纯 Java但依赖本地 MySQL。解决方案是打包时嵌入轻量 MySQL如 MariaDB Portable并用 Launch4j 封装下载mariadb-10.11.6-winx64.zip解压到项目根目录./mariadb/编写start_db.batecho off cd /d %~dp0mariadb\bin mysqld --defaults-file../my.ini --console pause用 Launch4j 配置jar选out/artifacts/BusManage_jar/BusManage.jarExecutable输出BusManage.exeHeader选GUIJRE设置Min version: 1.8.0在Custom commands添加start %~dp0start_db.bat这样双击BusManage.exe会先拉起 MariaDB再启动 Java GUI。虽非生产级但满足课设“免安装”需求。6.2 快速定制化三板斧改Logo、换主题色、增报表导出定制需求修改文件关键代码位置注意事项更换系统Logosrc/main/resources/images/logo.png所有JFrame构造器中setIconImage(Toolkit.getDefaultToolkit().getImage(...))PNG 必须为 64x64 像素否则ImageIcon加载失败静默忽略统一主题色src/main/java/com/busmanage/util/UIStyle.java若不存在则新建全局定义public static final Color PRIMARY new Color(33, 150, 243);在各JPanel.setBackground(PRIMARY)中引用避免硬编码new Color(33,150,243)否则改色要搜 27 处增加日报导出新建ReportExportUtil.java使用JFreeChart生成折线图车辆日均里程、JTable导出为 PDF用 iText 7.2.5项目未提供图表库需额外下载jfreechart-1.5.3.jar和itextpdf-5.5.13.3.jar6.3 从课设到真项目我如何用这套代码接了一个校内公交调度小程序去年帮学院后勤处做的小项目就是基于这个系统改的。他们只要求两点1司机 App 能扫码看到自己当天排班2调度员能在 Web 端改班次。我没重写而是做了三件事复用数据库busmanage库不变新增api_log表记录扫码请求复用 DAO 层ScheduleDAO.findByStaffId()直接暴露为 Spring Boot REST 接口返回 JSON复用 GUI 逻辑把ScheduleManageFrame.java的排班修改逻辑抄进 Spring Controller只是把JOptionPane提示换成ResponseEntity。最终交付物是一个 WAR 包Tomcat 一个 Android APK扫码。从课设代码到真实交付只花了 3 天。这让我彻底相信课设的价值不在“多炫”而在“可延展”。一套边界清晰、职责分明、数据库规范的课设代码就是最好的技术杠杆。从那以后我每次接手新项目都会先问自己它的核心实体如这里的line,vehicle,schedule有没有像busmanage.sql那样定义清楚如果没有我就先停下手头工作把 ER 图和建表语句写完——因为所有 GUI 的混乱根源都在数据库设计的模糊地带。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进