news 2026/10/20 13:27:49

Java Swing + MySQL 酒店管理系统实战:从零完成课程设计项目

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Swing + MySQL 酒店管理系统实战:从零完成课程设计项目

用 Java Swing 加 MySQL 做一套酒店管理系统,算是 Java 初学者最常碰到的练手课题,也是很多高校 Java 课程设计的热门选题。这个系统说白了就是围绕“客房、客户、订单”这几条核心数据做增删改查,再套一个登录界面和几个统计报表,听起来不复杂,但真正动手写的时候,它几乎能把 Java SE 阶段的重要知识点全部串起来:面向对象、集合、异常处理、JDBC、GUI 事件监听、数据库表设计,一个都不少。所以我的看法很直接:如果你正在学 Java,又不想整天对着控制台黑窗口敲代码,想做一个“能看得见界面、能操作按钮、能存进数据库”的东西,这套系统就是性价比极高的练手项目。

这篇文章完全按零基础视角来写,从环境准备、数据库建表,到核心模块代码实现,再到遇到的坑和排查思路,一次说清楚。适合学生做课程设计、转行者做求职项目,也适合刚进公司需要给内部做桌面小工具开发的开发者参考。

1. 先想清楚:酒店管理系统到底要解决什么问题

1.1 从前台开房的真实流程说起

做任何系统前,第一步不是打开 IDE 写代码,而是把业务场景想明白。酒店前台每天都在干什么?客人来了,前台查询空闲房间,录入客人身份信息,分配房间,记录入住时间;客人退房时,前台核对住了几天,按房价计算费用,收钱,释放房间。除此之外还有预约订房、换房、查看当日房态这些操作。

把这些动作翻译成技术语言,就变成了几个核心操作:查询房间状态、添加客户档案、生成入住记录、修改房间状态、计算并保存结算流水。换句话说,系统的本质是一堆数据的流动——房间从空闲变为入住,订单从创建走到结算,客户从首次登记变成再次到店的“回头客”。

我见过不少初学者上来就写界面,把按钮摆了一堆,结果数据库表还没建,最后代码越写越乱。正确顺序应该是先把“谁在什么时间做了什么操作,产生了什么数据变化”梳理出来,再想界面和代码。对酒店管理系统来说,最重要的三张底表就是房间、客户、订单记录,其他功能基本都是在围绕这三张表做文章。

1.2 模块清单和角色划分:先把边界划清楚

实际酒店系统会有很复杂的权限体系,但对于新手项目,建议把角色收敛成两类:管理员和前台操作员。管理员管基础数据,比如维护房间信息、价格、用户账号;前台操作员负责日常营业,比如入住登记、退房结算、查询订单。

模块拆下来大概是下面这些:

  • 登录模块:账号密码校验,区分管理员和普通前台
  • 客房管理:添加、修改、删除房间,按房间号或状态查询
  • 房型管理:标准间、大床房、豪华套间等,维护基础价格
  • 客户管理:登记客户姓名、身份证号、手机号,支持按姓名或证件号查询
  • 入住登记:选择空闲房间,填写客户信息,生成入住订单
  • 退房结算:根据入住时间和房型价格结算费用,更新房间状态
  • 订单查询:按订单号、房间号、客户姓名查询历史记录
  • 数据统计:统计当前入住率、今日收入,简单图表展示

功能不要一开始就铺开。零基础做项目最怕“贪多”,比如加会员卡积分、加微信提醒、加多级审批,这些做完大概率收不了场。先把上面这些核心模块跑通,后面再往上加东西,这才是正确节奏。

2. 技术选型和架构:为什么是 Swing + MySQL,代码该怎么组织

2.1 Swing 是不是“过时”了?学习价值在哪

很多人一听到 Swing 就觉得这是老古董。确实,现在新开发的桌面应用不多,Java 桌面端也早被 JavaFX 等方案分流。但 Swing 并没有死,很多企业内部工具、老系统维护项目还在用它,而且一旦理解了 Swing 的事件模型和组件体系,切换到其他 GUI 框架非常快。

更重要的是,Swing 是学习 Java GUI 最平滑的入口。它的组件命名直观,比如JFrame、JButton、JTable,事件监听机制也很规范,适合理解“用户点一下按钮,代码怎么响应”的完整链路。相比之下,JavaFX 功能更强,但概念更多,对零基础来说上手门槛反而高。课程设计用 Swing,投入产出比最高。

还有一个现实原因:大多数教材、视频、资料都是围绕 Swing 讲 Java GUI 的。遇到问题时,网上能搜到的案例最多,对学习者最友好。用商业项目标准去否定 Swing 的学习价值,就像说“学车不用练手动挡”,但手动挡练出来的基本功,放到哪里都不过时。

2.2 JDBC 和 MySQL 是怎么配合工作的

JDBC 是 Java 访问数据库的标准接口。它的工作方式很像你去餐厅吃饭:先连接上数据库服务,然后通过Connection建立会话,用PreparedStatement写好一条 SQL“订单”,执行后拿到ResultSet“上菜结果”,最后关闭连接释放资源。整个过程就这么简单,但细节里全是坑。

以 MySQL 为例,Java 程序本身不知道 MySQL 的通信协议,必须通过 MySQL 官方提供的驱动 jar 包来翻译。所以项目里要引入mysql-connector-java依赖,代码里还要用Class.forName("com.mysql.cj.jdbc.Driver")注册驱动。这一步看起来多余,但少了它就会报ClassNotFoundException。

JDBC 的关键点在于:连接要打开,用完要关闭。很多初学者只记得getConnection(),不记得close(),跑一会儿数据库连接数就爆了。所以后面写工具类时,我会把资源关闭统一处理好,这个习惯比多写几个功能重要得多。

2.3 用“三层结构”组织代码,避免一个类写到底

零基础学生最常见的写法是:界面和数据库代码全塞在一个类里,按钮点击事件里直接写 JDBC,一个MainFrame.java好几百行,改个需求牵一发动全身。这种代码跑通没问题,但维护起来非常痛苦。

我建议从第一个版本开始就保持简单的分层:

  • 实体层(entity):对应数据库表中的一行数据,比如Room、Customer、OrderInfo
  • 数据访问层(dao):负责和数据库交互,写 SQL、执行增删改查
  • 界面层(view):负责显示和事件监听,不直接操作数据库
  • 业务控制层(controller/action):在界面和 DAO 之间做逻辑编排,比如“入住”要先查房间状态、再插入订单、再改房间状态

这个分层不要求做到多标准,但至少要让view里看不到Connection和executeQuery()。打个比方,界面就像餐厅前厅,DAO 就像后厨,业务逻辑像传菜员,各管一段,出了问题好定位。后面第 4 部分的代码示例,就是按照这个名字结构来写的。

3. 数据库设计:万丈高楼平地起

3.1 需要哪几张表,为什么是这些

数据库表设计是整个项目的地基。酒店管理系统的核心业务是“房间被哪些客人住了多久,花了多少钱”,围绕这个核心,最少需要这几张表:

  • admin用户表:存储登录账号、密码、姓名
  • room_type房型表:存储房型名称和基础价格
  • room客房表:存储房间号、所属房型、当前状态
  • customer客户表:存储客户身份信息
  • check_in_info入住订单表:记录每次入住的关键信息,包括房间、客户、入住时间、退房时间、金额

有人会问为什么不能把所有数据塞进一张大表?比如把房间信息和房价都写在客房表里,把客户信息直接写在订单表里。这样做短期内方便,但会出现数据冗余:如果某个房价变了,得同时修改多条记录,很容易改漏。把房型独立成表,就是让“房价”这种基础数据和“入住订单”这种业务数据分开,这也是数据库第三范式的基本思想。

订单表里有外键关联到房间表和客户表,这样可以通过订单找到房间,也可以通过房间反查所有住过它的客户记录。外键在增加数据时能起到约束作用,比如插入订单时,如果填了一个不存在的room_id,数据库直接拒绝,这能帮你在源头拦住脏数据。

3.2 建表语句与字段说明

下面是整套系统的基础建表 SQL,数据库名定为hotel_db,统一使用utf8mb4字符集,避免中文乱码。

CREATE DATABASE hotel_db DEFAULT CHARACTER SET utf8mb4; USE hotel_db; CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(30), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE room_type ( id INT PRIMARY KEY AUTO_INCREMENT, type_name VARCHAR(30) NOT NULL, price DECIMAL(10,2) NOT NULL, bed_num TINYINT DEFAULT 1, remark VARCHAR(200) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE room ( id INT PRIMARY KEY AUTO_INCREMENT, room_no VARCHAR(10) NOT NULL UNIQUE, type_id INT NOT NULL, status TINYINT DEFAULT 0 COMMENT '0空闲 1入住 2维护 3预订', remark VARCHAR(200), CONSTRAINT fk_room_type FOREIGN KEY (type_id) REFERENCES room_type(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE customer ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(30) NOT NULL, id_card VARCHAR(18) NOT NULL, phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE check_in_info ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, room_id INT NOT NULL, customer_id INT NOT NULL, check_in_time DATETIME NOT NULL, check_out_time DATETIME, days INT DEFAULT 0, amount DECIMAL(10,2) DEFAULT 0, status TINYINT DEFAULT 0 COMMENT '0入住中 1已退房 2已取消', CONSTRAINT fk_order_room FOREIGN KEY (room_id) REFERENCES room(id), CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES customer(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

几个字段的设计需要注意。status字段用TINYINT而不是字符串,因为数字状态在代码里容易判断,扩展性也好。price和amount用DECIMAL而不是DOUBLE,因为浮点数做金额计算会产生精度问题,这在结账时是致命的。order_no用VARCHAR(32),后面可以拼上时间戳和房间号生成唯一编号。customer表的id_card加了NOT NULL,因为身份证是客户唯一标识。

注意:ENGINE=InnoDB一定要写,因为 InnoDB 才支持事务和外键约束。MyISAM 虽然也能建表,但事务回滚用不了,入住登记这种“改了房间状态又插入订单”的操作就危险了。

3.3 初始化数据与数据库连接配置

建好表之后,要先往里面插入基础数据,否则程序跑起来界面全是空的。至少需要一条管理员账号、几条房型数据、几间房。

INSERT INTO admin(username, password) VALUES ('admin', '123456'); INSERT INTO room_type(type_name, price, bed_num, remark) VALUES ('标准间', 168.00, 2, ''), ('大床房', 198.00, 1, ''), ('豪华套间', 388.00, 2, '带客厅'); INSERT INTO room(room_no, type_id, status) VALUES ('101', 1, 0), ('102', 1, 0), ('201', 2, 0), ('301', 3, 0);

初始化数据的密码建议学习阶段先用明文,跑通业务后再改成 MD5 加密存储,第 5 部分会专门说。

连接数据库的配置建议集中放在一个配置文件里,比如db.properties。这样当数据库密码改变时,不用进代码里到处找。

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/hotel_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=你的密码

characterEncoding=utf8是预防中文乱码的关键项。serverTimezone=Asia/Shanghai也建议写上,不然新版 MySQL 驱动经常会报时区相关的异常。

4. 零基础实战:从建工程到核心模块代码

4.1 新建项目和导入 MySQL 驱动

如果你用的是 IDEA,操作路径是:新建普通 Java 项目(不使用 Maven,先降低复杂度),然后准备一个lib目录,把mysql-connector-java的 jar 包放进去,右键 Add as Library。没有这个 jar 包,代码里所有import java.sql.*都会报错。

MySQL 驱动去官网下载即可,选择平台无关的 zip 包,解压后里面是一个mysql-connector-j-x.x.x.jar。另一个方法是用 Maven 依赖,但零基础我不建议第一时间引入 Maven 构建,先手工管理 jar 包反而能让你明白依赖到底是什么。

建议项目结构如下:

src ├── com.hotel.entity │ ├── Admin.java │ ├── Room.java │ ├── RoomType.java │ ├── Customer.java │ └── OrderInfo.java ├── com.hotel.dao │ ├── AdminDao.java │ ├── RoomDao.java │ ├── CustomerDao.java │ └── OrderDao.java ├── com.hotel.view │ ├── LoginFrame.java │ ├── MainFrame.java │ └── RoomManagePanel.java ├── com.hotel.util │ └── DBUtil.java └── com.hotel.StartApp.java

4.2 写数据库连接工具类

连接工具类是整个系统的基础。用一个静态代码块注册驱动,用getConnection()返回连接对象。这里我采用先写死账号密码的方式,方便零基础理解,后续再改成读取配置文件。

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/hotel_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "你的数据库密码"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

新手最容易忽略的是关闭连接。我在每个 DAO 方法里都用了 try-with-resources 写法,也就是在try()小括号里创建连接和预处理对象,这样方法结束后会自动执行 close,不用手动写 finally 去关闭。这个 JDK7 就开始支持的语法,强烈建议养成习惯。

4.3 登录界面:界面渲染 + 数据库校验

登录模块的逻辑非常清晰:用户输入账号密码,点击登录按钮,程序拿着账号密码去数据库比对,成功则打开主界面,失败则弹窗提示。

先写一个实体类Admin.java:

public class Admin { private int id; private String username; private String password; private String realName; // 省略 getter/setter }

再写AdminDao.java,负责按用户名和密码查询管理员。注意这里用的是PreparedStatement,不要用Statement+ 字符串拼接,因为PreparedStatement能有效防止 SQL 注入。SQL 注入是什么?简单说,如果用户输入' OR '1'='1这类内容,拼接 SQL 后逻辑直接被改写,数据库可能被非法进入。

public class AdminDao { public Admin login(String username, String password) { String sql = "SELECT * FROM admin WHERE username = ? AND password = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { Admin admin = new Admin(); admin.setId(rs.getInt("id")); admin.setUsername(rs.getString("username")); admin.setRealName(rs.getString("real_name")); return admin; } } } catch (SQLException e) { e.printStackTrace(); } return null; } }

界面层是一个JFrame,放两个JLabel、一个JTextField、一个JPasswordField、一个JButton。登录按钮的点击事件写法如下:

btnLogin.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String username = usernameField.getText().trim(); String password = new String(passwordField.getPassword()); if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(LoginFrame.this, "用户名和密码不能为空"); return; } Admin admin = new AdminDao().login(username, password); if (admin != null) { JOptionPane.showMessageDialog(LoginFrame.this, "登录成功"); new MainFrame().setVisible(true); dispose(); } else { JOptionPane.showMessageDialog(LoginFrame.this, "用户名或密码错误"); } } });

两个细节:密码框要用new String(passwordField.getPassword())而不是passwordField.getText(),因为JPasswordField的getText()已经被标记废弃,不被推荐;账号输入后调用trim()去掉首尾空格,避免用户多敲一个空格导致比对失败。

4.4 客房管理:JTable 列表和增删改查

客房管理是系统的核心界面,也是 Swing 里最值得练的部分。查询房间时,要把房间信息和对应的房型价格一并显示出来,SQL 需要用到LEFT JOIN:

SELECT r.id, r.room_no, r.status, t.type_name, t.price FROM room r LEFT JOIN room_type t ON r.type_id = t.id

对应的 DAO 方法:

public List<Room> findAll() { String sql = "SELECT r.id, r.room_no, r.status, t.type_name, t.price " + "FROM room r LEFT JOIN room_type t ON r.type_id = t.id"; List<Room> list = new ArrayList<>(); try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Room room = new Room(); room.setId(rs.getInt("id")); room.setRoomNo(rs.getString("room_no")); room.setStatus(rs.getInt("status")); RoomType type = new RoomType(); type.setTypeName(rs.getString("type_name")); type.setPrice(rs.getBigDecimal("price")); room.setRoomType(type); list.add(room); } } catch (SQLException e) { e.printStackTrace(); } return list; }

列表展示用JTable。初学者最容易卡住的地方是:JTable不能直接给一个List展示,它需要接收一个TableModel。所以要么用现成的DefaultTableModel,要么自定义表格模型。我建议先学会用DefaultTableModel,后面再深入自定义模型。

DefaultTableModel model = new DefaultTableModel(new Object[]{ "房间号", "房型", "价格", "状态" }, 0); for (Room room : roomDao.findAll()) { String statusText = ""; switch (room.getStatus()) { case 0: statusText = "空闲"; break; case 1: statusText = "入住中"; break; case 2: statusText = "维护中"; break; case 3: statusText = "已预订"; break; } model.addRow(new Object[]{ room.getRoomNo(), room.getRoomType().getTypeName(), room.getRoomType().getPrice(), statusText }); } roomTable.setModel(model);

添加房型、修改房价这种操作背后就是简单的INSERT和UPDATE。写增删改查时要注意一个原则:界面上操作的数据,最终不要用字符串拼接方式传 SQL,防止中文或特殊字符破坏语法。

删除客房时要特别小心外键约束。如果某个房间已经和订单表关联,直接删除会报外键约束错误。要么先处理订单记录,要么采用“逻辑删除”——给房间表加一个deleted字段,删除时只是把status改成维护状态,而不是物理删除行。对酒店这类业务系统来说,逻辑删除更合理,历史数据能保留。

4.5 入住登记与退房结算:事务怎么用

入住登记和退房结算是整个系统里最体现工程能力的地方,因为它们不是单条 SQL 操作,而是多步操作需要保持一致性。

办理入住时,需要做两件事:把房间状态改成“入住中”,插入一条入住订单。如果第一步成功了,第二步插入失败,房间就会被永久锁死。解决办法是用数据库事务:两步同时成功才提交,任何一步失败都回滚。

下面这段代码是入住事务的核心写法:

public boolean checkIn(int roomId, int customerId) { String updateRoom = "UPDATE room SET status = 1 WHERE id = ? AND status = 0"; String insertOrder = "INSERT INTO check_in_info(order_no, room_id, customer_id, check_in_time) " + "VALUES (?, ?, ?, NOW())"; Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); try (PreparedStatement ps1 = conn.prepareStatement(updateRoom); PreparedStatement ps2 = conn.prepareStatement(insertOrder)) { ps1.setInt(1, roomId); int row = ps1.executeUpdate(); if (row != 1) { conn.rollback(); return false; } String orderNo = System.currentTimeMillis() + "-" + roomId; ps2.setString(1, orderNo); ps2.setInt(2, roomId); ps2.setInt(3, customerId); ps2.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { conn.rollback(); e.printStackTrace(); return false; } } catch (SQLException e) { e.printStackTrace(); return false; } finally { try { if (conn != null) { conn.setAutoCommit(true); conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }

退房结算是把入住时间到退房时间换算成天数,再乘房型单价,得到应收金额。时间计算建议用java.time包里的LocalDateTime,不要再折腾老的Date。比如:

long days = ChronoUnit.DAYS.between(checkInTime, LocalDateTime.now()); if (days == 0) { days = 1; } BigDecimal amount = BigDecimal.valueOf(days).multiply(price);

为什么不足一天也要按一天算?这是酒店行业真实规则,写进去虽然只是一个小判断,但体现了你是在做业务系统,而不是纯练语法。

5. 常见问题排查:把坑提前告诉你

5.1 驱动加载失败、ClassNotFoundException

代码在Class.forName("com.mysql.cj.jdbc.Driver")处报错,绝大部分原因是 jar 包没被正确导入。mysql-connector-java的 jar 包不仅要出现在项目目录里,还要被标记为库引用。IDEA 里面右键 jar 包选择 “Add as Library” 是常见操作,别只复制到目录就不管了。

还有版本差异的问题。较老的驱动类名是com.mysql.jdbc.Driver,新版变成了com.mysql.cj.jdbc.Driver。如果你用的是新版驱动,就写带cj的包名;如果你用的驱动版本很老,就按老类名写。这个细节报错时最坑人,但也好排查,打开 jar 包看一眼里面有没有对应目录就行。

5.2 中文乱码到底出在哪一步

中文乱码是个经典老大难。处理中文乱码要分清三个地方:数据库存储、JDBC 连接、界面显示。

数据库建表时要用utf8mb4,连接 URL 要带characterEncoding=utf8。如果从命令行执行 SQL 插入中文乱码,命令行窗口还要加--default-character-set=utf8mb4。界面显示乱码,可以在 Windows 下检查代码文件编码是否为 UTF-8,不要用 GBK。我在实际测试中,按这三步排查基本都能解决。

5.3 界面卡死、表格不刷新

Swing 有一个事件分发线程(EDT),所有界面更新都应该在这个线程中进行。如果你在按钮点击事件里直接执行了一个耗时的数据库查询,界面会“假死”,窗口拖不动、按钮点不了。

新手阶段数据量小,卡死不明显,但这是个隐患。解决办法是耗时操作放到新线程里做,比如用SwingWorker或Thread,查询完成后再回到 EDT 刷新界面。零基础可以先用简单方案:查询数据用SwingUtilities.invokeLater()包一下表格模型更新。

表格不刷新则是另一个问题:你添加完房间后,界面上没有反应,感觉像是系统没生效。实际上数据已经进数据库了,只是JTable的TableModel还停留在旧数据。每次增删改操作后,都要重新执行一次findAll并重新设置模型。

5.4 外键约束导致删除失败

房间表的数据如果被订单表引用了,直接删除会报Cannot delete or update a parent row。很多初学者以为是程序写错了,跑去检查 SQL,其实是数据库外键在保护数据。

处理方法有三种。第一种是删除前先删除子表关联记录,但这会造成历史数据丢失;第二种是给外键加ON DELETE SET NULL,把订单表里的房间引用置空,但这需要修改字段允许为空;第三种是逻辑删除,即不真正删除数据,而是把房间状态改成“维护中”,让房间在查询列表中不再显示为可用。第三种最适合酒店业务,建议采用。

5.5 安全问题:SQL 注入和密码明文存储

测试时如果你把登录 SQL 写成字符串拼接,比如:

SELECT * FROM admin WHERE username = 'admin' AND password = '123456'

当用户名输入admin' --时,后面条件会被注释掉,非法用户也能登录。解决方式前面说过,所有 SQL 都用PreparedStatement的?占位符,数据库底层会自动对参数转义,这是最简单有效的防御手段。

密码明文存储是另一个常见问题。只要数据库被拖走,所有账号密码全部泄露。正确做法是把密码用 MD5 加盐哈希后再存,登录时把用户输入的密码也哈希一下做比对。MD5 本身不安全,但在学习项目中至少能培养安全意识。示例初始化数据里的123456只是用于业务跑通,正式版一定要换。

5.6 常见问题速查表

为便于快速定位,把上面这些问题整理成表格:

现象可能原因解决办法
ClassNotFoundExceptionjar 包未导入或驱动类名写错将 jar 包 Add as Library,检查驱动包名
中文乱码数据库编码/连接编码/文件编码不一致建库用 utf8mb4,URL 加 characterEncoding=utf8
点击按钮界面假死在 EDT 中执行耗时数据库操作用线程处理耗时查询,刷新 UI 时回到 EDT
添加数据后表格不显示TableModel 未重新加载增删改后重新调用 findAll 并 setModel
删除房间报外键错误房间被订单表引用采用逻辑删除或先处理子表数据
登录输入特殊字符绕过验证SQL 注入全部使用 PreparedStatement
金额计算出现小数误差用 double 存金额金额字段用 BigDecimal
数据库连接越来越多连接未关闭使用 try-with-resources 自动关闭

6. 做完之后怎么提升:从“能用”到“像样”

6.1 低成本高收益的功能增强

第一版系统跑通后,不要急着结束。给它加几个花不了太多精力、但能显著提升完成度的功能,会让你的项目在课程设计或面试展示时完全不一样。

推荐顺序是这样的。第一,加图表统计:引入 JFreeChart,展示近一周的收入柱状图和房型入住率饼图。不需要复杂的数据分析,写几个 SQL 统计查询,把结果喂给图表即可。第二,加预订功能:在订单表里加一个预订状态,选中房间后先标记“已预订”,到店后再转为“入住中”,这就覆盖了一整个真实业务闭环。第三,加导出功能:用JFileChooser把订单列表导出成 CSV 文件,用 Office 软件就能打开。第四,加可视化的房态图:用色块模拟楼层平面图,绿色空闲、红色入住、灰色维修,点击色块执行操作,这个视觉效果是最惊艳的。

这些功能每个都不难,但组合起来,项目就从“学生作业”变成了“像样的小系统”。

6.2 从零基础到精通的后续路线

如果你准备拿这个项目作为学习里程碑,甚至想加入简历,建议按下面路线走一遍:

第一遍,照着手把手教程把代码写完,目标是跑通功能,理解每个类之间的调用关系。第二遍,不看参考,从数据库设计开始,独立把这个系统重写一遍。这一遍你会发现第一遍漏掉和理解错的细节。第三遍,尝试把数据访问部分从 JDBC 换成 MyBatis,或者把界面从 Swing 换成 JavaFX,这种重构能让你真正理解“分层架构”的意义。

完成第三遍之后,可以把这个项目往“前后端分离方向”延伸:保留 Swing 作为后台管理端,新写一个针对住客的移动端 H5 或者小程序。此时你就已经不是零基础了,而是具备完整项目经验的 Java 开发者。

我自己的体会是,绝大多数人学 Java 卡住,不是因为知识点多,而是因为动手太少。看一百个视频不如亲手写一套酒店管理系统,尤其是当你真的把登录、订单、事务、报表这一条链路跑通的时候,你会明显感觉到之前零散的知识点开始串成一条线。后面再去学 Spring、MyBatis、Spring Boot 这些企业级框架,你会发现很多思想都是相通的,只是换了一副外壳。

最后再分享一个学习技巧:把代码写完后的一个星期,重新打开项目,找一个自己不熟悉的小模块,比如退房结算的时间计算逻辑,试着不看现有代码,独立用文字把你理解的流程写出来。如果能写清楚,说明你真的掌握了;如果写不出来,就回去再看一遍。这个过程比多写几个界面有价值得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 13:27:48

MCP是什么?大模型工具调用标准化协议从架构到实战全解析

这两年做 AI 应用落地的朋友&#xff0c;几乎没人绕得开 MCP 这三个字母。MCP 的全称是 Model Context Protocol&#xff0c;翻译过来是“模型上下文协议”&#xff0c;简单说&#xff0c;它就是大模型和外部工具、数据源之间的一套标准化沟通协议。如果你最近在折腾 AI 助手、…

作者头像 李华
网站建设 2026/10/20 13:26:36

SRGAN超分辨率原理:感知损失与对抗训练如何提升视觉真实感

简介&#xff1a;本资源是基于SRGAN&#xff08;生成对抗网络&#xff09;实现图像超分辨率&#xff08;4倍放大&#xff09;的完整深度学习项目&#xff0c;面向计算机视觉方向的研究者、深度学习初学者及图像处理工程师&#xff0c;聚焦解决高倍率超分中纹理细节丢失、感知质…

作者头像 李华
网站建设 2026/10/20 13:26:21

从零构建Python CNN水果识别系统:毕设级完整实操指南

每到毕业季&#xff0c;后台总能收到一批类似的问题&#xff1a;“有没有适合毕设的深度学习项目&#xff1f;不要那种烂大街的人脸识别、车牌识别&#xff0c;最好有一点新意&#xff0c;又不会难到做不出来。” 我自己的答案是&#xff1a;基于Python的CNN水果识别。别急着觉…

作者头像 李华
网站建设 2026/10/20 13:25:56

LSTM+DeepLog日志异常检测:从序列建模到生产级告警落地

简介&#xff1a;本资源是一套基于LSTM神经网络的日志异常检测项目源码&#xff0c;面向AI运维、系统监控及日志分析方向的中高级开发者与研究者&#xff0c;聚焦解决IT系统运行中关键异常的自动识别问题。项目以Deeplog框架为基底&#xff0c;完整实现日志序列建模、事件特征提…

作者头像 李华
网站建设 2026/10/20 13:25:53

内网离线安装 Docker 与 docker-compose 完整方案

简介&#xff1a;本资源专为内网隔离环境下的Linux系统运维人员设计&#xff0c;解决无外网连接时无法在线安装Docker及Docker-Compose的核心痛点&#xff0c;适用于政企、金融、科研等强安全管控场景的CentOS 7离线部署需求。压缩包共22个文件&#xff0c;含20个适配el7的RPM依…

作者头像 李华
网站建设 2026/10/20 13:25:39

rea 响应式实时架构解析:状态同步与事件驱动实践

1. 从“rea”这个标题说起&#xff1a;一个被低估的缩写背后藏着什么第一次看到“rea”这个标题的时候&#xff0c;我脑子里蹦出来的第一反应是——这大概率又是一个被缩写玩坏了的项目名。做技术的人都有个通病&#xff0c;喜欢把什么都缩成三个字母&#xff0c;仿佛长了就会要…

作者头像 李华