Java Web教室借用管理系统毕业设计项目实战
简介:本项目“Java JSP教室借用管理系统”是一个基于Java Web技术的企业级管理系统,使用JSP、Servlet、MySQL和MVC架构实现教室借用流程的信息化管理。系统包含用户认证、权限控制、数据CRUD、事务处理、前端界面设计、报表统计等功能,适合毕业设计与实战学习。通过本项目,学生可掌握Web开发核心技术,提升实际编程能力,为Java Web开发打下坚实基础。 
1. Java Web开发环境搭建
1.1 JDK安装与环境变量配置
在进行Java Web开发前,首先需要安装Java Development Kit(JDK)。推荐使用JDK 11或JDK 17,这两个版本均为长期支持版本(LTS),具有良好的兼容性和稳定性。
安装步骤:
- 访问 Oracle官网 或 OpenJDK 下载对应系统的JDK安装包。
- 安装完成后,设置环境变量:
- Windows系统 :- 设置
JAVA_HOME:例如C:\Program Files\Java\jdk-17.0.1 - 更新
Path:添加%JAVA_HOME%\bin - Linux/macOS系统 :
- 编辑
~/.bashrc或~/.zshrc文件,添加如下内容:bash export JAVA_HOME=/usr/lib/jvm/jdk-17.0.1 export PATH=$JAVA_HOME/bin:$PATH - 执行
source ~/.bashrc或source ~/.zshrc生效配置。
- 设置
验证安装:
在命令行中执行:
java -version
javac -version
若输出类似以下内容,则表示安装成功:
java version "17.0.1" 2021-10-19 LTS
Java(TM) SE Runtime Environment (build 17.0.1+12-LTS-19)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.1+12-LTS-19, mixed mode, sharing)
2. JSP基础语法与动态页面实现
JSP(Java Server Pages)是一种基于Java的技术,允许开发者将Java代码嵌入到HTML页面中,从而实现动态网页内容的生成。在教室借用管理系统中,JSP作为视图层的重要组成部分,负责接收来自Servlet的处理结果,并将数据动态展示给用户。本章将深入探讨JSP的基本语法、页面结构、内置对象的使用方式,并通过实战案例展示其在实际项目中的应用。
2.1 JSP的基本概念与执行流程
JSP 技术是 Java Web 开发中不可或缺的一部分。它在本质上是一种基于 Servlet 的封装,允许开发者以 HTML 的形式编写动态内容,并在服务器端运行 Java 代码。JSP 文件在第一次被访问时会被 Web 容器(如 Tomcat)编译为 Servlet,然后执行并返回 HTML 内容给客户端浏览器。
2.1.1 JSP的定义与运行机制
JSP 文件的后缀是 .jsp ,它本质上是一个文本文件,包含 HTML 标签和嵌入的 Java 代码片段。当用户请求一个 JSP 页面时,Web 容器会经历以下几个步骤:
- 翻译阶段(Translation) :将
.jsp文件翻译成.java文件(即对应的 Servlet 源码)。 - 编译阶段(Compilation) :将生成的
.java文件编译为.class字节码文件。 - 加载与执行阶段(Execution) :加载编译后的类并执行,最终生成 HTML 响应输出。
这一机制确保了 JSP 页面在首次请求时会经历一次编译过程,后续请求则直接执行编译后的类,提高了性能。
2.1.2 JSP与Servlet的关系
JSP 和 Servlet 是 Java Web 开发的两大核心组件,它们之间关系密切:
| 对比维度 | Servlet | JSP |
|---|---|---|
| 编写方式 | 纯 Java 代码 | HTML + Java 脚本 |
| 主要用途 | 控制逻辑、业务处理 | 视图展示、动态页面输出 |
| 编译过程 | 手动编写 Java 类 | 自动翻译为 Servlet |
| 开发效率 | 适合处理复杂逻辑 | 更适合页面设计和前端展示 |
JSP 的本质是 Servlet,所有 JSP 页面最终都会被编译为 Servlet 类,因此它们在底层是统一的。但在开发实践中,JSP 更适合处理页面展示,而 Servlet 更适合处理业务逻辑。
2.2 JSP页面元素与语法
JSP 页面由多种元素组成,包括指令标签、脚本元素和动作标签等。这些元素共同构成了 JSP 的语法体系,允许开发者灵活地嵌入 Java 逻辑与动态内容。
2.2.1 指令标签(page、include、taglib)
JSP 指令用于向容器提供页面处理的指令信息,常见的指令包括:
-
<%@ page %>:定义页面属性,如编码格式、引入包等。 -
<%@ include %>:静态包含其他页面内容。 -
<%@ taglib %>:引入 JSTL 或自定义标签库。
示例代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
逐行解析:
language="java":指定页面使用的脚本语言为 Java。contentType="text/html; charset=UTF-8":设置响应内容类型和字符编码。pageEncoding="UTF-8":指定页面本身的编码格式。<%@ include file="header.jsp" %>:将header.jsp文件内容静态包含到当前页面。<%@ taglib ... %>:引入 JSTL 核心标签库,使用前缀c。
2.2.2 脚本元素(声明、表达式、脚本)
JSP 支持三种脚本元素,用于在页面中嵌入 Java 代码:
- 声明(Declaration) :使用
<%! %>包裹,用于声明变量或方法。 - 表达式(Expression) :使用
<%= %>包裹,用于输出 Java 表达式的结果。 - 脚本(Scriptlet) :使用
<% %>包裹,用于编写 Java 语句。
示例代码:
<%! int count = 0; %>
<% count++; %>
<p>页面访问次数:<%= count %></p>
逻辑分析:
<%! int count = 0; %>:声明一个全局变量count,生命周期与页面一致。<% count++; %>:每次访问页面时,执行count自增操作。<%= count %>:将当前的count值输出到页面上。
⚠️ 注意:虽然脚本元素提供了灵活性,但在现代开发中,推荐使用 JSTL 和 EL 表达式来替代脚本代码,以提高可维护性和可读性。
2.2.3 动作标签(include、forward等)
JSP 动作标签是 JSP 的标准动作指令,以 XML 标签形式存在,由容器处理。常见的动作标签包括:
-
<jsp:include>:动态包含页面内容。 -
<jsp:forward>:请求转发到另一个页面或 Servlet。 -
<jsp:useBean>:创建或查找一个 JavaBean。
示例代码:
<jsp:include page="menu.jsp" />
<jsp:forward page="login.jsp" />
逻辑分析:
<jsp:include page="menu.jsp" />:动态包含menu.jsp页面,该页面内容会在运行时被处理。<jsp:forward page="login.jsp" />:将当前请求转发到login.jsp页面,客户端 URL 不会改变。
2.3 JSP内置对象与作用域
JSP 提供了九个内置对象,它们无需显式声明即可在 JSP 页面中使用。这些对象用于访问请求、响应、会话、应用等上下文信息。
2.3.1 request、response、session、application对象
| 内置对象 | 说明 | 常用方法示例 |
|---|---|---|
request |
封装客户端请求数据,如参数、头部、属性等 | request.getParameter("name") |
response |
用于向客户端发送响应,如设置状态码、重定向等 | response.sendRedirect("index.jsp") |
session |
用于在多个请求之间保存用户状态 | session.setAttribute("user", user) |
application |
代表整个 Web 应用程序,用于保存全局共享数据 | application.setAttribute("count", 10) |
示例代码:
<%
String name = request.getParameter("username");
session.setAttribute("user", name);
application.setAttribute("visitCount", 100);
%>
逻辑分析:
request.getParameter("username"):获取请求参数username。session.setAttribute("user", name):将用户信息保存在会话中。application.setAttribute("visitCount", 100):设置全局访问计数。
2.3.2 pageContext对象与作用域管理
pageContext 是 JSP 中非常重要的内置对象,它提供了对所有其他内置对象的访问,并支持作用域管理。
作用域分类:
| 作用域级别 | 说明 | 生命周期 |
|---|---|---|
| page | 当前页面内有效 | 页面加载完成即销毁 |
| request | 同一个请求中有效 | 请求结束即销毁 |
| session | 同一个用户会话中有效 | 会话失效或超时后销毁 |
| application | 整个 Web 应用程序中有效 | 应用重启或服务器关闭后销毁 |
示例代码:
<%
pageContext.setAttribute("msg", "Hello Page", PageContext.PAGE_SCOPE);
request.setAttribute("msg", "Hello Request", PageContext.REQUEST_SCOPE);
%>
逻辑分析:
pageContext.setAttribute(...):将变量msg设置为不同作用域。PageContext.PAGE_SCOPE表示仅在当前页面有效。PageContext.REQUEST_SCOPE表示在整个请求中有效。
2.4 教室借用管理系统中JSP页面实战
在教室借用管理系统中,JSP 被广泛用于展示教室信息、用户申请表单和用户信息页面。以下将通过几个实际案例,展示 JSP 在系统中的具体应用场景。
2.4.1 动态展示教室列表
假设教室信息存储在数据库中,我们通过 JSP 动态展示教室列表。
<table border="1">
<tr><th>教室编号</th><th>教室名称</th><th>容纳人数</th></tr>
<c:forEach items="${classrooms}" var="room">
<tr>
<td>${room.id}</td>
<td>${room.name}</td>
<td>${room.capacity}</td>
</tr>
</c:forEach>
</table>
逻辑分析:
<c:forEach>是 JSTL 的循环标签,用于遍历classrooms集合。${room.id}等 EL 表达式用于访问 JavaBean 属性。- 此页面假设
classrooms已由 Servlet 传入request作用域。
2.4.2 借用申请表单的JSP实现
用户申请借用教室时,需要填写表单。以下是表单页面的 JSP 实现:
<form action="apply" method="post">
教室编号:<input type="text" name="roomId"><br>
借用时间:<input type="datetime-local" name="applyTime"><br>
借用人:<input type="text" name="applicant"><br>
<input type="submit" value="提交申请">
</form>
逻辑分析:
- 表单提交到
/apply路径,方法为 POST。 name属性对应 Servlet 中通过request.getParameter()获取的参数名。- 使用
<input type="datetime-local">提供日期时间选择功能。
2.4.3 使用JSP实现用户信息展示
展示当前登录用户信息的 JSP 页面如下:
<%
User user = (User) session.getAttribute("user");
%>
<h2>欢迎,<%= user.getName() %></h2>
<p>你的角色是:<%= user.getRole() %></p>
逻辑分析:
- 从
session中取出用户对象user。 - 使用脚本输出用户姓名和角色。
- 实际项目中推荐使用 EL 表达式替代脚本,例如:
${user.name}。
本章从 JSP 的基本概念入手,逐步深入讲解其语法结构、内置对象的使用方式,并结合教室借用管理系统的实际需求,展示了 JSP 在动态页面开发中的具体应用。下一章将深入探讨 Servlet 的工作机制及其在请求处理中的核心作用。
3. Servlet请求处理与响应机制
在Java Web开发中,Servlet是处理HTTP请求与响应的核心组件之一。它不仅负责接收客户端发送的请求,还能根据业务逻辑生成响应内容,是实现动态网页交互的重要桥梁。本章将从Servlet的基础概念讲起,逐步深入至其生命周期、请求与响应处理机制,并结合教室借用管理系统的实际应用场景,展示如何通过Servlet实现用户登录验证、教室借用申请处理等功能。此外,还将介绍异常处理机制和日志记录工具的使用方法,帮助开发者构建更加健壮和可维护的Web应用。
3.1 Servlet基础与生命周期
Servlet是一种运行在服务器端的Java程序,能够动态生成Web内容。它作为客户端(如浏览器)和服务器之间交互的桥梁,广泛用于处理表单提交、访问数据库、生成HTML页面等操作。
3.1.1 Servlet的定义与接口结构
Servlet本质上是一个Java类,实现了 javax.servlet.Servlet 接口。开发者通常继承 HttpServlet 类来创建自己的Servlet类,以处理HTTP协议相关的请求。
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class HelloWorldServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>Hello World from Servlet!</h1>");
}
}
代码分析:
HttpServlet:继承该类可直接处理HTTP请求。doGet()方法:处理GET请求。HttpServletRequest:封装客户端请求信息。HttpServletResponse:用于向客户端发送响应。setContentType():设置响应内容类型为HTML。PrintWriter:用于向客户端输出HTML内容。
接口结构说明:
| 接口/类 | 作用说明 |
|---|---|
| Servlet | 定义Servlet基本接口,包含生命周期方法 |
| GenericServlet | 实现Servlet接口,提供通用协议支持 |
| HttpServlet | 继承GenericServlet,专用于处理HTTP请求 |
| ServletConfig | 提供Servlet配置信息 |
| ServletContext | 提供整个Web应用的上下文信息 |
3.1.2 Servlet的生命周期方法
Servlet的生命周期由容器(如Tomcat)管理,主要分为三个阶段:
- 初始化阶段 (init):容器调用
init(ServletConfig config)方法进行初始化。 - 服务阶段 (service):每次请求到达时,容器调用
service(ServletRequest req, ServletResponse res)方法。 - 销毁阶段 (destroy):容器关闭或Servlet被移除时调用
destroy()方法释放资源。
生命周期方法调用流程图(mermaid)
graph TD
A[客户端发送请求] --> B{Servlet是否已加载?}
B -- 是 --> C[调用service方法处理请求]
B -- 否 --> D[调用init方法初始化]
D --> C
C --> E[生成响应返回客户端]
F[容器关闭] --> G[调用destroy方法]
方法说明:
init():仅在Servlet第一次被调用时执行一次,用于加载配置或初始化资源。service():每次请求都会执行,根据请求类型(GET/POST等)调用对应的方法。destroy():在Servlet被销毁前调用,用于释放资源如关闭数据库连接等。
3.2 HTTP请求与响应处理
Servlet处理HTTP请求的核心在于如何获取请求参数、处理请求类型,并正确生成响应内容。
3.2.1 获取请求参数与处理GET/POST请求
Servlet中通过 HttpServletRequest 对象获取客户端提交的数据。
示例:处理GET和POST请求
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("admin".equals(username) && "123456".equals(password)) {
response.sendRedirect("success.jsp");
} else {
response.sendRedirect("error.jsp");
}
}
代码分析:
getParameter():获取请求中的参数值。doPost():处理POST请求,适用于表单提交等场景。sendRedirect():实现页面跳转,常用于登录成功或失败的处理。
GET与POST请求区别对比表:
| 特性 | GET请求 | POST请求 |
|---|---|---|
| 数据可见性 | 参数显示在URL中 | 数据在请求体中 |
| 缓存支持 | 支持 | 不支持 |
| 请求长度限制 | 有限(URL长度限制) | 无明显限制 |
| 安全性 | 不安全 | 较安全 |
| 幂等性 | 幂等 | 非幂等 |
| 适用场景 | 获取数据(如搜索) | 提交数据(如注册、登录) |
3.2.2 设置响应内容与重定向机制
Servlet通过 HttpServletResponse 对象设置响应头、状态码、内容类型,并实现页面重定向或转发。
示例:设置响应内容并重定向
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.write("登录失败,请重新输入用户名和密码。");
示例:使用sendRedirect实现页面跳转
response.sendRedirect("login.jsp");
示例:使用RequestDispatcher实现请求转发
RequestDispatcher dispatcher = request.getRequestDispatcher("success.jsp");
dispatcher.forward(request, response);
响应机制对比表:
| 方法名 | 是否改变URL | 是否共享request数据 | 适用场景 |
|---|---|---|---|
| sendRedirect() | 是 | 否 | 登录失败跳转、外部跳转 |
| forward() | 否 | 是 | 内部页面跳转、MVC控制层跳转 |
3.3 教室借用管理系统中的Servlet应用
在教室借用管理系统中,Servlet常用于处理用户登录、申请提交、数据查询等业务逻辑。
3.3.1 处理教室借用申请提交
示例:接收申请信息并保存到数据库
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String classroom = request.getParameter("classroom");
String reason = request.getParameter("reason");
String applyTime = request.getParameter("applyTime");
// 调用DAO保存数据
ClassroomDAO dao = new ClassroomDAO();
boolean success = dao.applyClassroom(classroom, reason, applyTime);
if (success) {
response.sendRedirect("apply_success.jsp");
} else {
response.sendRedirect("apply_error.jsp");
}
}
逻辑分析:
- 获取用户提交的教室名称、申请原因和时间。
- 调用DAO层方法将数据保存至数据库。
- 根据结果跳转至成功或失败页面。
3.3.2 实现用户登录验证
示例:登录验证Servlet
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UserDAO dao = new UserDAO();
boolean isValid = dao.validateUser(username, password);
if (isValid) {
HttpSession session = request.getSession();
session.setAttribute("user", username);
response.sendRedirect("dashboard.jsp");
} else {
response.sendRedirect("login.jsp?error=1");
}
}
代码说明:
HttpSession:用于保存用户登录状态。setAttribute():将用户信息存入会话中,供后续页面使用。redirect:根据登录结果跳转至不同页面。
3.3.3 Servlet与JSP的协作流程
在MVC架构中,Servlet通常作为控制器(Controller),负责接收请求并调用模型(Model),最终将结果传递给视图(View)——JSP页面。
示例:Servlet调用JSP显示数据
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ClassroomDAO dao = new ClassroomDAO();
List<Classroom> classrooms = dao.getAllClassrooms();
request.setAttribute("classrooms", classrooms);
RequestDispatcher dispatcher = request.getRequestDispatcher("classroom_list.jsp");
dispatcher.forward(request, response);
}
流程说明:
- Servlet接收请求。
- 调用DAO获取数据。
- 将数据存入request作用域。
- 请求转发至JSP页面进行展示。
3.4 异常处理与日志记录
在Web应用中,异常处理和日志记录是保证系统健壮性和可维护性的关键环节。
3.4.1 Servlet异常处理机制
Servlet容器支持全局异常处理和页面级别的错误配置。
web.xml中配置全局异常处理:
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
在Servlet中手动捕获异常:
try {
// 业务代码
} catch (Exception e) {
request.setAttribute("errorMessage", e.getMessage());
RequestDispatcher dispatcher = request.getRequestDispatcher("error.jsp");
dispatcher.forward(request, response);
}
3.4.2 使用Log4j记录系统日志
Log4j是一个强大的日志记录工具,可用于记录Servlet执行过程中的关键信息。
示例:引入Log4j并记录日志
import org.apache.log4j.Logger;
public class LoginServlet extends HttpServlet {
private static final Logger logger = Logger.getLogger(LoginServlet.class);
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
logger.info("用户登录尝试:" + username);
// 登录逻辑
}
}
Log4j配置示例(log4j.properties):
log4j.rootLogger=DEBUG, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
日志记录优势:
| 优势点 | 说明 |
|---|---|
| 可追溯性 | 便于追踪错误和调试问题 |
| 可审计性 | 用于安全审计和用户行为分析 |
| 性能监控 | 记录请求耗时,便于性能优化 |
| 自动归档 | 可配置日志文件滚动和归档策略 |
本章系统讲解了Servlet在Java Web开发中的核心地位,从基础概念、生命周期、请求响应处理机制到在教室借用管理系统中的具体应用,并介绍了异常处理和日志记录的最佳实践。这些内容为后续MVC架构设计和数据库操作奠定了坚实的基础。
4. MVC架构设计与实现
在现代Web开发中,MVC(Model-View-Controller)架构模式已成为构建可维护、可扩展和模块化系统的标准设计模式之一。尤其在Java Web开发中,MVC模式的引入极大地提升了项目的组织结构与开发效率。本章将以“教室借用管理系统”为背景,深入讲解MVC架构的设计原理、优势及其在项目中的具体实现方式。
4.1 MVC架构原理与优势
4.1.1 MVC模式的核心组件(Model、View、Controller)
MVC是一种软件架构模式,将应用程序分为三个核心组件:
- Model(模型) :负责处理数据逻辑和业务规则。在Java Web应用中,Model通常由JavaBean或POJO类、DAO(数据访问对象)和Service层组成。
- View(视图) :负责数据的展示,通常是HTML页面,也可以是JSON、XML等格式,在Java Web中主要使用JSP页面实现。
- Controller(控制器) :接收用户请求,协调Model与View之间的交互。在Java Web中,通常由Servlet来实现。
通过这种分层设计,开发者可以更清晰地管理代码结构,提高代码的可重用性与可维护性。
4.1.2 MVC在Java Web项目中的应用价值
MVC架构的核心优势在于:
| 优势 | 描述 |
|---|---|
| 分层清晰 | 各层职责分明,便于团队协作开发 |
| 可维护性强 | 各层之间解耦,修改某一层不影响其他层 |
| 易于测试 | 业务逻辑与展示分离,便于单元测试 |
| 可扩展性高 | 可以轻松地替换视图技术(如从JSP换成Thymeleaf)或数据源(如从MySQL换成PostgreSQL) |
在教室借用管理系统中,MVC架构的引入使得系统的结构更加清晰,业务逻辑、数据访问和页面展示各司其职,提高了开发效率并降低了后期维护成本。
4.2 教室借用管理系统中的MVC结构设计
4.2.1 分层结构划分(DAO、Service、Controller)
在本系统中,我们将MVC结构进一步细化为以下层次:
- DAO(Data Access Object)层 :负责与数据库进行交互,完成数据的CRUD操作。
- Service层 :处理业务逻辑,调用DAO层获取数据,并进行逻辑处理。
- Controller层 :接收用户请求,调用Service层处理业务,并将结果返回给View层。
- View层 :即JSP页面,负责接收Controller层传来的数据并进行展示。
该结构如下图所示:
graph TD
A[用户请求] --> B[Controller]
B --> C[Service]
C --> D[DAO]
D --> E[(数据库)]
E --> D
D --> C
C --> B
B --> F[View]
4.2.2 各层之间的调用关系
- Controller层调用Service层 :Controller负责接收请求,解析参数,调用相应的Service方法处理业务逻辑。
- Service层调用DAO层 :Service负责处理业务逻辑,如验证、计算等,调用DAO层获取或更新数据。
- DAO层访问数据库 :DAO负责执行SQL语句,与数据库进行数据交互。
- View层接收数据并渲染 :Controller将处理结果封装后传给View层进行页面渲染。
这种结构清晰的调用关系,使得系统的各个模块职责明确,便于开发和维护。
4.3 基于MVC实现教室借用管理功能
4.3.1 教室信息的增删改查控制层设计
以“教室信息管理”为例,展示Controller层的设计。
示例代码:教室信息控制类 ClassroomController.java
@WebServlet("/classroom")
public class ClassroomController extends HttpServlet {
private static final long serialVersionUID = 1L;
private ClassroomService classroomService = new ClassroomService();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("action");
switch (action) {
case "list":
listClassrooms(request, response);
break;
case "delete":
deleteClassroom(request, response);
break;
case "edit":
showEditForm(request, response);
break;
default:
listClassrooms(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("action");
if ("save".equals(action)) {
saveClassroom(request, response);
} else if ("update".equals(action)) {
updateClassroom(request, response);
}
}
private void listClassrooms(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Classroom> classrooms = classroomService.getAllClassrooms();
request.setAttribute("classrooms", classrooms);
RequestDispatcher dispatcher = request.getRequestDispatcher("/classroom-list.jsp");
dispatcher.forward(request, response);
}
private void deleteClassroom(HttpServletRequest request, HttpServletResponse response) throws IOException {
int id = Integer.parseInt(request.getParameter("id"));
classroomService.deleteClassroom(id);
response.sendRedirect("classroom?action=list");
}
private void showEditForm(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int id = Integer.parseInt(request.getParameter("id"));
Classroom classroom = classroomService.getClassroomById(id);
request.setAttribute("classroom", classroom);
RequestDispatcher dispatcher = request.getRequestDispatcher("/classroom-form.jsp");
dispatcher.forward(request, response);
}
private void saveClassroom(HttpServletRequest request, HttpServletResponse response) throws IOException {
String name = request.getParameter("name");
String location = request.getParameter("location");
int capacity = Integer.parseInt(request.getParameter("capacity"));
Classroom classroom = new Classroom();
classroom.setName(name);
classroom.setLocation(location);
classroom.setCapacity(capacity);
classroomService.saveClassroom(classroom);
response.sendRedirect("classroom?action=list");
}
private void updateClassroom(HttpServletRequest request, HttpServletResponse response) throws IOException {
int id = Integer.parseInt(request.getParameter("id"));
String name = request.getParameter("name");
String location = request.getParameter("location");
int capacity = Integer.parseInt(request.getParameter("capacity"));
Classroom classroom = new Classroom();
classroom.setId(id);
classroom.setName(name);
classroom.setLocation(location);
classroom.setCapacity(capacity);
classroomService.updateClassroom(classroom);
response.sendRedirect("classroom?action=list");
}
}
代码逻辑分析:
- doGet()方法 :根据请求参数
action判断用户操作类型(如列表、删除、编辑),并调用相应方法。 - doPost()方法 :处理表单提交操作,区分保存和更新。
- listClassrooms()方法 :调用Service层获取教室列表,并转发到JSP页面展示。
- deleteClassroom()方法 :获取教室ID,调用Service层删除数据,重定向回列表页。
- showEditForm()方法 :获取教室信息,转发到编辑页面。
- saveClassroom()和updateClassroom()方法 :接收表单参数,构建
Classroom对象,调用Service层进行保存或更新。
参数说明:
action:请求操作类型,如list、delete、edit、save、update。id:教室ID,用于唯一标识一条记录。name、location、capacity:教室的基本信息字段。
4.3.2 借用申请的业务逻辑处理
在教室借用申请流程中,Controller层接收用户提交的申请请求,调用Service层进行业务处理,包括:
- 验证用户权限;
- 检查教室是否已被借用;
- 保存申请记录;
- 发送通知或返回错误信息。
示例代码:借用申请控制类 BorrowApplicationController.java
@WebServlet("/apply")
public class BorrowApplicationController extends HttpServlet {
private BorrowService borrowService = new BorrowService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int classroomId = Integer.parseInt(request.getParameter("classroomId"));
String userId = request.getParameter("userId");
String borrowDate = request.getParameter("borrowDate");
String startTime = request.getParameter("startTime");
String endTime = request.getParameter("endTime");
boolean success = borrowService.applyForClassroom(classroomId, userId, borrowDate, startTime, endTime);
if (success) {
request.setAttribute("message", "申请成功!");
} else {
request.setAttribute("message", "该时间段已被占用,请选择其他时间。");
}
RequestDispatcher dispatcher = request.getRequestDispatcher("/apply-result.jsp");
dispatcher.forward(request, response);
}
}
4.3.3 数据展示与视图渲染
Controller层将处理结果封装后,通过 request.setAttribute() 将数据传递给JSP页面进行展示。
示例JSP页面: classroom-list.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<table border="1">
<tr>
<th>ID</th>
<th>名称</th>
<th>位置</th>
<th>容量</th>
<th>操作</th>
</tr>
<c:forEach items="${classrooms}" var="classroom">
<tr>
<td>${classroom.id}</td>
<td>${classroom.name}</td>
<td>${classroom.location}</td>
<td>${classroom.capacity}</td>
<td>
<a href="classroom?action=edit&id=${classroom.id}">编辑</a>
<a href="classroom?action=delete&id=${classroom.id}" onclick="return confirm('确定删除?')">删除</a>
</td>
</tr>
</c:forEach>
</table>
4.4 使用JSTL标签库优化页面展示
4.4.1 JSTL核心标签库(c:if、c:forEach等)
JSTL(JSP Standard Tag Library)是JSP的标签库标准,能够简化JSP页面中的逻辑控制,避免在JSP中嵌入过多Java代码。
示例:使用 <c:if> 和 <c:forEach>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:if test="${not empty message}">
<div class="alert">${message}</div>
</c:if>
<ul>
<c:forEach items="${classrooms}" var="classroom">
<li>${classroom.name} - ${classroom.location}</li>
</c:forEach>
</ul>
4.4.2 JSTL与EL表达式结合使用
EL(Expression Language)表达式用于在JSP中访问Java对象的属性值,结合JSTL使用可以实现动态数据绑定。
示例:EL表达式与JSTL配合
<c:choose>
<c:when test="${classroom.capacity > 50}">
<span style="color:green">大教室</span>
</c:when>
<c:otherwise>
<span style="color:orange">小教室</span>
</c:otherwise>
</c:choose>
通过JSTL与EL表达式的结合,可以实现页面逻辑的动态控制,使JSP页面更加简洁、易读和易于维护。
通过本章的学习,我们深入了解了MVC架构的设计原理与优势,并结合“教室借用管理系统”的具体需求,展示了如何在Java Web项目中实现MVC结构,包括Controller层的请求处理、Service层的业务逻辑处理、DAO层的数据访问以及JSP页面的数据展示。同时,我们还通过JSTL标签库与EL表达式的结合,优化了页面的动态展示效果,提升了开发效率与页面可维护性。
5. MySQL数据库设计与CRUD操作
5.1 教室借用管理系统数据库建模
5.1.1 系统实体与关系分析
在开发教室借用管理系统时,首要任务是明确系统中涉及的核心实体及其之间的关系。主要实体包括:
- 用户(User) :包括教师和管理员。
- 教室(Classroom) :可供借用的教室资源。
- 申请记录(Application) :用户申请借用教室的记录。
实体之间的关系如下:
- 一个用户可以提交多个申请记录。
- 每个申请记录对应一个教室。
- 一个教室可以被多个用户申请。
这些关系构成了系统的数据模型基础。
5.1.2 数据表设计(用户表、教室表、申请记录表等)
以下是系统核心数据表的设计:
| 表名 | 字段名 | 数据类型 | 说明 |
|---|---|---|---|
users |
id | INT (PK) | 用户ID |
| username | VARCHAR(50) | 用户名 | |
| password | VARCHAR(100) | 密码(加密存储) | |
| role | VARCHAR(20) | 用户角色(teacher/admin) | |
| created_at | DATETIME | 注册时间 | |
classrooms |
id | INT (PK) | 教室ID |
| room_number | VARCHAR(20) | 教室编号 | |
| capacity | INT | 教室容量 | |
| location | VARCHAR(100) | 教室位置 | |
applications |
id | INT (PK) | 申请ID |
| user_id | INT (FK) | 关联用户ID | |
| classroom_id | INT (FK) | 关联教室ID | |
| apply_date | DATE | 申请借用日期 | |
| start_time | TIME | 开始时间 | |
| end_time | TIME | 结束时间 | |
| status | VARCHAR(20) | 状态(pending/approved) |
5.2 JDBC数据库连接与操作
5.2.1 JDBC驱动加载与连接池配置
在Java Web项目中,使用JDBC连接MySQL数据库的基本步骤如下:
// 1. 加载JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立数据库连接
String url = "jdbc:mysql://localhost:3306/classroom_db?useSSL=false&serverTimezone=UTC";
String username = "root";
String password = "password";
Connection conn = DriverManager.getConnection(url, username, password);
为了提高性能和资源管理,建议使用连接池(如HikariCP)进行数据库连接管理:
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/classroom_db");
config.setUsername("root");
config.setPassword("password");
config.setMaximumPoolSize(10);
HikariDataSource dataSource = new HikariDataSource(config);
5.2.2 使用PreparedStatement执行CRUD操作
使用 PreparedStatement 可以有效防止SQL注入,并提高执行效率。以下是实现“新增教室”功能的示例代码:
public int addClassroom(String roomNumber, int capacity, String location) throws SQLException {
String sql = "INSERT INTO classrooms (room_number, capacity, location) VALUES (?, ?, ?)";
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, roomNumber);
stmt.setInt(2, capacity);
stmt.setString(3, location);
return stmt.executeUpdate(); // 返回受影响的行数
}
}
查询教室列表的代码如下:
public List<Classroom> getAllClassrooms() throws SQLException {
List<Classroom> classrooms = new ArrayList<>();
String sql = "SELECT * FROM classrooms";
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
Classroom classroom = new Classroom();
classroom.setId(rs.getInt("id"));
classroom.setRoomNumber(rs.getString("room_number"));
classroom.setCapacity(rs.getInt("capacity"));
classroom.setLocation(rs.getString("location"));
classrooms.add(classroom);
}
}
return classrooms;
}
5.3 DAO层设计与实现
5.3.1 数据访问对象(DAO)的设计原则
DAO(Data Access Object)模式是Java Web开发中常用的设计模式,用于封装对数据库的访问逻辑。设计DAO时应遵循以下原则:
- 每个实体对应一个DAO接口和实现类。
- DAO类中只处理数据持久化操作,不涉及业务逻辑。
- 使用工厂模式或依赖注入管理DAO实例。
5.3.2 用户信息DAO实现
以用户DAO为例,定义如下接口:
public interface UserDAO {
User getUserById(int id) throws SQLException;
User getUserByUsername(String username) throws SQLException;
int addUser(User user) throws SQLException;
int updateUser(User user) throws SQLException;
}
实现类代码片段如下:
public class UserDAOImpl implements UserDAO {
private HikariDataSource dataSource;
public UserDAOImpl(HikariDataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public User getUserByUsername(String username) throws SQLException {
String sql = "SELECT * FROM users WHERE username = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, username);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setRole(rs.getString("role"));
return user;
}
}
}
return null;
}
}
5.3.3 教室信息DAO实现
教室DAO接口定义如下:
public interface ClassroomDAO {
List<Classroom> getAllClassrooms() throws SQLException;
Classroom getClassroomById(int id) throws SQLException;
int addClassroom(Classroom classroom) throws SQLException;
int updateClassroom(Classroom classroom) throws SQLException;
int deleteClassroom(int id) throws SQLException;
}
实现类中 deleteClassroom 方法如下:
@Override
public int deleteClassroom(int id) throws SQLException {
String sql = "DELETE FROM classrooms WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, id);
return stmt.executeUpdate();
}
}
5.4 事务管理与数据一致性保障
5.4.1 事务的概念与ACID特性
事务是数据库操作的基本单位,具有ACID特性:
- A(原子性) :事务内的操作要么全部成功,要么全部失败。
- C(一致性) :事务执行前后,数据库的完整性约束保持不变。
- I(隔离性) :多个事务并发执行时,彼此隔离,不互相干扰。
- D(持久性) :事务一旦提交,修改永久保存在数据库中。
5.4.2 在教室借用流程中使用事务控制
在教室借用系统中,当用户提交借用申请时,可能需要同时更新 applications 表和 classrooms 表的状态。为了保证数据一致性,必须使用事务控制。
以下是事务控制的Java代码示例:
public boolean submitApplication(Application application) throws SQLException {
String insertAppSql = "INSERT INTO applications (user_id, classroom_id, apply_date, start_time, end_time, status) VALUES (?, ?, ?, ?, ?, ?)";
String updateRoomSql = "UPDATE classrooms SET status = 'occupied' WHERE id = ?";
try (Connection conn = dataSource.getConnection()) {
conn.setAutoCommit(false); // 开启事务
try (PreparedStatement stmt1 = conn.prepareStatement(insertAppSql)) {
stmt1.setInt(1, application.getUserId());
stmt1.setInt(2, application.getClassroomId());
stmt1.setDate(3, new java.sql.Date(application.getApplyDate().getTime()));
stmt1.setTime(4, application.getStartTime());
stmt1.setTime(5, application.getEndTime());
stmt1.setString(6, "pending");
stmt1.executeUpdate();
}
try (PreparedStatement stmt2 = conn.prepareStatement(updateRoomSql)) {
stmt2.setInt(1, application.getClassroomId());
stmt2.executeUpdate();
}
conn.commit(); // 提交事务
return true;
} catch (SQLException e) {
conn.rollback(); // 回滚事务
throw e;
}
}
通过上述事务控制机制,可以确保教室借用申请和教室状态更新操作要么同时成功,要么同时失败,从而保障了系统的数据一致性。
简介:本项目“Java JSP教室借用管理系统”是一个基于Java Web技术的企业级管理系统,使用JSP、Servlet、MySQL和MVC架构实现教室借用流程的信息化管理。系统包含用户认证、权限控制、数据CRUD、事务处理、前端界面设计、报表统计等功能,适合毕业设计与实战学习。通过本项目,学生可掌握Web开发核心技术,提升实际编程能力,为Java Web开发打下坚实基础。
更多推荐


所有评论(0)