实战:基于Maven+Servlet+MySQL的校园图书管理系统开发全流程

张开发
2026/4/17 15:27:34 15 分钟阅读

分享文章

实战:基于Maven+Servlet+MySQL的校园图书管理系统开发全流程
1. 项目背景与技术选型校园图书管理系统是每个学校都需要的核心应用之一。记得我刚工作那会儿学校图书馆还在用纸质登记簿记录借阅信息管理员经常要翻找半天。现在用Java Web技术开发这样一个系统不仅能提高工作效率还能为学生提供更好的服务体验。为什么选择MavenServletMySQL这个技术栈首先Maven作为项目构建工具能帮我们轻松管理各种依赖包。Servlet是Java Web开发的基础虽然现在流行Spring Boot但从Servlet学起能打好基础。MySQL则是中小型应用的首选数据库免费又好用。这个项目适合有一定Java基础想学习Web开发的同学。不需要你精通前端我们会用最简单的HTMLCSSjQuery来实现界面。重点放在后端业务逻辑和数据库操作上。2. 开发环境准备2.1 安装必备软件首先确保你的电脑上已经安装了JDK 1.8或以上版本IntelliJ IDEA社区版就够用MySQL 5.7或8.0Maven 3.6我建议使用IDEA而不是Eclipse因为IDEA对Maven项目的支持更好。安装完这些软件后记得配置环境变量。特别是Maven需要设置MAVEN_HOME和PATH。2.2 创建Maven项目打开IDEA选择新建项目选择Maven项目勾选Create from archetype选择maven-archetype-webapp填写GroupId和ArtifactId完成创建创建完成后项目结构应该是这样的book-system ├── src │ ├── main │ │ ├── java │ │ ├── resources │ │ └── webapp │ │ ├── WEB-INF │ │ └── index.jsp └── pom.xml2.3 配置pom.xmlpom.xml是Maven项目的核心配置文件。我们需要添加以下依赖dependencies !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.23/version /dependency !-- Servlet API -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- Jackson JSON处理 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.12.3/version /dependency !-- Lombok简化代码 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.20/version scopeprovided/scope /dependency /dependencies3. 数据库设计与实现3.1 数据库表设计图书管理系统的核心是数据库设计。我们需要考虑以下几个实体用户管理员学生图书借阅记录班级可选建表SQL如下CREATE TABLE user ( id int NOT NULL AUTO_INCREMENT, username varchar(20) NOT NULL, password varchar(20) NOT NULL, nickname varchar(20) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE book ( id int NOT NULL AUTO_INCREMENT, book_name varchar(50) NOT NULL, author varchar(20) DEFAULT NULL, price decimal(10,2) DEFAULT NULL, status tinyint DEFAULT 1 COMMENT 1-可借 0-已借出, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE student ( id int NOT NULL AUTO_INCREMENT, student_name varchar(20) NOT NULL, student_no varchar(20) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE borrow_record ( id int NOT NULL AUTO_INCREMENT, book_id int NOT NULL, student_id int NOT NULL, borrow_time datetime NOT NULL, return_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY book_id (book_id), KEY student_id (student_id), CONSTRAINT borrow_record_ibfk_1 FOREIGN KEY (book_id) REFERENCES book (id), CONSTRAINT borrow_record_ibfk_2 FOREIGN KEY (student_id) REFERENCES student (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;3.2 数据库连接配置在src/main/resources下创建db.properties文件jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/book_system?useSSLfalseserverTimezoneUTC jdbc.usernameroot jdbc.password123456然后创建一个DBUtil工具类来管理数据库连接public class DBUtil { private static final Properties props new Properties(); static { try { props.load(DBUtil.class.getClassLoader().getResourceAsStream(db.properties)); Class.forName(props.getProperty(jdbc.driver)); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection( props.getProperty(jdbc.url), props.getProperty(jdbc.username), props.getProperty(jdbc.password) ); } public static void close(Connection conn, Statement stmt, ResultSet rs) { // 关闭资源代码... } }4. 核心功能实现4.1 用户登录功能首先创建User实体类Data public class User { private Integer id; private String username; private String password; private String nickname; }然后实现登录ServletWebServlet(/login) 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 ps null; ResultSet rs null; try { conn DBUtil.getConnection(); String sql SELECT * FROM user WHERE username? AND password?; ps conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); rs ps.executeQuery(); if (rs.next()) { User user new User(); user.setId(rs.getInt(id)); user.setUsername(rs.getString(username)); user.setNickname(rs.getString(nickname)); request.getSession().setAttribute(user, user); response.sendRedirect(index.jsp); } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } } }4.2 图书借阅功能实现图书借阅的核心逻辑WebServlet(/borrow) public class BorrowServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int bookId Integer.parseInt(request.getParameter(bookId)); int studentId Integer.parseInt(request.getParameter(studentId)); Connection conn null; PreparedStatement ps null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 检查图书是否可借 String checkSql SELECT status FROM book WHERE id? FOR UPDATE; ps conn.prepareStatement(checkSql); ps.setInt(1, bookId); ResultSet rs ps.executeQuery(); if (!rs.next() || rs.getInt(status) 0) { response.getWriter().write({\success\:false,\message\:\图书不可借\}); return; } // 更新图书状态 String updateSql UPDATE book SET status0 WHERE id?; ps conn.prepareStatement(updateSql); ps.setInt(1, bookId); ps.executeUpdate(); // 创建借阅记录 String insertSql INSERT INTO borrow_record(book_id, student_id, borrow_time) VALUES(?,?,NOW()); ps conn.prepareStatement(insertSql); ps.setInt(1, bookId); ps.setInt(2, studentId); ps.executeUpdate(); conn.commit(); response.getWriter().write({\success\:true}); } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } response.getWriter().write({\success\:false,\message\:\借书失败\}); } finally { DBUtil.close(conn, ps, null); } } }4.3 图书归还功能图书归还的逻辑与借阅类似但需要更新图书状态和借阅记录WebServlet(/return) public class ReturnServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int recordId Integer.parseInt(request.getParameter(recordId)); Connection conn null; PreparedStatement ps null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 获取借阅记录详情 String selectSql SELECT book_id FROM borrow_record WHERE id? AND return_time IS NULL; ps conn.prepareStatement(selectSql); ps.setInt(1, recordId); ResultSet rs ps.executeQuery(); if (!rs.next()) { response.getWriter().write({\success\:false,\message\:\无效的借阅记录\}); return; } int bookId rs.getInt(book_id); // 更新图书状态 String updateBookSql UPDATE book SET status1 WHERE id?; ps conn.prepareStatement(updateBookSql); ps.setInt(1, bookId); ps.executeUpdate(); // 更新借阅记录 String updateRecordSql UPDATE borrow_record SET return_timeNOW() WHERE id?; ps conn.prepareStatement(updateRecordSql); ps.setInt(1, recordId); ps.executeUpdate(); conn.commit(); response.getWriter().write({\success\:true}); } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } response.getWriter().write({\success\:false,\message\:\还书失败\}); } finally { DBUtil.close(conn, ps, null); } } }5. 前后端交互实现5.1 使用AJAX获取图书列表前端页面通过AJAX请求获取图书列表数据function loadBooks(page, size) { $.ajax({ url: book/list, type: GET, data: { page: page || 1, size: size || 10 }, success: function(res) { if (res.success) { renderBooks(res.data); renderPagination(res.total, size || 10); } else { alert(res.message); } } }); } function renderBooks(books) { var html ; books.forEach(function(book) { html tr; html td book.id /td; html td book.bookName /td; html td (book.author || ) /td; html td (book.price || ) /td; html td (book.status 1 ? 可借 : 已借出) /td; html td; if (book.status 1) { html button classbtn btn-sm btn-primary onclickshowBorrowModal( book.id )借书/button; } html /td; html /tr; }); $(#bookTable tbody).html(html); }5.2 后端接口实现图书列表接口的实现WebServlet(/book/list) public class BookListServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int page Integer.parseInt(request.getParameter(page)); int size Integer.parseInt(request.getParameter(size)); Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DBUtil.getConnection(); // 查询总数 String countSql SELECT COUNT(*) FROM book; ps conn.prepareStatement(countSql); rs ps.executeQuery(); rs.next(); long total rs.getLong(1); // 查询分页数据 String sql SELECT * FROM book LIMIT ?,?; ps conn.prepareStatement(sql); ps.setInt(1, (page - 1) * size); ps.setInt(2, size); rs ps.executeQuery(); ListBook books new ArrayList(); while (rs.next()) { Book book new Book(); book.setId(rs.getInt(id)); book.setBookName(rs.getString(book_name)); book.setAuthor(rs.getString(author)); book.setPrice(rs.getBigDecimal(price)); book.setStatus(rs.getInt(status)); books.add(book); } ResponseResult result new ResponseResult(); result.setSuccess(true); result.setTotal(total); result.setData(books); response.setContentType(application/json;charsetutf-8); response.getWriter().write(new ObjectMapper().writeValueAsString(result)); } catch (SQLException e) { e.printStackTrace(); ResponseResult result new ResponseResult(); result.setSuccess(false); result.setMessage(查询失败); response.getWriter().write(new ObjectMapper().writeValueAsString(result)); } finally { DBUtil.close(conn, ps, rs); } } }5.3 统一响应格式为了方便前后端交互我们定义一个统一的响应格式Data public class ResponseResult { private boolean success; private String code; private String message; private Long total; private Object data; public static ResponseResult success(Object data) { ResponseResult result new ResponseResult(); result.setSuccess(true); result.setCode(200); result.setData(data); return result; } public static ResponseResult error(String message) { ResponseResult result new ResponseResult(); result.setSuccess(false); result.setCode(500); result.setMessage(message); return result; } }6. 项目部署与测试6.1 使用Tomcat部署在IDEA中配置Tomcat点击Run - Edit Configurations添加Tomcat Server - Local配置Tomcat安装目录设置Deployment添加Artifact启动Tomcat前确保MySQL服务已启动数据库表和数据已初始化pom.xml中的依赖都已下载完成启动Tomcat后访问http://localhost:8080/book-system6.2 功能测试要点测试时重点关注以下场景同一本书能否被多人同时借阅并发控制借阅超期处理图书库存状态是否正确更新分页查询是否正确各种异常情况的处理如网络中断、数据库连接失败等6.3 性能优化建议当系统用户量增大时可以考虑以下优化使用数据库连接池如HikariCP对频繁查询的表添加索引实现缓存机制如Redis缓存热门图书信息对前端资源进行压缩和合并使用Nginx做反向代理和负载均衡7. 常见问题解决7.1 中文乱码问题遇到中文乱码时检查以下几个方面数据库连接URL中添加characterEncodingutf8Servlet设置response.setContentType(application/json;charsetutf-8)JSP页面添加% page contentTypetext/html;charsetUTF-8 %确保MySQL表的字符集是utf8mb47.2 事务管理问题在多表操作时确保使用事务try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 执行多个SQL操作 conn.commit(); } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } throw e; } finally { conn.setAutoCommit(true); DBUtil.close(conn, ps, rs); }7.3 跨域问题如果前后端分离开发可能会遇到跨域问题。可以在Servlet中添加response.setHeader(Access-Control-Allow-Origin, *); response.setHeader(Access-Control-Allow-Methods, GET,POST,PUT,DELETE); response.setHeader(Access-Control-Allow-Headers, Content-Type);8. 项目扩展方向完成基础功能后可以考虑添加以下功能图书分类管理借阅统计报表图书预约功能逾期罚款计算读者信用积分系统多校区图书调拨移动端适配这个项目虽然基础但涵盖了Web开发的各个环节。我在实际开发中发现处理好事务和并发是关键。特别是借书操作一定要加锁确保数据一致性。

更多文章