简介:这是一套基于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 默认只允许localhost;GRANT权限未包含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 Dependencies:File → Project Structure → Modules → Dependencies →
+→ JARs or directories → 选中项目根目录下的lib/mysql-connector-java-8.0.29.jar; - 配置运行参数 VM options:Edit Configurations → VM options 输入
-Dfile.encoding=UTF-8,否则 Windows 下读取中文提示框会乱码; - 检查 db.properties 路径:源码中数据库连接信息硬编码在
src/main/resources/db.properties,内容为:
注意url=jdbc:mysql://localhost:3306/busmanage?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username=admin password=admin123serverTimezone=Asia/Shanghai是解决The server time zone value '...' is unrecognized报错的后悔药,allowPublicKeyRetrieval=true是绕过 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) Vector<String> row = new Vector<>(); row.add(plate); row.add(brand); row.add(model); tableModel.addRow(row); // ← 这行保证GUI立即响应,无需reload }vehicleDAO.insert()底层调用的是预编译 SQL:
public 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(); List<String> vehicles = vehicleDAO.findByLine(selectedLine); // 查询该线路分配的车辆 vehicleComboBox.setModel(new DefaultComboBoxModel<>(vehicles.toArray(new String[0]))); });提交排班前,执行严格校验:
private boolean validateSchedule(Schedule s) { // 校验1:同一车辆当天是否已有排班 List<Schedule> 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 name='test' WHERE id=1; -- 报错:ERROR 1142 (42000): UPDATE command denied to user 'dispatcher'@'localhost' for table 'employee' -- 尝试删线路也会失败 DELETE FROM busmanage.line WHERE id=1; -- 同样权限拒绝这种基于 MySQL 原生 GRANT 的权限控制,比在 Java 代码里if(role.equals("admin"))更底层、更可靠。课设中用数据库权限代替代码权限,是值得学习的工程取舍。
5.3 验证员工登录后仅能看到自己的排班:从 GUI 到 SQL 的穿透式检查
员工登录 GUI 后,“我的排班”面板应只显示该员工 ID 对应的记录。这背后是ScheduleDAO.findByStaffId(String staffId)方法在起作用。我们反向验证:先在 GUI 中记下当前员工姓名(如“张三”),然后查employee表得到其id:
SELECT id FROM busmanage.employee WHERE name='张三'; -- 假设返回 105再查schedule表:
SELECT * FROM busmanage.schedule WHERE staff_id = 105; -- GUI 中显示的排班记录,必须与该 SQL 结果完全一致(包括日期、班次、车辆)如果 GUI 显示了staff_id=106的记录,说明 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.bat:@echo off cd /d "%~dp0mariadb\bin" mysqld --defaults-file="../my.ini" --console pause - 用 Launch4j 配置:
jar选out/artifacts/BusManage_jar/BusManage.jar;Executable输出BusManage.exe;Header选GUI;JRE设置Min version: 1.8.0;在Custom commands添加:start "" "%~dp0start_db.bat"
这样双击BusManage.exe会先拉起 MariaDB,再启动 Java GUI。虽非生产级,但满足课设“免安装”需求。
6.2 快速定制化三板斧:改Logo、换主题色、增报表导出
| 定制需求 | 修改文件 | 关键代码位置 | 注意事项 |
|---|---|---|---|
| 更换系统Logo | src/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.jar |
6.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 的混乱,根源都在数据库设计的模糊地带。
希望帮到你。
本文还有配套的精品资源,点击获取