ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

SQL Server+Swing实现宾馆管理系统:数据库设计、JDBC连接与避坑指南

SQL Server+Swing实现宾馆管理系统:数据库设计、JDBC连接与避坑指南 简介一份基于SQL Server与Java Swing开发的宾馆管理系统课程设计资源适合数据库课程设计、Java GUI编程练习或酒店管理入门项目参考。资源覆盖数据库脚本、Java源码、可执行文件和使用说明文档既能辅助理解系统设计也能作为同类课设的改造起点。压缩包内共四十个文件主要包括Java编译类文件、源码文件、JAR依赖包、SQL数据库脚本以及数据库数据文件和Word说明文档包体整体约2.02MB。其中SQL脚本与mdf/ldf数据库文件可直接挂接使用docx说明文档梳理了部署步骤与注意事项适合快速搭建演示环境。目前已有三百七十七人学习过内容涉及数据库建表、日志管理、Swing界面打包运行和miglayout布局依赖等关键部分通过源码目录与数据库脚本可快速掌握房间、入住、结账等模块的对应关系整体目录结构清晰便于按需提取核心代码与配置。 做宾馆管理系统这个项目的时候我记得最清楚的一次崩溃是程序写完了、界面也调好了结果在演示前十分钟发现数据库连不上。当时用的就是SQL Server和Swing这套组合数据库一崩前面所有工作全部白费。后来我才意识到用这套技术栈做管理系统难点从来不在写功能而在数据库的安装配置、连接稳定性、以及各种莫名其妙的坑。这篇就把我从零到一做完整个系统的过程完整拆开包括表结构怎么设计、Swing界面怎么组织、哪些地方容易翻车希望帮后来的人少走弯路。使用SQL SERVERSwing实现宾馆管理系统这个项目是典型的Java桌面应用课程设计选题用SQL Server存数据用Swing搭界面两者通过JDBC连接。系统要解决的核心问题是宾馆日常运营中的房间管理、宾客入住、退房结算、预订登记这些流程把从前台手工登记、纸质账本记录的原始方式变成一套可视化的电脑操作流程。我先说结论这套组合虽然看起来有点“复古”但对学习者和实战练手来说非常合适。SQL Server负责数据的持久化存储和事务处理Swing负责和用户交互两者分工明确能让开发者在短时间内完整体验一遍从建库、建表、连接、查询到界面绑定的全流程。我写的所有代码和思路都是可复现的用的就是最基础的JDBC加Swing组件没有任何花哨的框架依赖。1. 项目整体设计与需求梳理1.1 这个系统到底要做什么刚开始别急着写代码先把需求理清楚。我接手这个项目时先列了一个完整的业务清单确保数据库设计的时候不会漏字段、不会缺表。一个标准宾馆管理系统至少要覆盖这些功能房间信息管理房间编号、楼层、房型、价格、状态空闲/入住/脏房/维修的增删改查。宾客信息管理姓名、身份证号、电话、会员信息等基础档案方便入住时快速登记。预订管理客人提前订房记录预订日期、入住日期、预离日期支持取消预订。入住登记开单把空闲房间分配给宾客记录入住时间、押金、入住天数。退房结算根据实际住宿天数、房间单价计算金额支持押金抵扣生成结算记录。统计报表按日或按月统计入住率、营业额、房型销售情况为经营决策提供参考。我把这些需求整理成了功能模块图虽然没有用专业工具画但在脑子里过一遍后面写表结构就有方向了。核心点在于房间和宾客之间是多对多的关系一个房间不同时间段住不同的客人一个客人也可以多次入住不同房间所以中间必须有一张订单/入住记录表来做关联。这是整个数据库设计的枢纽。1.2 为什么选 SQL Server Swing而不是其他组合可能有人会问现在都什么年代了为什么还要用Swing做界面、用SQL Server做数据库我的看法是这个组合有自己的独特价值尤其在学习和课程设计场景下它是最稳的搭配。先说SQL Server。它是微软家的关系型数据库有完整的图形化管理工具SQL Server Management Studio创建数据库、设计表、写查询语句都有可视化支持对初学者特别友好。MySQL也好但很多学校机房默认装的就是SQL Server而且SQL Server的T-SQL语法、事务处理、约束机制都非常标准学好它再转其他数据库基本是无痛的。另外在实际项目中很多中小型企业的管理系统确实还在用SQL Server学这个并不算“学了个没用的东西”。再说Swing。虽然它长得朴素但它是Java原生GUI框架里资料最多、最稳定的一个。网上搜“Java登录界面”“Swing增删改查”一抓一大把现成代码遇到问题很容易找到解决方案。JavaFX虽然更现代但资料相对少对新手踩坑成本更高。Swing的核心组件JTable、JTextField、JComboBox、JButton、CardLayout功能完全够用而且不依赖外部jar包部署简单双击就能跑。这套组合还有一层好处它强制你把JDBC搞明白。用MyBatis或者Hibernate这些框架数据库连接细节都被封装了出了问题反而难以定位。用原生JDBC你要自己管驱动加载、连接创建、PreparedStatement拼参数、ResultSet遍历、资源关闭这些步骤虽然繁琐但对理解Java和数据库的交互机制非常有帮助。我做完这个项目之后再去看MyBatis的源码很多设计思路一下就通了。2. 数据库设计从表结构到初始化数据2.1 核心表设计与关系梳理数据库是整个系统的心脏。我在设计表结构时遵循了一个原则功能驱动表设计而不是先建表再想功能。也就是说每建一张表之前先在纸上画出它的业务场景明确这张表要支撑哪些页面和操作。最终我的数据库命名为HotelDB里面设计了四张核心表第一张是房间表Room字段包括房间ID主键自增、房间号、房型、床位数、门市价、房间状态、备注。房间状态我用了字符串类型的varchar取值是“空闲”“入住”“脏房”“维修”方便直接显示在界面上。虽然用int类型配合枚举更规范但对小项目来说字符串可读性更强调试也方便。第二张是宾客表Guest字段包括宾客ID主键自增、姓名、身份证号、手机号、会员等级、注册日期。身份证号我设置了唯一约束因为在实际场景中不允许同一个身份证登记两次。这里有个小知识点身份证号是18位varchar(18)就够了但如果你要考虑港澳台或者护照字段长度建议放宽到30。第三张是订单表Orders这是系统的核心业务表。字段包括订单ID主键、订单编号业务上可读的唯一编号、宾客ID外键、房间ID外键、预订日期、入住日期、预离日期、实际退房日期、押金、订单状态。订单状态我用“已预订”“已入住”“已退房”“已取消”四个值来表示订单生命周期。这张表是典型的主外键关联表它把宾客和房间联系起来了。第四张是操作日志表Log记录谁在什么时间执行了什么操作。虽然这个需求不是硬性的但我强烈建议加上。遇到数据异常的时候翻日志能快速定位问题。字段就四个日志ID、操作类型、操作内容、操作时间。四张表的关系很清晰一个宾客可以有多条订单记录一个房间也可以出现在多条订单记录里数据冗余被订单表拆解了符合第三范式的基本要求。CREATE DATABASE HotelDB; GO USE HotelDB; GO CREATE TABLE Room ( room_id INT IDENTITY(1,1) PRIMARY KEY, room_no VARCHAR(10) NOT NULL UNIQUE, room_type VARCHAR(20) NOT NULL, bed_count INT NOT NULL, price DECIMAL(10,2) NOT NULL, room_status VARCHAR(10) DEFAULT 空闲, remark VARCHAR(200) ); GO CREATE TABLE Guest ( guest_id INT IDENTITY(1,1) PRIMARY KEY, guest_name VARCHAR(50) NOT NULL, id_card VARCHAR(30) NOT NULL UNIQUE, phone VARCHAR(20), member_level VARCHAR(10) DEFAULT 普通, reg_date DATETIME DEFAULT GETDATE() ); GO CREATE TABLE Orders ( order_id INT IDENTITY(1,1) PRIMARY KEY, order_no VARCHAR(32) NOT NULL UNIQUE, guest_id INT NOT NULL REFERENCES Guest(guest_id), room_id INT NOT NULL REFERENCES Room(room_id), book_date DATETIME DEFAULT GETDATE(), checkin_date DATETIME NOT NULL, checkout_date DATETIME NOT NULL, actual_checkout DATETIME, deposit DECIMAL(10,2) DEFAULT 0, order_status VARCHAR(10) DEFAULT 已预订 ); GO这里有一个细节必须注意为了保持历史数据的完整性我特意没有在Orders表里直接存“房间价格”这个字段。为什么不存因为房间的价格会调整如果订单只依赖Room表的当前价格那历史账单的金额就会跟着变结算就会出错。正确做法是在订单表里冗余一个“下单时的房价”防止价格变动影响历史数据。我实际开发中这个坑踩过一次客人退房结算时发现金额和入住时对不上就是因为房间价格被修改了。所以建议你在Orders表里再加一个字段room_price_at_checkin DECIMAL(10,2)用下单时的价格快照来结算。2.2 初始数据准备与约束设计建完表之后别急着写Java代码先把初始数据插进去。系统要跑得起来房间数据必须有不然整个前台界面全是空的。我手动插了20条房间数据覆盖单人间、标准间、大床房、豪华套房四种房型价格从128到688不等。插数据的时候顺手把房间状态初始化为“空闲”这样后面测试入住流程就有素材了。除了数据我还建了几个必要约束和索引。比如Orders表的guest_id和room_id外键能保证不会出现“订单关联了一个不存在的宾客”这种脏数据order_no做了唯一约束避免并发时生成重复订单编号checkin_date和checkout_date之间我加了一个CHECK约束保证入住时间早于退房时间。虽然Java代码里也能校验但数据库层面做约束是最可靠的一道防线。ALTER TABLE Orders ADD CONSTRAINT CK_Orders_Date CHECK (checkin_date checkout_date); CREATE INDEX IX_Orders_RoomId ON Orders(room_id); CREATE INDEX IX_Orders_GuestId ON Orders(guest_id);索引的作用要解释一下。Orders表会是整个系统查询最频繁的表前台查房态、后台统计报表都绕着它转。在room_id和guest_id上建索引能让基于外键的查询速度大幅提升。对于几百条数据可能感觉不到差别但数据量到几万条之后有没有索引完全是两个体验。2.3 视图与存储过程的选用在我设计数据库时还额外创建了一个视图V_RoomStatus把Room表、Orders表的状态关联起来直接查询出每个房间当前是否被预订、住客是谁、预计什么时候退房。视图的好处是Java端SQL语句变得极其简单只需SELECT * FROM V_RoomStatus不需要在Java代码里写复杂的多表关联。至于存储过程我的建议是项目初期不要用把SQL逻辑直接写在Java的DAO层里。这样代码可读性高、调试方便。等项目稳定了、确实发现某段SQL频繁复用再封装成存储过程不迟。很多人喜欢一上来就把所有业务逻辑塞进存储过程结果Java代码只剩下一堆存储过程调用出了问题排查链路变长这是得不偿失的。3. Swing 界面搭建与 JDBC 连接3.1 界面布局思路主窗口 卡片式面板Swing界面的搭建我走了不少弯路。最开始我打算用一个窗口塞进所有功能放上七八个按钮和表格结果界面混乱到连我自己都不想用。后来参考了别人做的管理系统成熟布局发现最优解是主窗口 顶部菜单栏 左侧导航栏 右侧卡片式主面板。左侧导航栏放功能菜单项包括房态管理、入住登记、退房结算、预订管理、宾客管理、统计报表。点击不同菜单右侧主面板切换对应的内容面板。实现这个效果用Swing的CardLayout最方便每个功能模块都是一个独立JPanel把它们注册到同一个CardLayout里通过show()方法切换。CardLayout cardLayout new CardLayout(); JPanel mainPanel new JPanel(cardLayout); mainPanel.add(roomPanel, room); mainPanel.add(checkinPanel, checkin); mainPanel.add(checkoutPanel, checkout); mainPanel.add(reservePanel, reserve); mainPanel.add(guestPanel, guest); mainPanel.add(reportPanel, report); // 导航按钮点击时切换卡片 cardLayout.show(mainPanel, checkin);这样做的好处非常明显每个功能模块的代码相互独立新增一个功能只需要新建一个JPanel注册进去不影响其他模块代码维护难度大大降低。3.2 JDBC 连接配置与工具类封装JDBC连接是这套系统里最容易出问题的一环。先说一下我的整体连接层次用一个DBUtil工具类负责获取连接和关闭资源业务层DAO通过这个工具类拿到Connection对象执行SQL。这是最标准的Java数据库开发模式也是很多企业项目的基础雏形。public class DBUtil { private static final String URL jdbc:sqlserver://localhost:1433;DatabaseNameHotelDB;encryptfalse;trustServerCertificatetrue; private static final String USER sa; private static final String PASSWORD 你的密码; static { try { Class.forName(com.microsoft.sqlserver.jdbc.SQLServerDriver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { try { if (rs ! null) rs.close(); if (ps ! null) ps.close(); if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这段代码有两个坑提醒大家注意。第一URL里必须加上encryptfalse;trustServerCertificatetrue这两个参数。因为较新版本的Microsoft JDBC驱动默认开启加密连接而你本地SQL Server如果没有配置证书连接就会直接报错提示SSL证书相关的问题。第二驱动类名是老版本驱动的com.microsoft.sqlserver.jdbc.SQLServerDriver新驱动也是如此这个不变但一定要把对应的mssql-jdbc-x.x.x.jre8.jar加到项目的构建路径里否则Class.forName直接抛ClassNotFoundException。关于sa账号还是Windows认证的问题我的建议是本地开发用sa账号最省事。SQL Server默认安装时sa账号是禁用的需要先用Windows认证登录数据库管理器在安全性-登录名里找到sa右键属性设置密码并启用登录。同时还要把服务器属性里的“SQL Server 和 Windows 身份验证模式”打开。这两步不设置好Java代码里用sa连数据库肯定连不上。3.3 写SQL的安全姿势PreparedStatement代替拼接我在代码里严格贯彻了“禁止字符串拼接SQL”的原则全部使用PreparedStatement预编译参数。原因很简单第一是防止SQL注入攻击第二是避免字符串拼接时引号匹配问题。比如查询某个宾客的订单时如果写成SELECT * FROM Orders WHERE guest_id id一旦id传进来的值是1 OR 11整张表的数据都会被掏出来这是安全漏洞。String sql SELECT * FROM Orders WHERE guest_id ?; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, guestId); ResultSet rs ps.executeQuery();这里额外说一个小技巧在Swing的表格数据刷新上我写了一个通用的loadData()方法每次执行完增删改操作后就调用一次重新从数据库查询最新数据填充到JTable里。这样能保证界面数据始终和数据库同步避免出现操作完后表格数据还是旧数据的情况。4. 核心功能模块的实现细节4.1 入住登记与房间状态流转入住登记是整个系统的门面功能前台的日常工作主要就是它。界面设计上左边放房间列表用JTable展示颜色区分状态右边放宾客信息表单和操作按钮。操作流程是这样的前台选中一个“空闲”状态的房间录入宾客姓名、身份证、手机号设置入住天数系统自动计算预离日期和应收押金点击“办理入住”完成开单。入住操作的事务逻辑是关键。它不是一个简单的INSERT而是两个动作必须同时成功第一往Orders表插入一条新订单记录第二把Room表对应房间的状态从“空闲”改成“入住”。这两步必须放在同一个数据库事务里否则就会出现“订单创建了但房间还是空闲”的数据不一致状态。Connection conn DBUtil.getConnection(); conn.setAutoCommit(false); try { // 插入订单记录 String insertOrder INSERT INTO Orders(order_no, guest_id, room_id, checkin_date, checkout_date, deposit, order_status) VALUES(?,?,?,?,?,?,?); PreparedStatement ps1 conn.prepareStatement(insertOrder); // ... 设置参数 ps1.executeUpdate(); // 更新房间状态 String updateRoom UPDATE Room SET room_status入住 WHERE room_id?; PreparedStatement ps2 conn.prepareStatement(updateRoom); ps2.setInt(1, roomId); ps2.executeUpdate(); conn.commit(); } catch (SQLException e) { conn.rollback(); e.printStackTrace(); } finally { conn.setAutoCommit(true); DBUtil.close(conn, null, null); }订单号生成我踩过一个坑。最开始用的方法是System.currentTimeMillis()加三位随机数拼一个字符串但后来发现同一毫秒内连续两次开单有可能撞号。后来改成DD yyyyMMddHHmmss 四位自增序号自增序号用一个静态变量保存在内存里每次取号加一实测下来再没出现过重复。如果你有更严谨的并发要求也可以用数据库序列或者雪花算法但小项目这个方案完全够用。生成订单号还有一个细节日期格式化用LocalDateTime和DateTimeFormatter不要用旧版SimpleDateFormat因为后者线程不安全。虽然Swing是单线程模型但养成用新API的习惯总是好的。4.2 退房结算与金额计算退房功能比入住复杂一些因为涉及金额计算。我的结算逻辑是这样实现的选中一条“已入住”状态的订单系统拉取订单信息、关联宾客信息和房间单价这里用我前面说的room_price_at_checkin快照然后自动计算明细。金额计算规则总房费 实际入住天数 × 单价。实际入住天数的计算是难点不是简简单单拿退房时间减入住时间除以一天毫秒数就完事的。比如客人2月1日23:59入住2月2日00:01退房实际只住了一个小时但按行业惯例应该算一天的房费如果客人超时到18:00退房通常还要再算半天或者多收一天。具体规则各家宾馆不同我实现的是最常见的一种按自然日计算当天入住算一天第二天中午12点前退房算一天超过12点算一天半超过18点算两天。这个计算逻辑写起来要小心边界情况很多。long diffMillis actualCheckout.getTime() - checkinDate.getTime(); long diffDays TimeUnit.MILLISECONDS.toDays(diffMillis); // 判断是否超过当天18点 Calendar cal Calendar.getInstance(); cal.setTime(actualCheckout); int hour cal.get(Calendar.HOUR_OF_DAY); if (diffMillis % (24 * 60 * 60 * 1000) ! 0) { diffDays 1; // 不满一天按一天 }加一个提醒日期计算这里强烈建议先把逻辑图层层拆开单独写一个BillingUtil工具类里面放计算天数的静态方法配上几个单元测试。我当年就是偷懒没写测试结果跨月、跨年这种边界日期算错了好几次最后还是老老实实补了测试用例。退房操作同样需要事务更新订单状态为“已退房”、写入实际退房时间、把房间状态改成“脏房”。注意不是改成“空闲”因为客人退房后保洁还没打扫完这个房间不能直接安排下一位客人入住。这个逻辑我称之为状态流转的“标准路径”空闲→入住→脏房→空闲中间多加一道“脏房”状态能极大提升系统的真实可用性。4.3 预订管理与房态冲突检测预订管理解决的是“客人还没到店但想提前锁定房间”的需求。预订界面相对简单核心逻辑是冲突检测用户选择时间段和房型后系统要查询该时间段内指定房型还有没有空闲房间。查询逻辑考虑起来有一点绕但可以用SQL的NOT EXISTS子查询很优雅地解决。核心就是找那些“在目标时间段内没有被未取消订单占用的房间”SELECT r.* FROM Room r WHERE r.room_type ? AND r.room_status 空闲 AND NOT EXISTS ( SELECT 1 FROM Orders o WHERE o.room_id r.room_id AND o.order_status IN (已预订, 已入住) AND ? o.checkout_date AND o.checkin_date ? )这个SQL里面用了两个参数三处?含义分别是新订单的入住日期和退房日期。它的区间重叠判断逻辑是当且仅当新入住日期早于已存在订单的退房日期、且新退房日期晚于已存在订单的入住日期时两个订单的时间段才有交集。这段逻辑我建议反复推敲它涵盖了各种边界情况前后订单首尾相接不会误判为冲突完全包含也不会漏判是时间冲突检测的经典写法。预订到的客人到店之后选中“已预订”订单点击“办理入住”系统把订单状态从“已预订”改成“已入住”同时更新房间状态为“入住”流程就走通了。5. 实际开发中踩过的坑与排查技巧5.1 SQL Server 连接不上的几类原因必须说SQL Server的连接问题占了我整个开发周期至少三分之一的时间。我把最典型的问题列成一张速查表照着检查能省下好几个小时问题现象可能原因排查与处理ClassNotFoundException缺少JDBC驱动jar包下载mssql-jdbc驱动加入构建路径Connection refused: connectSQL Server未启动或端口不对检查SQL Server服务是否运行确认端口1433Login failed for user sasa账号禁用或密码错误用Windows认证登录启用sa并重置密码TCP/IP 协议未启用SQL Server网络配置缺失打开SQL Server配置管理器启用TCP/IPSSL 证书相关错误JDBC驱动默认启用加密URL加encryptfalse或trustServerCertificatetrueCannot open database数据库不存在或账号无权限确认HotelDB已经创建授权sa对库的访问权限连接问题排查有一个土办法非常管用先用SQL Server Management Studio手动连一次数据库如果能连上说明数据库本身没问题问题一定出在Java代码或者驱动的配置上如果连不上先解决SQL Server自己这边的问题再回头查代码。用排除法砍掉一半的可能原因排查效率立刻翻倍。5.2 中文乱码与日期时间格式问题Swing界面和SQL Server之间的中文乱码是我这个项目里最烦人的问题。现象很典型往数据库里插入中文数据时存进去的是“???”查出来也是“???”但SQL Server Management Studio里直接插入中文却一切正常。问题的根源是字符编码不一致。SQL Server的字段要用nvarchar/nchar类型而不是varchar/char因为前者是Unicode编码支持中文等全球字符后者按数据库默认字符集存储在中文Windows环境下虽然是GBK但Java字符串默认是UTF-16两边一对接就直接乱掉。所以我的解决方案是所有可能存中文的字段全部使用nvarchar同时Java端连接字符串里不额外设置编码参数让驱动默认走Unicode。还有一个容易忽略的点JTable显示中文乱码和数据库没关系是Swing默认字体在部分系统上的渲染问题。解决办法是在界面初始化时给所有组件设置中文字体例如new Font(Microsoft YaHei, Font.PLAIN, 14)或者用UIManager.setLookAndFeel配合系统字体。试过就知道设置字体后的界面颜值也会提升不少。日期时间问题同样来自Java的java.util.Date和数据库的DATETIME之间的转换。Java预编译语句里ps.setTimestamp()处理DATETIME最稳对应的rs.getTimestamp()取值后转成java.util.Date或LocalDateTime保证时间精度和数据一致性。另外SQL Server的GETDATE()返回的是数据库服务器时间Java的new Date()返回的是应用所在机器时间如果数据库和应用跑在不同的机器上注意时区差异可能导致日期错位。5.3 一些实用的开发小经验项目做到最后我总结了几条对后来者特别有帮助的小经验。第一数据库文件一定要勤备份。我提供的方式是在开发过程中定期用SQL Server Management Studio的“生成脚本”功能把表结构、存储过程、视图还有基础数据全部导出一份SQL文件。这样不管代码怎么改数据库随时能重建不至于因为一次错误操作就把整个库搞坏。第二JTable的使用上有两个常见痛点。一是默认情况下不能直接编辑单元格需要重写isCellEditable()方法并实现TableModel二是表格数据更新后界面不自动刷新需要调用table.repaint()配合DefaultTableModel.setDataVector()重建数据。如果你能在项目初期就把这两个机制搞清楚后面做任何管理系统的界面都会顺手很多。第三整个项目的代码组织建议按MVC的思想分层数据库工具类DBUtil放Model层业务逻辑方法放DAO层比如RoomDao、OrderDaoSwing的JPanel和事件处理放View层。虽然桌面程序不像Web项目那样严格要求分层但一个清晰的结构能让代码量到一万行的时候依然不乱也更方便半年后的你读懂自己写的东西。第四界面上的按钮点击之后一定要有反馈。我最初做的时候点击“保存”按钮没有任何反应数据其实已经写进数据库了但用户看不到任何提示就以为系统卡了。正确的做法是执行成功弹出JOptionPane.showMessageDialog提示框失败时弹出错误信息。这个细节看起来微不足道但对使用体验的影响非常大。还有一个提醒无关技术但很重要系统里涉及宾客身份证号、手机号这些个人信息开发和学习时用假数据就好不要用真实人员的身份信息来测试这是基本的职业操守也能避免不必要的麻烦。做完这个项目我最大的体会是技术栈老不老不是关键关键是能不能把一条完整的数据链路打通让数据从界面输入、流经业务逻辑、落进数据库、再被查询展示出来。SQL Server加Swing这套组合虽然朴素但它逼着我把数据库设计、JDBC、事务、GUI事件处理每一个环节都亲手摸了一遍。后来我再接Web项目时很多基础概念都是一通百通的。如果你也在做类似的课程设计或者练手项目建议务必在上面这些细节上多花点时间。至少在你演示系统的时候不会像我当年那样栽在数据库连接这种“低级但是致命”的问题上。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

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