目录
ListBooksServlet.java(或直接在JSP中处理)
注意:直接在GET请求中删除数据(如上例中的Delete链接)不是最佳实践,因为它可以被意外地触发,并且可能不被浏览器缓存机制正确处理。在实际应用中,应使用POST请求并确认用户意图。
在Java Web开发领域,图书管理系统是一个经典的实战项目,它不仅能够帮助我们深入理解Java EE技术栈,还能在实际应用中锻炼我们的编程能力和系统设计思维。以下是一个基于Java Web技术的图书管理系统制作指南,旨在帮助初学者到中级开发者构建并部署一个基本的图书管理系统。
一、项目概述
1.1 项目目标
- 实现对图书信息的增删改查(CRUD)操作。
- 支持用户登录与权限管理。
- 图书借阅与归还功能。
- 系统管理员可以管理用户信息和图书库存。
1.2 技术选型
- 前端技术:HTML, CSS, JavaScript, JSP/Servlet, Bootstrap(可选)
- 后端技术:Java, Servlet, JDBC, JavaBeans
- 数据库:MySQL
- 开发工具:Eclipse/IntelliJ IDEA, MySQL Workbench
- 服务器:Tomcat
二、环境搭建
2.1 安装Java JDK
从Oracle官网下载并安装JDK,配置JAVA_HOME环境变量,并将JDK的bin目录添加到PATH环境变量中。
2.2 安装并配置Tomcat
下载Tomcat安装包,解压并配置环境变量(可选),确保Tomcat可以正常启动。
2.3 安装MySQL
安装MySQL数据库,并创建一个用于图书管理系统的数据库及相应的表。
2.4 开发工具安装
安装Eclipse或IntelliJ IDEA,并配置Tomcat服务器和MySQL数据库连接。
三、系统设计与实现
3.1 数据库设计
设计包括用户表、图书表、借阅记录表等数据库表,并定义表之间的关联关系。
示例SQL语句:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(50) NOT NULL,
role ENUM('admin', 'user') NOT NULL
);
CREATE TABLE books (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
author VARCHAR(100),
isbn VARCHAR(20) UNIQUE,
quantity INT NOT NULL
);
CREATE TABLE borrow_records (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
book_id INT,
borrow_date DATE,
return_date DATE,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (book_id) REFERENCES books(id)
);
3.2 系统架构设计
- 表示层:使用JSP和Servlet处理用户请求和响应。
- 业务逻辑层:编写JavaBeans或Service类处理业务逻辑。
- 数据访问层:使用JDBC连接数据库,执行SQL语句。
3.3 关键模块实现
3.3.1 用户登录
- 前端:创建登录表单,提交用户名和密码到后端。
-
前端JSP页面 (login.jsp)
-
<!DOCTYPE html> <html> <head> <title>Login</title> </head> <body> <h2>Login to the Library Management System</h2> <form action="LoginServlet" method="post"> Username: <input type="text" name="username" required><br> Password: <input type="password" name="password" required><br> <input type="submit" value="Login"> </form> </body> </html>
- 后端:Servlet接收请求,调用DAO层验证用户名和密码,根据验证结果重定向到相应页面。
-
后端Servlet (LoginServlet.java)
-
import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { // 加载和注册JDBC驱动 Class.forName("com.mysql.cj.jdbc.Driver"); // 建立数据库连接 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/library_management", "username", "password"); // SQL查询语句 String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, username); pstmt.setString(2, password); rs = pstmt.executeQuery(); if (rs.next()) { // 登录成功,设置请求属性并转发到另一个页面 request.setAttribute("userRole", rs.getString("role")); response.sendRedirect("home.jsp"); // 假设有一个home.jsp页面用于显示用户主页 } else { // 登录失败,重定向回登录页面并显示错误信息 request.setAttribute("error", "Invalid username or password"); request.getRequestDispatcher("login.jsp").forward(request, response); } } catch (Exception e) { e.printStackTrace(); request.setAttribute("error", "Database error"); request.getRequestDispatcher("login.jsp").forward(request, response); } finally { // 关闭资源 try { if (rs != null) rs.close(); if (pstmt != null) pstmt.close(); if (conn != null) conn.close(); } catch (Exception e) { e.printStackTrace(); } } } }
- 这里的数据库连接信息(如URL、用户名和密码)需要根据你的MySQL服务器配置进行更改。
- 密码存储在实际应用中应该使用哈希处理,而不是明文存储。
- 这个示例中省略了错误处理和资源管理的最佳实践(如使用try-with-resources语句),以保持示例的简洁性。
- 真实环境中,应该使用连接池来管理数据库连接,以提高性能和资源利用率。
3.3.2 图书管理
- 增加图书:表单提交图书信息,Servlet处理请求,调用DAO层将数据保存到数据库。
- 删除图书:通过图书ID删除图书,需考虑库存量更新。
- 修改图书:表单提交修改后的图书信息,Servlet处理并更新数据库。
- 查询图书:支持按标题、作者等条件查询,结果以列表形式展示。
我们将创建四个Servlet来处理CRUD操作:AddBookServlet
、DeleteBookServlet
、UpdateBookServlet
和ListBooksServlet
(或ViewBooksServlet
,取决于你的命名偏好)。由于篇幅限制,这里只给出AddBookServlet
的示例代码,其他Servlet将遵循类似的模式。
AddBookServlet.java
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/AddBookServlet")
public class AddBookServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String title = request.getParameter("title");
String author = request.getParameter("author");
String isbn = request.getParameter("isbn");
String publicationYear = request.getParameter("publication_year");
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 加载和注册JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 建立数据库连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/library_management", "username", "password");
// SQL插入语句
String sql = "INSERT INTO books (title, author, isbn, publication_year) VALUES (?, ?, ?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, title);
pstmt.setString(2, author);
pstmt.setString(3, isbn);
pstmt.setInt(4, Integer.parseInt(publicationYear));
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
response.sendRedirect("listBooks.jsp"); // 假设有一个listBooks.jsp页面用于显示所有图书
} else {
// 处理插入失败的情况
request.setAttribute("error", "Failed to add book");
request.getRequestDispatcher("addBook.jsp").forward(request, response); // 假设有一个addBook.jsp页面用于添加图书
}
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("error", "Database error");
request.getRequestDispatcher("addBook.jsp").forward(request, response);
} finally {
// 关闭资源(略)
}
}
}
前端JSP页面
你需要为每种操作创建相应的JSP页面。例如,addBook.jsp
可能包含一个表单,用于输入图书的详细信息。
<!DOCTYPE html>
<html>
<head>
<title>Add Book</title>
</head>
<body>
<h2>Add a New Book</h2>
<form action="AddBookServlet" method="post">
Title: <input type="text" name="title" required><br>
Author: <input type="text" name="author" required><br>
ISBN: <input type="text" name="isbn" required><br>
Publication Year: <input type="number" name="publication_year" required><br>
<input type="submit" value="Add Book">
</form>
</body>
</html>
DeleteBookServlet.java
@WebServlet("/DeleteBookServlet")
public class DeleteBookServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id");
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 数据库连接代码(省略,与AddBookServlet相同)
String sql = "DELETE FROM books WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, Integer.parseInt(id));
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
response.sendRedirect("listBooks.jsp"); // 假设listBooks.jsp用于显示所有图书
} else {
// 处理删除失败的情况
request.setAttribute("error", "Failed to delete book");
request.getRequestDispatcher("listBooks.jsp").forward(request, response);
}
} catch (Exception e) {
// 异常处理代码(省略,与AddBookServlet相同)
} finally {
// 关闭资源(省略)
}
}
}
注意:在实际应用中,删除操作可能会通过POST请求进行,并且可能需要额外的确认步骤,以防止误删除。
UpdateBookServlet.java
@WebServlet("/UpdateBookServlet")
public class UpdateBookServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 类似AddBookServlet,但使用UPDATE SQL语句
// 假设表单包含id, title, author, isbn, publication_year等字段
// ...(省略具体实现,与AddBookServlet相似,但SQL语句为UPDATE)
}
}
ListBooksServlet.java(或直接在JSP中处理)
通常,列表功能可能直接在JSP页面中处理,使用JSTL和SQL标签库(如JSTL SQL)或直接在JSP中嵌入Java代码(不推荐,因为这会降低可维护性和安全性)。但出于示例目的,我们可以假设它只是一个Servlet,它查询数据库并将结果发送到JSP页面。
JSP页面
对于更新和删除操作,你可能需要在listBooks.jsp
中添加相应的链接或按钮,这些链接或按钮将向相应的Servlet发送请求。
listBooks.jsp 示例片段
<table>
<tr>
<th>ID</th>
<th>Title</th>
<th>Author</th>
<th>ISBN</th>
<th>Publication Year</th>
<th>Actions</th>
</tr>
<!-- 假设bookList是从请求属性中获取的图书列表 -->
<c:forEach var="book" items="${bookList}">
<tr>
<td>${book.id}</td>
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
<td>${book.publicationYear}</td>
<td>
<a href="updateBook.jsp?id=${book.id}">Update</a>
<a href="DeleteBookServlet?id=${book.id}" onclick="return confirm('Are you sure you want to delete this book?');">Delete</a>
</td>
</tr>
</c:forEach>
</table>
注意:直接在GET请求中删除数据(如上例中的Delete链接)不是最佳实践,因为它可以被意外地触发,并且可能不被浏览器缓存机制正确处理。在实际应用中,应使用POST请求并确认用户意图。
3.3.3 借阅与归还
- 借阅:用户选择图书并提交借阅请求,系统验证库存和用户权限后更新数据库。
- 归还:用户提交归还请求,系统更新借阅记录和图书库存。
四、系统测试
进行单元测试、集成测试和用户验收测试,确保系统各模块功能正常,符合需求。
五、部署与维护
将项目打包并部署到服务器上,进行线上测试与调试。定期检查系统性能,根据用户反馈进行必要的维护与升级。