一、项目概述与设计思路
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();
@Test
public 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框架改造项目
-
研究数据库事务隔离级别的实际影响
如果您在实现过程中遇到任何问题,欢迎在评论区留言讨论。觉得本文有帮助的话,请点赞收藏支持!