JavaWeb课程设计——商品信息管理系统

商品信息管理系统

目录

商品信息管理系统

一、系统概要设计

1.1 系统概述

1.2. 功能需求

1.3 系统架构

1.4. 技术选型

二、数据库设计

2.1数据库逻辑结构设计

2.2 数据库表

三、系统实现

3.1 用户注册

3.2 用户登录

3.3 增加商品

3.4 删除商品

3.5 修改商品

3.6 查看商品

3.7 查询商品

3.8 修改密码

3.9 验证码验证

3.10 分页查看

3.11 退出系统

四、项目总结 


        Hello,艾薇巴蒂,新年快乐!已许久未更,刚期末结束,趁着有些东西没忘完,打算更1篇,后面可能会很长时间不更新。 

        后台私信中看到需要源码和数据集的,现在仍需要的,在私信时烦请具体说明是哪个文章的代码,代码或软件安装出现错误时也可私信,看到会回复。

        谢谢关注。

需注意:

1.完整的项目架构如下图,文章所提供的代码并不完整

a2a22cc5736a47f3a3a7e5f2a0d22d32.png

2.在jsp代码中,css美化没有并加进去,css美化我是用的是内部样式,尽量用外部样式(创建一个css文件夹,在此文件夹中添加美化信息)

3.数据库使用的MySQL,创建数据库、表格、插入数据的SQL语句如下:

//创建DB
CREATE DATABASE bookstore;

//使用此DB
USE bookstore;

//创建表
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL
);
CREATE TABLE product (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    price DOUBLE NOT NULL
);

//想users插入数据
INSERT INTO users (username, password) VALUES ('admin', '123456');
INSERT INTO users (username, password) VALUES ('ppl', '111111');
INSERT INTO users (username, password) VALUES ('xw', '000000');
INSERT INTO users (username, password) VALUES ('xzc', '111000');
INSERT INTO users (username, password) VALUES ('gl', '000111');


//想product插入数据
INSERT INTO product (id,name, price) VALUES (1,'a', 10);
INSERT INTO product (id,name, price) VALUES (2,'b', 20);
INSERT INTO product (id,name, price) VALUES (3,'c', 30);
INSERT INTO product (id,name, price) VALUES (4,'d', 40);
INSERT INTO product (id,name, price) VALUES (5,'e', 50);
INSERT INTO product (id,name, price) VALUES (6,'f', 60);
INSERT INTO product (id,name, price) VALUES (7,'g', 70);
INSERT INTO product (id,name, price) VALUES (8,'h', 80);
INSERT INTO product (id,name, price) VALUES (9,'i', 99);
INSERT INTO product (id,name, price) VALUES (10,'j', 9);
INSERT INTO product (id,name, price) VALUES (11,'k', 8);
INSERT INTO product (id,name, price) VALUES (12,'l', 7);
INSERT INTO product (id,name, price) VALUES (13,'m', 99.99);
INSERT INTO product (id,name, price) VALUES (14,'n', 99.99);

数据库名随便,插入product表格的数据尽量在10+,因为后面设置的有分页查看,每10条信息为一页,且与数据库进行连接是,用户名和密码要正确

 4.项目中创建的均是servlet,而不是class文件

 5.在listProduct.jsp中,可以分为两部分(servlet文件和jsp文件),我这里没有修改

 6.登录时,验证码不区分大小写;登录后,下面的部分信息如果在eclipse显示不全,可以复制上面的URL在浏览器中打开

 7.整个项目在eclipse中进行,不是IntelliJ IDEA

 8.我是用的是WebServlet注解,可以选择在web.xml中添加注解

 9.在项目导入时,如果Tomcat版本不同,会出现报错,解决:点击项目—右键—Build Path—Configure—找到并点击Java Build Path—点击顶部Libraries—点击JRE System—点击Edits—选择Workplace Default—Finish

一、系统概要设计

1.1 系统概述

        本系统为一个商品信息管理系统,旨在提供一个平台,让用户能够方便地管理商品信息,包括用户注册、登录、商品的增加、删除、修改、查看和查询等功能。

1.2. 功能需求

系统主要功能如下:

主要功能表

序号

功能

功能说明

1

用户注册

没有账户的用户可以进行注册操作

2

用户登录

用户进行登陆操作

3

退出系统

在用户登陆进入系统后可以选择退出系统

4

增加商品信息

用户可以增加商品信息,包括商品号、商品名称、价格

5

删除商品信息

用户可以通过删除商品号,进而删除商品信息

6

修改商品信息

用户可以修改商品信息,包括商品号、商品名称、价格

7

查看商品信息

商品数量过多时,用户可以分页查看商品信息,包括商品号、商品名称、价格

8

查询商品信息

用户通过查询商品号来可以查询商品信息,此时跳转到查询结果页面,可以看到商品号、商品名称、价格

9

验证码验证

在登录页面进行验证码验证是否为真人,点击可以进行刷新

10

分页查询

商品信息过多时,一页显示不全,此时进行分页查看

11

用户修改密码

已经登录过的用户可以进行修改自己的密码

1.3 系统架构

        系统采用B/S架构,用户通过浏览器访问系统,服务器端处理业务逻辑并响应用户请求。

1.4. 技术选型

        前端技术:HTML, CSS, JavaScript

        后端技术:Java Servlet, JSP

        数据库:MySQL

        其他技术:JDBC, HTML表单,验证码生成技术

二、数据库设计

2.1数据库逻辑结构设计

(1)用户表(users)

        id:主键,唯一标识一个用户。

        name:用户姓名或用户名。

        password:用户密码,存储时使用加密形式。

(2)商品表(product)

        id:主键,唯一标识一个商品。

        name:商品名称。

        price:商品价格。

2.2 数据库表

(1) 用户表(users)

属性名数据类型长度说明
idINT11主键,自增
nameVARCHAR255用户名
passwordVARCHAR255加密后的密码

(2) 商品信息表(product)

属性名数据类型长度说明
idINT11主键,自增
nameVARCHAR255商品名称
priceDOUBLE10,2商品价格,两位小数

三、系统实现

3.1 用户注册

1.流程设计

    1.用户访问注册页面

    2.用户填写注册表单

    3.用户提交表单

    4.服务器处理注册请求并响应

    5.页面跳转:如果用户注册成功或重新注册,可以点击页面底部的跳转登录页面

2.技术应用

逻辑业务处理:

    1.显示消息:根据请求属性显示错误或成功消息。

    3.表单提交:用户填写用户名和密码,提交表单。

    3.后端处理:registerServlet处理注册请求。

3.技术选择分析

1.页面跳转使用了:表单提交。

        在注册流程中,使用表单提交重定向可以确保在用户提交表单后,所有的业务逻辑都在服务器端完成,然后再根据业务逻辑的结果决定是否跳转以及跳转到哪个页面。

2.页面传值使用了请求属性Request Attributes。

        在注册流程中,使用请求属性的优点在于它允许RegisterServlet在处理完业务逻辑后,将结果传递给JSP页面,而不需要在URL中显示这些信息。

4.关键代码

RegisterProduct

@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {
	private UserDAO userDAO = new UserDAO();

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		// 从请求中获取 username 和 password 参数
		if (username == null || username.trim().isEmpty() || password == null || password.trim().isEmpty()) {
			request.setAttribute("errorType", "emptyFields");// 设置错误类型为 "emptyFields"														
			request.getRequestDispatcher("register.jsp").forward(request, response);
			return;
		}

		User user = new User();
		user.setUsername(username);
		user.setPassword(password);
		// 创建 User 对象,并设置用户名和密码。
		try {
			if (userDAO.addUser(user)) // 调用 userDAO.addUser(user) 方法尝试将用户添加到数据库
			{
				request.setAttribute("successMessage", "注册成功");
				request.getRequestDispatcher("register.jsp").forward(request, response);
			} else {
				request.setAttribute("errorType", "registrationFailed");
				request.getRequestDispatcher("register.jsp").forward(request, response);
			}
		} catch (SQLException e) {
			e.printStackTrace();
			request.setAttribute("errorType", "databaseError");
			request.getRequestDispatcher("register.jsp").forward(request, response);
		}
	}
}

register.jsp

<body>
	<div class="register-container">
		<h1>注册</h1>
		<%
            String errorType = (String) request.getAttribute("errorType");
            String successMessage = (String) request.getAttribute("successMessage");
            if (errorType != null) {
                if ("emptyFields".equals(errorType)) {
                    out.println("<p class='error-message'>用户名或密码为空。</p>");
                } else if ("registrationFailed".equals(errorType)) {
                    out.println("<p class='error-message'>注册失败,请检查用户名是否已存在或其他信息是否正确。</p>");
                } else if ("databaseError".equals(errorType)) {
                    out.println("<p class='error-message'>数据库错误,请稍后再试。</p>");
                }
            } else if (successMessage != null) {
                out.println("<p class='success-message'>" + successMessage + "</p>");
            }
        %>
		<form action="registerServlet" method="post">
			用户名: <input type="text" name="username"><br> 密码: <input
				type="password" name="password"><br> <input
				type="submit" value="注册">
		</form>
		<a href="login.jsp">已有账号?登录</a>
	</div>
</body>

5.实现

898e85fe9a65404d864b8cff40bd7252.png

图3.1-1 用户注册

3.2 用户登录

1.流程设计

        1.验证码生成

        2.用户登录请求

        3.登录验证

        4.用户认证

        5.错误处理

        6.页面显示

2.技术应用

(1)LoginServlet

      1.  `doGet`方法生成一个四位随机验证码,存储在session中,并生成对应的验证码图片返回给客户端。

       2. `doPost`方法处理登录请求,验证用户名、密码和验证码的有效性。

        如果用户名、密码或验证码为空、验证码不匹配、用户不存在或密码错误、数据库查询出现异常,重定向到登录页面并附带错误信息。

        如果用户验证成功,重定向到成功页面。

(2)login.jsp:

        1.显示登录表单,包含用户名、密码和验证码输入。

        2.使用JavaScript实现验证码图片的刷新功能。

        3.提交表单后,数据发送到`loginServlet`进行处理。

3.技术选择分析

        1.使用response.sendRedirect()进行服务器端重定向:这样服务器端重定向可以保持服务器对HTTP请求和响应的控制,适合在服务器端处理逻辑后决定跳转路径。

        2.使用请求参数(URL参数)和session属性进行页面传值:URL参数适用于传递简单数据,session属性适用于保持用户状态。

request.setCharacterEncoding()

response.setCharacterEncoding()response.setContentType()

避免乱码。

4.关键代码

LoginServlet

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
	private UserDAO userDAO = new UserDAO();
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String capText = generateCaptchaText(4);  
		// 调用generateCaptchaText方法,生成一个长度为4的随机验证码
		request.getSession().setAttribute("captcha", capText);// 将生成的验证码文本capText存储到用户的会话(Session)中
		//session.setAttribute(“key”,value)是session设置值的方法
		BufferedImage bi = new BufferedImage(160, 40, BufferedImage.TYPE_INT_RGB);
		// 创建了一个类型为BufferedImage的图像缓冲区,大小为160x40像素,使用RGB颜色模型。
		Graphics2D g = bi.createGraphics();// 获取图像缓冲区的Graphics2D对象,用于在图像上绘制文本和图形。
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, 160, 40);
		// 设置背景颜色并填充
		g.setFont(new Font("Fixedsys", Font.BOLD, 20));// 设置字体
		g.setColor(Color.BLACK);
		g.drawString(capText, 30, 24);
		// 将画笔颜色设置为黑色,并在图像上绘制验证码文本capText,位置为(30, 24)。
		g.dispose();// 释放图形上下文资源:
		OutputStream out = response.getOutputStream();
		ImageIO.write(bi, "png", out);
		out.close();
		// 获取HttpServletResponse对象的输出流,使用ImageIO.write方法将图像缓冲区bi以PNG格式写入输出流,最后关闭输出流
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		String captcha = request.getParameter("captcha");

		// 检查用户名、密码和验证码是否为空
		if (username == null || username.trim().isEmpty() || password == null || password.trim().isEmpty()
				|| captcha == null || captcha.trim().isEmpty()) {
			response.sendRedirect("login.jsp?errorType=emptyFields");
			// sendRedirect方法将用户重定向到login.jsp页面,并附加一个查询参数errorType,其值为captchaError。
			return;
		}

		// 验证验证码
		String sessionCaptcha = (String) request.getSession().getAttribute("captcha");
		// 从当前请求的会话中获取名为"captcha"的属性,并将其值存储在名为sessionCaptcha的String变量中
		if (sessionCaptcha == null || !sessionCaptcha.equalsIgnoreCase(captcha)) 
		//如果sessionCaptcha为null,则表达式为true。
		//如果sessionCaptcha不为null,但sessionCaptcha和captcha不相等(忽略大小写),
		//则!sessionCaptcha.equalsIgnoreCase(captcha)为true,整个表达式也为true。
		{
			response.sendRedirect("login.jsp?errorType=captchaError");
			return;
		}

		try {
			User user = userDAO.getUser(username, password);
			if (user != null) {
				response.sendRedirect("success.jsp");
			} else {
				boolean userExists = userDAO.userExists(username);
				if (userExists) {
					response.sendRedirect("error.jsp?errorType=passwordError");
					//如果用户名存在但密码错误,重定向到error.jsp页面,并附加一个查询参数errorType=passwordError,用于显示密码错误的信息。
				} else {
					response.sendRedirect("error.jsp?errorType=usernameNotFound");
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
			response.sendRedirect("error.jsp?errorType=serverError");
		}
	}

	private String generateCaptchaText(int length) {
		String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
		Random rnd = new Random();
		StringBuilder sb = new StringBuilder();
		// 创建了一个StringBuilder对象,用于高效地构建字符串
		while (sb.length() < length) // 循环生成随机字符
		{
			int index = rnd.nextInt(chars.length());
			sb.append(chars.charAt(index));
		} // 每次迭代中,它生成一个随机索引index,然后使用这个索引从字符集chars中选择一个字符,并将其追加到StringBuilder对象sb中。
		return sb.toString();
	}
}

login.jsp

<script>
        function refreshCaptcha() {
            var captchaImage = document.getElementById('captchaImage');
            // 添加一个随机数参数以确保浏览器不缓存图片
            captchaImage.src = 'loginServlet?' + new Date().getTime();
        }
    </script>
</head>
<body>
	<div class="login-container">
		<h1>登录</h1>
		<%
            String errorType = request.getParameter("errorType");
            if ("emptyFields".equals(errorType)) {
                out.println("<p class='error-message'>用户名或密码或验证码为空。</p>");
            } else if ("captchaError".equals(errorType)) {
                out.println("<p class='error-message'>验证码错误。</p>");
            }
        %>
		<form action="loginServlet" method="post">
			用户名: <input type="text" name="username"><br> 密码: <input
				type="password" name="password"><br>
			<div class="captcha-container">
				<!-- 为图片添加ID和点击事件 -->
				<img src="loginServlet" alt="验证码" id="captchaImage"
					class="captcha-image" onclick="refreshCaptcha()"> <input
					type="text" name="captcha" placeholder="验证码">
			</div>
			<input type="submit" value="登录">
		</form>
		<a href="register.jsp">没有账号?注册</a>
	</div>
</body>

 5.实现

13db705225ee4b9dbcf711fb82975ae5.png

图3.2-1用户登录

3.3 增加商品

1.流程设计

        1. 用户请求添加商品页面 

        2. 表单提交

        3. 数据验证

        4. 数据库查询

        5. 数据库插入

        6. 结果处理

        7. 错误处理

        8. 页面链接

2.技术应用

(1)逻辑业务处理

      1. 接收表单数据:`AddProductServlet`接收来自`addProduct.jsp`的POST请求,获取商品标号、名称和价格。

        2. 验证输入:检查商品信息是否完整,不完整则返回错误信息。

        3. 检查商品存在性:查询数据库,检查商品标号是否已存在,存在则返回错误信息。

        4. 插入新商品:若商品不存在,将新商品信息插入数据库。

        5. 操作反馈:根据数据库操作结果,要么重定向到商品列表页,要么返回错误信息。

(2)流程控制:

        1.`AddProductServlet`中的`doPost`方法控制整个添加商品的流程。

        2. 使用`if`语句验证输入数据的完整性。

        3. 使用`try-catch`块处理JDBC和SQL异常。

(3)类的调用:

         1.调用`DriverManager`类加载和注册JDBC驱动。

         2.使用`Connection`类建立与数据库的连接。

         3.利用`PreparedStatement`类执行SQL查询和插入操作。

(4)方法的调用:

         1.`request.getParameter`方法获取表单参数。

         2.`Class.forName`方法加载JDBC驱动类。

         3.`DriverManager.getConnection`方法获取数据库连接。

        4.`PreparedStatement.executeQuery`和`PreparedStatement.executeUpdate`方法执行查询和更新操作。

       5.`request.setAttribute`和`request.getRequestDispatcher`方法在输入验证失败时转发请求到`addProduct.jsp`。

         6.`response.sendRedirect`方法在商品添加成功后重定向到商品列表页。

3.技术选择分析

使用页面跳转的方式:

   1. 请求转发(Request Dispatch):

   使用`request.getRequestDispatcher("/addProduct.jsp").forward(request, response);`进行请求转发,主要用于在服务器内部将请求从一个Servlet转发到另一个JSP页面,不改变浏览器的URL。

   2. 服务器端重定向(Send Redirect):

   使用`response.sendRedirect("listProduct.jsp");`进行服务器端重定向,这会导致浏览器接收到新的URL地址,并重新发起请求。

4.关键代码

addProductServlet

@WebServlet(name = "AddProductServlet", urlPatterns = { "/addProduct" })
public class AddProductServlet extends HttpServlet {
	private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
	private static final String DB_URL = "jdbc:mysql://localhost:3306/bookstore";
	private static final String USER = "root";
	private static final String PASS = "Pu200398";

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String productId = request.getParameter("id");
		String productName = request.getParameter("name");
		String productPrice = request.getParameter("price");

		// 简单的数据验证
		if (productId == null || productId.trim().isEmpty() || productName == null || productName.trim().isEmpty()
				|| productPrice == null || productPrice.trim().isEmpty())// trim():这个方法用于去除字符串开头和结尾的空白字符(如空格、制表符等)
		{
			request.setAttribute("error", "提示:商品标号、名称和价格都是必填项。");//在JSP页面中显示错误信息
			request.getRequestDispatcher("/addProduct.jsp").forward(request, response);// 将请求转发给addProduct.jsp页面
		} else {
			try {
				// 加载 JDBC 驱动
				Class.forName(JDBC_DRIVER);

				// 建立数据库连接
				try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
						// 创建 SQL 查询语句
						PreparedStatement pstmtCheck = conn.prepareStatement("SELECT * FROM product WHERE id = ?")) {

					// 设置参数
					pstmtCheck.setString(1, productId);

					// 执行查询操作
					ResultSet rs = pstmtCheck.executeQuery();

					// 检查商品是否已存在
					if (rs.next()) // 通过调用 rs.next()来检查 ResultSet 中是否有数据
					{
						request.setAttribute("error", "商品已存在");
						request.getRequestDispatcher("/addProduct.jsp").forward(request, response);
						return;
					}
				}

				// 建立数据库连接
				try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
						// 创建 SQL 插入语句
						PreparedStatement pstmtInsert = conn
								.prepareStatement("INSERT INTO product (id, name, price) VALUES (?, ?, ?)")) {

					// 设置参数
					pstmtInsert.setString(1, productId);
					pstmtInsert.setString(2, productName);
					pstmtInsert.setDouble(3, Double.parseDouble(productPrice));

					// 执行插入操作
					int rowsAffected = pstmtInsert.executeUpdate();

					// 处理结果
					if (rowsAffected > 0) {
						response.sendRedirect("listProduct.jsp"); // 商品添加成功,重定向到商品列表页面
					} else {
						request.setAttribute("error", "添加商品时出错,请重试。");
						request.getRequestDispatcher("/addProduct.jsp").forward(request, response);
					}
				}
			} catch (ClassNotFoundException e) {
				request.setAttribute("error", "错误:JDBC 驱动未找到。");
			} catch (SQLException e) {
				request.setAttribute("error", "错误:数据库操作失败。");
			} catch (NumberFormatException e) {
				request.setAttribute("error", "错误:价格必须是有效的数字。");
			}
		}
	}
}

addProduct.jsp

<body>
	<div class="add-product-container">
		<h1>添加商品</h1>
		<% String error = (String) request.getAttribute("error");
           if (error != null) { %>
		<div class="error"><%= error %></div>
		<% } %>
		<form action="addProduct" method="post">
			<div class="form-group">
				<label for="id">商品标号:</label> <input type="text" id="id" name="id">
			</div>
			<div class="form-group">
				<label for="name">商品名称:</label> <input type="text" id="name"
					name="name">
			</div>
			<div class="form-group">
				<label for="price">商品价格:</label> <input type="number" id="price"
					name="price" step="0.01" min="0">
			</div>
			<div class="form-group">
				<input type="submit" value="提交">
			</div>
		</form>
		<div class="links">
			<a href="listProduct.jsp"><i class="fas fa-list-alt"></i> 查看商品</a> <a
				href="updateProduct.jsp"><i class="fas fa-edit"></i> 更新商品</a> <a
				href="deleteProduct.jsp"><i class="fas fa-trash"></i> 删除商品</a> <a
				href="searchProduct.jsp"><i class="fas fa-search"></i> 查询商品</a> <a
				href="index.jsp"><i class="fas fa-home"></i> 返回首页</a>
		</div>
	</div>
</body>

 5.实现

00dff3e4b64d4b3c85f463de531b1b80.png

图3.3-1 添加商品

3.4 删除商品

1.流程设计

        1.用户请求

        2.删除商品页面

        3.表单提交

        4.数据验证

        5.数据库连接与删除操作

        6.结果处理

        7.错误处理

        8.页面显示

2.技术应用

(1)逻辑业务处理:

        1. 接收请求:`DeleteProductServlet`接收来自`deleteProduct.jsp`页面的POST请求,获取要删除的商品标号。

        2. 数据验证:检查商品标号是否已填写,如果为空,则设置错误信息并重新显示删除页面。

        3. 数据库操作:

        4. 结果反馈:

        5.异常处理:捕获并处理可能的异常,如JDBC驱动未找到或数据库操作失败,并设置相应的错误信息。

(2)类的调用

        1.DriverManager:用于通过getConnection方法获取数据库连接。

        2.PreparedStatement:用于执行带参数的SQL语句。

        3.Class.forName:用于加载JDBC驱动类。

3)方法的调用

        1.request.getParameter(String name):从请求中获取名为name的参数值

        2.response.sendRedirect(String location):客户端重定向到location指定的url

        3.request.getRequestDispatcher(String path).forward(ServletRequest request, ServletResponse response):将请求转发到指定的path。 

        4.request.setAttribute(String name, Object value):将名称为name的属性设置为value,以便在JSP页面中使用。

3.技术选择分析

        页面传值使用了:请求属性(Request Attributes)。在DeleteProductServlet中,通过request.setAttribute方法设置属性,然后在deleteProduct.jsp页面中通过request.getAttribute方法获取这些属性值。

4.关键代码

DeleteProductServlet

@WebServlet(name = "DeleteProductServlet", urlPatterns = { "/deleteProduct" })
public class DeleteProductServlet extends HttpServlet {
	private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
	private static final String DB_URL = "jdbc:mysql://localhost:3306/bookstore";
	private static final String USER = "root";
	private static final String PASS = "Pu200398";

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String productId = request.getParameter("id");

		if (productId == null || productId.trim().isEmpty()) {
			request.setAttribute("error", "提示:商品标号是必填项。");
			//使用setAttribute在请求对象(request)中添加一个属性。,设置属性名为 "erroe",值为 "提示:商品标号是必填项."
			request.getRequestDispatcher("/deleteProduct.jsp").forward(request, response);
		} else {
			try {
				Class.forName(JDBC_DRIVER);
				try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
						PreparedStatement pstmt = conn.prepareStatement("DELETE FROM product WHERE id = ?")) 
				//如果存储过程中存在输入参数,可以防止sql注入攻击
				{
					pstmt.setString(1, productId);
					int rowsAffected = pstmt.executeUpdate();
					//调用executeUpdate()执行存储过程,并返回受影响的行数
					if (rowsAffected > 0) {
						request.setAttribute("message", "商品已成功删除。");
					} else {
						request.setAttribute("error", "未找到要删除的商品,请重试。");
					}
				}
			} catch (ClassNotFoundException e) {
				request.setAttribute("error", "错误:JDBC 驱动未找到。");
			} catch (SQLException e) {
				request.setAttribute("error", "错误:数据库操作失败。");
			}
			request.getRequestDispatcher("/deleteProduct.jsp").forward(request, response);
		}
	}
}

deleteProduct.jsp

<body>
	<div class="delete-product-container">
		<h1>删除商品</h1>
		<%
			String error = (String) request.getAttribute("error");
			if (error != null) {
		%>
		<div class="error"><%=error%></div>
		<%
			}
			String message = (String) request.getAttribute("message");
			if (message != null) {
		%>
		<div class="message"><%=message%></div>
		<%
			}
		%>
		<form action="deleteProduct" method="post">
			<label for="productId">商品标号:</label> <input type="text"
				id="productId" name="id"
				value="<%=request.getParameter("id") != null ? request.getParameter("id") : ""%>">
			<input type="submit" value="提交">
		</form>
		<div class="links">
			<a href="addProduct.jsp" class="btn-link"><i class="fas fa-plus"></i>
				添加商品</a> <a href="listProduct.jsp" class="btn-link"><i
				class="fas fa-list"></i> 查看商品</a> <a href="updateProduct.jsp"
				class="btn-link"><i class="fas fa-edit"></i> 更新商品</a> <a
				href="searchProduct.jsp" class="btn-link"><i
				class="fas fa-search"></i> 查询商品</a> <a href="index.jsp"><i
				class="fas fa-home"></i> 返回首页</a>
		</div>
	</div>
</body>

5.实现

370a7016220f4390957430842cfff417.png

图3.4-1 删除商品

3.5 修改商品

1.流程设计

        1.用户请求更新商品页面

        2.表单提交

        3.数据验证

        4.数据库更新操作

        5.结果处理

        6.错误处理

        7.页面显示

2.技术应用

(1)逻辑业务处理:

        1. 接收数据:`UpdateProductServlet`接收来自`updateProduct.jsp`的表单数据,包括商品标号、名称和价格。

        2. 数据验证:检查输入是否完整,不完整则转发回`updateProduct.jsp`并显示错误。

        3. 数据库更新:使用JDBC连接数据库,执行SQL更新语句,更新商品信息。

        4. 结果处理

(2)流程控制

        1. 接收请求:  `UpdateProductServlet`的`doPost`方法接收来自`updateProduct.jsp`页面的POST请求。

        2. 参数获取:从请求中获取商品的标号(`id`)、名称(`name`)和价格(`price`)。

        3. 输入验证:检查获取的参数是否为空,如果为空,则设置错误属性(`errorType`)并转发请求到`updateProduct.jsp`页面。

4. 数据库操作:

        1.如果输入有效,尝试加载数据库驱动(`Class.forName(JDBC_DRIVER)`)。

        2.设置更新语句的参数,并执行更新操作。

        3.建立数据库连接,并准备更新语句。

5. 结果判断:

        1.根据更新操作影响的行数判断商品是否更新成功。

        2.如果更新成功,重定向到商品列表页面。

        3.如果更新失败,设置商品未找到的错误属性,转发请求到`updateProduct.jsp`页面。

6. 异常处理:

      1.  `ClassNotFoundException`:JDBC驱动类未找到。

      2.  `SQLException`:数据库连接或操作失败。

      3.  `NumberFormatException`:价格参数格式不正确。

7. 页面反馈:

    `updateProduct.jsp`页面根据请求属性中的错误信息,显示相应的错误消息或更新成功的反馈。

3.技术选择分析

1.请求转发(Request Dispatch):

        使用request.getRequestDispatcher("updateProduct.jsp").forward(request, response);进行请求转发,主要用于在服务器内部将请求从一个Servlet转发到另一个JSP页面,不改变浏览器的URL。

2.服务器端重定向(Send Redirect):

        使用response.sendRedirect("listProduct.jsp");进行服务器端重定向,这会导致浏览器接收到新的URL地址,并重新发起请求。

4.关键代码

UpdateProductServlet

@WebServlet(name = "UpdateProductServlet", urlPatterns = { "/updateProduct" })
public class UpdateProductServlet extends HttpServlet {
	private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
	private static final String DB_URL = "jdbc:mysql://localhost:3306/bookstore";
	private static final String USER = "root";
	private static final String PASS = "Pu200398";

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String productId = request.getParameter("id");
		String productName = request.getParameter("name");
		String productPrice = request.getParameter("price");
		// 从请求中获取产品ID、名称和价格。

		if (productId == null || productId.trim().isEmpty() || productName == null || productName.trim().isEmpty()
				|| productPrice == null || productPrice.trim().isEmpty()) {
			request.setAttribute("error", "ID、名称、价格为必填项,请填写完整信息。");
			request.getRequestDispatcher("updateProduct.jsp").forward(request, response);
			// 设置一个错误类型为"inputError"的属性,并转发请求到updateProduct.jsp页面显示错误消息。
		} else {
			try {
				Class.forName(JDBC_DRIVER);
				try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
						PreparedStatement pstmt = conn
								.prepareStatement("UPDATE product SET name = ?, price = ? WHERE id = ?")) {
					pstmt.setString(1, productName);
					pstmt.setDouble(2, Double.parseDouble(productPrice));
					pstmt.setString(3, productId);
					// 设置SQL语句的参数。第一个 第二个 第迪桑
					int rowsAffected = pstmt.executeUpdate();// 执行更新操作,并获取受影响的行数

					if (rowsAffected > 0) {
						response.sendRedirect("listProduct.jsp");
					} else {
						request.setAttribute("errorType", "productNotFound");
						request.getRequestDispatcher("updateProduct.jsp").forward(request, response);
					}
				}
			} catch (ClassNotFoundException e) {
				request.setAttribute("errorType", "driverNotFound");
				request.getRequestDispatcher("updateProduct.jsp").forward(request, response);
			} catch (SQLException e) {
				request.setAttribute("errorType", "databaseError");
				request.getRequestDispatcher("updateProduct.jsp").forward(request, response);
			} catch (NumberFormatException e) {
				request.setAttribute("errorType", "priceFormatError");
				request.getRequestDispatcher("updateProduct.jsp").forward(request, response);
			}
		}
	}
}

5.实现

8551bdbc620a491ab83914b3b22f6d3d.png

图3.5-1 更新商品信息

3.6 查看商品

1.流程设计

        1. 页面设置

        2. HTML结构

        3. 数据库连接

        4. 分页逻辑

        5. 查询总记录数

        6. 显示商品列表

        7. 分页导航

        8. 异常处理

        9. 资源释放

2.技术应用

逻辑业务处理:

        1. 获取页面参数:从请求中获取`page`参数,确定当前页码。

        2. 数据库连接:建立与MySQL数据库的连接。

        3. 分页查询:根据当前页码查询相应的商品数据。

        4. 显示商品列表:遍历查询结果,将商品信息以表格形式显示。

        5. 分页导航:生成分页导航链接,允许用户切换页面。

        6. 异常处理:捕获并处理可能的异常,显示错误信息。

        7. 资源清理:关闭数据库连接和相关资源。

3.技术选择分析

        页面跳转使用的是客户端重定向方式,具体是通过在<a>标签的href属性中设置新的URL来实现的。这种方式的跳转是通过浏览器来完成的,用户点击链接后,浏览器会请求新的URL地址,从而实现页面的跳转

4.关键代码

listProduct.jsp

<body>
	<div class="container">
		<header>
			<div id="branding">
				<h1>商品信息管理系统</h1>
			</div>
		</header>
		<h2>Product List</h2>
		<table>
			<thead>
				<tr>
					<th>id</th>
					<th>name</th>
					<th>price</th>
				</tr>
			</thead>
			<tbody>
				<%
                    int pageSize = 10; // 每页显示的记录数
                    String pageStr = request.getParameter("page");
                    int currentPage = (pageStr != null && !pageStr.isEmpty()) ? Integer.parseInt(pageStr) : 1;
                    int start = (currentPage - 1) * pageSize;
                    //检查 pageStr 是否不为 null 且不为空字符串。
                    //如果这个条件为 true,则执行 Integer.parseInt(pageStr),将字符串 pageStr 转换为整数,并赋值给 currentPage。
					//如果 pageStr 为 null 或为空字符串,则 currentPage 被赋值为1,即默认的第一页。

                    String url = "jdbc:mysql://localhost:3306/bookstore";
                    String user = "root";
                    String password = "Pu200398"; 

                    Connection conn = null;
                    PreparedStatement pstmt = null;
                    //PreparedStatement这个变量将用于执行参数化的SQL查询或更新操作,它比 Statement 提供了更好的性能和安全性
                    ResultSet rs = null;//存储和处理查询结果
                    ResultSet totalRs = null;
                    //存储获取总记录数,以便于进行分页处理。

                    try {
                        Class.forName("com.mysql.cj.jdbc.Driver");
                        conn = DriverManager.getConnection(url, user, password);

                        // 获取总记录数
                        String totalSql = "SELECT COUNT(*) AS total FROM product";
                        Statement stmt = conn.createStatement();
                        totalRs = stmt.executeQuery(totalSql);
                        if (totalRs.next())//检查结果集中是否有数据
                      
                        {
                            int totalRecords = totalRs.getInt("total");
                            //从 totalRs 结果集中获取名为 "total" 的列的值,并将其转换为 int 类型,代表数据库中符合条件的总记录数。
                            int totalPages = (int) Math.ceil((double) totalRecords / pageSize);
							//将 totalRecords 转换为 double 类型,然后除以 pageSize(每页显示的记录数)。
							//使用 Math.ceil() 方法对结果向上取整。
							//最后,将 Math.ceil() 的结果转换为 int 类型,并赋值给 totalPages。
                            // 显示产品列表
                            String sql = "SELECT id, name, price FROM product LIMIT ?, ?";
                            pstmt = conn.prepareStatement(sql);
                            pstmt.setInt(1, start);
                            pstmt.setInt(2, pageSize);
                            rs = pstmt.executeQuery();

                            while (rs.next()) {
                                out.println("<tr>");
                                out.println("<td>" + rs.getInt("id") + "</td>");
                                out.println("<td>" + rs.getString("name") + "</td>");                      
                                out.println("<td>" + rs.getDouble("price") + "</td>");
                                out.println("</tr>");
                            }

                            // 分页导航
                            %>
				<nav>
					<ul class="pagination">
						<% if (currentPage > 1) { %>
						<li><a href="listProduct.jsp?page=<%= currentPage - 1 %>">上一页</a></li>
						<% } %>
						<% for (int i = 1; i <= totalPages; i++) { %>
						<li><a href="listProduct.jsp?page=<%= i %>"><%= i %></a></li>
						<% } %>
						<% if (currentPage < totalPages) { %>
						<li><a href="listProduct.jsp?page=<%= currentPage + 1 %>">下一页</a></li>
						<% } %>
					</ul>
				</nav>
				<%
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        out.println("<tr><td colspan='3'>Error: Unable to fetch product.</td></tr>");
                    } finally {
                        try {
                            if (rs != null) rs.close();
                            if (totalRs != null) totalRs.close();
                            if (pstmt != null) pstmt.close();
                            if (conn != null) conn.close();
                        } catch (SQLException se) {
                            se.printStackTrace();
                        }
                    }
                %>
			</tbody>
		</table>
		<footer>
			<a href="addProduct.jsp" class="btn-link"><i class="fas fa-plus"></i>
				添加商品</a> <a href="updateProduct.jsp" class="btn-link"><i
				class="fas fa-edit"></i> 更新商品</a> <a href="deleteProduct.jsp"
				class="btn-link"><i class="fas fa-trash"></i> 删除商品</a> <a
				href="searchProduct.jsp" class="btn-link"><i
				class="fas fa-search"></i> 查询商品</a> <a href="index.jsp"><i
				class="fas fa-home"></i> 返回首页</a>
		</footer>
	</div>
</body>

5.实现

82881ec8903941309c0fcf043487d592.png

图3.6-1 查看商品列表

3.7 查询商品

1.流程设计

        1. 用户输入

        2. 表单提交

        3. 参数验证

        4. 数据库查询

        5. 结果处理

        6. 页面重定向

        7. 结果显示

2.技术应用

逻辑业务的处理:

(1)HttpServlet 类:

        SearchProductServlet类继承自HttpServlet类,它是一个Servlet类,用于处理HTTP请求。

(2)doPost 方法:

        SearchProductServlet类中的doPost方法是处理POST请求的主要方法。它接收用户从搜索表单提交的数据。

(3)request 和 response 对象:

        1.HttpServletRequest request:用于获取客户端的请求数据。

        2.HttpServletResponse response:用于构造响应发送回客户端。

(4)数据库操作:

        1.使用java.sql包中的类和方法进行数据库操作。

        2.DriverManager.getConnection:获取数据库连接。

        3.Connection:代表与数据库的连接。

        4.PreparedStatement:用于执行SQL查询的预编译语句。

        5.ResultSet:保存查询结果。

(5)异常处理:

        try-catch块用于捕获和处理可能发生的异常,如ClassNotFoundException(JDBC驱动未找到)和SQLException(数据库操作失败)。

(6)重定向和请求属性设置:

        1.request.setAttribute:设置请求属性,用于将错误信息或产品信息传递给JSP页面。

        2.request.getRequestDispatcher:获取请求调度器,用于将请求转发到JSP页面。

        3.response.sendRedirect:重定向到结果页面,并附带查询结果或错误信息。

(7)URLEncoder.encode:

        用于对重定向URL中的参数进行编码,确保特殊字符被正确处理。

3.技术选择分析

(1)页面跳转方式及优点:

        使用的跳转方式:在`SearchProductServlet`中,使用了`response.sendRedirect`方法进行页面跳转。

        选择这种方式的原因:`response.sendRedirect`是一种服务器端跳转方式,它指示服务器向浏览器发送一个状态码和一个`Location`头,告诉浏览器去加载新的URL。

(2)页面传值方式及优点:

        使用的方式:在`SearchProductServlet`中,使用了URL参数(查询字符串)的方式进行页面传值。

        选择这种方式的原因:通过URL参数传递数据是一种简单且广泛使用的方法,适用于传递少量数据。

4.关键代码

SearchProductServlet

@WebServlet(name = "SearchProductServlet", urlPatterns = { "/searchProduct" })
public class SearchProductServlet extends HttpServlet {
	private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
	private static final String DB_URL = "jdbc:mysql://localhost:3306/bookstore";
	private static final String USER = "root";
	private static final String PASS = "Pu200398";

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String productId = request.getParameter("id");

		if (productId == null || productId.trim().isEmpty()) {
			request.setAttribute("error", "提示:商品标号是必填项。");
			request.getRequestDispatcher("/searchProduct.jsp").forward(request, response);
		} // 设置一个错误消息到请求属性中zaijsp页面显示,并转发请求到searchProduct.jsp页面,以便显示错误消息。
		else {
			try {
				Class.forName(JDBC_DRIVER);
				try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
						PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM product WHERE id = ?")) {
					pstmt.setString(1, productId);
					ResultSet rs = pstmt.executeQuery();

					if (rs.next()) {
						String name = rs.getString("name");
						double price = rs.getDouble("price");
						// 将结果作为请求参数传递
						response.sendRedirect("searchResult.jsp?product=" + URLEncoder
								.encode("商品标号: " + productId + ", 名称: " + name + ", 价格: " + price, "UTF-8"));
					} // 如果查询结果至少有一条记录,则从ResultSet中获取产品名称和价格,并构造一个查询字符串,
						// 使用URLEncoder对查询字符串进行编码以确保URL的有效性,然后重定向到searchResult.jsp页面,并附带产品信息。
					else {
						response.sendRedirect("searchResult.jsp?error=" + URLEncoder.encode("未找到商品。", "UTF-8"));
					}
				}
			} catch (ClassNotFoundException e) {
				response.sendRedirect("searchResult.jsp?error=" + URLEncoder.encode("错误:JDBC 驱动未找到。", "UTF-8"));
			} catch (SQLException e) {
				response.sendRedirect("searchResult.jsp?error=" + URLEncoder.encode("错误:数据库操作失败。", "UTF-8"));
			}
		}
	}
}

5.实现

d6f39f2e419a4f40b75bcc3d759ce51d.png

图3.7-1 查询商品

3d98e0fbd777445a930c869021e32be7.png

图3.7-2查询结果

3.8 修改密码

1.流程设计

        1. 用户输入

        2. 表单提交

        3. 参数验证

        4. 数据库查询

        5. 密码更新

        6. 错误处理

        7.页面跳转

        8.结果显示

2.技术应用

(1)逻辑业务处理:

        1. 用户输入验证:检查用户输入的用户名、旧密码和新密码是否为空。

        2. 用户身份验证:验证用户提供的用户名和旧密码是否与数据库中的记录匹配。

        3. 密码更新:如果用户身份验证成功,更新用户的密码。

        4. 结果反馈:根据操作结果,设置相应的错误消息或成功消息。

(2)流程控制:

    1. 输入验证:如果输入为空,设置错误类型为`emptyFields`并转发请求到`changePassword.jsp`。

        2. 数据库操作:尝试从数据库获取用户信息,并在找到用户后更新密码。

        3. 异常处理:捕获并处理`SQLException`,设置错误类型为`databaseError`。

        4. 结果转发:无论操作成功与否,都将请求转发回`changePassword.jsp`,并附带相应的消息。

(3)类的调用:

        1. UserDAO 类: `UserDAO`是一个数据访问对象类,用于与数据库交互。

        2. User 类: `User`类代表用户实体,包含用户信息

(4)方法的调用:

        1. getUser: `userDAO.getUser(username, oldPassword)`:根据用户名和旧密码从数据库获取用户信息。

        2. updateUserPassword: `userDAO.updateUserPassword(user)`:更新数据库中用户的密码。

        3. setPassword: `user.setPassword(newPassword)`:设置`User`对象的新密码。

3.技术选择分析

页面跳转方式及优点:

        使用的跳转方式:在ChangePasswordServlet中,使用了request.getRequestDispatcher().forward()方法进行页面跳转。

        选择这种方式的原因:request.getRequestDispatcher().forward()是一种服务器端的请求转发方式,它将请求转发到同一个Web应用中的另一个资源。

4.关键代码

   ChangePasswordServlet

@WebServlet(name = "ChangePasswordServlet", urlPatterns = { "/changePassword" })
public class ChangePasswordServlet extends HttpServlet {
	private UserDAO userDAO = new UserDAO();

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String username = request.getParameter("username");
		String oldPassword = request.getParameter("oldPassword");
		String newPassword = request.getParameter("newPassword");
		// 用getParameter方法来获取名为"**"的表单参数的值,并将其存储在String变量**中
		if (username == null || username.trim().isEmpty() || oldPassword == null || oldPassword.trim().isEmpty()
				|| newPassword == null || newPassword.trim().isEmpty()) {
			request.setAttribute("errorType", "emptyFields");
			// 使用setAttribute在请求对象(request)中添加一个属性。这里的属性名是"errorType",在jsp页面显示错误信息
			request.getRequestDispatcher("changePassword.jsp").forward(request, response);
			// 将当前的请求(request)和响应(response)对象转发给changePassword.jsp页面
			return;
		}

		try {
			User user = userDAO.getUser(username, oldPassword);
			if (user != null) {
				user.setPassword(newPassword);// 用户的密码被更新为新密码
				if (userDAO.updateUserPassword(user)) {
					request.setAttribute("successMessage", "密码修改成功");
					// 调用userDAO.updateUserPassword(user)方法尝试更新数据库中的密码
				} else {
					request.setAttribute("errorType", "updateFailed");
				}
			} else {
				request.setAttribute("errorType", "wrongCredentials");
			}
		} catch (SQLException e) {
			e.printStackTrace();
			request.setAttribute("errorType", "databaseError");
		}
		request.getRequestDispatcher("changePassword.jsp").forward(request, response);
	}
}

changepassword.jsp

<body>
	<div class="change-password-container">
		<h1>修改密码</h1>
		<% String errorType = (String) request.getAttribute("errorType");
           String successMessage = (String) request.getAttribute("successMessage");
           if (errorType != null) { %>
		<p class="error-message"><%= errorType.equals("emptyFields") ? "用户名、旧密码、新密码不能为空。" 
			:errorType.equals("wrongCredentials") ? "用户名或旧密码错误。" 
			: errorType.equals("updateFailed") ? "密码修改失败,请重试。" : "数据库错误,请稍后再试。" %></p>
		<% } else if (successMessage != null) { %>
		<p class="success-message"><%= successMessage %></p>
		<% } %>
		<form action="changePassword" method="post">
			用户名: <input type="text" name="username" required><br>
			旧密码: <input type="password" name="oldPassword" required><br>
			新密码: <input type="password" name="newPassword" required><br>
			<input type="submit" value="修改密码">
		</form>
		<div class="links">
			<a href="login.jsp">验证登录</a> <a href="index.jsp">返回首页</a>
		</div>
	</div>
</body>

 5.实现

9389ca3ca1174aedb6d5d5e43f13a83d.png

图3.8-1 修改密码

3.9 验证码验证

1.流程设计

        1.生成验证码

        2.绘制验证码图像

        3.处理登录请求

        4.验证验证码

        5.用户身份验证

        6.异常处理

2.技术应用

(1)流程控制:

        1. GET请求处理:当接收到GET请求时,生成验证码文本,将其存储在会话中,并生成验证码图像发送给客户端。

        2. POST请求处理:

               1. 当接收到POST请求时,获取用户名、密码和用户输入的验证码。

               2. 根据验证结果,重定向到不同的页面:成功页面、密码错误页面、用户名未找到页面或服务器错误页面。

                3.通过`UserDAO`查询数据库验证用户名和密码

        3.用户验证:                                                                                                                        

                1.验证用户输入的验证码是否与会话中存储的验证码匹配,如果不匹配,则重定向到登录页面并显示验证码错误信息。

                2.验证这些输入是否为空,如果为空,则重定向到登录页面并显示错误信息。

(2)类的调用:

        1. UserDAO类: `UserDAO`是一个数据访问对象类,用于与数据库交互,执行用户验证和查询操作。

        2. User类: `User`类代表用户实体,包含用户信息,如用户名和密码。

(3)方法的调用:

        1. generateCaptchaText:私有方法`generateCaptchaText`用于生成指定长度的随机验证码文本。

        2. getUser:`userDAO.getUser(username, password)`:根据用户名和密码从数据库获取用户信息。

        3. userExists: `userDAO.userExists(username)`:检查数据库中是否存在指定的用户名。

3.技术选择分析

        使用的跳转方式:在LoginServlet中,使用了response.sendRedirect方法进行页面跳转。

        选择这种方式的原因:response.sendRedirect是一种服务器端跳转方式,它指示服务器向浏览器发送一个状态码和一个Location头,告诉浏览器去加载新的URL。

4.关键代码

private String generateCaptchaText(int length) {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random rnd = new Random();
        StringBuilder sb = new StringBuilder();
        while (sb.length() < length) {
            int index = rnd.nextInt(chars.length());
            sb.append(chars.charAt(index));
        }
        return sb.toString();
    }

5.实现

44a30957401e4377bded9f64dac904ba.png

图3.9-1 验证码验证登录

464d1d9ced7f41a39b0c4e61082c8807.png

图3.9-2 登陆成功

3.10 分页查看

1.流程设计

        1.设置每页记录数

        2. 获取当前页码

        3. 计算起始记录位置

        4. 数据库连接

        5. 查询总记录数

        6. 计算总页数

        7. 分页查询商品

        8. 显示商品列表

        9. 生成分页导航

        10. 异常处理

        11. 资源释放

        12. 显示页脚链接

2.技术应用

(1)逻辑业务处理:

1. 分页逻辑:

           1.确定每页显示的商品数量(`pageSize`)。

           2.根据请求参数确定当前页码(`currentPage`)。

           3.计算分页查询的起始位置(`start`)。

2. 数据库操作:

           1.连接数据库并查询总记录数以计算总页数。

           2.执行分页查询以获取当前页的商品数据。

3. 数据显示:将查询到的商品数据显示在HTML表格中。

4. 分页导航:生成分页导航链接,允许用户浏览其他页码的商品。

5. 异常处理:处理可能发生的异常,如数据库连接失败或SQL执行错误。

(2)流程控制:

        1. 尝试/catch块:使用`try`块执行数据库操作,`catch`块捕获并处理异常。

        2. 条件判断:

                  判断是否获取到了有效的页码。

                  判断查询结果是否为空,以决定是否显示分页导航。

        3. finally块:无论操作成功或失败,都执行资源释放操作。

(3)类的调用:

        1. `java.sql`包中的类:

            `Connection`:数据库连接对象。

            `PreparedStatement`:用于执行参数化查询的语句对象。

            `ResultSet`:保存查询结果的对象。

        2. `DriverManager`类: `DriverManager.getConnection()`:获取数据库连接。

        3. `Class.forName()`方法:加载数据库驱动类。

(4)方法的调用:

        1. `executeQuery()`方法: 执行SQL查询并返回`ResultSet`对象。

        2. `prepareStatement()`方法:准备执行参数化查询的`PreparedStatement`对象。

        3. `close()`方法:关闭`ResultSet`、`PreparedStatement`和`Connection`对象,释放数据库资源。

3.技术选择分析

处理乱码的方式是通过设置HTTP响应和请求的字符编码来预防乱码,具体体现在以下部分:

   <%@ page contentType="text/html; charset=UTF-8" language="java" %>

以及在发送验证码图像时,确保了响应输出流的编码:

   ImageIO.write(bi, "png", out);

   out.close();

这里没有直接处理乱码,而是通过设置正确的字符编码来预防乱码的产生。

4.关键代码

int pageSize = 10;

// 从请求中获取当前页码,如果请求中没有提供页码或者页码为空字符串,则默认为第一页

String pageStr = request.getParameter("page");

int currentPage = (pageStr != null && !pageStr.isEmpty()) ? Integer.parseInt(pageStr) : 1;

// 计算分页查询的起始位置,用于LIMIT查询中

int start = (currentPage - 1) * pageSize;

String totalSql = "SELECT COUNT(*) AS total FROM product";

// 创建Statement对象,用于执行静态SQL语句

Statement stmt = conn.createStatement();

// 使用Statement对象执行查询,获取查询结果集

ResultSet totalRs = stmt.executeQuery(totalSql);

// 检查查询结果集是否包含记录,并从中获取总记录数

if (totalRs.next())

{

    int totalRecords = totalRs.getInt("total");

    // 计算总页数,向上取整以确保所有记录都能被显示

    int totalPages = (int) Math.ceil((double) totalRecords / pageSize);

    // 以下是分页查询和显示的代码...

}

5.实现

82309efc0fd0470fabb414d8a0c41e12.png

图3..10-1 分页查看

3.11 退出系统

1.关键代码

Logout.jsp

<%
    session.invalidate(); // 使当前会话失效
    response.sendRedirect("login.jsp"); // 重定向到登录页面
%>

2..实现

bcfae9038ee4424591ce8a8c5af1989a.png

图3.11-1 退出系统

四、项目总结 

问题:在查看商品信息时,如果数据库中商品过多,在查看时信息显示不全,并没有考虑到进行分页查看;其次在登陆页面,添加了验证码进行验证,但是在登录时发现验证码在页面上并不显示;修改密码时,有新旧密码,但在页面更新后发现数据库中密码没有变化,无法与数据库进行交互;在登录和注册页面,无需输入任何内容就可以登录和注册且在注册后直接跳转到登录页面,并没有提示是否注册成功;在查询商品信息时,跳转到另一个页面后不显示任何内容;在删除商品时使用的是“商品名称”进行删除,并没有考虑到用ID号。

解决办法:在查看商品信息时通过增加分页查询来查看完整信息;在修改密码过程中,变为了输入用户名、旧密码、 新密码来代替旧密码、新密码、确认新密码最终实现修改密码这一功能;而在登录和注册页面,通过设置条件判断输入是否为空来进行验证。最后一些其他的问题,利用大模型进行解决,在进行多次尝试后有的仍然没有解决,通过在网上搜索进行修改代码,最终解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

噗-噗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值