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

Java火车票系统实战:Access MDB+Swing实现余票扣减与退票回滚

  • 首页
  • 资讯中心
  • /
  • Java火车票系统实战:Access MDB+Swing实现余票扣减与退票回滚

相关资讯

ESP32免烧录改WiFi:NVS存储与浏览器配置工具原理 2026/10/8 19:07:21
TPS259483AYWPR+PIC32MX470F512L工业级电源路径保护方案 2026/10/8 19:07:21
Agent-Reach 实战:用 Python CLI 让 AI Agent 在命令行稳定干活 2026/10/8 19:07:21

最新资讯

游戏引擎物理与动画系统架构设计:从碰撞检测到布娃娃协同的工程实践
hyperframes超帧:从多帧融合到AI视觉的帧组处理范式
Qt常用控件实战:从表格卡顿到多线程崩溃的避坑指南
Agent上下文工程实战:从Token预算到多轮记忆隔离
图解AI应用架构设计:从分层到RAG与Agent的完整落地指南
BERT微调做论文摘要抽取的实战指南

今日推荐

context-mode实战指南:从全量塞入到结构化裁剪与检索增强
大模型对话上下文管理实战:三种模式与Token优化
抖音用户主页视频数据爬虫详解:点赞、收藏、分享字段抓取与 TaoToken 统一 Key 配置

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Java火车票系统实战:Access MDB+Swing实现余票扣减与退票回滚

发布时间:2026/10/8 19:07:21
Java火车票系统实战:Access MDB+Swing实现余票扣减与退票回滚 简介这是一套面向Java初学者与数据库课程设计者的火车票售票系统实战项目基于Java SE与关系型数据库含MDB文件实现解决小型票务场景下的车次管理、余票查询、售票退票等核心业务问题。资源共89个文件包含14个Java源码文件含主程序与DAO逻辑、43个编译后class文件、15个HTML页面用于简易前端展示、11个GIF动图可能为界面操作示意以及trainmaindb.mdb数据库文件和jpx配置文件等整体压缩包仅323KB轻量易部署。已有3472人学习下载适合课程设计、毕业设计或JavaSQL综合实训。读者可直接运行查看完整MVC结构实现掌握JDBC连接Access数据库、Swing/AWT界面交互、车次与座位状态持久化管理等关键技能并通过train_table表结构与html文档快速理解数据模型与功能模块对应关系。1. 这不是Demo是能跑通「余票实时扣减退票回滚」的Java火车票系统基于Access MDB的本地化票务闭环适合课程设计、毕设答辩和Java数据库入门实战你可能在搜“Java火车票系统”时点开十几个项目——结果全是空壳界面、硬编码模拟数据、连SQL都写在Java字符串里。而这个train_table.srctrainmaindb.mdb的组合是少有的、真正用JDBC连接真实数据库、完成「查询→锁定座位→扣减库存→生成订单→退票还原」全链路的可运行系统。它不依赖Tomcat或Spring Boot纯Swing GUI JDBC Access MDB启动即用双击train_table.jar就能进售票窗口所有增删改查都走标准SQLINSERT/UPDATE/SELECT/DELETEtrain_table表结构清晰包含train_id,start_time,end_time,seat_total,seat_left等字段余票扣减逻辑藏在TrainService.java的sellTicket()方法里——不是if-else模拟而是UPDATE train_table SET seat_left seat_left - 1 WHERE train_id ? AND seat_left 0加事务控制。它不适合高并发线上场景但对Java初学者理解「数据库事务边界」「Swing事件驱动与DB交互耦合」「MDB文件作为轻量级持久化载体」这三个痛点比任何Spring Boot教程都直给。如果你正卡在「学完JDBC不知道怎么串起来」、「课程设计要交可演示系统」、「面试被问‘怎么保证卖票不超卖’答不出具体代码」这个包就是你的后悔药。2. 从MDB文件到Swing界面环境准备与核心模块拆解2.1 环境依赖JDK 8 Access Database Engine 无额外框架这个系统刻意规避了MySQL安装、驱动配置、服务部署等门槛选择.mdb作为数据库载体本质是利用Windows自带的Jet引擎能力。关键事实trainmaindb.mdb不是随便建的Access文件它已预置三张表——train_table车次信息、user_table用户账号、order_table订单记录且train_table.seat_left字段默认值为正整数这是余票逻辑成立的前提。JDK必须是8u202或更低版本JDK 9移除了sun.jdbc.odbc.JdbcOdbcDriver而本系统仍用ODBC桥接推荐使用 Adoptium Temurin 8u362 。无需下载额外JDBC驱动Windows 10/11默认带Microsoft Access Database Engine 2010/2016 Redistributable32位若报java.sql.SQLException: No suitable driver found请确认系统架构32/64位与Engine版本匹配——这是第一个血泪经验。提示不要试图用IntelliJ IDEA直接打开.mdb文件查看数据。Access Engine不提供图形化管理界面验证数据是否生效请用Java代码执行SELECT * FROM train_table并打印结果。2.2 源码结构解析src目录下的7个核心Java类分工压缩包中src/train_table/路径下共7个.java文件不是杂乱堆砌而是按MVC雏形组织文件名职责关键方法/字段技术要点MainFrame.javaSwing主窗口含菜单栏售票/退票/查询/管理和内容面板initComponents()构建GUI组件树所有按钮监听器绑定到ActionListener匿名内部类事件分发逻辑集中TrainService.java业务逻辑中枢封装所有数据库操作sellTicket(String trainId),refundTicket(int orderId)使用Connection.setAutoCommit(false)开启事务conn.commit()/rollback()控制一致性DBUtil.java数据库连接工厂单例模式管理ConnectiongetConnection(),closeConnection(Connection)URL格式为jdbc:odbc:Driver{Microsoft Access Driver (*.mdb)};DBQ绝对路径路径含中文会失败Train.java车次实体类POJO映射train_table表字段private String trainId; private int seatLeft;注意seatLeft是int类型但MDB中对应字段为Number(长整型)类型转换需显式处理Order.java订单实体关联train_id和user_idprivate int orderId; private String trainId;order_table主键order_id为AutoNumber插入时不需赋值UserLogin.java登录验证入口校验user_tablecheckLogin(String username, String password)密码明文存储user_table.pwd字段仅用于教学切勿用于真实系统QueryPanel.java查询功能独立面板复用MainFrame容器loadTrainData()加载车次列表到JTableJTable数据源为DefaultTableModelsetValueAt()动态刷新余票数这些类共同构成一个「无Spring、无Hibernate、无XML配置」的极简MVCMainFrame是ViewTrainService是ControllerDBUtilTrain/Order是Model层。没有IOC容器对象创建全靠new没有ORM映射SQL语句硬编码在TrainService的String sql UPDATE ...中——这恰恰是理解JDBC本质的最佳切口。2.3 启动流程从双击jar到看到售票窗口的5个关键步骤系统打包为train_table.jar但直接双击可能失败缺少JRE或路径问题。可靠启动法# 1. 解压后进入根目录确认存在 trainmaindb.mdb 和 train_table.jar ls -l # 输出应含trainmaindb.mdb train_table.jar src/ doc/ # 2. 设置JAVA_HOME指向JDK8以Windows为例 set JAVA_HOMEC:\Program Files\Java\jdk1.8.0_202 set PATH%JAVA_HOME%\bin;%PATH% # 3. 验证Access驱动可用执行此命令不报错即成功 java -cp train_table.jar train_table.MainFrame # 4. 若提示Could not find or load main class检查MANIFEST.MF中Main-Class是否为train_table.MainFrame # 可用记事本打开train_table.jar内META-INF/MANIFEST.MF确认 # 5. 成功启动后首屏为登录窗口输入预置账号admin / 123456 # 登录后进入主界面顶部菜单栏可见售票、退票、车次查询等选项为什么强调java -cp启动因为train_table.jar的MANIFEST.MF未声明Class-Path所有依赖如rt.jar中的JDBC类需由JVM自动加载。双击失败90%源于JRE版本不匹配或Access驱动未注册而命令行能精准暴露错误栈——比如SQLException: [Microsoft][ODBC Driver Manager] Data source name not found说明Access Engine未安装。3. 售票与退票的事务实现看懂sellTicket()和refundTicket()里的锁与回滚3.1 售票逻辑UPDATE前的SELECT FOR UPDATE替代方案Access不支持SELECT ... FOR UPDATE语法系统采用「乐观锁重试」策略规避超卖。核心代码在TrainService.sellTicket()public boolean sellTicket(String trainId) { String sql SELECT seat_left FROM train_table WHERE train_id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, trainId); ResultSet rs ps.executeQuery(); if (rs.next() rs.getInt(seat_left) 0) { // 余票充足执行扣减 String updateSql UPDATE train_table SET seat_left seat_left - 1 WHERE train_id ? AND seat_left 0; try (PreparedStatement updatePs conn.prepareStatement(updateSql)) { updatePs.setString(1, trainId); int affected updatePs.executeUpdate(); if (affected 1) { // 扣减成功插入订单 insertOrder(trainId, getCurrentUserId()); conn.commit(); return true; } } } } catch (SQLException e) { e.printStackTrace(); try { conn.rollback(); // 关键事务回滚 } catch (SQLException ex) { ex.printStackTrace(); } } return false; // 无票或更新失败 }这段代码的精妙之处在于UPDATE ... WHERE seat_left 0既是业务校验也是原子性保障。即使两个线程同时读到seat_left1第二个UPDATE因WHERE条件不满足此时seat_left已被第一个线程改为0而返回affected0自然失败。无需synchronized块避免了单线程瓶颈。insertOrder()在同一个conn中执行确保订单与余票变更在同一事务内。3.2 退票逻辑refundTicket()如何安全还原库存退票不是简单UPDATE seat_left seat_left 1必须校验订单状态和车次有效性public boolean refundTicket(int orderId) { String sql SELECT train_id, status FROM order_table WHERE order_id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, orderId); ResultSet rs ps.executeQuery(); if (rs.next() valid.equals(rs.getString(status))) { String trainId rs.getString(train_id); // 先更新订单状态为canceled updateOrderStatus(orderId, canceled); // 再增加余票 String updateSql UPDATE train_table SET seat_left seat_left 1 WHERE train_id ?; try (PreparedStatement updatePs conn.prepareStatement(updateSql)) { updatePs.setString(1, trainId); updatePs.executeUpdate(); } conn.commit(); return true; } } catch (SQLException e) { e.printStackTrace(); try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } return false; }关键设计点订单表order_table.status字段区分valid/canceled防止重复退票退票操作分两步先改订单状态再增库存顺序不可逆若先增库存再改状态中间崩溃会导致状态不一致updateOrderStatus()方法同样在conn中执行保证原子性。3.3 查询模块QueryPanel.loadTrainData()如何实现动态刷新车次查询界面不是静态列表而是实时读取train_table最新数据public void loadTrainData() { String sql SELECT train_id, start_time, end_time, seat_total, seat_left FROM train_table ORDER BY train_id; DefaultTableModel model (DefaultTableModel) trainTable.getModel(); model.setRowCount(0); // 清空旧数据 try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Object[] row { rs.getString(train_id), rs.getString(start_time), rs.getString(end_time), rs.getInt(seat_total), rs.getInt(seat_left) // 注意此处直接读取最新seat_left值 }; model.addRow(row); } } catch (SQLException e) { JOptionPane.showMessageDialog(this, 查询失败 e.getMessage()); } }为什么每次点击“查询”按钮都调用此方法因为Swing GUI与数据库无长连接每次操作都是短连接。model.setRowCount(0)清空表格再addRow()填充确保显示的是数据库当前快照而非内存缓存——这是初学者常误以为“数据没更新”的根源。4. 避坑指南5个让90%人卡住的AccessJDBC实战陷阱4.1 现象启动报java.lang.ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver原因JDK 8u202之后版本包括所有JDK 9已彻底移除ODBC桥接驱动而本系统DBUtil.java中仍写Class.forName(sun.jdbc.odbc.JdbcOdbcDriver)。解决降级到JDK 8u202推荐 Adoptium JDK8u202-b08 或修改DBUtil.getConnection()为使用UCanAccess驱动需下载ucanaccess-5.0.1.jar并添加到classpathURL改为jdbc:ucanaccess://path/to/trainmaindb.mdb。4.2 现象登录成功后主界面空白菜单栏不显示原因MainFrame.java中initComponents()方法调用了this.setExtendedState(JFrame.MAXIMIZED_BOTH)但在某些Windows缩放设置如125%下导致组件渲染异常。解决注释掉该行或改为this.setSize(1024, 768)重启程序即可正常显示。4.3 现象售票时提示“余票不足”但数据库中seat_left明明大于0原因trainmaindb.mdb文件被其他程序如Access软件独占锁定JDBC连接只能读取旧快照。Access MDB不支持多进程写入同一时刻仅允许一个连接修改。解决关闭所有Access进程任务管理器中结束MSACCESS.EXE或复制一份trainmaindb.mdb作为工作副本修改DBUtil.java中数据库路径指向新文件。4.4 现象退票后余票数没增加订单状态却变成canceled原因refundTicket()中updateOrderStatus()和UPDATE train_table不在同一事务块或conn.commit()被遗漏。检查DBUtil.getConnection()返回的Connection是否被多个方法复用——本系统要求所有DAO操作共享同一Connection实例。解决确认refundTicket()方法内Connection conn是try-with-resources外声明的且updateOrderStatus()和UPDATE语句共用该conn。4.5 现象train_table.html文档打不开显示乱码原因该HTML是用GB2312编码保存但现代浏览器默认UTF-8解析。解决用记事本打开train_table.html另存为→编码选择“ANSI”Windows平台即GB2312保存后重新打开。5. 进阶验证用SQL脚本校验事务一致性与边界场景5.1 构建压力测试模拟并发售票的临界点Access虽非高并发数据库但可通过多线程验证事务隔离性。编写测试类ConcurrencyTest.javapublic class ConcurrencyTest { public static void main(String[] args) throws InterruptedException { int totalTickets 1; // 设定初始余票为1 // 先用SQL将某车次seat_left设为1 // UPDATE train_table SET seat_left 1 WHERE train_id G101 ListThread threads new ArrayList(); for (int i 0; i 10; i) { Thread t new Thread(() - { TrainService service new TrainService(); boolean success service.sellTicket(G101); System.out.println(Thread.currentThread().getName() 卖票结果 success); }); threads.add(t); } for (Thread t : threads) t.start(); for (Thread t : threads) t.join(); // 最终查询余票 String checkSql SELECT seat_left FROM train_table WHERE train_id G101; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(checkSql); ResultSet rs ps.executeQuery()) { if (rs.next()) { System.out.println(最终余票 rs.getInt(seat_left)); // 应输出0 } } } }预期结果10个线程中仅1个返回true其余9个false最终seat_left0。若出现seat_left-1说明事务未生效或WHERE seat_left 0条件失效——此时需检查JDK版本和Access Engine兼容性。5.2 边界场景验证表用SQL手动触发并观察系统行为场景SQL操作预期系统响应验证方式超卖防护UPDATE train_table SET seat_left 0 WHERE train_id G101再次点击“售票”按钮提示“余票不足”查看Swing弹窗消息退票幂等UPDATE order_table SET status canceled WHERE order_id 1001再执行退票返回false余票不变查询train_table.seat_left值订单关联失效DELETE FROM train_table WHERE train_id G101再尝试退票ID1001的订单refundTicket()返回false无异常抛出控制台无SQLException打印时间格式异常UPDATE train_table SET start_time 25:00:00 WHERE train_id G101QueryPanel.loadTrainData()抛出SQLException弹窗提示“查询失败”观察GUI错误提示注意执行上述SQL需用Access软件或UCanAccess命令行工具切勿在Java代码中直接执行否则可能破坏事务一致性。5.3 从MDB迁移到MySQL3步替换数据库的实操清单若需升级为MySQL如课程设计要求只需修改3处驱动与URL删除DBUtil.java中Class.forName(sun.jdbc.odbc.JdbcOdbcDriver)添加MySQL驱动mysql-connector-java-8.0.33.jar到classpath修改DBUtil.getConnection()中URL为jdbc:mysql://localhost:3306/traindb?serverTimezoneGMT%2B8useSSLfalse。建表SQL适配将trainmaindb.mdb导出为CSV用MySQL Workbench导入seat_left字段类型从Access的Number改为MySQL的INT NOT NULL DEFAULT 0添加PRIMARY KEY和INDEX提升查询性能。事务隔离级别调整MySQL默认REPEATABLE READ而Access无此概念在TrainService的sellTicket()开头添加conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE)强化并发控制。从那以后我每次验证数据库逻辑都强制走一遍「手动SQL修改→启动Java系统→观察GUI响应→反向查库确认」四步闭环。不是为了炫技而是因为Access MDB的“黑匣子”特性——它不报错但数据可能没刷盘它不提醒但事务可能没提交。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

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

快速链接

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

服务项目

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

联系方式

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

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