这个Java图书管理系统是一个基于JSP的Web应用,采用了经典的MVC架构模式。系统分为读者用户和管理员两大角色模块,实现了图书借阅管理的全流程数字化。作为Java Web开发的经典练手项目,它涵盖了从数据库设计到前后端交互的完整开发链路。
我在实际开发过程中发现,这类系统虽然功能看似简单,但要做好需要考虑很多细节。比如借阅状态的并发控制、图书检索的性能优化、用户权限的精细化管理等。下面我就从技术实现角度,带大家深入剖析这个项目的核心设计和关键代码。
系统采用B/S架构,主要技术栈包括:
选择这套技术栈主要基于以下考虑:
提示:实际项目中可以考虑用Spring Boot替代原生Servlet,能大幅减少样板代码量。
核心表结构设计如下:
books表(图书信息)
sql复制CREATE TABLE books (
book_id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
author VARCHAR(50) NOT NULL,
isbn VARCHAR(20) UNIQUE,
category_id INT,
status TINYINT DEFAULT 0 COMMENT '0-可借阅 1-已借出',
publish_date DATE,
FOREIGN KEY (category_id) REFERENCES categories(category_id)
);
users表(用户信息)
sql复制CREATE TABLE users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(30) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
real_name VARCHAR(30),
phone VARCHAR(20),
user_type TINYINT DEFAULT 0 COMMENT '0-普通用户 1-管理员'
);
borrow_records表(借阅记录)
sql复制CREATE TABLE borrow_records (
record_id INT PRIMARY KEY AUTO_INCREMENT,
book_id INT NOT NULL,
user_id INT NOT NULL,
borrow_date DATETIME DEFAULT CURRENT_TIMESTAMP,
return_date DATETIME,
FOREIGN KEY (book_id) REFERENCES books(book_id),
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
数据库设计有几个关键点需要注意:
登录流程实现代码示例:
java复制public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
try (Connection conn = DBUtil.getConnection()) {
String sql = "SELECT * FROM users WHERE username=? AND password=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, MD5Util.encrypt(password));
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
User user = new User();
user.setUserId(rs.getInt("user_id"));
user.setUserType(rs.getInt("user_type"));
request.getSession().setAttribute("currentUser", user);
response.sendRedirect(user.getUserType() == 1 ? "/admin" : "/user");
} else {
request.setAttribute("errorMsg", "用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
安全注意事项:
借阅业务逻辑实现:
java复制public class BorrowService {
public boolean borrowBook(int bookId, int userId) {
Connection conn = null;
try {
conn = DBUtil.getConnection();
conn.setAutoCommit(false); // 开启事务
// 检查图书状态
String checkSql = "SELECT status FROM books WHERE book_id=? FOR UPDATE";
PreparedStatement checkStmt = conn.prepareStatement(checkSql);
checkStmt.setInt(1, bookId);
ResultSet rs = checkStmt.executeQuery();
if (!rs.next() || rs.getInt("status") != 0) {
return false;
}
// 更新图书状态
String updateSql = "UPDATE books SET status=1 WHERE book_id=?";
PreparedStatement updateStmt = conn.prepareStatement(updateSql);
updateStmt.setInt(1, bookId);
updateStmt.executeUpdate();
// 创建借阅记录
String insertSql = "INSERT INTO borrow_records(book_id, user_id) VALUES(?,?)";
PreparedStatement insertStmt = conn.prepareStatement(insertSql);
insertStmt.setInt(1, bookId);
insertStmt.setInt(2, userId);
insertStmt.executeUpdate();
conn.commit();
return true;
} catch (SQLException e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
return false;
} finally {
DBUtil.close(conn);
}
}
}
关键实现细节:
sql复制-- 为常用查询字段添加索引
CREATE INDEX idx_books_title ON books(title);
CREATE INDEX idx_books_author ON books(author);
CREATE INDEX idx_borrow_user ON borrow_records(user_id);
java复制public class BookCache {
private static final Map<Integer, Book> cache = new ConcurrentHashMap<>();
public static Book getBook(int bookId) {
if (cache.containsKey(bookId)) {
return cache.get(bookId);
}
Book book = BookDAO.getById(bookId);
if (book != null) {
cache.put(bookId, book);
}
return book;
}
}
java复制public List<Book> getBooksByPage(int pageNum, int pageSize) {
String sql = "SELECT * FROM books LIMIT ?,?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, (pageNum - 1) * pageSize);
pstmt.setInt(2, pageSize);
ResultSet rs = pstmt.executeQuery();
List<Book> books = new ArrayList<>();
while (rs.next()) {
books.add(new Book(rs));
}
return books;
} catch (SQLException e) {
e.printStackTrace();
return Collections.emptyList();
}
}
java复制public class XSSFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest req = (HttpServletRequest) request;
XSSRequestWrapper wrappedRequest = new XSSRequestWrapper(req);
chain.doFilter(wrappedRequest, response);
}
}
public class XSSRequestWrapper extends HttpServletRequestWrapper {
public String getParameter(String name) {
String value = super.getParameter(name);
return cleanXSS(value);
}
private String cleanXSS(String value) {
if (value == null) return null;
return value.replaceAll("<", "<").replaceAll(">", ">");
}
}
jsp复制<!-- 在JSP表单中添加CSRF Token -->
<input type="hidden" name="csrfToken" value="${sessionScope.csrfToken}">
bash复制mysql -u root -p < database/schema.sql
mysql -u root -p < database/data.sql
xml复制<!-- Maven war插件配置 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<warName>library</warName>
</configuration>
</plugin>
xml复制<!-- 在web.xml中添加字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
java复制public class RedisUtil {
private static JedisPool jedisPool;
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
jedisPool = new JedisPool(config, "localhost", 6379);
}
public static void set(String key, String value) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.set(key, value);
}
}
}
java复制@WebServlet("/api/books/*")
public class BookApiServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String pathInfo = req.getPathInfo();
// 解析URL路径,返回对应数据
}
}
java复制public class RecommendService {
public List<Book> recommendBooks(int userId) {
// 基于用户借阅历史的简单推荐算法
String sql = "SELECT b.* FROM books b " +
"JOIN categories c ON b.category_id=c.category_id " +
"WHERE b.category_id IN (" +
" SELECT DISTINCT b.category_id FROM borrow_records br " +
" JOIN books b ON br.book_id=b.book_id " +
" WHERE br.user_id=?) " +
"AND b.book_id NOT IN (" +
" SELECT book_id FROM borrow_records WHERE user_id=?) " +
"LIMIT 5";
// 执行查询并返回结果
}
}
在实际开发过程中,我发现图书管理系统的边界其实可以不断扩展。比如加入电子书在线阅读、馆际互借、移动端应用等功能。这个基础版本已经包含了核心的业务逻辑,后续可以根据实际需求进行功能增强。