简介:基于 Java Swing 的在线图书商城管理系统以完整源码和数据库打包提供,适合 Java 课程设计、毕业设计以及希望学习桌面 GUI 开发的读者参考。系统覆盖图书展示、购物车、订单处理等商城核心流程,界面直观,既可运行演示,也可作为二次开发骨架,便于理解桌面商城的基本模块划分。压缩包共 59 个文件,包含 29 个 Java 源文件、16 个 XML 界面与工程配置文件、6 张界面相关图片、SQL 数据库脚本及 properties 等配置,整体约 413KB,工程按 Maven/IDEA 结构组织,导入后可按模块快速定位。附带 database.sql 能直接建库,适合对照学习 Swing 事件监听、布局管理和 JDBC 数据访问的配合方式;源码中 GUI 设计与业务逻辑分层清晰,便于改造成完整课设或毕设作品。已有 483 人学习下载,适合需要现成高分方案或入门桌面商城开发的读者。
1. 在线图书商城管理系统:这套 Swing 源码到底能做什么
很多人一看到 Swing 就以为是上世纪的老古董,但你翻过近几年的 Java 面试题和课程设计题目会发现,“带 GUI 界面的在线图书商城”依然是出现频率最高的综合练习之一。这个管理系统说白了就是用 Java Swing 画窗口,用 JDBC 连 MySQL,把注册登录、图书浏览、购物车、下单结算这些环节做成一个完整闭环。源码包里同时附带了数据库脚本,不需要你从零设计表结构。适合想在一周内跑通一个完整 Java 项目的在校生,也适合想快速复习事件监听、集合和 SQL 的开发者。这篇文章会以这类源码包为例,带你走完从解压到跑通、再改到答辩能讲的完整路线。
2. 把源码跑起来:JDK、MySQL 与最小启动命令
拿到 zip 后的第一件事不是急着双击运行,而是把运行环境核对清楚。Swing 项目不像 Spring Boot 自带嵌入式容器,它依赖的 GUI 库来自 JDK,但数据库驱动通常以 jar 包形式放在 lib 目录。如果驱动没被正确引入,代码写得再漂亮也跑不出登录窗口。
2.1 拿到 zip 后先检查这些文件
常见做法是解压后看到这样几类东西:src 目录放 Java 源码,sql 目录放建库建表脚本,lib 目录放第三方 jar 包,额外的 README、txt 或 pdf 用来写使用说明。我一般不会直接去翻 README,而是先打开 src 目录找继承JFrame或包含main方法的类,这类类就是整个程序的启动入口;如果 IDE 支持搜索,直接搜public static void main,很快就能定位主类名。
| 文件/目录 | 常见作用 | 需要确认的点 |
|---|---|---|
| src | Java 源码 | 确认包名结构和主类 |
| sql / *.sql | 建库建表脚本 | 确认有没有 CREATE DATABASE |
| lib | 第三方依赖 | 确认是否存在 MySQL 驱动 |
| README | 配置说明 | 确认数据库账号密码要求 |
这里最容易犯的错是只看 README 就开始导库,结果脚本里可能没有CREATE DATABASE,直接导到了上一次测试留下的旧库。我的经验是先把 SQL 文件打开,搜一下CREATE DATABASE和USE这两个关键字,再决定下一步怎么导。
2.2 建库导数据:MySQL 命令与参数说明
如果 SQL 脚本自带建库语句,最省事的命令是:
mysql -u root -p < book_store.sql这里-u root是用户名,-p表示需要输入密码,< book_store.sql把脚本内容作为 SQL 输入到 MySQL 控制台。逻辑上等价于按顺序执行脚本里的每一句 SQL,适合一次性导入。但这个命令对“脚本里没有建库语句”的情况没法处理,它会直接报No database selected。
更稳妥的做法是先手动建库,再指定库导入。在 MySQL 会话里执行:
CREATE DATABASE IF NOT EXISTS book_store DEFAULT CHARACTER SET utf8mb4; USE book_store; SOURCE /path/to/book_store.sql;SOURCE是 MySQL 客户端命令,不是 SQL 标准语句,和mysql < file相比,它能在执行过程中持续输出错误信息,而且能正确处理脚本里的USE切换。DEFAULT CHARACTER SET utf8mb4是为了让图书名称这类中文数据不乱码;如果脚本里已经定义了CHARSET=utf8mb4,这里可以省略。导入完成后用SHOW TABLES;看一眼表数量,再用SELECT COUNT(*) FROM book;确认有种子数据,这一步能省掉后面很多“连不上、查不到”的排查时间。
2.3 启动 GUI:IDE 配置和命令行两种方式
带 GUI 的项目,我首选 IDEA 或 Eclipse,在 IDE 里可以直接看源码、打断点。打开项目后先检查 Project Structure 里的 SDK 版本,再把 lib 目录加入 Libraries。接着找到带main方法的窗口类,右键 Run。如果没有编译错误,几秒钟后就能看到登录窗口。
命令行方式看似麻烦,但能真正验证依赖是否完整,也能避免 IDE 悄悄给你的 classpath“加料”造成的假象:
javac -encoding UTF-8 -cp "lib/*" -d out src/**/*.java java -cp "out;lib/*" com.bookstore.MainFrame-encoding UTF-8指定源码编码,Windows 上默认经常是 GBK,不加这句,中文注释和字符串在编译后就会乱;-cp "lib/*"用通配符引入所有 jar,注意 Java 通配符只匹配 jar,不匹配子目录;-d out把编译产物放到 out 目录,避免和源码混在一起;最后的com.bookstore.MainFrame要替换成实际主类的全限定名。Windows 用分号分隔 classpath,Linux 和 macOS 用冒号,跨环境时这个细节最容易让人翻车。到这里如果登录窗口能弹出来,环境就算打通了,接下来该做的不是点按钮,而是先看懂数据库表结构。
3. 数据库设计与源码结构:购物车和订单是怎么串起来的
在线图书商城看起来是 Swing 界面的事,本质上是几张表在后台撑着。界面只是把表数据映射成表格和输入框。把表结构拆明白,后面改功能才有方向。
3.1 用户、图书、分类三张基础表
任何商城都绕不开用户和商品。用户表不用多说,图书表必须存价格和库存,分类表用来做列表筛选。常见字段设计如下。
| 表名 | 核心字段 | 说明 |
|---|---|---|
| user | id, username, password, role, created_time | role 区分管理员和普通会员 |
| category | id, name, parent_id | parent_id 支持二级分类,没有就填 0 |
| book | id, category_id, title, author, publisher, price, stock, cover, description | cover 存图片相对路径 |
给一段最小建表 SQL:
CREATE TABLE `book` ( `id` INT NOT NULL AUTO_INCREMENT, `category_id` INT DEFAULT NULL, `title` VARCHAR(128) NOT NULL, `author` VARCHAR(64) DEFAULT NULL, `price` DECIMAL(10,2) NOT NULL, `stock` INT NOT NULL DEFAULT 0, `cover` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;价格为什么用DECIMAL(10,2)而不是FLOAT?因为 FLOAT 是近似值,计算订单合计时会出现 19.99 变成19.989999的问题。这种数据一旦展示到 Swing 表格里,用户一眼就会觉得系统不可靠。AUTO_INCREMENT让主键自增,但这也是第 5 章要说的一个坑点。ENGINE=InnoDB必须写,MyISAM 不支持事务,而这个项目下单时要扣库存,没有事务的话,库存和订单会各扣各的。
3.2 购物车与订单表的外键关系
购物车有两种常见设计:一种是把 cart 建成数据库表,另一种是只在内存里用集合保存。带数据库的在线商城源码一般两种都会涉及,但订单表无论如何要落库。购物车只是临时状态,用户关掉窗口就没了;订单是业务记录,必须长期保存。user 和 book 之间通过 cart 关联,订单则要把下单时的书名和价格快照到 order_item,防止以后改 book 表价格导致历史订单金额跟着变。
CREATE TABLE `t_order` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `user_id` INT NOT NULL, `total_amount` DECIMAL(10,2) NOT NULL, `status` TINYINT DEFAULT 0, `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ); CREATE TABLE `order_item` ( `id` INT NOT NULL AUTO_INCREMENT, `order_id` BIGINT NOT NULL, `book_id` INT NOT NULL, `book_name` VARCHAR(128) NOT NULL, `price` DECIMAL(10,2) NOT NULL, `quantity` INT NOT NULL, PRIMARY KEY (`id`) );注意我给订单表起了t_order而不是order,因为order是 SQL 关键字,不转义会直接报语法错误。如果源码里用了别的名字,就不要乱改。order_item 里的book_name和price就是快照,精明的做法是加入购物车那一刻就把当前价格带过去,而不是下单时再查一次,否则用户在购物车里看到 20 元,结算时变成 25 元,体验会很糟糕。
3.3 源码分包:界面、业务、DAO 的常见结构
源码质量好不好,看包名就能猜个大概。常见分包是com.bookstore.ui、com.bookstore.dao、com.bookstore.model、com.bookstore.util,职责分别对应 Swing 窗口、JDBC 数据访问、实体对象、数据库连接工具。有人会把所有类塞进默认包,开始写很快,等到一个窗口类几百行时,找 bug 只能靠 Ctrl+F 来回跳。
判断源码是不是值得学习,我一般会画一条调用链:登录窗口MainFrame->UserDao.findByUsernameAndPassword->DBUtils.getConnection-> user 表。这条链能跑通,说明 UI 和 DAO 分层清楚。最怕的是直接把Connection写在按钮事件里,界面每点一次按钮就经历一次连接打开、查询、关闭,代码看着短,后面想改数据库密码或换连接池时,只能满文件找字符串。这套源码的核心增删改查,全是通过 DAO 层完成的,这也是 Java 基础能力的一个集中体现。
4. 核心 GUI 功能拆解:登录、商品列表购物车、下单
这一章进入 Swing 本身。改源码之前先搞懂三件事:事件监听怎么触发业务、JTable 表格怎么刷新、下单怎么保证事务。这三个点是系统的核心,也是面试时最容易被追问的地方。
4.1 登录窗口:Swing 事件监听与 JDBC 验证
登录按钮的常见实现,是在ActionListener里取输入框的值,调用 DAO 查询,成功则跳转主界面,失败则弹窗提示。一段典型代码是这样的:
loginBtn.addActionListener(e -> { String username = userField.getText().trim(); String password = new String(pswdField.getPassword()); User user = new UserDao().findByUsernameAndPassword(username, password); if (user != null) { dispose(); new MainFrame(user).setVisible(true); } else { JOptionPane.showMessageDialog(LoginFrame.this, "用户名或密码错误", "登录失败", JOptionPane.ERROR_MESSAGE); } });这段代码里有两个细节值得品。第一,getPassword()返回的是char[],不是 String。原因很简单,String 是不可变的,会一直留在内存里,而字符数组用完可以马上清空,这是 Java 基础题里常考的安全点。第二,trim()去掉用户输入两端的空格,否则用户在用户名前面不小心多打一个空格,数据库里怎么查都查不到。登录成功后把user对象传给MainFrame,后面下单时才能拿到user_id,这就是分层的意义。
4.2 图书列表到购物车:JTable 与 DefaultTableModel
图书列表最常用的是JTable,配合DefaultTableModel来管理数据。JTable 本身不直接放数据,而是通过 TableModel 暴露行列结构,这个设计一开始容易绕晕。
DefaultTableModel model = new DefaultTableModel( new Object[]{"ID", "书名", "作者", "价格", "库存"}, 0); bookTable.setModel(model); List<Book> books = new BookDao().listAllBooks(); for (Book b : books) { model.addRow(new Object[]{ b.getId(), b.getTitle(), b.getAuthor(), b.getPrice(), b.getStock() }); }new Object[]{...}, 0表示初始 0 行数据,但列头已经定义好,之后每次addRow就往末尾加一行。这里必须把books这个 List 保存为成员变量,不能只活在局部变量里。因为点击“加入购物车”按钮时,还要通过选中的行号去取对应的 Book 对象。如果 addRow 之后把 List 丢了,选行时只能拿到表格里的字符串,拿不到图书 ID,下单流程就断了。
“加入购物车”的按钮逻辑一般是:
int row = bookTable.getSelectedRow(); if (row < 0) { JOptionPane.showMessageDialog(this, "请先选择一本书"); return; } Book selected = books.get(row); cart.addItem(selected, 1);getSelectedRow()返回 -1 表示当前没有选中任何行,必须先判空,否则books.get(-1)会抛异常。点击后不直接写数据库,而是更新内存里的购物车,等结算时再统一处理,这样用户连续加购多本书时体验更顺。
4.3 订单生成:事务处理和库存扣减
很多初学 Swing 的人会在下单时先 INSERT 订单,再 UPDATE 库存,这两步之间一旦出错,订单就会变成半截数据。正确做法是把操作包进同一个事务,任何一步失败直接回滚。
Connection conn = DBUtils.getConnection(); conn.setAutoCommit(false); try { long orderId = insertOrder(conn, userId, totalAmount); for (CartItem item : cart.getItems()) { updateStock(conn, item.getBookId(), item.getQuantity()); insertOrderItem(conn, orderId, item); } conn.commit(); } catch (SQLException ex) { conn.rollback(); throw new RuntimeException("下单失败,订单未保存", ex); } finally { conn.setAutoCommit(true); conn.close(); }setAutoCommit(false)是关键,它告诉 JDBC 不要每条 SQL 都自动提交,等手动 commit。updateStock里应该写成UPDATE book SET stock = stock - ? WHERE id = ? AND stock >= ?,这样能在数据库层面防止库存扣成负数。如果影响行数为 0,说明库存不足,应该抛异常回滚。最后在 finally 里把autoCommit恢复为 true 并关闭连接,这个习惯能防止连接被污染。整个过程依赖 InnoDB,如果导入脚本时用了 MyISAM,事务就是摆设,所以第 3 章的建表语句不是白写的。
5. Swing 图书商城开发避坑:5 个必读的常见问题
到这里系统能跑了,但真正投入改功能时,花时间最多的往往不是业务代码,而是环境、编码、线程这些底层细节。我按现象、原因、解决的顺序整理了五条,都是这类 GUI 项目里高发的问题。
5.1 JDBC 驱动连接失败
现象是点登录后控制台立刻抛java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver,或者报No suitable driver found。原因有两类:一类是 lib 目录下的 MySQL 驱动 jar 没有被加进 classpath,另一类是源码还写着老式的com.mysql.jdbc.Driver,而本机 MySQL 驱动是 8.x。解决办法是先看Class.forName(...)里的路径,MySQL 8 以上必须用com.mysql.cj.jdbc.Driver;然后在 IDE 里重新把 lib 目录加入 Libraries。命令行启动就按第 2 章-cp "lib/*"的方式处理。出现这个异常不代表数据库有问题,先检查驱动路径。
5.2 中文乱码
现象是图书标题显示成???或者方框乱码。原因可能出现在三个环节:数据库连接 URL 没指定字符集、表结构用了 latin1、源码编译时用了 GBK 而运行环境是 UTF-8。我的排查顺序先看连接 URL:
jdbc:mysql://localhost:3306/book_store?useUnicode=true&characterEncoding=utf8没有这段参数就加上;再看 SQL 脚本里的CHARSET=utf8mb4;最后确认编译命令或 IDE 编码设置是 UTF-8。三处必须一致,只改任何一处都治不好。这种问题在现场实机演示时出现最尴尬,所以在第一次导库时就要把检查做完。
5.3 图片封面显示不出来
现象是窗口能开,但图书封面 JLabel 是空的,控制台报NullPointerException或者找不到文件。原因是代码里用了相对路径new ImageIcon("images/cover.png"),而 IDE 的运行工作目录不一定在项目根目录。解决方法是把图片放到根目录下的 images 文件夹,然后用类路径读取:
java.net.URL url = getClass().getResource("/cover.png"); if (url != null) { label.setIcon(new ImageIcon(url)); }getResource从 classpath 根开始找,比依赖当前工作目录可靠得多。记得先判断返回值是否为 null,否则一把空 URL 传给ImageIcon,表现依然是空白。这个坑在源码发给别人后尤其明显,因为每个人的工作目录不一样。
5.4 窗口假死
现象是点击查询按钮后,整个界面拖不动,像被冻住。原因是 JDBC 查询、文件读取这些耗时操作直接写在按钮事件里,而 Swing 是单线程模型,所有界面刷新都在 Event Dispatch Thread,也就是 EDT 上执行。你在 EDT 里跑一条慢 SQL,界面自然卡死。解决办法是把耗时操作放进SwingWorker<Void, Void>,在doInBackground里查数据库,在done里刷新 JTable。很多人为了省事会跳过这一步,但一旦数据库数据量上去,假死就会出现。这条属于加分项,也是区分“能跑”和“能演示”的分界线。
5.5 订单 id 冲突
现象是导入种子数据后,新增订单时报主键重复,表里面明明没有这个 id。原因是 SQL 脚本里手工指定了t_order.id的值,但 InnoDB 的自增计数器不会跟着更新,下一次插入可能撞到已有最大 id 上。解决办法是导完数据后执行:
ALTER TABLE t_order AUTO_INCREMENT = 1;或者干脆把脚本里 INSERT 语句中的 id 列去掉,让数据库自动分配。这个坑非常隐蔽,因为开发环境数据少时不容易触发,等到你自己连续下几单,或者演示时突然插入一条订单,就会冒出来。遇到“该表还有自增主键冲突”的报错,先往这个方向查,不要瞎改代码。
6. 把商城系统改到答辩能讲:三个高性价比技巧
系统跑通只是开始,真正想把它变成简历里的项目,还需要做几个小改造。这三个技巧投入不大,但对代码质量和演示效果提升很明显。
6.1 给 JTable 加关键字过滤
图书列表页一般会有一个搜索框,最简单也最可靠的做法是内存过滤。先把所有图书加载到 List,再根据输入框关键字筛选,重新填充 TableModel:
filterBtn.addActionListener(e -> { String keyword = searchField.getText().trim(); model.setRowCount(0); for (Book b : allBooks) { if (b.getTitle().contains(keyword) || b.getAuthor().contains(keyword)) { model.addRow(new Object[]{ b.getId(), b.getTitle(), b.getAuthor(), b.getPrice(), b.getStock() }); } } });setRowCount(0)先清空所有行,再重新 addRow。这种方法比每次敲字都查数据库快,也不会在低端机器上把界面卡死。如果面试官问“数据量大怎么办”,你再引出LIKE查询和SwingWorker,展示层次就够了。
6.2 把数据库密码从源码里剥出来
很多课设源码直接把 MySQL 账号密码写在DBUtils.java里。这是坏习惯,一则源码发出去等于泄露密码,二则换一台机器还要重新编译。我一般会在 src 目录放一个db.properties:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/book_store?useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=your_password然后在DBUtils里用Properties.load读取文件,驱动和 URL 都不再硬编码。这一步工作量很小,但能体现你对项目可维护性的理解。
6.3 用手工用例覆盖核心闭环
答辩最怕现场翻车,我自己的习惯是先走一遍完整用例表:管理员登录、添加图书,然后普通用户注册、搜索图书、加入购物车、下单,再去数据库确认订单生成且库存减少。订单场景再补一条异常路径:把库存改成 1,同时下两本,确认下单失败且订单表没有半截数据。这比任何口头承诺都有说服力。
我见过不少同学把精力花在改界面皮肤上,结果演示时败在乱码和连不上数据库。拿到这类源码,先别急着美化,把核心链路磨一遍,比什么都管用。希望帮到你。
本文还有配套的精品资源,点击获取