news 2026/7/14 4:42:39

项目实战:从零构建Java+MySQL图书借阅系统(Swing版)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
项目实战:从零构建Java+MySQL图书借阅系统(Swing版)

1. 项目概述与需求分析

图书借阅系统是图书馆日常运营的核心工具,也是Java初学者练手的经典项目。这个Swing版系统主要面向高校课程设计或毕业设计场景,需要实现以下核心功能:

  • 用户角色分离:管理员负责图书/用户管理,读者完成借阅/归还操作
  • 基础CRUD:图书信息增删改查、用户信息维护
  • 借阅逻辑:包含借书、还书、续借、逾期计算等完整流程
  • 数据统计:图书库存、借阅排行榜等可视化报表

我去年指导学生做这个项目时,发现新手常卡在三个地方:数据库设计不合理导致后期逻辑混乱、Swing事件绑定不熟练、借还书的事务处理出错。下面我们就从最关键的数据库设计开始,一步步避开这些坑。

2. 数据库设计与实现

2.1 表结构设计

用Navicat新建名为library_db的数据库,字符集选utf8mb4。核心四张表的设计要点:

-- 管理员表 CREATE TABLE `admin` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(20) NOT NULL UNIQUE, `password` CHAR(32) NOT NULL -- 存储MD5加密结果 ); -- 图书表(注意索引设计) CREATE TABLE `book` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `isbn` VARCHAR(17) NOT NULL UNIQUE, `name` VARCHAR(100) NOT NULL, `author` VARCHAR(50) NOT NULL, `press` VARCHAR(50) COMMENT '出版社', `stock` INT DEFAULT 1 CHECK(stock >= 0) -- 库存不能为负 ); CREATE INDEX idx_book_name ON book(name); -- 读者表 CREATE TABLE `reader` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `card_id` VARCHAR(20) NOT NULL UNIQUE COMMENT '借书证号', `name` VARCHAR(20) NOT NULL, `password` CHAR(32) NOT NULL ); -- 借阅记录表(核心业务表) CREATE TABLE `borrow` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `book_id` INT NOT NULL, `reader_id` INT NOT NULL, `borrow_date` DATETIME DEFAULT CURRENT_TIMESTAMP, `return_date` DATETIME COMMENT '应还日期', `actual_return` DATETIME COMMENT '实际归还', FOREIGN KEY (book_id) REFERENCES book(id), FOREIGN KEY (reader_id) REFERENCES reader(id) );

避坑指南

  1. 密码字段一定要用CHAR定长存储加密结果
  2. ISBN字段建议遵循978-7-04-048911-2这种带分隔符格式
  3. 借阅表的日期字段要区分"应还"和"实还"

2.2 初始化测试数据

插入示例数据时注意业务规则:

-- 管理员账号 INSERT INTO admin VALUES(null, 'admin', MD5('123456')); -- 图书数据(真实ISBN) INSERT INTO book VALUES (null, '978-7-121-35412-6', 'Java核心技术', 'Cay S. Horstmann', '电子工业出版社', 5), (null, '978-7-302-48527-9', 'MySQL必知必会', 'Ben Forta', '清华大学出版社', 3); -- 读者账号 INSERT INTO reader VALUES (null, '2023001', '张三', MD5('111111')), (null, '2023002', '李四', MD5('222222'));

3. Java项目搭建

3.1 Maven项目初始化

在IDEA中新建Maven项目,pom.xml关键依赖:

<dependencies> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!-- 日期处理 --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.10.10</version> </dependency> <!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>

3.2 数据库工具类

封装JDBC操作时要注意连接池和异常处理:

public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/library_db?useSSL=false&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "yourpassword"; // 获取连接(实际项目建议用HikariCP) public static Connection getConnection() { try { return DriverManager.getConnection(URL, USER, PASSWORD); } catch (SQLException e) { throw new RuntimeException("数据库连接失败", e); } } // 关闭资源 public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if(rs != null) rs.close(); if(stmt != null) stmt.close(); if(conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }

4. 登录模块开发

4.1 登录界面设计

使用Swing构建登录窗口时,建议用JPanel+GridBagLayout实现响应式布局:

public class LoginFrame extends JFrame { private JTextField tfUsername; private JPasswordField pfPassword; public LoginFrame() { setTitle("图书管理系统登录"); setSize(400, 300); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5, 5, 5, 5); // 用户名标签 gbc.gridx = 0; gbc.gridy = 0; panel.add(new JLabel("用户名:"), gbc); // 用户名输入框 gbc.gridx = 1; gbc.fill = GridBagConstraints.HORIZONTAL; tfUsername = new JTextField(15); panel.add(tfUsername, gbc); // 密码标签(gridy+1) gbc.gridx = 0; gbc.gridy = 1; panel.add(new JLabel("密码:"), gbc); // 密码输入框 gbc.gridx = 1; pfPassword = new JPasswordField(15); panel.add(pfPassword, gbc); // 登录按钮(事件绑定在4.2节) gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2; JButton btnLogin = new JButton("登录"); panel.add(btnLogin, gbc); add(panel); } }

4.2 登录验证逻辑

密码验证要使用MD5加密比对,防止SQL注入:

// 在LoginFrame类中添加事件监听 btnLogin.addActionListener(e -> { String username = tfUsername.getText().trim(); String password = new String(pfPassword.getPassword()); if(username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, "用户名密码不能为空"); return; } // 密码MD5加密 String encryptedPwd = DigestUtils.md5Hex(password); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); String sql = "SELECT * FROM admin WHERE username=? AND password=?"; ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, encryptedPwd); rs = ps.executeQuery(); if(rs.next()) { new MainFrame().setVisible(true); // 跳转主界面 dispose(); // 关闭登录窗口 } else { JOptionPane.showMessageDialog(this, "用户名或密码错误"); } } catch (SQLException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(this, "数据库查询失败"); } finally { DBUtil.close(conn, ps, rs); } });

5. 图书管理模块

5.1 图书查询功能

实现多条件模糊查询时,注意SQL拼接的安全性问题:

public List<Book> searchBooks(String name, String author) { List<Book> books = new ArrayList<>(); StringBuilder sql = new StringBuilder("SELECT * FROM book WHERE 1=1 "); List<Object> params = new ArrayList<>(); if(!name.isEmpty()) { sql.append("AND name LIKE ? "); params.add("%" + name + "%"); } if(!author.isEmpty()) { sql.append("AND author LIKE ? "); params.add("%" + author + "%"); } Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); ps = conn.prepareStatement(sql.toString()); for(int i=0; i<params.size(); i++) { ps.setObject(i+1, params.get(i)); } rs = ps.executeQuery(); while(rs.next()) { Book book = new Book(); book.setId(rs.getInt("id")); book.setIsbn(rs.getString("isbn")); book.setName(rs.getString("name")); book.setAuthor(rs.getString("author")); book.setPress(rs.getString("press")); book.setStock(rs.getInt("stock")); books.add(book); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } return books; }

5.2 借书业务实现

借书操作需要处理事务,确保库存和借阅记录同步更新:

public boolean borrowBook(int bookId, int readerId) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 检查库存 PreparedStatement ps1 = conn.prepareStatement( "SELECT stock FROM book WHERE id=? FOR UPDATE"); ps1.setInt(1, bookId); ResultSet rs = ps1.executeQuery(); if(!rs.next() || rs.getInt("stock") <= 0) { return false; } // 2. 减少库存 PreparedStatement ps2 = conn.prepareStatement( "UPDATE book SET stock=stock-1 WHERE id=?"); ps2.setInt(1, bookId); ps2.executeUpdate(); // 3. 添加借阅记录(应还日期=当前时间+30天) PreparedStatement ps3 = conn.prepareStatement( "INSERT INTO borrow(book_id, reader_id, return_date) " + "VALUES(?, ?, DATE_ADD(NOW(), INTERVAL 30 DAY))"); ps3.setInt(1, bookId); ps3.setInt(2, readerId); ps3.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { if(conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if(conn != null) { try { conn.setAutoCommit(true); } catch (SQLException e) { e.printStackTrace(); } } DBUtil.close(conn, null, null); } }

6. 报表统计功能

6.1 使用JFreeChart生成图表

在pom.xml添加依赖后,实现借阅排行榜:

<dependency> <groupId>org.jfree</groupId> <artifactId>jfreechart</artifactId> <version>1.5.3</version> </dependency>

生成饼图的代码示例:

public JPanel createBookRankChart() { // 获取借阅量前5的图书 Map<String, Integer> data = new LinkedHashMap<>(); String sql = "SELECT b.name, COUNT(*) as cnt FROM borrow br " + "JOIN book b ON br.book_id=b.id " + "GROUP BY b.name ORDER BY cnt DESC LIMIT 5"; try (Connection conn = DBUtil.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { while(rs.next()) { data.put(rs.getString("name"), rs.getInt("cnt")); } } catch (SQLException e) { e.printStackTrace(); } // 创建数据集 DefaultPieDataset dataset = new DefaultPieDataset(); data.forEach(dataset::setValue); // 创建图表 JFreeChart chart = ChartFactory.createPieChart( "图书借阅排行榜", dataset, true, true, false); // 返回图表面板 return new ChartPanel(chart); }

7. 系统打包与部署

7.1 使用Maven打包可执行JAR

在pom.xml中添加maven-assembly-plugin:

<build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <mainClass>com.yourpackage.MainClass</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

运行mvn package后,在target目录会生成包含所有依赖的jar文件,直接通过java -jar命令即可运行。

7.2 常见问题解决

  1. 中文乱码问题

    • MySQL连接字符串添加useUnicode=true&characterEncoding=UTF-8
    • Swing界面设置默认字体:
      UIManager.put("Button.font", new Font("微软雅黑", Font.PLAIN, 12)); UIManager.put("Label.font", new Font("微软雅黑", Font.PLAIN, 12));
  2. 界面卡顿优化

    • 耗时操作放在SwingWorker中执行
    • 列表数据超过1000条时启用分页查询
  3. 数据库连接泄漏

    • 使用try-with-resources确保资源关闭
    • 定期检查SHOW PROCESSLIST
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 4:40:59

智能代理技能(Agent Skills)开发指南与技术解析

1. Agent Skills概述与核心价值Agent Skills&#xff08;智能代理技能&#xff09;是当前人工智能领域最前沿的技术方向之一&#xff0c;它通过模块化封装特定领域的能力&#xff0c;使智能体能够像人类专家一样完成复杂任务。不同于传统程序化的固定流程&#xff0c;Agent Ski…

作者头像 李华
网站建设 2026/7/14 4:40:48

企业级AI Agent落地:从模型能力到工程体系的跨越

上周&#xff0c;一位做企业数字化转型的朋友深夜发来消息&#xff1a;“我们花大价钱接入了号称最强的 AI 模型&#xff0c;但团队用起来还是像在开盲盒——单次演示效果惊艳&#xff0c;一到真实业务流程就各种不稳定。到底差在哪一步&#xff1f;”这个问题&#xff0c;几乎…

作者头像 李华
网站建设 2026/7/14 4:39:51

单片机DAY_2

1.GPIOGPIO&#xff1a;通用目的输入输出两种工作模式输出模式&#xff1a;LED控制&#xff0c;数码管控制&#xff0c;蜂鸣器控制输入模式&#xff1a;检测引脚电平变化&#xff08;检测外部电平信号 按键检测、检测ADC变化&#xff09;2.中断中断的概念&#xff1a;CPU在处理…

作者头像 李华
网站建设 2026/7/14 4:37:40

L9958与TM4C1294NCZAD电机控制方案解析

1. 为什么选择L9958与TM4C1294NCZAD组合在电机控制领域&#xff0c;硬件选型直接决定了系统性能上限。L9958作为意法半导体(ST)推出的H桥驱动芯片&#xff0c;与德州仪器(TI)的TM4C1294NCZAD微控制器组合&#xff0c;形成了当前中高端电机控制方案的黄金搭档。L9958的核心优势在…

作者头像 李华
网站建设 2026/7/14 4:36:54

Chapter 2 The Well: Navigating Parasitics and Performance in CMOS Fabrication

1. CMOS制造中的阱技术&#xff1a;性能与寄生的博弈第一次接触CMOS工艺设计时&#xff0c;我被N-Well和P-Well的复杂关系弄得晕头转向。这就像在建造一栋高楼时&#xff0c;既要考虑每个房间的独立功能&#xff0c;又要确保整栋建筑的结构稳定。在40nm工艺节点的一个项目中&am…

作者头像 李华
网站建设 2026/7/14 4:35:08

Lua C接口封装设计:构建安全高效的C/C++与Lua双向通信桥梁

1. 项目概述&#xff1a;为什么需要重新设计Lua的C接口&#xff1f;在游戏引擎、嵌入式系统或者任何需要脚本扩展能力的C/C项目中&#xff0c;集成Lua虚拟机几乎是标准操作。但如果你真的动手做过&#xff0c;大概率会和我一样&#xff0c;对Lua原生的C API又爱又恨。爱的是它足…

作者头像 李华