恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Java+Swing+MySQL停车场管理系统:JDBC连接与计费逻辑实战详解

  • 首页
  • 资讯中心
  • /
  • Java+Swing+MySQL停车场管理系统:JDBC连接与计费逻辑实战详解

相关资讯

560003报错别慌:3步修复+完整示例,老鸟的避坑指南 2026/9/23 2:15:37
GitHub日榜解读:从趋势信号到高效实践的开源开发者指南 2026/9/23 2:15:37
3步搞定交配姿势,这份速查手册让项目不再卡壳 2026/9/23 2:15:37

最新资讯

GitHub开源AI热榜项目评估指南:从筛选到落地的完整方法论
基于Flask和Django的亚健康大数据分析平台构建
5个惊起一滩鸥鹭最佳实践:源码拆解项目搭建痛点
美团骑手app仿写入门到精通: 3步搞定跑不通的代码
Cherry Studio与OpenClaw 2026协同工作流优化指南
埋点工具选型三类分层:轻量自助、中台协同、企业治理

今日推荐

3招搞定手机怎么下载微信面试难题实战项目解析
清单计价规范2013手写实现:3个血泪坑教你避开90%的返工
搞定msn股票中国数据延迟:实战项目里省下的200ms

本周热门

BrewUI:给Homebrew套上图形界面,让macOS软件包管理更简单
BrewUI:让Homebrew包管理变得可视化与高效
公式与文本对齐全攻略:从Word到LaTeX的实用技巧

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

Java+Swing+MySQL停车场管理系统:JDBC连接与计费逻辑实战详解

发布时间:2026/9/23 2:15:37
Java+Swing+MySQL停车场管理系统:JDBC连接与计费逻辑实战详解 简介这是一套基于Java Swing与MySQL的停车场管理系统项目源码面向Java课程设计、毕业设计也适合希望练习图形界面与数据库开发的中级学习者。系统围绕管理员与车辆两大核心数据表实现用户登录注册、车辆信息浏览、车辆入场/出场登记、删除和查询等常见功能运行于JDK 1.8与MySQL 8.0.13环境。压缩包共49个文件总大小约1.96MB结构上区分src源码目录、bin编译输出、db数据库脚本及项目配置其中12个Java源代码对应各功能窗口31个class文件为编译后的类文件1份SQL脚本用于创建管理员表和车辆表另有classpath、project、prefs等工程配置及运行截图便于对照界面效果。当前已有466人学习对正在完成课程设计或毕业设计的读者很有参考价值通过源码与截图对照可快速理解登录注册、车辆出入场登记、信息查询删除等功能的实际写法与Swing事件驱动流程也便于二次开发。整体具备完整可运行性适合直接用于课程设计或进一步添加计费、车位管理等扩展功能。1. 先想清楚一件事这个系统到底打算替谁管停车场“基于JavaSwingmysql停车场管理系统”这串字几乎每个做了Java课程设计的人都不陌生。它看起来不算新技术——没有Spring Boot、没有Redis、没有微服务甚至Java Swing还带着一股“老古董”的味道。但如果你真的动手写过就知道这套组合能在一千行左右的代码里把跟数据库相关的所有基本功全练一遍JDBC连接管理、SQL的增删改查、事务边界、结果集转对象、界面上数据刷新全是面试八股文里反复出现的内容。它的价值在于没有框架帮你兜底链路短、能查、能跑适合作为第一个能交出去的完整管理系统。这篇文章会把一个最小可用的方案讲透MySQL建库建表、Swing做主界面、JDBC做数据访问、停车计费逻辑、卡号与车位状态流转。你不用一次写完照着章节拆开做每一节都能单独跑通。新手按顺序跟熟手可以直接跳到第5章看那些藏得比较深的坑。2. 为什么是JavaSwingMySQL选型理由和它的边界2.1 这套技术栈的一头一尾都怎么选先处理两个选型问题数据库用MySQL哪个版本Java用哪个JDK。去MySQL官网下载安装包一般不要直接拿最新大版本。比如你机器上装的是Java 8那MySQL用5.7或8.0都是可以的但注意8.0的JDBC驱动名和5.7不一样涉及Class.forName的写法后面代码里会体现。常见做法是选MySQL 8.0因为工作中绝大多数环境已经是8.0起步你写的JDBC代码往后迁移改动最小。JDK方面Swing在Java 8和Java 17里都能正常new出来如果电脑上有多个版本建议给这个项目固定用Java 8原因不是Swing不兼容新版本而是后面如果还要接别的课程设计很多老代码按Java 8写的统一环境少踩坑。另一个容易被忽略的选型问题是JDBC连接要不要自己写。有的同学会直接在业务代码里Class.forName、getConnection、写SQL一个类里全是跟数据库打交道的重复代码后面想换个库或者加日志改动量巨大。我一般建议哪怕是课程设计也独立出一个DBUtil类把连接、关闭、查询、更新四种操作收口后续所有业务模块只面对DBUtil思路清晰很多。这个取舍跟公司里用不用MyBatis是两回事——课程设计的关键是把“写SQL和管连接”这件事自己掌控住。2.2 数据库设计先行车辆表、车位表、计费规则表停车场管理系统核心就三个实体车辆、车位、计费规则。表结构尽量朴素不要为了展示技术堆外键和存储过程。推荐建四张表car车牌、进场时间、出场时间、状态在场/已离场、卡号、关联车位parking_space车位编号、区域、是否占用charge_rule计费规则首小时费用、超时每小时费用、封顶金额、规则生效日期order_record每次停车生成的订单包含进出场时间和费用CREATE TABLE car ( id INT PRIMARY KEY AUTO_INCREMENT, plate VARCHAR(20) NOT NULL, card_no VARCHAR(30) UNIQUE, entry_time DATETIME, exit_time DATETIME, status TINYINT DEFAULT 1 COMMENT 1在场 0离场, space_id VARCHAR(10) ) DEFAULT CHARSETutf8mb4; CREATE TABLE parking_space ( space_id VARCHAR(10) PRIMARY KEY, area VARCHAR(20), status TINYINT DEFAULT 0 COMMENT 0空闲 1占用 ) DEFAULT CHARSETutf8mb4; CREATE TABLE charge_rule ( id INT PRIMARY KEY, first_hour_fee DECIMAL(10,2), hourly_fee DECIMAL(10,2), daily_cap DECIMAL(10,2) ) DEFAULT CHARSETutf8mb4; CREATE TABLE order_record ( id INT PRIMARY KEY AUTO_INCREMENT, plate VARCHAR(20), card_no VARCHAR(30), entry_time DATETIME, exit_time DATETIME, total_fee DECIMAL(10,2), rule_id INT ) DEFAULT CHARSETutf8mb4;逻辑说明car表的status字段是核心状态所有业务查询都围绕它来过滤parking_space表只需要维护占用状态不做历史轨迹计费规则单独成表好处是以后调价不需要改代码改数据库即可。需要说明的是plate没有设为唯一因为同一辆车可能多次进场保留多条记录才符合现实场景判断在场车用status1来筛不能靠plate重复与否。参数说明所有字符串统一utf8mb4避免后面中文乱码问题。DECIMAL用来放金额绝不用FLOAT否则结算时出现0.999999这种数会让人崩溃。card_no加唯一约束是为了保证一张卡同一时间只能停一辆车后面并发章节会展开讲。四张表之间不强制建外键因为课程设计里数据操作基本是单表外键反而会让删除和更新变得啰嗦。3. 用Swing把停车场主界面搭出来布局从哪里起步3.1 主窗口分区操作区、列表区、状态栏Swing做管理系统主界面一般就是经典的三区结构上方放操作按钮进场、出场、查询、刷新中间放车位状态面板或者停车记录表格底部放一个状态栏显示当前时间、在场车数和空闲车位数。不需要复杂布局嵌套右侧做操作按钮左侧做数据展示直接这么画JFrame frame new JFrame(停车场管理系统); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(1000, 600); frame.setLocationRelativeTo(null); // 顶部按钮栏 JPanel topPanel new JPanel(); topPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); topPanel.add(new JButton(车辆进场)); topPanel.add(new JButton(车辆出场)); topPanel.add(new JButton(刷新车位)); topPanel.add(new JButton(查询记录)); // 中间车位面板用GridLayout绘制车位格子 JPanel spacePanel new JPanel(new GridLayout(4, 8, 5, 5)); for (int i 1; i 32; i) { JLabel label new JLabel(P i, SwingConstants.CENTER); label.setOpaque(true); label.setBackground(Color.GREEN); // 绿色表示空闲 label.setBorder(BorderFactory.createLineBorder(Color.GRAY)); spacePanel.add(label); } // 底部状态栏 JLabel statusBar new JLabel( 在场车辆0 空闲车位32); frame.setLayout(new BorderLayout()); frame.add(topPanel, BorderLayout.NORTH); frame.add(spacePanel, BorderLayout.CENTER); frame.add(statusBar, BorderLayout.SOUTH); frame.setVisible(true);逻辑说明GridLayout用于车位格子非常合适固定行列每个格子就是一个JLabel通过背景色区分状态。BorderLayout的NORTH/CENTER/SOUTH划分天然匹配顶部按钮、中间面板、底部状态栏三块区域。这里只写界面框架数据绑定在后面章节补。参数说明GridLayout(4, 8, 5, 5)表示4行8列格子横纵间距都是5像素setBackground设置背景色需要配合setOpaque(true)这个需要注意JLabel默认是透明的不设置Opaque的话背景色不会生效。这个细节看起来小但第一次做的人几乎都会遇到“明明setBackground了怎么还是灰色”的问题。3.2 用CardLayout切换面板进出场单独开小窗主界面只管列表和状态进场和出场操作最好单独弹对话框不要挤在主界面上。两个方案一个是用JDialog弹独立窗口另一个是主窗体用CardLayout切换页面。前者实现简单后者适合后续扩展成“首页/车辆管理/订单查询”多页面的管理系统。我建议起步用JDialog每弹一个窗口只做一件事逻辑清晰不容易把状态搞混。public class EntryDialog extends JDialog { private JTextField plateField new JTextField(12); private JTextField cardField new JTextField(12); private JComboBoxString spaceCombo new JComboBox(); public EntryDialog(Frame owner) { super(owner, 车辆进场, true); setSize(320, 220); setLocationRelativeTo(owner); setLayout(new GridLayout(3, 2, 10, 10)); add(new JLabel(车牌号)); add(plateField); add(new JLabel(卡号)); add(cardField); add(new JLabel(选择车位)); add(spaceCombo); JButton ok new JButton(确认进场); ok.addActionListener(e - doEntry()); add(ok); add(new JButton(取消)); } private void doEntry() { // 数据库插入逻辑在后续章节实现 System.out.println(进场车牌 plateField.getText() 卡号 cardField.getText()); dispose(); } }逻辑说明JDialog的第二个参数true表示模态弹出后必须处理完才能操作主窗口这对出入场操作是合理约束——避免用户一边点进场一边改主界面数据。布局用GridLayout(3, 2, 10, 10)排两列左侧标签、右侧输入控件成对出现简单对称不会因为窗口拉大变形。参数说明JTextField(12)的12不是限制输入长度而是计算列宽的字符数只影响显示宽度限制输入长度要另外写DocumentFilter或setColumns配合验证。JComboBox在这里只负责显示空闲车位后续绑定数据库时数据在弹窗前从DBUtil拉好放入组件。3.3 表格刷新停车记录列表怎么不卡顿主界面的停车记录表是核心展示强烈建议用JTable加DefaultTableModel的方式不要手动去拼JLabel也不要每次刷新都重新new JTable挂到面板上。正确做法是只更新TableModel的数据即可DefaultTableModel model new DefaultTableModel( new String[]{车牌, 卡号, 进场时间, 状态}, 0); JTable table new JTable(model); JScrollPane scroll new JScrollPane(table); frame.add(scroll, BorderLayout.CENTER); // 刷新方法先清空再插入 model.setRowCount(0); for (ParkingRecord r : records) { model.addRow(new Object[]{ r.getPlate(), r.getCardNo(), r.getEntryTime(), 在场 }); }逻辑说明代码里的思路是先清空全部行再重新插入操作简单数据量在本系统规模下完全可接受。为什么不建议table new JTable(newModel)再重新挂载——因为每次都替换组件会导致表格滚动条复位、选中状态丢失用户稍微多点几次刷新就会发现列表“跳一下”体验很差。参数说明setRowCount(0)是清空model所有行最直接的方式addRow接收Object[]数组每一列对应表头数组定义的列。需要注意表头数组在new DefaultTableModel时传入后列名每次调用setColumnIdentifiers可以重设但没必要这里列是固定的。4. 数据库连接和数据操作JDBC代码做到能复用4.1 DBUtil封装连接、关闭、查询、更新数据库连接的代码只写一次后面所有业务全靠它。连接参数固定后不要散落在各个类里统一放在一个DBUtil的静态方法中public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/parking?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 你的密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException ignored) {} } if (stmt ! null) { try { stmt.close(); } catch (SQLException ignored) {} } if (conn ! null) { try { conn.close(); } catch (SQLException ignored) {} } } public static ListCar queryCarsOnParking() { String sql SELECT id, plate, card_no, entry_time, status FROM car WHERE status 1; ListCar cars new ArrayList(); try (Connection conn getConnection(); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql)) { while (rs.next()) { Car car new Car(); car.setId(rs.getInt(id)); car.setPlate(rs.getString(plate)); car.setCardNo(rs.getString(card_no)); car.setEntryTime(rs.getTimestamp(entry_time)); car.setStatus(rs.getInt(status)); cars.add(car); } } catch (SQLException e) { e.printStackTrace(); } return cars; } }逻辑说明静态代码块里只做一次驱动注册Class.forName(com.mysql.cj.jdbc.Driver)是MySQL 8.0的驱动类名如果用的是5.x版本驱动名字是com.mysql.jdbc.Driver写错会报ClassNotFoundException。try-with-resources是Java 7之后的推荐写法连接、语句、结果集全自动关闭不需要手动防泄漏。真正的业务方法返回的结果封装成ListCar这样上层Swing代码不接触JDBC细节拿到的是干净的业务对象。参数说明URL里的useSSLfalse控制是否启用SSL连接本地开发没有加密证书直接关掉避免报警告characterEncodingutf8解决中文乱码serverTimezoneAsia/Shanghai解决MySQL时区导致的时间偏移8小时问题。这三个参数就是网上大量报错的根源少一个都会出现怪现象。4.2 车辆进场和出场涉及update、事务、和并发进场逻辑本质上是一组数据库操作插入一条车辆记录、把对应车位状态改成占用。这两步必须在一个事务里完成否则车辆插入成功车位没更新就会出现车位显示空闲但实际被占的脏数据。什么时候会翻车车辆插入成功、车位更新SQL写错或者反过来都会留下不一致状态。public boolean entryCar(String plate, String cardNo, String spaceId) { String insertCar INSERT INTO car (plate, card_no, entry_time, status, space_id) VALUES (?, ?, NOW(), 1, ?); String updateSpace UPDATE parking_space SET status 1 WHERE space_id ? AND status 0; try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement ps1 conn.prepareStatement(insertCar); PreparedStatement ps2 conn.prepareStatement(updateSpace)) { ps1.setString(1, plate); ps1.setString(2, cardNo); ps1.setString(3, spaceId); int carRows ps1.executeUpdate(); ps2.setString(1, spaceId); int spaceRows ps2.executeUpdate(); if (carRows 0 || spaceRows 0) { conn.rollback(); return false; } conn.commit(); return true; } catch (SQLException e) { conn.rollback(); e.printStackTrace(); return false; } } catch (SQLException e) { e.printStackTrace(); return false; } }逻辑说明setAutoCommit(false)把连接切到手动提交模式之后所有SQL会等commit()才真正落库。executeUpdate返回受影响行数——车位更新时带了AND status 0条件如果车位已经被占更新行数为0说明本次操作无效需要回滚。这个status 0条件就是处理并发的第一道防线比先查出状态再判断要安全得多。参数说明UPDATE parking_space SET status 1 WHERE space_id ? AND status 0里的AND status 0是并发场景下的关键写法。两个用户同时扫同一个空闲车位MySQL的更新锁会让第二个更新等待第一个提交后第二个匹配不到status 0的行返回0行从而回滚避免了超卖车位的问题。虽然课程设计里不会有真正的高并发但这个写法值得养成习惯。出场逻辑稍微复杂先查出停车记录和对应计费规则算费用然后更新车辆状态为离场、释放车位、插入订单表。算费用不是简单乘法牵扯到跨天、超时、封顶这些现实规则单独拆一节来讲。4.3 计费逻辑跨天、超时、封顶怎么算才不亏计费是停车场管理系统的核心业务看起来简单时长乘单价实际上坑非常多。常见规则是首小时收费X元超过1小时后按每30分钟或每小时加收Y元单日封顶Z元跨天的部分重新按首小时计。这个逻辑放在Java代码里算不要试图靠SQL算SQL处理日期差只能拿到总分钟数无法清晰表达分段计费规则。public BigDecimal calcFee(Timestamp entryTime, Timestamp exitTime, ChargeRule rule) { long minutes (exitTime.getTime() - entryTime.getTime()) / (1000 * 60); if (minutes 60) { return rule.getFirstHourFee(); } BigDecimal fee rule.getFirstHourFee(); long extraHours (minutes - 60 59) / 60; // 向上取整 fee fee.add(rule.getHourlyFee().multiply(BigDecimal.valueOf(extraHours))); if (fee.compareTo(rule.getDailyCap()) 0) { fee rule.getDailyCap(); } return fee; }逻辑说明(minutes - 60 59) / 60是向上取整的经典写法超过1分钟就按1小时计而不是整除取整。比如停了1小时01分钟extraHours是(61-6059)/601即多收1小时。封顶用compareTo比较超过就截到封顶金额。为什么用BigDecimal而不是double——上面代码里的multiply和add如果换double的*和会出现14.999999这种精度问题这是面试常问的坑。参数说明如果业务要求更细的按30分钟计费把公式改成(minutes - 60 29) / 30即可如果要求早晚高峰不同费用就要引入时间段判断核心不变——把计费规则的每个参数都放数据库表里代码里不要硬编码金额。5. 避坑几个让我抓狂的典型问题5.1 中文乱码控制台和界面全是问号现象表结构、Java代码、控制台三处单独看都是中文连起来查询后显示乱码插入的中文变成“???”界面上显示一堆问号。原因三个环节的字符集不统一。MySQL服务端字符集可能是utf8JDBC连接串里没加characterEncodingutf8或者代码文件本身的编码是GBK但编译环境按UTF-8处理。解决从上到下统一三重设置。第一建库语句加DEFAULT CHARSETutf8mb4第二JDBC连接串里加characterEncodingutf8见4.1节代码第三IDE里把项目文件编码统一UTF-8。这三处都到位乱码问题可以杜绝剩下的个别情况是Windows控制台显示问题不影响实际存储。5.2 刷新表格疯狂闪动滚动条回顶现象查出来的数据每2秒刷新一次表格不停跳动看到一半的记录一刷新就跳回顶部体验很差。原因每次刷新都重新new JTable替换到面板上表格实例发生变化滚动位置全部丢失或者用了model.setRowCount(0)清空后重新添加但滚动条位置没有被保存恢复。解决表格只创建一次所有刷新都通过DefaultTableModel操作行数据。如果数据量大或者刷新频繁先记录scrollPane.getVerticalScrollBar().getValue()加完数据后用SwingUtilities.invokeLater把滚动条设置回去。本项目的记录量很小主要修复点是不要反复替换组件实例。5.3 MySQL 8.0 报 ClassNotFoundException: com.mysql.jdbc.Driver现象数据库驱动明明在项目里代码也写了Class.forName运行时照样报类找不到。原因MySQL 8.0的驱动类名改了。8.0之前是com.mysql.jdbc.Driver8.0之后是com.mysql.cj.jdbc.Driver。用了旧名字当然找不到类。解决换成com.mysql.cj.jdbc.Driver。如果机器上是MySQL 5.7但驱动Jar是8.0的版本也可以直接用新版驱动类名新版向下兼容连接5.7没问题。5.4 ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock现象在Linux下安装MySQL后用命令行访问报无法连接而且报错信息指向一个不存在的socket文件。原因MySQL服务没有启动或者启动后socket文件路径和客户端预期的不一致。这个问题经常在刚装完MySQL、还没有初始化服务时出现。解决先确认服务状态是否在跑如果没启动就启动服务再连接。如果已经启动仍然报错直接在连接命令里指定本机IPmysql -h127.0.0.1 -uroot -p绕开socket连接方式。5.5 出场界面重复点击产生多条订单现象点“确认出场”按钮没反应或卡顿又多点了几下结果订单表出现多条相同离场记录车位也被重复释放或报错。原因出场逻辑没有保证幂等。按钮没被禁用同一辆车被处理了两次第二次处理的UPDATE没有带上status 1条件导致已经离场的车又被更新一次。解决出场时更新车辆的SQL必须加状态条件UPDATE car SET status 0, exit_time NOW() WHERE plate ? AND status 1如果更新行数为0说明这辆车已经在离场状态直接返回并提示“该车已出场”。界面上同时把确认按钮在首次点击后设为setEnabled(false)双保险。6. 除了增删改查这套系统还能往哪个方向进阶如果你已经把上面的代码跑通可以静下心考虑这个系统还能做什么。常用的方案是给价格规则加一个生效时间段字段实现“白天时段和夜间不同计费”这种做法改动集中在calcFee方法难点在于表达时段边界但逻辑不复杂。另外一个很实用的进阶是内存数据表把常用数据加载到HashMap里减少数据库查询比如把车位状态表和计费规则表在启动时预加载查询时直接读内存只有修改时才写数据库。实践中这类管理系统最适合往“报表方向”演进——出入场记录按天、周、月统计收费总额再画一个简单的折线图。统计逻辑不算复杂一条GROUP BY DATE(entry_time) SUM(total_fee)就能拉出数据但它在展示上比增删改查显得完整得多适合期末验收或走简历项目展示。动手完成上述这个系统后我建议你做一个复盘在纯Java和MySQL之上试着说出每个功能点对应了什么基础概念。Swing的状态栏刷新练的是Timer任务调度进出场事务练的是原子性连接通过驱动创建练的是SPI和反射。这套项目的含金量不在代码量而在用到的每一个概念都能在面试题里被追问。把数据库表和代码敲完运行顺了我比较习惯再回头写一份文档截图加上关键代码。这个习惯救过我的课程设计答辩。希望这一路的梳理帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号