☰
Java图书管理系统实战:从需求分析到分层架构实现
2026/10/19 1:29:32 网站建设 项目流程

在实际 Java 开发学习中,构建一个完整的项目是巩固基础、理解面向对象设计和数据库操作的最佳途径。图书管理系统作为经典的教学案例,涵盖了用户管理、图书信息维护、借阅归还流程、数据持久化等核心业务,非常适合初学者将零散的 Java 语法、集合框架、JDBC 等知识串联起来。很多新手在尝试时,往往卡在项目结构设计、数据库连接、业务逻辑分层和异常处理这些环节,导致代码混乱、难以调试。

本文将带你从零开始,构建一个控制台版本的图书管理系统。我们将采用分层架构思想,使用 MySQL 作为数据库,JDBC 进行数据操作,并融入面向对象的设计原则。整个过程不仅会展示代码怎么写,更会解释为什么这样设计,以及每一步可能遇到的坑和排查方法。完成这个项目后,你将能掌握一个典型 Java 应用从需求分析、数据库设计、编码实现到测试运行的全流程,为后续学习 Spring Boot 等框架打下坚实基础。

1. 系统需求分析与核心概念理解

在动手编码之前,明确系统要做什么、有哪些角色、涉及哪些核心概念至关重要。这能避免后续开发过程中频繁返工和逻辑混乱。

1.1 业务需求与角色定义

一个最简单的图书管理系统通常涉及两类用户:管理员和普通读者。他们的核心操作权限不同。

  • 管理员:拥有系统的最高权限,负责基础数据的维护。
    • 图书管理:新增图书、删除图书、修改图书信息、查询所有图书。
    • 用户管理:新增读者、删除读者、修改读者信息、查询所有读者。
  • 普通读者:只能进行与自身相关的借阅操作。
    • 借阅图书:查询可借图书并进行借阅。
    • 归还图书:归还自己已借阅的图书。
    • 查询借阅记录:查看自己的借阅历史。

基于以上需求,我们可以抽象出几个核心的实体(Entity)或模型(Model),它们是系统数据的载体。

1.2 核心实体类设计

实体类对应数据库中的表,每个属性对应表中的字段。良好的实体设计是项目成功的一半。

  1. 图书(Book)

    • id: 图书唯一标识(主键,通常自增)。
    • isbn: 国际标准书号,具有唯一性,可用于精确检索。
    • name: 图书名称。
    • author: 作者。
    • publisher: 出版社。
    • price: 价格(使用BigDecimal类型,避免浮点数精度问题)。
    • stock: 库存数量。借阅时减少,归还时增加。
  2. 用户(User)

    • id: 用户唯一标识(主键)。
    • username: 用户名(用于登录,应唯一)。
    • password: 密码(存储时应加密,此处为简化先使用明文)。
    • name: 真实姓名。
    • role: 角色(如admin或reader),用于权限控制。
  3. 借阅记录(BorrowRecord)

    • id: 记录唯一标识。
    • userId: 借阅用户ID(外键,关联User.id)。
    • bookId: 借阅图书ID(外键,关联Book.id)。
    • borrowTime: 借阅时间。
    • returnTime: 归还时间(为空表示未归还)。
    • status: 借阅状态(如BORROWED,RETURNED),方便查询。

注意:在实际项目中,密码必须加密存储(如使用 BCrypt),角色和权限可能更复杂(如使用 RBAC 模型)。本文为简化教学,先实现核心流程。

2. 开发环境与项目结构准备

一个清晰的项目结构能让代码管理事半功倍。我们使用 Maven 来管理依赖和构建项目。

2.1 环境与工具清单

在开始前,请确保你的开发环境已就绪。

工具/环境推荐版本作用说明验证命令
JDK8, 11 或 17Java 运行和编译环境java -version
IDEIntelliJ IDEA集成开发环境,提高效率-
构建工具Maven 3.6+依赖管理和项目构建mvn -v
数据库MySQL 5.7+数据持久化存储mysql --version
数据库客户端Navicat, DBeaver 或命令行执行 SQL,管理数据库-

2.2 创建 Maven 项目与目录结构

在 IntelliJ IDEA 中,选择New Project->Maven,使用合适的 JDK 版本,创建项目。项目创建完成后,手动补充以下目录结构。这不是 Maven 的标准结构,但更符合业务逻辑分层,便于理解。

book-management-system ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── example │ │ │ └── bookms │ │ │ ├── Main.java # 程序入口 │ │ │ ├── model # 实体类包 │ │ │ │ ├── Book.java │ │ │ │ ├── User.java │ │ │ │ └── BorrowRecord.java │ │ │ ├── dao # 数据访问层包 │ │ │ │ ├── BaseDao.java # 通用数据库操作 │ │ │ │ ├── BookDao.java │ │ │ │ ├── UserDao.java │ │ │ │ └── BorrowRecordDao.java │ │ │ ├── service # 业务逻辑层包 │ │ │ │ ├── BookService.java │ │ │ │ ├── UserService.java │ │ │ │ └── BorrowRecordService.java │ │ │ └── util # 工具类包 │ │ │ ├── DBUtil.java # 数据库连接工具 │ │ │ └── ConsoleUtil.java # 控制台输入输出工具 │ │ └── resources │ │ └── db.properties # 数据库配置文件 │ └── test # 测试代码(可选) └── pom.xml # Maven 配置文件

2.3 配置 Maven 依赖与数据库

在pom.xml中添加 MySQL 驱动和单元测试依赖。

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>book-management-system</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- MySQL 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> </project>

在src/main/resources目录下创建db.properties文件,用于存放数据库连接信息,实现配置与代码分离。

# db.properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/book_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=your_password

注意:请将your_password替换为你本地 MySQL 的 root 密码。serverTimezone参数非常重要,不设置可能导致时区错误。

3. 数据库设计与数据访问层实现

数据层是应用的基石,设计不当会导致后续业务逻辑复杂且性能低下。

3.1 创建数据库与表

使用 MySQL 客户端连接数据库,执行以下 SQL 语句创建数据库和表。

-- 创建数据库,如果已存在则忽略 CREATE DATABASE IF NOT EXISTS `book_db` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE `book_db`; -- 用户表 CREATE TABLE `user` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名', `password` VARCHAR(100) NOT NULL COMMENT '密码', `name` VARCHAR(50) NOT NULL COMMENT '真实姓名', `role` VARCHAR(20) NOT NULL DEFAULT 'reader' COMMENT '角色:admin/reader' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -- 图书表 CREATE TABLE `book` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `isbn` VARCHAR(20) NOT NULL UNIQUE COMMENT 'ISBN号', `name` VARCHAR(200) NOT NULL COMMENT '图书名称', `author` VARCHAR(100) NOT NULL COMMENT '作者', `publisher` VARCHAR(100) COMMENT '出版社', `price` DECIMAL(10, 2) NOT NULL COMMENT '价格', `stock` INT NOT NULL DEFAULT 0 COMMENT '库存数量' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='图书表'; -- 借阅记录表 CREATE TABLE `borrow_record` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `user_id` INT NOT NULL COMMENT '借阅用户ID', `book_id` INT NOT NULL COMMENT '借阅图书ID', `borrow_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '借阅时间', `return_time` DATETIME NULL COMMENT '归还时间', `status` VARCHAR(20) NOT NULL DEFAULT 'BORROWED' COMMENT '状态:BORROWED/RETURNED', FOREIGN KEY (`user_id`) REFERENCES `user`(`id`), FOREIGN KEY (`book_id`) REFERENCES `book`(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='借阅记录表'; -- 插入初始管理员用户 (密码为明文 'admin123',仅用于演示) INSERT INTO `user` (`username`, `password`, `name`, `role`) VALUES ('admin', 'admin123', '系统管理员', 'admin');

3.2 实现数据库连接工具类

为了避免在每个数据库操作中重复编写加载驱动、获取连接、关闭资源的代码,我们封装一个DBUtil工具类。

// src/main/java/com/example/bookms/util/DBUtil.java package com.example.bookms.util; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; public class DBUtil { private static String driver; private static String url; private static String username; private static String password; // 静态代码块,在类加载时读取配置文件 static { try { InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("db.properties"); Properties props = new Properties(); props.load(is); driver = props.getProperty("jdbc.driver"); url = props.getProperty("jdbc.url"); username = props.getProperty("jdbc.username"); password = props.getProperty("jdbc.password"); Class.forName(driver); // 加载驱动 } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException("数据库配置加载失败", e); } } // 获取数据库连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } // 关闭资源 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(); } } }

3.3 实现实体类与数据访问层

以Book实体和BookDao为例,展示如何将数据库表映射为 Java 对象,并实现增删改查。

首先创建Book实体类,属性与数据库表字段一一对应。

// src/main/java/com/example/bookms/model/Book.java package com.example.bookms.model; import java.math.BigDecimal; public class Book { private Integer id; private String isbn; private String name; private String author; private String publisher; private BigDecimal price; private Integer stock; // 构造方法、Getter和Setter省略,实际开发中必须生成 // 可以使用IDE的Generate功能或Lombok注解简化 }

然后创建BookDao类,负责所有与book表交互的数据库操作。这里采用经典的 JDBC 模板代码。

// src/main/java/com/example/bookms/dao/BookDao.java package com.example.bookms.dao; import com.example.bookms.model.Book; import com.example.bookms.util.DBUtil; import java.math.BigDecimal; import java.sql.*; import java.util.ArrayList; import java.util.List; public class BookDao { // 新增图书 public boolean addBook(Book book) { String sql = "INSERT INTO book (isbn, name, author, publisher, price, stock) VALUES (?, ?, ?, ?, ?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, book.getIsbn()); pstmt.setString(2, book.getName()); pstmt.setString(3, book.getAuthor()); pstmt.setString(4, book.getPublisher()); pstmt.setBigDecimal(5, book.getPrice()); pstmt.setInt(6, book.getStock()); return pstmt.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } // 根据ID删除图书 public boolean deleteBookById(Integer id) { String sql = "DELETE FROM book WHERE id = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, id); return pstmt.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } // 更新图书信息 public boolean updateBook(Book book) { String sql = "UPDATE book SET isbn=?, name=?, author=?, publisher=?, price=?, stock=? WHERE id=?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, book.getIsbn()); pstmt.setString(2, book.getName()); pstmt.setString(3, book.getAuthor()); pstmt.setString(4, book.getPublisher()); pstmt.setBigDecimal(5, book.getPrice()); pstmt.setInt(6, book.getStock()); pstmt.setInt(7, book.getId()); return pstmt.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } // 查询所有图书 public List<Book> getAllBooks() { List<Book> bookList = new ArrayList<>(); String sql = "SELECT * FROM book"; try (Connection conn = DBUtil.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { 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.setPublisher(rs.getString("publisher")); book.setPrice(rs.getBigDecimal("price")); book.setStock(rs.getInt("stock")); bookList.add(book); } } catch (SQLException e) { e.printStackTrace(); } return bookList; } // 根据ID查询图书 public Book getBookById(Integer id) { String sql = "SELECT * FROM book WHERE id = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, id); try (ResultSet rs = pstmt.executeQuery()) { if (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.setPublisher(rs.getString("publisher")); book.setPrice(rs.getBigDecimal("price")); book.setStock(rs.getInt("stock")); return book; } } } catch (SQLException e) { e.printStackTrace(); } return null; } }

按照同样的模式,完成UserDao和BorrowRecordDao的实现。UserDao需要实现用户登录验证(根据用户名和密码查询),BorrowRecordDao需要实现借阅、归还和查询记录等功能。

4. 业务逻辑层与控制台交互实现

数据访问层(DAO)只负责数据的存取,业务规则(如借书前检查库存、还书时更新状态)应在业务逻辑层(Service)处理。

4.1 实现业务逻辑层

以BookService为例,它调用BookDao,并可以加入业务逻辑,比如新增图书前检查 ISBN 是否重复。

// src/main/java/com/example/bookms/service/BookService.java package com.example.bookms.service; import com.example.bookms.dao.BookDao; import com.example.bookms.model.Book; import java.util.List; public class BookService { private BookDao bookDao = new BookDao(); public boolean addBook(Book book) { // 这里可以加入业务逻辑,例如检查ISBN是否已存在 // 为了简化,我们假设直接插入 return bookDao.addBook(book); } public boolean deleteBook(Integer id) { // 删除前可以检查是否有未归还的借阅记录 return bookDao.deleteBookById(id); } public boolean updateBook(Book book) { return bookDao.updateBook(book); } public List<Book> getAllBooks() { return bookDao.getAllBooks(); } public Book getBookById(Integer id) { return bookDao.getBookById(id); } }

BorrowRecordService是业务核心,它需要协调BookDao和BorrowRecordDao,在一个事务内完成借阅和归还操作。

// src/main/java/com/example/bookms/service/BorrowRecordService.java package com.example.bookms.service; import com.example.bookms.dao.BookDao; import com.example.bookms.dao.BorrowRecordDao; import com.example.bookms.model.Book; import com.example.bookms.model.BorrowRecord; import java.sql.Connection; import java.sql.SQLException; import java.util.Date; import java.util.List; public class BorrowRecordService { private BorrowRecordDao borrowRecordDao = new BorrowRecordDao(); private BookDao bookDao = new BookDao(); public boolean borrowBook(Integer userId, Integer bookId) { // 1. 检查图书库存 Book book = bookDao.getBookById(bookId); if (book == null || book.getStock() <= 0) { System.out.println("图书不存在或库存不足!"); return false; } // 2. 减少库存 book.setStock(book.getStock() - 1); boolean updateSuccess = bookDao.updateBook(book); if (!updateSuccess) { return false; } // 3. 创建借阅记录 BorrowRecord record = new BorrowRecord(); record.setUserId(userId); record.setBookId(bookId); record.setBorrowTime(new Date()); record.setStatus("BORROWED"); return borrowRecordDao.addRecord(record); } public boolean returnBook(Integer recordId) { // 1. 查询借阅记录 BorrowRecord record = borrowRecordDao.getRecordById(recordId); if (record == null || "RETURNED".equals(record.getStatus())) { System.out.println("借阅记录无效或已归还!"); return false; } // 2. 更新记录状态和归还时间 record.setReturnTime(new Date()); record.setStatus("RETURNED"); boolean updateRecordSuccess = borrowRecordDao.updateRecord(record); if (!updateRecordSuccess) { return false; } // 3. 增加图书库存 Book book = bookDao.getBookById(record.getBookId()); book.setStock(book.getStock() + 1); return bookDao.updateBook(book); } public List<BorrowRecord> getRecordsByUser(Integer userId) { return borrowRecordDao.getRecordsByUserId(userId); } }

注意:上面的borrowBook和returnBook方法存在一个严重问题:库存更新和记录更新不是原子操作。如果更新库存成功但创建记录失败,会导致数据不一致。生产环境必须使用数据库事务(Connection.setAutoCommit(false))来保证。此处为简化演示,先忽略事务。

4.2 实现控制台菜单与用户交互

最后,在Main类中实现一个简单的控制台菜单,将各个模块串联起来。

// src/main/java/com/example/bookms/Main.java package com.example.bookms; import com.example.bookms.model.User; import com.example.bookms.service.UserService; import com.example.bookms.util.ConsoleUtil; import java.util.Scanner; public class Main { private static User currentUser = null; // 当前登录用户 private static Scanner scanner = new Scanner(System.in); private static UserService userService = new UserService(); public static void main(String[] args) { System.out.println("=== 图书管理系统 ==="); while (true) { if (currentUser == null) { showLoginMenu(); } else { if ("admin".equals(currentUser.getRole())) { showAdminMenu(); } else { showReaderMenu(); } } } } private static void showLoginMenu() { System.out.println("\n1. 登录"); System.out.println("0. 退出"); System.out.print("请选择: "); int choice = scanner.nextInt(); scanner.nextLine(); // 消耗换行符 switch (choice) { case 1: login(); break; case 0: System.out.println("感谢使用,再见!"); System.exit(0); break; default: System.out.println("无效选择!"); } } private static void login() { System.out.print("用户名: "); String username = scanner.nextLine(); System.out.print("密码: "); String password = scanner.nextLine(); currentUser = userService.login(username, password); if (currentUser != null) { System.out.println("登录成功!欢迎," + currentUser.getName()); } else { System.out.println("用户名或密码错误!"); } } private static void showAdminMenu() { System.out.println("\n=== 管理员菜单 ==="); System.out.println("1. 图书管理"); System.out.println("2. 用户管理"); System.out.println("3. 查看所有借阅记录"); System.out.println("9. 注销"); System.out.print("请选择: "); int choice = scanner.nextInt(); scanner.nextLine(); switch (choice) { case 1: // 调用图书管理子菜单 BookManagementMenu.showMenu(scanner); break; case 2: // 调用用户管理子菜单 UserManagementMenu.showMenu(scanner); break; case 3: // 查看所有借阅记录 break; case 9: currentUser = null; System.out.println("已注销。"); break; default: System.out.println("无效选择!"); } } private static void showReaderMenu() { System.out.println("\n=== 读者菜单 ==="); System.out.println("1. 查询图书"); System.out.println("2. 借阅图书"); System.out.println("3. 归还图书"); System.out.println("4. 我的借阅记录"); System.out.println("9. 注销"); System.out.print("请选择: "); int choice = scanner.nextInt(); scanner.nextLine(); switch (choice) { case 1: // 查询图书 break; case 2: // 借阅图书 break; case 3: // 归还图书 break; case 4: // 查看我的借阅记录 break; case 9: currentUser = null; System.out.println("已注销。"); break; default: System.out.println("无效选择!"); } } }

你需要创建BookManagementMenu和UserManagementMenu等类来细化每个菜单项的功能,调用对应的Service方法,并通过ConsoleUtil(一个封装了Scanner的工具类)与用户交互。

5. 运行验证与常见问题排查

完成所有代码后,运行Main类的main方法。如果一切顺利,你将看到登录界面。使用初始插入的管理员账号admin/admin123登录,即可进入管理员菜单。

5.1 常见启动问题排查

在运行过程中,你可能会遇到以下典型问题:

问题现象可能原因检查与解决方式
ClassNotFoundException: com.mysql.cj.jdbc.Driver1. MySQL 驱动依赖未下载。
2.pom.xml中依赖版本错误或未同步。
1. 在 IDEA 中,点击 Maven 面板的刷新按钮。
2. 检查本地 Maven 仓库是否存在对应 jar 包。
Communications link failure或连接被拒绝1. MySQL 服务未启动。
2.db.properties中的 URL、端口、用户名、密码错误。
3. 数据库book_db不存在。
1. 确保 MySQL 服务已运行 (net start mysql)。
2. 仔细核对配置文件。
3. 执行本文 3.1 节的 SQL 创建数据库。
The server time zone value '...' is unrecognized数据库连接 URL 未指定时区。在db.properties的jdbc.url中确保包含&serverTimezone=Asia/Shanghai(或其他有效时区)。
控制台输入Scanner跳转或出错nextInt()后未消耗换行符。在nextInt()、nextDouble()后加一行scanner.nextLine();来读取多余的换行符。
增删改操作成功,但数据库没变化未提交事务(如果使用了事务)或连接未关闭自动提交。检查代码中是否显式调用了connection.commit()。对于简单的 JDBC,默认是自动提交的。更常见的是 SQL 语句写错,但未抛出异常。建议打印 SQL 语句或在数据库客户端直接执行测试。
查询结果始终为null或空列表1. SQL 查询条件错误。
2. 实体类属性与ResultSet字段名不匹配。
3. 结果集遍历逻辑错误(如if(rs.next())写成了while)。
1. 打印出执行的 SQL 语句进行调试。
2. 检查rs.getXxx(“字段名”)中的字段名是否与数据库表完全一致(区分大小写)。
3. 使用调试模式,查看ResultSet对象内部数据。

5.2 项目结构检查清单

在遇到问题时,可以按照以下清单逐一核对:

  1. 依赖检查:pom.xml中的 MySQL 驱动版本是否正确?Maven 是否已下载依赖?
  2. 配置检查:src/main/resources/db.properties文件是否存在?路径和文件名是否正确?配置项的值是否正确?
  3. 数据库检查:MySQL 服务是否运行?book_db数据库及三张表是否创建成功?初始管理员用户是否存在?
  4. 实体类检查:Book、User、BorrowRecord类的属性名、类型是否与数据库表字段对应?Getter/Setter 方法是否完整?
  5. DAO 层检查:SQL 语句字符串是否正确?PreparedStatement的参数设置(setXxx)顺序和类型是否与 SQL 中的?匹配?
  6. Service 层检查:业务逻辑(如库存检查)是否正确?是否处理了可能的异常或边界情况?
  7. 控制台交互检查:菜单逻辑是否清晰?Scanner的使用是否正确,有无输入流混乱?

6. 从演示到生产:关键优化与扩展方向

当前版本是一个为了教学清晰而简化的控制台程序。要将其变成一个健壮、可维护的项目,还需要在许多方面进行加强。

6.1 安全性优化

  1. 密码加密:绝对不要明文存储密码。使用BCryptPasswordEncoder等工具进行哈希加盐存储。登录时比较哈希值。
    // 示例:使用 Spring Security 的 BCrypt // String encodedPassword = BCrypt.hashpw(rawPassword, BCrypt.gensalt()); // boolean matches = BCrypt.checkpw(rawPassword, storedHash);
  2. SQL 注入防护:始终坚持使用PreparedStatement,避免字符串拼接 SQL。
  3. 输入验证:对所有用户输入(如 ISBN、价格)进行有效性验证,防止非法数据进入系统。

6.2 代码结构与可维护性优化

  1. 引入接口:为BookDao、BookService等定义接口(如IBookDao,IBookService),基于接口编程,提高可测试性和可替换性。
  2. 依赖注入:避免在类内部new对象。可以引入简单的工厂模式或学习 Spring 框架的 IoC 容器。
  3. 异常处理:定义业务异常类,在 Service 层抛出,在 UI 层统一捕获并给出友好提示,而不是到处打印e.printStackTrace()。
  4. 日志记录:使用SLF4J+Logback替代System.out.println,可以灵活控制日志级别和输出目的地。

6.3 功能扩展方向

  1. Web 化:这是最自然的扩展。学习Servlet+JSP或直接使用Spring Boot框架,将控制台菜单变为网页。
  2. 加入事务管理:如前所述,借阅和归还操作必须放在一个数据库事务中。可以使用JDBC手动管理,或使用Spring的@Transactional注解。
  3. 增加分页查询:当图书或借阅记录很多时,需要分页功能。在 SQL 中使用LIMIT ?, ?实现。
  4. 模糊搜索:实现按书名、作者进行模糊查询(LIKE)。
  5. 借阅规则:增加借阅期限、超期罚款、同一用户最大借阅数等业务规则。
  6. 数据导出:将图书列表、借阅记录导出为 Excel 或 PDF 文件。

通过完成这个项目,你不仅实践了 Java 核心语法和 JDBC,更重要的是体会了如何将一个业务需求分解为模块、设计数据库、组织代码结构、处理异常以及迭代优化。接下来,你可以选择上述任何一个扩展方向进行深入研究,例如尝试用 Spring Boot 重写这个系统,这将让你对现代 Java 企业级开发有更深刻的理解。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询