一、项目概述与设计思路
1.1 为什么选择图书管理系统
图书管理系统是学习编程的经典项目,它涵盖了:
-
控制台交互:学习用户输入输出处理
-
数据库操作:掌握CRUD核心功能
-
业务逻辑:理解实际应用场景
-
系统架构:实践分层设计思想
1.2 系统功能设计
核心功能模块:
1. 图书管理- 添加新书- 删除图书- 修改图书信息- 查询图书(按ID/书名/作者)2. 借阅管理- 图书借出- 图书归还- 借阅记录查询3. 用户管理- 读者注册- 读者信息修改- 读者注销4. 统计报表- 图书库存统计- 借阅排行榜- 逾期未还清单
1.3 技术选型
技术组件 | 选择方案 | 备注 |
---|---|---|
开发语言 | Java 8+ | 兼顾教学与实用 |
数据库 | MySQL 8.0 | 免费开源,应用广泛 |
数据库连接 | JDBC | 学习原生数据库操作 |
控制台框架 | - | 纯Java实现 |
单元测试 | JUnit 5 | 保证代码质量 |
日志系统 | SLF4J + Logback | 记录系统运行状态 |
二、数据库设计与实现
2.1 数据库表结构设计
ER图关键实体:
图书(Book) ---< 借阅记录(BorrowRecord) >--- 读者(Reader)
建表SQL:
-- 图书表
CREATE TABLE books (book_id INT AUTO_INCREMENT PRIMARY KEY,isbn VARCHAR(20) NOT NULL UNIQUE,title VARCHAR(100) NOT NULL,author VARCHAR(50) NOT NULL,publisher VARCHAR(50),publish_date DATE,price DECIMAL(10,2),stock INT DEFAULT 1 COMMENT '库存数量',create_time DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 读者表
CREATE TABLE readers (reader_id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(50) NOT NULL,gender CHAR(1) CHECK (gender IN ('M', 'F')),phone VARCHAR(20),email VARCHAR(100),register_date DATE DEFAULT (CURRENT_DATE),membership_level INT DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 借阅记录表
CREATE TABLE borrow_records (record_id INT AUTO_INCREMENT PRIMARY KEY,book_id INT NOT NULL,reader_id INT NOT NULL,borrow_date DATETIME DEFAULT CURRENT_TIMESTAMP,due_date DATETIME GENERATED ALWAYS AS (borrow_date + INTERVAL 30 DAY) STORED,return_date DATETIME,status TINYINT DEFAULT 1 COMMENT '1-借出 2-已还 3-逾期',FOREIGN KEY (book_id) REFERENCES books(book_id),FOREIGN KEY (reader_id) REFERENCES readers(reader_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2 索引优化设计
-- 提高查询性能的索引
CREATE INDEX idx_books_title ON books(title);
CREATE INDEX idx_books_author ON books(author);
CREATE INDEX idx_borrow_records_status ON borrow_records(status);
CREATE INDEX idx_borrow_records_due_date ON borrow_records(due_date);
2.3 初始化测试数据
-- 插入示例图书
INSERT INTO books (isbn, title, author, publisher, price, stock)
VALUES
('9787111636667', 'Java核心技术 卷I', 'Cay S. Horstmann', '机械工业出版社', 119.00, 5),
('9787115523660', 'Effective Java', 'Joshua Bloch', '机械工业出版社', 129.00, 3),
('9787302515421', 'Python编程:从入门到实践', 'Eric Matthes', '人民邮电出版社', 89.00, 7);-- 插入示例读者
INSERT INTO readers (name, gender, phone, email)
VALUES
('张三', 'M', '13800138001', 'zhangsan@example.com'),
('李四', 'F', '13900139001', 'lisi@example.com');
三、Java核心代码实现
3.1 数据库连接层
DBUtil.java - 数据库工具类:
public class DBUtil {private static final String URL = "jdbc:mysql://localhost:3306/library_db?useSSL=false&serverTimezone=UTC";private static final String USER = "root";private static final String PASSWORD = "123456";static {try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}public static Connection getConnection() throws SQLException {return DriverManager.getConnection(URL, USER, 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.2 实体类设计
Book.java - 图书实体:
public class Book {private Integer bookId;private String isbn;private String title;private String author;private String publisher;private Date publishDate;private BigDecimal price;private Integer stock;// 构造方法、getter和setter省略// 建议使用Lombok @Data注解简化代码
}
3.3 数据访问层(DAO)
BookDAO.java - 图书数据访问:
public class BookDAO {// 添加新书public boolean addBook(Book book) {String sql = "INSERT INTO books (isbn, title, author, publisher, publish_date, price, stock) " +"VALUES (?, ?, ?, ?, ?, ?, ?)";try (Connection conn = DBUtil.getConnection();PreparedStatement pstmt = conn.prepareStatement(sql)) {pstmt.setString(1, book.getIsbn());pstmt.setString(2, book.getTitle());pstmt.setString(3, book.getAuthor());pstmt.setString(4, book.getPublisher());pstmt.setDate(5, new java.sql.Date(book.getPublishDate().getTime()));pstmt.setBigDecimal(6, book.getPrice());pstmt.setInt(7, book.getStock());return pstmt.executeUpdate() > 0;} catch (SQLException e) {e.printStackTrace();return false;}}// 按ID查询图书public Book getBookById(int bookId) {String sql = "SELECT * FROM books WHERE book_id = ?";Book book = null;try (Connection conn = DBUtil.getConnection();PreparedStatement pstmt = conn.prepareStatement(sql)) {pstmt.setInt(1, bookId);try (ResultSet rs = pstmt.executeQuery()) {if (rs.next()) {book = new Book();book.setBookId(rs.getInt("book_id"));book.setIsbn(rs.getString("isbn"));// 设置其他属性...}}} catch (SQLException e) {e.printStackTrace();}return book;}// 其他CRUD方法...
}
3.4 业务逻辑层(Service)
LibraryService.java - 核心业务逻辑:
public class LibraryService {private BookDAO bookDAO = new BookDAO();private ReaderDAO readerDAO = new ReaderDAO();private BorrowRecordDAO recordDAO = new BorrowRecordDAO();// 借书业务方法public boolean borrowBook(int bookId, int readerId) {// 检查图书库存Book book = bookDAO.getBookById(bookId);if (book == null || book.getStock() <= 0) {System.out.println("图书不存在或库存不足");return false;}// 检查读者是否存在Reader reader = readerDAO.getReaderById(readerId);if (reader == null) {System.out.println("读者不存在");return false;}// 检查是否已借过同一本书未还if (recordDAO.hasUnreturnedRecord(bookId, readerId)) {System.out.println("您已借阅该书且未归还");return false;}// 开启事务Connection conn = null;try {conn = DBUtil.getConnection();conn.setAutoCommit(false);// 1. 减少库存bookDAO.updateStock(conn, bookId, -1);// 2. 创建借阅记录BorrowRecord record = new BorrowRecord();record.setBookId(bookId);record.setReaderId(readerId);recordDAO.addRecord(conn, record);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);conn.close();} catch (SQLException e) {e.printStackTrace();}}}}// 其他业务方法...
}
3.5 控制台界面实现
ConsoleUI.java - 用户交互界面:
public class ConsoleUI {private Scanner scanner = new Scanner(System.in);private LibraryService libraryService = new LibraryService();public void start() {while (true) {showMainMenu();int choice = getIntInput("请选择操作:");switch (choice) {case 1:manageBooks();break;case 2:manageReaders();break;case 3:manageBorrowing();break;case 4:generateReports();break;case 0:System.out.println("感谢使用图书管理系统,再见!");return;default:System.out.println("无效选择,请重新输入");}}}private void showMainMenu() {System.out.println("\n===== 图书管理系统 =====");System.out.println("1. 图书管理");System.out.println("2. 读者管理");System.out.println("3. 借阅管理");System.out.println("4. 统计报表");System.out.println("0. 退出系统");}private void manageBooks() {while (true) {System.out.println("\n===== 图书管理 =====");System.out.println("1. 添加新书");System.out.println("2. 查询图书");System.out.println("3. 修改图书信息");System.out.println("4. 删除图书");System.out.println("0. 返回上级菜单");int choice = getIntInput("请选择操作:");switch (choice) {case 1:addNewBook();break;case 2:searchBooks();break;// 其他case...case 0:return;default:System.out.println("无效选择");}}}private void addNewBook() {System.out.println("\n--- 添加新书 ---");String isbn = getStringInput("ISBN:");String title = getStringInput("书名:");String author = getStringInput("作者:");Book book = new Book();book.setIsbn(isbn);book.setTitle(title);book.setAuthor(author);// 设置其他属性...if (libraryService.addBook(book)) {System.out.println("添加图书成功!");} else {System.out.println("添加图书失败");}}// 其他方法...private int getIntInput(String prompt) {while (true) {try {System.out.print(prompt);return Integer.parseInt(scanner.nextLine());} catch (NumberFormatException e) {System.out.println("请输入有效数字!");}}}private String getStringInput(String prompt) {System.out.print(prompt);return scanner.nextLine();}
}
四、项目扩展与优化
4.1 功能扩展建议
-
预约功能:
-
允许读者预约已被借出的图书
-
图书归还时通知预约读者
-
-
逾期罚款:
-
计算逾期天数
-
按规则自动计算罚款金额
-
-
图书推荐:
-
基于借阅历史的简单推荐
-
热门图书推荐
-
4.2 代码优化方向
引入连接池:
// 使用HikariCP替代原生JDBC连接
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/library_db");
config.setUsername("root");
config.setPassword("123456");
HikariDataSource dataSource = new HikariDataSource(config);
使用DAO接口:
public interface BookDAO {boolean addBook(Book book);Book getBookById(int bookId);// 其他方法...
}public class BookDAOImpl implements BookDAO {// 实现方法...
}
日志记录:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class BookDAOImpl implements BookDAO {private static final Logger logger = LoggerFactory.getLogger(BookDAOImpl.class);public boolean addBook(Book book) {logger.debug("尝试添加图书:{}", book.getTitle());// 实现代码...}
}
4.3 异常处理改进
自定义异常类:
public class LibraryException extends Exception {public LibraryException(String message) {super(message);}public LibraryException(String message, Throwable cause) {super(message, cause);}
}// 使用示例
public void borrowBook(int bookId, int readerId) throws LibraryException {try {// 业务逻辑...} catch (SQLException e) {throw new LibraryException("借书操作失败", e);}
}
五、项目部署与测试
5.1 单元测试示例
BookDAOTest.java:
public class BookDAOTest {private BookDAO bookDAO = new BookDAOImpl();@Testpublic void testAddAndGetBook() {Book book = new Book();book.setIsbn("978-3-16-148410-0");book.setTitle("测试图书");book.setAuthor("测试作者");boolean added = bookDAO.addBook(book);assertTrue(added);Book retrieved = bookDAO.getBookByIsbn("978-3-16-148410-0");assertNotNull(retrieved);assertEquals("测试图书", retrieved.getTitle());}// 其他测试方法...
}
5.2 系统测试流程
-
图书管理测试:
-
添加不同种类的图书
-
测试各种查询条件组合
-
验证库存更新逻辑
-
-
借还书测试:
-
正常借书/还书流程
-
测试库存不足情况
-
验证逾期计算正确性
-
-
并发测试:
-
模拟多个用户同时借阅同一本书
-
验证库存扣减的原子性
-
结语
通过这个图书管理系统项目,我们完整实践了:
-
控制台程序的交互设计
-
数据库表结构设计与优化
-
JDBC的实战应用
-
分层架构的实现
-
基础业务逻辑开发
进一步学习建议:
-
尝试使用MyBatis重构数据访问层
-
添加Web界面转型为B/S架构
-
学习使用Spring框架改造项目
-
研究数据库事务隔离级别的实际影响
如果您在实现过程中遇到任何问题,欢迎在评论区留言讨论。觉得本文有帮助的话,请点赞收藏支持!