项目结构
项目搭建
静态页面的搭建
配置 Tomcat
Rebuild Project
拷贝静态资源后,先 Rebuild Project,确保资源被加载到了 out 工作目录下
<!-- 老师先使用临时方案,后面我们在修改 ctrl+home 页面最上 ctrl+end 页面最下-->
<!-- 引入jquery-->
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function () {
//绑定按钮注册点击事件
$("#sub-btn").click(function () {
//过五关斩六将
//用户名不匹配
var username = $("#username").val();
var usernamePattern = /^[a-zA-Z0-9_]{6,10}$/;
if (!usernamePattern.test(username)) {
$("span[class = 'errorMsg']").html("用户名必须字母,数字下划线组成,并且长度6-10位");
return false;//不进行提交
}
//密码不匹配
var password = $("#password").val();
var passwordPattern = /^[a-zA-Z0-9_]{6,10}$/;
if (!passwordPattern.test(password)) {
$("span[class = 'errorMsg']").html("密码必须字母,数字下划线组成,并且长度6-10位");
return false;
}
//验证输入的两次密码不匹配,并且不是数字字母下划线开头的
var repwd = $("#repwd").val();
if (password !== repwd) {
$("span[class = 'errorMsg']").html("两次输入的密码不匹配");
return false;
}
//邮箱格式不匹配
var email = $("#email").val();
var emailPattern = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9.-]+(\.[a-zA-Z]{2,4})$/;
if (!emailPattern.test(email)) {
$("span[class = 'errorMsg']").html("邮箱格式不正确");
return false;
}
//还需要增加验证码的匹配,
//验证通过
$("span[class = 'errorMsg']").html("验证通过");
return true; //发送注册表单数据给到服务器Tomcat=>Servlet-根据表单的action
})
})
</script>
注册功能
前端JS 校验
后端
核心框架图
1. 会员表的设计
-- 创建家具网购需要的数据库和表-- 创建 home_furnishing
-- 删除,一定要小心
DROP DATABASE IF EXISTS home_furnishing;
-- 创建数据库
CREATE DATABASE home_furnishing;
USE home_furnishing;
DROP TABLE `member`
-- 创建会员表
CREATE TABLE `member`(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL UNIQUE,
`password` VARCHAR(32) NOT NULL,
`email` VARCHAR(64)
)CHARSET utf8 ENGINE INNODB
-- 测试数据
INSERT INTO member(`username`,`password`,`email`)
VALUES('admin',MD5('admin'),'hsp@hanshunping.net');
INSERT INTO member(`username`,`password`,`email`)
VALUES('milan123',MD5('milan123'),'milan123@hanshunping.net');
SELECT * FROM member
SELECT `id`,`username`,`password`,`email` FROM `member`
WHERE `username`='admin';
INSERT INTO `member`(`username`,`password`,`email`)
VALUES('jack',MD5('jack'), 'jack@sohu.com');
SELECT `id`,`username`,`password`,`email` FROM `member`
WHERE `username`='admin' AND `password`=MD5('admin')
-- 创建家居表(表如何设计)
-- 设计furn表 家居表
-- 老师说 需求-文档-界面
-- 技术细节
-- 有时会看到 id int(11) ... 11 表示的显示宽度,存放的数据范围和int 配合zerofill
-- int(2) .... 2 表示的显示宽度
-- 67890 => int(11) 00000067890
-- 67890 => int(2) 67890
-- 创建表的时候,一定注意当前是DB
-- 表如果是第一次写项目,表的字段可能会增加,修改,删除
CREATE TABLE `furn`(
`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, #id
`name` VARCHAR(64) NOT NULL, #家居名
`maker` VARCHAR(64) NOT NULL, #制造商
`price` DECIMAL(11,2) NOT NULL , #价格 定点数
`sales` INT UNSIGNED NOT NULL, #销量
`stock` INT UNSIGNED NOT NULL, #库存
`img_path` VARCHAR(256) NOT NULL #存放图片的路径
)CHARSET utf8 ENGINE INNODB
-- 测试数据, 参考 家居购物-数据库设计.sql
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '北欧风格小桌子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/6.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '简约风格小椅子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/4.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '典雅风格小台灯' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/14.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '温馨风格盆景架' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/16.jpg');
DELETE FROM `furn` WHERE id = 8
-- 修改...
UPDATE `furn` SET `name` = '北欧风格小桌子' , `maker` = '熊猫家居', `price` = 200 ,
`sales` = 112 , `stock` = 88 , `img_path` = 'assets/images/product-image/6.jpg'
WHERE id = 1
SELECT COUNT(*) FROM `furn` -- 11 -> 3
-- 模糊查询 Mysql基础
SELECT COUNT(*) FROM `furn` WHERE NAME LIKE '%%'
SELECT `id`, `name` , `maker`, `price`, `sales`, `stock`,
`img_path` imgPath FROM furn WHERE `name` LIKE '%沙发%' LIMIT 0, 3
-- 订单表 order
-- 参考界面来写订单
-- 认真分析字段和字段类型
-- 老师说明: 每个字段, 使用not null 来约束
-- 字段类型的设计, 应当和相关的表的字段有对应
-- 外键是否要给? 1. 需要[可以从db层保证数据的一致性 ]
-- 2. 不需要[] [外键对效率有影响, 应当从程序的业务保证一致性]
-- 是否需要一个外键的约束?
-- FOREIGN KEY(`member_id`) REFERENCES `member`(`id`)
CREATE TABLE `order` (
`id` VARCHAR(64) PRIMARY KEY, -- 订单号
`create_time` DATETIME NOT NULL, -- 订单生成时间
`price` DECIMAL(11,2) NOT NULL, -- 订单的金额
`status` TINYINT NOT NULL, -- 状态 0 未发货 1 已发货 2 已结账
`member_id` INT NOT NULL -- 该订单对应的会员id
)CHARSET utf8 ENGINE INNODB
-- 创建订单项表
CREATE TABLE `order_item`(
id INT PRIMARY KEY AUTO_INCREMENT, -- 订单项的id
`name` VARCHAR(64) NOT NULL, -- 家居名
`price` DECIMAL(11,2) NOT NULL, -- 家居价格
`count` INT NOT NULL, -- 数量
`total_price` DECIMAL(11,2) NOT NULL, -- 订单项的总价
`order_id` VARCHAR(64) NOT NULL -- 对应的订单号
)CHARSET utf8 ENGINE INNODB
-- 编写一个添加order的sql
INSERT INTO `order`(`id`,`create_time`,`price`,`status`,`member_id`)
VALUES('sn000001',NOW(),100,0,2)
-- 编写一个添加orderitem的sql
-- 每写一行,自己要知道,自己在做什么?
INSERT INTO `order_item`(`id`,`name`,`price`,`count`,`total_price`,`order_id`)
VALUES(NULL,'北欧小沙发',200,2,400,'sn00002')
2. JavaBean-Member
package com.hspedu.furns.entity;
/**
* @author 韩顺平
* @version 1.0
*/
public class Member {
private Integer id;
private String username;
private String password;
private String email;
//一定要提供一个无参构造器-底层使用一个反射创建Member对象
//使用到无参构造器.
public Member() {
}
public Member(Integer id, String username, String password, String email) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Member{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
3. 导入德鲁伊工具类-utils
package com.hspedu.furms.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @author 韩顺平
* @version 1.0
* 基于druid数据库连接池的工具类
*/
public class JDBCUtilsByDruid {
private static DataSource ds;
//在静态代码块完成 ds初始化
static {
Properties properties = new Properties();
try {
//因为我们是web项目,他的工作目录在out, 文件的加载,需要使用类加载器
//找到我们的工作目录
properties.load(JDBCUtilsByDruid.class.getClassLoader()
.getResourceAsStream("druid.properties"));
// properties.load(new FileInputStream("src\\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//编写getConnection方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//关闭连接, 老师再次强调: 在数据库连接池技术中,close 不是真的断掉连接
//而是把使用的Connection对象放回连接池
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
4. 配置properties
配置文件在 Src 目录下
#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/home_furnishing?rewriteBatchedStatements=true
username=root
password=hsp
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000
5. Test-数据库的连接与关闭
package com.hspedu.furns.test;
import com.hspedu.furns.utils.JDBCUtilsByDruid;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author 韩顺平
* @version 1.0
*/
public class JDBCUtilsByDruidTest {
@Test
public void getConnection() throws SQLException {
//如果这里你看不懂, 回去看mysql ,jdbc, 数据库连接池 , 满汉楼
Connection connection = JDBCUtilsByDruid.getConnection();
System.out.println("connection= " + connection);
JDBCUtilsByDruid.close(null, null, connection);
}
}
6. MemberDAO 实现接口
注册的功能的规范
功能 1:通过会员名查询该会员是否存在,null 不存在可以注册
功能 2:往数据库中插入 1 条会员的数据(JavaBean)
package com.hspedu.furns.dao;
import com.hspedu.furns.entity.Member;
/**
* @author 韩顺平
* @version 1.0
*/
public interface MemberDAO {
//小伙伴要自己分析,需要哪些方法
//提供一个通过用户名返回对应的Member
public Member queryMemberByUsername(String username);
//提供一个保存Member对象到数据库/表member表
public int saveMember(Member member);
}
7. MemberDAOImpl 实现类
实现规范了接口的功能
功能 1:通过用会员名查询,并返回 1 个会员对象
功能 2:传入 1 个会员对象,添加到数据库中
package com.hspedu.furns.dao.impl;
import com.hspedu.furns.dao.BasicDAO;
import com.hspedu.furns.dao.MemberDAO;
import com.hspedu.furns.entity.Member;
/**
* @author 韩顺平
* @version 1.0
*/
public class MemberDAOImpl extends BasicDAO<Member> implements MemberDAO {
/**
* 通过用户名返回对应的Member
*
* @param username 用户名
* @return 对应的Member, 如果没有该Member, 返回 null
*/
@Override
public Member queryMemberByUsername(String username) {
//老师提示,sql 先在sqlyog 测试,然后再拿到程序中
//这里可以提高我们的开发效率,减少不必要的bug
String sql = "SELECT `id`,`username`,`password`,`email` FROM `member` " +
" WHERE `username`=?";
return querySingle(sql, Member.class, username);
}
/**
* 保存一个会员
*
* @param member 传入Member对象
* @return 返回-1 就是失败,返回其它的数字就是受影响的行数
*/
@Override
public int saveMember(Member member) {
String sql = "INSERT INTO `member`(`username`,`password`,`email`) " +
" VALUES(?,MD5(?), ?)";
return update(sql, member.getUsername(),
member.getPassword(), member.getEmail());
}
@Override
public Member queryMemberByUsernameAndPassword(String username, String password) {
String sql = "SELECT `id`,`username`,`password`,`email` FROM `member` " +
" WHERE `username`=? and `password`=md5(?)";
return querySingle(sql, Member.class, username, password);
}
}
8. MemberDAOTest 测试类
因为 MemberDAOImpl 类实现了 MemberDAO 接口,多态的特性
创建 1 个父类的接口指向子类对象的对象就可以
调用其子类重写的方法
package com.hspedu.furns.test;
import com.hspedu.furns.dao.MemberDAO;
import com.hspedu.furns.dao.impl.MemberDAOImpl;
import com.hspedu.furns.entity.Member;
import org.junit.jupiter.api.Test;
/**
* @author 韩顺平
* @version 1.0
*/
public class MemberDAOTest {
private MemberDAO memberDAO = new MemberDAOImpl();
@Test
public void queryMemberByUsername() {
if(memberDAO.queryMemberByUsername("adminx") == null) {
System.out.println("该用户名不存在...");
} else {
System.out.println("该用户名存在...");
}
}
@Test
public void saveMember() {
Member member =
new Member(null, "king", "king", "king@sohu.com");
if(memberDAO.saveMember(member) == 1) {
System.out.println("添加OK");
} else {
System.out.println("添加失败...");
}
}
}
9. MemberService 实现接口
package com.hspedu.furns.service;
import com.hspedu.furns.entity.Member;
/**
* @author 韩顺平
* @version 1.0
*/
public interface MemberService {
//注册用户
public boolean registerMember(Member member);
//判断用户名是否存在
public boolean isExistsUsername(String username);
/**
* 根据登录传入的member信息,返回对应的在DB中的member对象
* @param member 是根据用户登录构建一个member
* @return 返回的是对应的db中的member对象,如果不存在,返回null
*/
public Member login(Member member);
}
10. MemberServiceImpl 实现类
package com.hspedu.furns.service.impl;
import com.hspedu.furns.dao.MemberDAO;
import com.hspedu.furns.dao.impl.MemberDAOImpl;
import com.hspedu.furns.entity.Member;
import com.hspedu.furns.service.MemberService;
/**
* @author 韩顺平
* @version 1.0
*/
public class MemberServiceImpl implements MemberService {
//定义一个MemberDao属性
private MemberDAO memberDAO = new MemberDAOImpl();
@Override
public boolean registerMember(Member member) {
return memberDAO.saveMember(member) == 1 ? true : false;
}
/**
* 判断用户名是否存在
*
* @param username 用户名
* @return 如果存在返回true, 否则返回false
*/
@Override
public boolean isExistsUsername(String username) {
//老韩的小技巧
//如果看某个方法 ctrl + b => 定位到memberDAO 遍历类型的方法
//如果使用 ctrl+alt+ b=> 实现类的方法
//如果有多个类,实现了该方法 会弹出选择的对话框.
return memberDAO.queryMemberByUsername(username) == null ? false : true;
}
}
11. MemberServiceTest 测试类
10.测试类
实现MemberService 接口,重写方法
继承MemberDAOImpl 实现类,调用增删改查功能
即在重写方法的内部调用父类的功能,完成测试
package com.hspedu.furns.test;
import com.hspedu.furns.entity.Member;
import com.hspedu.furns.service.MemberService;
import com.hspedu.furns.service.impl.MemberServiceImpl;
import org.junit.jupiter.api.Test;
/**
* @author 韩顺平
* @version 1.0
*/
public class MemberServiceTest {
private MemberService memberService = new MemberServiceImpl();
@Test
public void isExistsUsername() {
if(memberService.isExistsUsername("king")) {
System.out.println("用户名存在...");
} else {
System.out.println("用户名不存在...");
}
}
@Test
public void registerMember() {
//构建一个Member对象
Member member = new Member(null, "mary", "mary", "mary@qq.com");
if(memberService.registerMember(member)) {
System.out.println("注册用户成功...");
} else {
System.out.println("注册用户失败...");
}
}
}
12.RegisterServlet
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/17 2:58
* @Version 1.0
*/
import com.hspedu.furms.domain.Member;
import com.hspedu.furms.service.MemberService;
import com.hspedu.furms.service.impl.MemberServiceImel;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "RegisterServlet", value = "/registerServlet")
public class RegisterServlet extends HttpServlet {
//调用Service层
private MemberService memberServiceImpl = new MemberServiceImel();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 得到表单数据
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
// 判断该用户是否存在,不存在,则进行注册
if (!memberServiceImpl.isExistByUserName(username)) {
Member member = new Member(null, username, password, email);
System.out.println(member);
// //判断是否注册成功
boolean isRegister = memberServiceImpl.isRegister(member);
if (isRegister) {// 注册成功-不要使用请求转发-刷新页面会重复注册
//request.getRequestDispatcher("/views/member/register_ok.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/views/member/register_ok.html");
} else {// 注册失败
//request.getRequestDispatcher("/views/member/register_fail.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/views/member/register_fail.html");
}
// 用户存在,则返回到登录界面-使用重定向
}
}
}
登录功能
前端-用户名密码 JS 校验
//给登录绑定点击事件
$("#login_btn").click(function () {
var login_username = $("#login_username").val();
var login_password = $("#login_password").val();
//验证用户名-长度6-10位,数字字母下划线
var login_usernamePattern = /^\w{3,10}$/;
if (!login_usernamePattern.test(login_username)) {
return false;
}
//验证密码-长度6-10位,数字字母下划线
var login_passwordPattern = /^\w{3,10}$/;
if (!login_passwordPattern.test(login_password)) {
return false;
}
return true;
})
后端
表的设计
直接从会员表中,查询即可
JavaBean-Member
MemberDAO 接口
1.传入 1 个用户名和密码,返回 1 个 Member 对象
//登录功能
public Member login(String username,String password);
MemberDAOImp 实现类
2.因为继承了 BasicDAO 且实现了 MemberDAO 接口
3.重写接口方法,并通过调用 querySingle 方法返回 1 个 Member 对象
该方法的返回值的类型与接口定义方法的返回值类型相同
//登录功能——SQL
@Override
public Member login(String username, String password) {
String sql = "select * from `member` where username=? and password = md5(?)";
return querySingle(sql, Member.class, username, password);
}
MemberDAOImpTest 测试类
4.继承实现类或者通过多态的方式创建实现类的对象
调用实现类中定义的方法,测试功能是否正常
@Test
public void queryMemberByUnameAndPwd() {
String username = "jack666";
String password = "123456";
Member member = login(username, password);
if (member != null) {
System.out.println("login ok");
} else {
System.out.println("login error");
}
}
MemberService 接口
5. 定义抽象方法:根据 DAO 接口登录方法的返回值类型,判断是否登录成功(boolean)
传入用户名和密码,判断是否登录成功(boolean)
//判断是否登录成功
//用户在登录的时候传入的Member
public boolean isLogin(Member member);
MemberServiceImp 实现类
6.继承MemberDAOImp类并实现MemberService 接口
在重写方法中调用其父类的方法
package com.hspedu.furms.service.impl;
import com.hspedu.furms.dao.MemberDAO;
import com.hspedu.furms.dao.impl.MemberImpl;
import com.hspedu.furms.domain.Member;
import com.hspedu.furms.service.MemberService;
/**
* ClassName: MemberServiceImel
* Package: com.hspedu.furms.service.impl
* Description:
*
* @Author 王文福
* @Create 2024/1/17 2:40
* @Version 1.0
*/
public class MemberServiceImel extends MemberImpl implements MemberService {
//调用DAO层的查询的方法
private MemberDAO memberDAO = new MemberImpl();
//判断用户是否登录成功
//调用DAO层的实现类的方法,传入会员对象的用户名和密码
@Override
public boolean isLogin(Member member) {
return memberDAO.login(member.getUsername(), member.getPassword()) != null ? true : false;
}
}
MemberServiceTest 测试类
7.继承实现类或者根据多态创建MemberServiceImp 实现类对象
完成对功能的测试
package com.hspedu.furms.test;
import com.hspedu.furms.domain.Member;
import com.hspedu.furms.service.MemberService;
import com.hspedu.furms.service.impl.MemberServiceImel;
import org.junit.Test;
/**
* ClassName: MemberServiceImelTest
* Package: com.hspedu.furms.test
* Description:
*
* @Author 王文福
* @Create 2024/1/17 2:47
* @Version 1.0
*/
public class MemberServiceImelTest {
//调用Service测试
private MemberService memberService = new MemberServiceImel();
@Test
public void isLoginTest() {
Member member = new Member(null, "jack555", "123456", null);
boolean isLogin = memberService.isLogin(member);
if (isLogin) {
System.out.println("login success");
} else {
System.out.println("login fail");
}
}
}
LoginServlet 类
8.根据多态的特性,创建MemberServiceDAOImp 实现类的对象
调用实现类重写了接口的方法,传入用户名和密码,返回 boolean 类型 的值判断是否登录成功
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/17 15:11
* @Version 1.0
*/
import com.hspedu.furms.domain.Member;
import com.hspedu.furms.service.MemberService;
import com.hspedu.furms.service.impl.MemberServiceImel;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "LoginServlet", value = "/loginServlet")
public class LoginServlet extends HttpServlet {
//调用Service层
private MemberService memberServiceImpl = new MemberServiceImel();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("LoginServlet");
//参数值与html标签的name属性值相同
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
Member member = new Member(null, username, password, null);
boolean isLogin = memberServiceImpl.isLogin(member);
if (isLogin) { //登录成功
request.getRequestDispatcher("/views/member/login_ok.html").forward(request, response);
// System.out.println("login success");
//登录失败
} else {
// System.out.println("login fail");
request.getRequestDispatcher("/views/member/login.html").forward(request, response);
}
}
}
登录错误回显
① 请求转发,一直是同一个请求【请求的数据可以共享】
② 在resp响应浏览器内容时,如果有中文字符,则在响应之前,需要设置content-type,即 resp.setContentType("text/html;charset=utf-8");
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/17 15:11
* @Version 1.0
*/
import com.hspedu.furms.domain.Member;
import com.hspedu.furms.service.MemberService;
import com.hspedu.furms.service.impl.MemberServiceImel;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
@WebServlet(name = "LoginServlet", value = "/loginServlet")
public class LoginServlet extends HttpServlet {
// 调用Service层
private MemberService memberServiceImpl = new MemberServiceImel();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("LoginServlet");
//设置请求转发后对象的响应数据的格式:utf-8
// 参数值与html标签的name属性值相同
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
Member member = new Member(null, username, password, null);
boolean isLogin = memberServiceImpl.isLogin(member);
if (isLogin) { // 登录成功
request.getRequestDispatcher("/views/member/login_ok.html").forward(request, response);
System.out.println("login success");
// 登录失败
} else {
// 该用户不存在
if (!memberServiceImpl.isExistByUserName(username)) {
//设置请求转发后对象的响应数据的格式:utf-8
response.setContentType("text/html;charset=utf-8");
// 设置属性
request.setAttribute("isNotExistUser", "user not exist");
// 请求转发到login.jsp
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
// 用户名存在,密码不正确
} else {
//设置请求转发后对象的响应数据的格式:utf-8
response.setContentType("text/html;charset=utf-8");
request.setAttribute("username",username);
request.setAttribute("loginFailPrompt", "user or password error");
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
}
}
}
}
login.jsp
jsp 本质是 Servlet
请求转发后得到 request 对象和 response 对象的数据
该用户不存在,则提示该用户不存在
该用户存在,则用户名不需要重复输入
Web 层-Servlet 减肥
需求分析
1. 将处理注册和登录的功能封装成 1 个方法
2.在 1 个 Servlet 请求中根据 name属性名的默认值来决定调用哪个方法
方案 1:
1. 将处理注册和登录的功能进行封装到 1 个 Servlet 中
2.使用 if...else 方法根据 name 的默认值来决定调用哪个方法
前端
1. 在注册表单标签最开始处加入一行 input 表单标签,且 from 表单提交到同 1 个 Servlet
<input type="hidden" name="action" value="register"/>
2. 在 登录 表单标签最开始处加入一行input 表单标签,且 from 表单提交到同 1 个 Servlet
<input type="hidden" name="action" value="login"/>
后端
1. 将注册和登录的功能封装到 1 个 Servlet 中
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/17 20:54
* @Version 1.0
*/
import com.hspedu.furms.domain.Member;
import com.hspedu.furms.service.MemberService;
import com.hspedu.furms.service.impl.MemberServiceImel;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@WebServlet(name = "MemberServlet", value = "/memberServlet")
public class MemberServlet extends HttpServlet {
// 调用Service层
private MemberService memberServiceImpl = new MemberServiceImel();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
/** 登录功能
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 20:57
*/
public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("LoginServlet");
// 设置请求转发后对象的响应数据的格式:utf-8
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");// 等同于response.setHeader("contentType", "text/html; charset=utf-8");它其实会覆盖
response.setCharacterEncoding("utf-8");
// 参数值与html标签的name属性值相同
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
Member member = new Member(null, username, password, null);
boolean isLogin = memberServiceImpl.isLogin(member);
if (isLogin) { // 登录成功
request.getRequestDispatcher("/views/member/login_ok.html").forward(request, response);
System.out.println("login success");
// 登录失败
} else {
// 该用户不存在
if (!memberServiceImpl.isExistByUserName(username)) {
// 设置属性
request.setAttribute("isNotExistUser", "用户不存在");
// 请求转发到login.jsp
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
// 用户名存在,密码不正确
} else {
request.setAttribute("loginFailPrompt", "账号或者密码有误");
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
}
}
}
/** 注册功能
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 20:57
*/
public void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 得到表单数据
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("password");
// 判断该用户是否存在,不存在,则进行注册
if (!memberServiceImpl.isExistByUserName(username)) {
Member member = new Member(null, username, password, email);
System.out.println(member);
// //判断是否注册成功
boolean isRegister = memberServiceImpl.isRegister(member);
if (isRegister) {// 注册成功
request.getRequestDispatcher("/views/member/register_ok.html").forward(request, response);
} else {// 注册失败
request.getRequestDispatcher("/views/member/register_fail.html").forward(request, response);
}
// 用户存在,则返回到登录界面
} else {
System.out.println("Member is exist");
request.getRequestDispatcher("/views/member/login.html").forward(request, response);
response.setContentType("text/html;charset=uft-8");
}
}
}
2. 使用 if...else 方法根据 name 的默认值来决定调用哪个方法
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 得到的是表单的name属性action的value值
String action = request.getParameter("action");
//
if ("register".equals(action)) {
register(request, response);
}
if ("login".equals(action)) {
login(request, response);
}
}
方案 2:反射+模板设计模式+动态绑定
- 定义 1 个 BasicServlet 抽象类,
定义抽象方法 login 和 register
定义 dopost 方法,在该方法中调用定义的抽象方法
- 当子类继承了抽象类,重写抽象方法
- 通过反射创建抽象类实例,调用 dopost 方法
- 根据动态绑定机制,调用子类的方法
1.定义 BasicServlet 抽象类
定义抽象方法 login 和 register
定义 dopost 方法,在该方法中调用定义的抽象方法
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/17 22:19
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
// 1.定义抽象Servlet类
// 2.定义登录与注册的抽象方法
// 3.根据action来绝对的调用哪个方法
public abstract class BasicServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 根据得到的name值判断调用哪个抽象方法
String action = request.getParameter("action");
if ("login".equals(action)) {
login(request, response);
} else if ("register".equals(action)) {
register(request, response);
}
}
// 登录功能
public abstract void login(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
// 注册功能
public abstract void register(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}
2.定义MemberServlet子类
package com.hspedu.furms.web;
import com.hspedu.furms.domain.Member;
import com.hspedu.furms.service.MemberService;
import com.hspedu.furms.service.impl.MemberServiceImel;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* ClassName: Basic
* Package: com.hspedu.furms.web
* Description:
* 封装登录和注册的功能
*
* @Author 王文福
* @Create 2024/1/17 22:30
* @Version 1.0
*/
@WebServlet(name = "MemberServlet", value = "/memberServlet")
//当访问当前Web路径时 调用的是父类的doPost方法
public class Basic extends BasicServlet {
private MemberService memberServiceImpl = new MemberServiceImel(); // DAO层
/**
* 登录功能
*
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 20:57
*/
@Override
public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置请求转发后对象的响应数据的格式:utf-8
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");// 等同于response.setHeader("contentType", "text/html; charset=utf-8");它其实会覆盖
response.setCharacterEncoding("utf-8");
// 参数值与html标签的name属性值相同
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
Member member = new Member(null, username, password, null);
boolean isLogin = memberServiceImpl.isLogin(member);
if (isLogin) { // 登录成功
request.getRequestDispatcher("/views/member/login_ok.html").forward(request, response);
// 登录失败
} else {
// 该用户不存在
if (!memberServiceImpl.isExistByUserName(username)) {
// 设置属性
request.setAttribute("isNotExistUser", "用户不存在");
// 请求转发到login.jsp
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
// 用户名存在,密码不正确
} else {
request.setAttribute("loginFailPrompt", "账号或者密码有误");
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
}
}
}
/**
* 注册功能
*
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 20:57
*/
@Override
public void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 得到表单数据
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("password");
// 判断该用户是否存在,不存在,则进行注册
if (!memberServiceImpl.isExistByUserName(username)) {
Member member = new Member(null, username, password, email);
System.out.println(member);
// //判断是否注册成功
boolean isRegister = memberServiceImpl.isRegister(member);
if (isRegister) {// 注册成功
request.getRequestDispatcher("/views/member/register_ok.html").forward(request, response);
} else {// 注册失败
request.getRequestDispatcher("/views/member/register_fail.html").forward(request, response);
}
// 用户存在,则返回到登录界面
} else {
System.out.println("Member is exist");
request.getRequestDispatcher("/views/member/login.html").forward(request, response);
response.setContentType("text/html;charset=uft-8");
}
}
}
使用反射
MemberServlet子类
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/17 20:54
* @Version 1.0
*/
import com.hspedu.furms.domain.Member;
import com.hspedu.furms.service.MemberService;
import com.hspedu.furms.service.impl.MemberServiceImel;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet(name = "MemberServlet", value = "/memberServlet")
// 继承BasicServlet,调用父类的doPost方法
public class MemberServlet extends BasicServlet {
// 调用Service层
private MemberService memberServiceImpl = new MemberServiceImel();
/**
* 登录功能
*
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 20:57
*/
public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置请求转发后对象的响应数据的格式:utf-8
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");// 等同于response.setHeader("contentType", "text/html; charset=utf-8");它其实会覆盖
response.setCharacterEncoding("utf-8");
// 参数值与html标签的name属性值相同
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
Member member = new Member(null, username, password, null);
boolean isLogin = memberServiceImpl.isLogin(member);
if (isLogin) { // 登录成功
request.getRequestDispatcher("/views/member/login_ok.html").forward(request, response);
// 登录失败
} else {
// 该用户不存在
if (!memberServiceImpl.isExistByUserName(username)) {
// 设置属性
request.setAttribute("isNotExistUser", "用户不存在");
// 请求转发到login.jsp
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
// 用户名存在,密码不正确
} else {
request.setAttribute("loginFailPrompt", "账号或者密码有误");
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
}
}
}
/**
* 注册功能
*
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 20:57
*/
public void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 得到表单数据
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
// 判断该用户是否存在,不存在,则进行注册
if (!memberServiceImpl.isExistByUserName(username)) {
Member member = new Member(null, username, password, email);
System.out.println(member);
// //判断是否注册成功
boolean isRegister = memberServiceImpl.isRegister(member);
if (isRegister) {// 注册成功
request.getRequestDispatcher("/views/member/register_ok.html").forward(request, response);
} else {// 注册失败
request.getRequestDispatcher("/views/member/register_fail.html").forward(request, response);
}
// 用户存在,则返回到登录界面
} else {
System.out.println("Member is exist");
request.setAttribute("registerFail", "该用户存在,无法注册");
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
}
}
}
BasicServlet抽象Servlet类
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/17 22:19
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.lang.reflect.Method;
// 1.定义抽象Servlet类-不需要指定Web路径-通过子类掉父类doPost方法
// 2.定义登录与注册的抽象方法
// 3.根据action来绝对的调用哪个方法
// 当访问MemberServlet的Web路径时,掉模板Servlet父类的doPost方法,又因为重写了抽象方法
// 调用当前MemberServlet中重写的方法
// 4.通过反射创建当前类的实例,调用doPost方法,根据动态绑定机制调用子类的重写的方法
public abstract class BasicServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 得到action的值,该值与调用的子类Servlet的方法名相同
String action = request.getParameter("action");
// 反射+模板设计模式+动态绑定
// 使用反射,并根据action的值获取当前子类Servlet实例的方法
// 简化了大量使用if...else
try {
Method declareMethod = this.getClass().getMethod
(action, HttpServletRequest.class, HttpServletResponse.class);
// 调用方法
declareMethod.invoke(this, request, response);
} catch (Exception e) {
throw new RuntimeException(e);
}
// System.out.println("BasicServlet");
// // 根据得到的name值判断调用哪个抽象方法
// System.out.println("BasicServlet");
// String action = request.getParameter("action");
// if ("login".equals(action)) {
// login(request, response);
// } else if ("register".equals(action)) {
// register(request, response);
// } else {
// response.setContentType("text/html;charset=utf-8");
// response.getWriter().write("action参数不匹配");
// }
// }
//
// // 登录功能
// public abstract void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
//
// // 注册功能
// public abstract void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
}
显示家具(46)
核心框架图
前端 JS 登录校验
后端
表的设计
家具表
-- 创建家居表(表如何设计)
-- 设计furn表 家居表
-- 老师说 需求-文档-界面
-- 技术细节
-- 有时会看到 id int(11) ... 11 表示的显示宽度,存放的数据范围和int 配合zerofill
-- int(2) .... 2 表示的显示宽度
-- 67890 => int(11) 00000067890
-- 67890 => int(2) 67890
-- 创建表的时候,一定注意当前是DB
-- 表如果是第一次写项目,表的字段可能会增加,修改,删除
CREATE TABLE `furn`(
`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, #id
`name` VARCHAR(64) NOT NULL, #家居名
`maker` VARCHAR(64) NOT NULL, #制造商
`price` DECIMAL(11,2) NOT NULL , #价格 定点数
`sales` INT UNSIGNED NOT NULL, #销量
`stock` INT UNSIGNED NOT NULL, #库存
`img_path` VARCHAR(256) NOT NULL #存放图片的路径
)CHARSET utf8 ENGINE INNODB
-- 测试数据, 参考 家居购物-数据库设计.sql
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '北欧风格小桌子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/6.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '简约风格小椅子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/4.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '典雅风格小台灯' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/14.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '温馨风格盆景架' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/16.jpg');
JavaBean-Furn
package com.hspedu.furms.entity;
import java.math.BigDecimal;
/**
* ClassName: Furn
* Package: com.hspedu.furms.entity
* Description:
*
* @Author 王文福
* @Create 2024/1/18 15:14
* @Version 1.0
*/
public class Furn {
private Integer id;// 防止null
private String name;
private String maker;
private BigDecimal price;
private Integer sales;
private Integer stock;
// 表的字段img_path,JavaBean定义的字段imgPath
// 解决方案:在使用SQL语句的时候必须使用别名as 命名为imgPath
private String imgPath;
// 无参构造器-反射
public Furn() {
}
public Furn(Integer id, String name, String maker, BigDecimal price, Integer sales, Integer stock, String imgPath) {
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
this.sales = sales;
this.stock = stock;
this.imgPath = imgPath;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMaker() {
return maker;
}
public void setMaker(String maker) {
this.maker = maker;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
}
@Override
public String toString() {
return "Furn{" + "id=" + id + ", name='" + name + '\'' + ", maker='" + maker + '\'' + ", price=" + price + ", sales=" + sales + ", stock=" + stock + ", imgPath='" + imgPath + '\'' + '}';
}
}
显示家具-DAO
FurnDAO 接口
package com.hspedu.furms.dao;
import com.hspedu.furms.entity.Furn;
import java.util.List;
/**
* ClassName: FurnDAO
* Package: com.hspedu.furms.dao
* Description:
* 规范家具方法的接口
*
* @Author 王文福
* @Create 2024/1/18 15:28
* @Version 1.0
*/
public interface FurnDAO {
// 查询所有家具,返回1个List集合
public List<Furn> queryAllFurn();
}
FurnDAOImpl 实现类
package com.hspedu.furms.dao.impl;
import com.hspedu.furms.dao.BasicDAO;
import com.hspedu.furms.dao.FurnDAO;
import com.hspedu.furms.entity.Furn;
import java.util.List;
/**
* ClassName: FurnDAOImpl
* Package: com.hspedu.furms.dao.impl
* Description:
*
* @Author 王文福
* @Create 2024/1/18 15:31
* @Version 1.0
*/
public class FurnDAOImpl extends BasicDAO<Furn> implements FurnDAO {
// 查询所有家具-返回所有家具
@Override
public List<Furn> queryAllFurn() {
String sql = "SELECT `id`,`name`,`maker`,`price`,
`sales`,`stock`,`img_path` AS imgPath FROM furn";
return queryMulti(sql, Furn.class);
}
}
FurnDAOImpl 测试类
package com.hspedu.furms.test;
import com.hspedu.furms.dao.FurnDAO;
import com.hspedu.furms.dao.impl.FurnDAOImpl;
import com.hspedu.furms.entity.Furn;
import org.junit.Test;
import java.util.List;
/**
* ClassName: FurnDAOImpTest
* Package: com.hspedu.furms.test
* Description:
* 家具增删改查测试
*
* @Author 王文福
* @Create 2024/1/18 15:37
* @Version 1.0
*/
public class FurnDAOImpTest {
// 调用DAO层实例对象进行测试
private FurnDAO furnDAO = new FurnDAOImpl();
@Test
public void queryAllFurn() {
// 返回1个家具集合
List<Furn> furnList = furnDAO.queryAllFurn();
// 遍历该集合取出所有的家具
for (Furn furn : furnList) {
System.out.println(furn);
}
}
}
FurnService 接口
package com.hspedu.furms.service;
import com.hspedu.furms.entity.Furn;
import java.util.List;
/**
* ClassName: FurnService
* Package: com.hspedu.furms.service
* Description:
* 家具Service接口的规范
*
* @Author 王文福
* @Create 2024/1/18 15:50
* @Version 1.0
*/
public interface FurnService {
// 显示所有家具
public List<Furn> queryAllFurn();
}
FurnServiceImpl 实现类
package com.hspedu.furms.service.impl;
import com.hspedu.furms.dao.FurnDAO;
import com.hspedu.furms.dao.impl.FurnDAOImpl;
import com.hspedu.furms.entity.Furn;
import com.hspedu.furms.service.FurnService;
import java.util.List;
/**
* ClassName: FurnServiceImpl
* Package: com.hspedu.furms.service.impl
* Description:
*
* @Author 王文福
* @Create 2024/1/18 15:51
* @Version 1.0
*/
public class FurnServiceImpl implements FurnService {
// Service调用DAO层
private FurnDAO furnDAO = new FurnDAOImpl();
@Override
public List<Furn> queryAllFurn() {
// 得到家具的集合
return furnDAO.queryAllFurn();
}
}
FurnServiceImplTest 测试类
package com.hspedu.furms.test;
import com.hspedu.furms.entity.Furn;
import com.hspedu.furms.service.FurnService;
import com.hspedu.furms.service.impl.FurnServiceImpl;
import org.junit.Test;
import java.util.List;
/**
* ClassName: FurnServiceImplTest
* Package: com.hspedu.furms.test
* Description:
*
* @Author 王文福
* @Create 2024/1/18 15:55
* @Version 1.0
*/
public class FurnServiceImplTest {
// 调用Service层实体对象进行测试
private FurnService furnService = new FurnServiceImpl();
// 显示所有家具
@Test
public void queryAllFurn() {
List<Furn> furnList = furnService.queryAllFurn();
for (Furn furn : furnList) {
System.out.println(furn);
}
}
}
FurnServlet-Web 层
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/18 16:11
* @Version 1.0
*/
import com.hspedu.furms.entity.Furn;
import com.hspedu.furms.service.FurnService;
import com.hspedu.furms.service.impl.FurnServiceImpl;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.List;
// 注解的路径:/manage/furnServlet 过滤使用
@WebServlet(name = "FurnServlet", value = "/manage/furnServlet")
// 继承BasicServlet:反射+模板设计模式+动态绑定
// 注意还需要配置前端的action与方法名时相同的
public class FurnServlet extends BasicServlet {
// Web层调用Service层
private FurnService furnService = new FurnServiceImpl();
// 显示所有家具-返回的是1个List集合的对象
public void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Furn> furnList = furnService.queryAllFurn();
for (Furn furn : furnList) {
System.out.println(furn);
}
}
}
配置 BasicServlet 请求的 get 方法
当用户请求 get 方法的时候会掉 dopost 方法
public abstract class BasicServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
测试访问地址
http://localhost:8080/furniture/manage/furnServlet?action=list
1.主机名:http://localhost:8080 域名+端口号
- 项目名:furniture
- 访问的 Servlet 路径:/manage/furnServlet
- get 请求: ?
- 调用的 Servlet 的哪个方法:list
- 看后台是否调用了该方法
-
请求转发
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/18 16:11
* @Version 1.0
*/
import com.hspedu.furms.entity.Furn;
import com.hspedu.furms.service.FurnService;
import com.hspedu.furms.service.impl.FurnServiceImpl;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.List;
// 注解的路径:/manage/furnServlet 过滤使用
@WebServlet(name = "FurnServlet", value = "/manage/furnServlet")
// 继承BasicServlet:反射+模板设计模式+动态绑定
// 注意还需要配置前端的action与方法名时相同的
public class FurnServlet extends BasicServlet {
// Servlet层掉Service层
private FurnService furnService = new FurnServiceImpl();
// 显示所有家具-返回的是1个List集合的对象
public void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 家具的信息
List<Furn> furnList = furnService.queryAllFurn();
// 将得到的家具信息,放入到request域,并请求转发
request.setAttribute("furns", furnList);
request.getRequestDispatcher("/views/manage/furn_manage.jsp").forward(request, response);
}
}
furn_manage.jsp
<%--<!DOCTYPE html>--%>
<%--指定页面显示的数据的格式--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="ie=edge"/>
<title>韩顺平教育-家居网购</title>
<%--指定项目根目录--%>
<base href="<%=request.getContextPath()+"/"%>+">
<!-- 移动端适配 -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<link rel="stylesheet" href="assets/css/vendor/vendor.min.css"/>
<link rel="stylesheet" href="assets/css/plugins/plugins.min.css"/>
<link rel="stylesheet" href="assets/css/style.min.css">
</head>
<body>
<!-- Header Area start -->
<div class="header section">
<!-- Header Top End -->
<!-- Header Bottom Start -->
<div class="header-bottom d-none d-lg-block">
<div class="container position-relative">
<div class="row align-self-center">
<!-- Header Logo Start -->
<div class="col-auto align-self-center">
<div class="header-logo">
<a href="index.html"><img src="assets/images/logo/logo.png" alt="Site Logo"/></a>
</div>
</div>
<!-- Header Logo End -->
<!-- Header Action Start -->
<div class="col align-self-center">
<div class="header-actions">
<div class="header_account_list">
<a href="javascript:void(0)" class="header-action-btn search-btn"><i
class="icon-magnifier"></i></a>
<div class="dropdown_search">
<form class="action-form" action="#">
<input class="form-control" placeholder="Enter your search key" type="text">
<button class="submit" type="submit"><i class="icon-magnifier"></i></button>
</form>
</div>
</div>
<!-- Single Wedge Start -->
<div class="header-bottom-set dropdown">
<a href="#">后台管理</a>
</div>
</div>
</div>
<!-- Header Action End -->
</div>
</div>
</div>
<!-- Header Bottom End -->
<!-- Header Bottom Start 手机端的header -->
<div class="header-bottom d-lg-none sticky-nav bg-white">
<div class="container position-relative">
<div class="row align-self-center">
<!-- Header Logo Start -->
<div class="col-auto align-self-center">
<div class="header-logo">
<a href="index.html"><img width="280px" src="assets/images/logo/logo.png" alt="Site Logo"/></a>
</div>
</div>
<!-- Header Logo End -->
</div>
</div>
</div>
<!-- Main Menu Start -->
<div style="width: 100%;height: 50px;background-color: black"></div>
<!-- Main Menu End -->
</div>
<!-- Cart Area Start -->
<div class="cart-main-area pt-100px pb-100px">
<div class="container">
<h3 class="cart-page-title">家居后台管理</h3>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-12">
<form action="#">
<div class="table-content table-responsive cart-table-content">
<table>
<thead>
<tr>
<th>图片</th>
<th>家居名</th>
<th>商家</th>
<th>价格</th>
<th>销量</th>
<th>库存</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="product-thumbnail">
<a href="#"><img class="img-responsive ml-3" src="assets/images/product-image/1.jpg"
alt=""/></a>
</td>
<td class="product-name"><a href="#">Product Name</a></td>
<td class="product-name"><a href="#">蚂蚁家居</a></td>
<td class="product-price-cart"><span class="amount">60.00</span></td>
<td class="product-quantity">
100
</td>
<td class="product-quantity">
80
</td>
<td class="product-remove">
<a href="#"><i class="icon-pencil"></i></a>
<a href="#"><i class="icon-close"></i></a>
</td>
</tr>
<tr>
<td class="product-thumbnail">
<a href="#"><img class="img-responsive ml-3" src="assets/images/product-image/2.jpg"
alt=""/></a>
</td>
<td class="product-name"><a href="#">Product NameProduct Name</a></td>
<td class="product-name"><a href="#">蚂蚁家居</a></td>
<td class="product-price-cart"><span class="amount">60.00</span></td>
<td class="product-quantity">
100
</td>
<td class="product-quantity">
80
</td>
<td class="product-remove">
<a href="#"><i class="icon-pencil"></i></a>
<a href="#"><i class="icon-close"></i></a>
</td>
</tr>
</tbody>
</table>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Cart Area End -->
<!-- Footer Area Start -->
<div class="footer-area">
<div class="footer-container">
<div class="footer-top">
<div class="container">
<div class="row">
<!-- Start single blog -->
<!-- End single blog -->
<!-- Start single blog -->
<div class="col-md-6 col-sm-6 col-lg-3 mb-md-30px mb-lm-30px" data-aos="fade-up"
data-aos-delay="400">
<div class="single-wedge">
<h4 class="footer-herading">信息</h4>
<div class="footer-links">
<div class="footer-row">
<ul class="align-items-center">
<li class="li"><a class="single-link" href="about.html">关于我们</a></li>
<li class="li"><a class="single-link" href="#">交货信息</a></li>
<li class="li"><a class="single-link" href="privacy-policy.html">隐私与政策</a></li>
<li class="li"><a class="single-link" href="#">条款和条件</a></li>
<li class="li"><a class="single-link" href="#">制造</a></li>
</ul>
</div>
</div>
</div>
</div>
<!-- End single blog -->
<!-- Start single blog -->
<div class="col-md-6 col-lg-2 col-sm-6 mb-lm-30px" data-aos="fade-up" data-aos-delay="600">
<div class="single-wedge">
<h4 class="footer-herading">我的账号</h4>
<div class="footer-links">
<div class="footer-row">
<ul class="align-items-center">
<li class="li"><a class="single-link" href="my-account.html">我的账号</a>
</li>
<li class="li"><a class="single-link" href="cart.html">我的购物车</a></li>
<li class="li"><a class="single-link" href="login.html">登录</a></li>
<li class="li"><a class="single-link" href="wishlist.html">感兴趣的</a></li>
<li class="li"><a class="single-link" href="checkout.html">结账</a></li>
</ul>
</div>
</div>
</div>
</div>
<!-- End single blog -->
<!-- Start single blog -->
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="800">
</div>
<!-- End single blog -->
</div>
</div>
</div>
<div class="footer-bottom">
<div class="container">
<div class="row flex-sm-row-reverse">
<div class="col-md-6 text-right">
<div class="payment-link">
<img src="#" alt="">
</div>
</div>
<div class="col-md-6 text-left">
<p class="copy-text">Copyright © 2021 韩顺平教育~</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Footer Area End -->
<script src="assets/js/vendor/vendor.min.js"></script>
<script src="assets/js/plugins/plugins.min.js"></script>
<!-- Main Js -->
<script src="assets/js/main.js"></script>
</body>
</html>
前端
快速定位,并使用 JSP 的 for 循环将数据渲染到页面上
使用 forEach 标签将 tr 标签包裹
在通过 furn.属性名的方式取出数据并填写到指定位置上
<c:forEach items="${requestScope.furns}" var="furn">
<tr>
<td class="product-thumbnail">
<a href="#"><img class="img-responsive ml-3" src="${furn.imgPath}"
alt=""/></a>
</td>
<td class="product-name"><a href="#">${furn.name}</a></td>
<td class="product-name"><a href="#">${furn.maker}</a></td>
<td class="product-price-cart"><span class="amount">${furn.price}</span></td>
<td class="product-quantity">
${furn.sales}
</td>
<td class="product-quantity">
${furn.stock}
</td>
<td class="product-remove">
<a href="#"><i class="icon-pencil"></i></a>
<a href="#"><i class="icon-close"></i></a>
</td>
</tr>
</c:forEach>
测试访问该路径显示家具的信息
http://localhost:8080/furniture/manage/furnServlet?action=list
后端-管理员登录
管理员表的设计
#管理员表
CREATE TABLE IF NOT EXISTS `admin`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT 'id',
`uname` VARCHAR(32) UNIQUE DEFAULT '' COMMENT '用户名',
`pwd` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '密码',
PRIMARY KEY(`id`) #主键id
# INNODB引擎
# CHARSET字符集(保存字符的大小,gbk1个字符占2个字节,utf-8,1个字符占3个字节)
# 校验规则:utf8_bin(区分大小写)
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE utf8_bin
#查询
SELECT * FROM `admin`;
Admin-JavaBean
package com.hspedu.furms.entity;
/**
* ClassName: Admin
* Package: com.hspedu.furms.entity
* Description:
* 管理员对象
*
* @Author 王文福
* @Create 2024/1/18 20:18
* @Version 1.0
*/
public class Admin {
private Integer id;
private String uname;
private String pwd;
// 无参构造器-反射对象
public Admin() {
}
public Admin(Integer id, String uname, String pwd) {
this.id = id;
this.uname = uname;
this.pwd = pwd;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "Admin{" + "id=" + id + ", uname='" + uname + '\'' + ", pwd='" + pwd + '\'' + '}';
}
}
AdminDAO 接口
package com.hspedu.furms.dao;
import com.hspedu.furms.entity.Admin;
import com.hspedu.furms.entity.Member;
/**
* ClassName: AdminDAO
* Package: com.hspedu.furms.dao
* Description:
*
* @Author 王文福
* @Create 2024/1/18 20:20
* @Version 1.0
*/
public interface AdminDAO {
/**
* 查询指定管理员的信息
*
* @param uname: 用户名
* @param pwd: 密码
* @return Admin
* @author "卒迹"
* @description TODO
* @date 20:22
*/
public Admin queryAdminByUnameAndPwd(String uname, String pwd);
}
AdminDAOImp 实现类
package com.hspedu.furms.dao.impl;
import com.hspedu.furms.dao.AdminDAO;
import com.hspedu.furms.dao.BasicDAO;
import com.hspedu.furms.entity.Admin;
/**
* ClassName: AdminDAOImpl
* Package: com.hspedu.furms.dao.impl
* Description:
* 实现类-管理员
*
* @Author 王文福
* @Create 2024/1/18 20:24
* @Version 1.0
*/
public class AdminDAOImpl extends BasicDAO<Admin> implements AdminDAO {
// 查询指定用户-登录
@Override
public Admin queryAdminByUnameAndPwd(String uname, String pwd) {
String sql = "SELECT * FROM `admin` WHERE uname=? AND pwd=?";
return querySingle(sql, Admin.class, uname, pwd);
}
}
AdminDAOImpTest 测试类
package com.hspedu.furms.test;
import com.hspedu.furms.dao.AdminDAO;
import com.hspedu.furms.dao.impl.AdminDAOImpl;
import com.hspedu.furms.entity.Admin;
import org.junit.Test;
/**
* ClassName: AdminDAOImplTest
* Package: com.hspedu.furms.test
* Description:
* 测试类-管理员
*
* @Author 王文福
* @Create 2024/1/18 20:27
* @Version 1.0
*/
public class AdminDAOImplTest {
// 调用DAO层-完成测试
private AdminDAO adminDAO = new AdminDAOImpl();
/**
* 登录功能-测试
*
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 20:30
*/
@Test
public void queryAdminByUnameAndPwd() {
String uname = "admin";
String pwd = "admin";
Admin admin = adminDAO.queryAdminByUnameAndPwd(uname, pwd);
if (admin != null) {
System.out.println("login success");
} else {
System.out.println("login fail");
}
}
}
AdminService 接口
package com.hspedu.furms.service;
import com.hspedu.furms.entity.Admin;
import com.hspedu.furms.entity.Member;
/**
* ClassName: AdminService
* Package: com.hspedu.furms.service
* Description:
* 管理员Service
*
* @Author 王文福
* @Create 2024/1/18 20:34
* @Version 1.0
*/
public interface AdminService {
/**
* 登录:Admin对象通过在Web传入的
*
* @param admin: 在实现类中,通过admin对象得到用户名和密码进行查询,如果为null则返回true,否则为false
* @return boolean
* @author "卒迹"
* @description TODO
* @date 20:35
*/
public boolean queryAdminByUnameAndPwd(Admin admin);
}
AdminServiceImpl 实现类
package com.hspedu.furms.service.impl;
import com.hspedu.furms.dao.AdminDAO;
import com.hspedu.furms.dao.impl.AdminDAOImpl;
import com.hspedu.furms.entity.Admin;
import com.hspedu.furms.service.AdminService;
/**
* ClassName: AdminServiceImpl
* Package: com.hspedu.furms.service.impl
* Description:
*
* @Author 王文福
* @Create 2024/1/18 20:40
* @Version 1.0
*/
public class AdminServiceImpl implements AdminService {
// Service层掉用DAO层
private AdminDAO adminDAO = new AdminDAOImpl();
/**
* 登录
*
* @param admin:在实现类中,通过admin对象得到用户名和密码进行查询,如果不为null则返回true,否则为false
* @return boolean
* @author "卒迹"
* @description TODO
* @date 20:41
*/
@Override
public boolean isLogin(Admin admin) {
return adminDAO.queryAdminByUnameAndPwd(admin.getUname(), admin.getPwd()) != null ? true : false;
}
}
AdminServiceImplTest 测试类
package com.hspedu.furms.test;
import com.hspedu.furms.dao.AdminDAO;
import com.hspedu.furms.entity.Admin;
import com.hspedu.furms.service.AdminService;
import com.hspedu.furms.service.impl.AdminServiceImpl;
import org.junit.Test;
/**
* ClassName: AdminServiceImpTest
* Package: com.hspedu.furms.test
* Description:
*
* @Author 王文福
* @Create 2024/1/18 20:54
* @Version 1.0
*/
public class AdminServiceImpTest {
// 调用Service层进行测试
private AdminService adminService = new AdminServiceImpl();
@Test
public void isLogin() {
String uname = "admin";
String pwd = "admin";
Admin admin = new Admin(null, uname, pwd);
boolean isLogin = adminService.isLogin(admin);
if (isLogin) {// 登录成功
System.out.println("login success");
} else {
System.out.println("login fail");
}
}
}
AdminServlet-Web
登录成功后,请求转发到
/manage/furnServlet?action=list
如果没有登录,则不让访问-需要使用 Session 配合过滤器
package com.hspedu.furms.web; /**
* ClassName: ${NAME}
* Package: ${PACKAGE_NAME}
* Description:
*
* @Author 王文福
* @Create 2024/1/18 21:00
* @Version 1.0
*/
import com.hspedu.furms.entity.Admin;
import com.hspedu.furms.service.AdminService;
import com.hspedu.furms.service.impl.AdminServiceImpl;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "AdminServlet", value = "/adminServlet")
// action与方法名相同
public class AdminServlet extends BasicServlet {
private AdminService adminService = new AdminServiceImpl();
public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 得到用户名和密码(name)
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
// 新建Admin
Admin admin = new Admin(null, username, password);
// 判断是否登录成功
boolean isLogin = adminService.isLogin(admin);
if (isLogin) {// 登录成功
// System.out.println("login success");
// 请求转发:后台管理:显示家具
request.getRequestDispatcher("/manage/furnServlet?action=list").forward(request, response);
// response.sendRedirect(request.getContextPath()+"/manage/furnServlet");
} else {
// System.out.println("login fail");
// 请求转发到管理登录界面,并传入request域
request.setAttribute("error", "用户名或密码有误");
request.getRequestDispatcher("/views/manage/manage_login.jsp").forward(request, response);
}
}
}
前端
manage_login.html 修改为 jsp
name 的 value 值与管理员登录的方法名一致的
添加家具
核心框架图
furn.add.html 改 jsp,并修改基本路径和页面的类型
/views/manage/furn.add.jsp
FurnDAO 接口
添加 1 个 Frun 对象,
返回受影响的行(int) 如果 1 添加成功,否则 添加失败
public boolean add(Furn furn);
public boolean add(Furn furn);
FurnDAOImpl 实现类
继承 BasicDAO<Furn>实现了 FurnDAO 接口
重写实现了接口的方法,并调用父类 BaiscDAO 添加数据的方法
/**
* 添加家具-返回受影响的行
*
* @param furn:
* @return int 1表示添加成功,否则表示失败
* @author "卒迹"
* @description TODO
* @date 15:07
*/
@Override
public boolean add(Furn furn) {
String sql = "INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`) \n" + "VALUES(NULL ,? , ? , ? , ? , ? ,?);";
return update(sql, furn.getName(), furn.getMaker(),
furn.getPrice(), furn.getSales(), furn.getStock(), furn.getImgPath())
== 1 ? true : false;
}
FurnDAOImplTest 测试类
创建 FurnDAOImpl 对象,调用添加数据的方法完成测试
// 添加家具
@Test
public void add() {
// 添加家具
Furn furn = new Furn(null, "温馨风格盆景架", "蚂蚁家居", new BigDecimal(180), 666, 7, "assets/images/product-image/16.jpg");
boolean isAdd = furnDAO.add(furn);
if (isAdd) {
System.out.println("add success");
} else {
System.out.println("add fail");
}
}
FurnService 接口
形参Furn 对象(Web 层传入的)
Service 层方法内部调用 DAO 层添加方法,
根据返回值,判断是否添加成功
public boolean add(Furn furn);
//添加家具
public boolean add(Furn furn);
FurnServiceImpl 实现类
Service 层-调用 FurnDAO 层
//添加家具
@Override
public boolean add(Furn furn) {
return furnDAO.add(furn);
}
FurnServiceImplTest 测试类
//添加家具
@Test
public void add(){
// 添加家具
Furn furn = new Furn(null, "温馨风格盆景架", "蚂蚁家居", new BigDecimal(180), 666, 7, "assets/images/product-image/16.jpg");
boolean isAdd = furnService.add(furn);
if (isAdd) {
System.out.println("add success");
} else {
System.out.println("add fail");
}
}
FurnServlet-Web 层
添加成功后,不要使用请求转发
测试 add 方法的调用
/** action值与方法名相同
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 15:38
*/
public void add(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("add method");
}
Furn_Add.jsp
name = action
value = add 与方法名一致
访问 manage/furnServlet 网址,调用父类 BasicServlet 的 dopost 方法
根据 action 设置的 value 值 add
通过反射+模板设计+动态绑定 调用当前 FunServlet(this) 的 add 方法
访问地址:
添加家具功能:
http://localhost:8080/furniture/manage/furnServlet?action=add
查看后台是否调用了该方法
核心思路
访问 furn.add.jsp 页面
当添加家具后,会通过 post 请求上传表单
通过 add 方法中 request 对象接收到表单的数据
得到的参数取决于 name 的属性的值
然后创建 furn 对象并调用 add 方法将该对象添加到数据库中
并判断添加是否成功,如果成功则调用 list 方法,重新的显示所有的家具
测试点击添加家具后,后台是否收到了表单提交的数据
解决中文乱码问题:
在 BasicServlet 的 dopost 方法中添加以下代码
//设置请求的数据格式:utf-8
request.setCharacterEncoding("utf-8");
/**
* action值与方法名相同
*
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 15:38
*/
public void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
String maker = request.getParameter("maker");
String price = request.getParameter("price");
String sales = request.getParameter("sales");
String stock = request.getParameter("stock");
//上传的图片选择默认的路径
String imgPath = "assets/images/product-image/default.jpg";
//System.out.println("name=" + name);
//System.out.println("maker=" + maker);
//System.out.println("price=" + price);
//System.out.println("sales=" + sales);
//System.out.println("stock=" + stock);
//创建Furn对象,添加到数据库中
Furn furn = new Furn(null, name, maker, new BigDecimal(price),
new Integer(sales), new Integer(stock), imgPath);
boolean isAdd = furnService.add(furn);
if (isAdd) {//添加成功
list(request, response); //重新显示所有家具信息
} else {
//请求转发-添加家具页面
request.setAttribute("error", "添加失败");
request.getRequestDispatcher("/views/manage/furn.add.jsp").
forward(request, response);
}
}
解决表单重复提交的问题
在进行增删改的操作的时候,使用重定向
请求转发的本质:将实现的功能交给其他的 Servlet 去实现
只有 1 次 reuqest 请求,且是同 1 个 requset 请求
当刷新页面的时候,会将添加的功能和显示家具的功能一起执行
重定向:两次 request, 只会执行显示家具的功能
JS 前端的验证
$("#add_furn").click(function () {
//验证价格
var price = $("#add_price").val();
var pricePattern = /^\d+(\.\d{1,2})?$/;
if(!pricePattern.test(price)){
$("#add_prompt").html("<h4>价格输入有误</h4>")
return false;
}
//验证销量-正整数
var sales = $("#add_sales").val();
var salesPattern = /^[1-9]\d*$/;
if(!salesPattern.test(sales)){
$("#add_prompt").html("<h4>销量(sales)不是1个正整数</h4>")
return false;
}
//验证数量-正整数
var stock = $("#add_stock").val();
var stockPattern = /^[1-9]\d*$/;
if(!stockPattern.test(stock)){
$("#add_prompt").html("<h4>库存(stock)不是1个正整数</h4>")
return false;
}
return true; //验证通过
})
后端数据校验说明
为什么需要后端数据校验?
因为人家有可能使用其他的工具
如使用 postman 直接绕过浏览器发出请求,绕过 前端 js 验证
或者修改 js 代码来绕过前端验证
思路分析
1.得到表单提交的数据后使用正则表达式对数据进行校验
一般使用定义的工具类
验证通过-才进行添加
验证没有通过-请求转发回到添加的页面,并提示用户输入的参数有误
package com.hspedu.furms.utils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* ClassName: WebUtils
* Package: com.hspedu.furms.utils
* Description:
*
* @Author 王文福
* @Create 2024/1/19 18:59
* @Version 1.0
*/
public class WebUtils {
/**
* 验证价格
*
* @param price:
* @return boolean
* @author "卒迹"
* @description TODO
* @date 19:01
*/
public static boolean isPrice(String price) {
String pricePattern = "^\\d+(\\.\\d{1,2})?$";
Pattern pattern = Pattern.compile(pricePattern);//正则表达式
Matcher matcher = pattern.matcher(price);//验证价格
//boolean isPrice = matcher.matches();
return matcher.matches();
}
/**
* 验证该数是否是1个正整数
*
* @param number:
* @return boolean
* @author "卒迹"
* @description TODO
* @date 19:04
*/
public static boolean positiveInteger(String number) {
String pricePattern = "^[1-9]\\d*$";
Pattern pattern = Pattern.compile(pricePattern);//正则表达式
Matcher matcher = pattern.matcher(number);//验证
//boolean isPositiveInteger = matcher.matches();
return matcher.matches();
}
}
调用工具类-验证 FurnServlet
前端-furn_add.jsp
删除家具
需求分析
1. 得到表单中的每一项数据
2.使用 update 根据 where 条件将字段滞空
3.重定向(重新显示家具消息)
FurnDAO 接口
//删除家具,根据id号删除
public boolean deleteFurnByID(int id);
FurnDAOImpl 实现类
/**
* 删除家具-删除成功返回1,删除失败返回0
*
* @param id: 根据id删除
* @return boolean
* @author "卒迹"
* @description TODO
* @date 2:37
*/
@Override
public boolean deleteFurnByID(int id) {
String sql = "DELETE FROM `furn` WHERE `id`= ?";
return update(sql, id) == 1 ? true : false;
}
FrunDAOImplTest 测试类
@Test
//删除指定家具
public void deleteFurn() {
int id = 6;
boolean isDelete = furnDAO.deleteFurnByID(id);
if (isDelete) {//删除成功
System.out.println("delete success");
} else {
System.out.println("delete fail");
}
}
FurnService 接口
/** 根据id,删除家具
* @param id:
* @return boolean
* @author "卒迹"
* @description TODO
* @date 2:46
*/
public boolean deleteFurnByID(int id);
FurnServiceImpl 实现类
/** 删除家具-id
* @param id:
* @return boolean
* @author "卒迹"
* @description TODO
* @date 2:46
*/
@Override
public boolean deleteFurnByID(int id) {
return furnDAO.deleteFurnByID(id);
}
FurnServiceImplTest 测试类
/**
* 删除家具-根据id进行删除操作
*
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 23:49
*/
@Test
public void deleteFurnByID() {
int id = 7;
boolean isDelete = furnService.deleteFurnByID(id);
if (isDelete) {
System.out.println("delete success");
} else {
System.out.println("delete fail");
}
}
FurnService 接口
//查询指定家具的信息-id-返回Furn对象
public Furn queryFurnByID(int id);
//修改家具-传入Furn家具对象,返回修改成功或者失败的提示信息
public boolean updateFurnByID(Furn furn);
FurnServiceImpl 实现类
/** 根据id-查询家具信息
* @param id:
* @return Furn
* @author "卒迹"
* @description TODO
* @date 5:55
*/
@Override
public Furn queryFurnByID(int id) {
return furnDAO.queryFurnByID(id);
}
/** 修改家具信息-传入家具对象,并返回修改成功/修改失败的提示信息
* @param furn:
* @return boolean
* @author "卒迹"
* @description TODO
* @date 5:55
*/
@Override
public boolean updateFurnByID(Furn furn) {
return furnDAO.updateFurnByID(furn);
}
FurnServiceImplTest-测试类
/**
* 根据id-查询家具信息
*
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 5:57
*/
@Test
public void queryFurnByID() {
Furn furn = furnService.queryFurnByID(1);
if (furn != null) {
System.out.println(furn);
} else {
System.out.println("furn is null");
}
}
/**根据id-修改家具信息-并传入Furn对象,并通过其属性修改表的字段信息
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 6:00
*/
@Test
public void updateFurnByID(){
Furn furn = new Furn(1, "北欧浣熊风格小桌子", "小浣熊家具", new BigDecimal(180.00), new Integer(666), new Integer(7), "assets/images/product-image/6.jpg");
boolean isUpdate = furnService.updateFurnByID(furn);
if (isUpdate) {//修改成功
System.out.println("update success");
} else {
System.out.println("update fail");
}
}
FurnServlet-Web 层
一、确保能够调用到 Web 层的删除家具的方法
后端
1.在 FurnServlet 中定义删除家具的方法
前端(furn_manage.jsp 家具管理页面)
1.快速定义到删除的按钮,为 a 标签绑定 action 链接
name action 固定写法
value deleteFurnByID 调用方法名
name id 删除的 id 号
value ${furn.id} 删除的 id 号(数据库获取的)
2.当点击 a 链接后,看 FurnServlet 中deleteFurnByID方法是否被调用
3.为删除按钮 a 标签绑定点击事件,并提供确认/取消的弹窗
后端:
1. 得到 id 的值,判断是否是 正整数
如果成立:根据 id 从数据库中删除该家具信息,并重新显示家具信息
如果不成立 请求转发,提示用户,id 参数非法
修改家具
需要得到 id 与修改的参数
update furn
set name=?,maker = ?,price=?,sales=?,stock=?
where id =?;
分析:提供两个方法
1. 根据 id 查询家具的信息
当用户点击修改按钮时,先根据 id 查询当前家具的信息,返回 家具 Furn 对象,通过请求转发将各个数据填充到修改家具的页面 各个input 表单value 中
2. 根据 id 并传入家具 Furn 对象,修改家具信息
当用户修改家具信息时,根据 id 修改家具信息,将所有的数据 封装成 Furn 对象调用 update 方法修改家具,修改完成后重定 向显示家具的页面
FurnDAO 接口
//查询指定家具的信息-id-返回Furn对象
public Furn queryFurnByID(int id);
//修改家具-传入Furn家具对象,返回修改成功或者失败的提示信息
public boolean updateFurnBuID(Furn furn);
FurnDAOImpl 实现类
/**
* 根据id-查询家具
* 查询必须返回id-需要通过id修改家具
* @param id:
* @return boolean
* @author "卒迹"
* @description TODO
* @date 5:00
*/
@Override
public Furn queryFurnByID(int id) {
String sql = "SELECT `id`,`name`,`maker`,`price`,`sales`,`stock`,`img_path` AS imgPath FROM `furn` WHERE id = ?";
return querySingle(sql, Furn.class, id);
}
/**
* 根据id-修改家具
*
* @param furn:
* @return boolean
* @author "卒迹"
* @description TODO
* @date 5:00
*/
@Override
public boolean updateFurnByID(Furn furn) {
String sql = "update `furn` set name=?,maker=?,price=?,sales=?,stock=? where id =?";
return update(sql,furn.getName(),furn.getMaker(),furn.getPrice(),furn.getSales(),
furn.getStock(),furn.getId())==1 ? true : false;
}
FurnDAOImplTest 测试类
/**
* 查询指定家具信息-根据id
*
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 5:19
*/
@Test
public void queryFurnByID() {
Furn furn = furnDAO.queryFurnByID(1);
if (furn != null) {
System.out.println(furn);
} else {
System.out.println("furn is null");
}
}
/**
* 根据id修改指定家具信息
*
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 5:20
*/
@Test
public void updateFurnByID() {
Furn furn = new Furn(1, "北欧风格小桌子", "小浣熊家具", new BigDecimal(180.00), new Integer(666), new Integer(7), "assets/images/product-image/6.jpg");
boolean isUpdate = furnDAO.updateFurnByID(furn);
if (isUpdate) {//修改成功
System.out.println("update success");
} else {
System.out.println("update fail");
}
}
FurnServlet-Web 层
根据 id-显示家具的信息
前端
点击按钮后,跳转到 furnServlet 页面
并调用 showFurn 方法,根据 id 去数据库中查询该家具的信息
href="manage/furnServlet?action=showFurn&id=${furn.id}"
后端代码-是否调用了 ShowFurn 方法,且得到 id
后端-根据 id 查询家具信息并通过请求转发返回 furn 对象
前端-渲染查询家具的数据
当我们点击修改按钮的时候,会将查询的指定 id 的家具信息
渲染到 input 表单上
FurnServlet 中定义 update 的方法
点击修改家具按钮后
通过 From 表单的 action 路径定位到更新/修改家具 update 的方法
1.传 id,根据 id 修改家具
2.指定 action 的值,该值必须与修改家具的方法名相同
当点击修改表单按钮后,验证该方法是否被调用
分页显示
需求分析
将前端请求的当前页数和当前记录数发送给后端
后端根据分页查询的公式返回查询的数据-返回给前端进行展示
因为总的页数是根据总记录数动态变化的-返回总记录给到前端
返回的数据:1.分页的 Furn 集合数据 2.总记录数
核心框架图
1.新建 Page-JavaBean
属性
核心思路
DAO 层分页查询(1)和查询总的记录数(2)
Service 层-将查询到的数据封装到 Page 对象中进行返回
2.FurnDAO 接口
//分页查询-(当前页数-1)*每页显示条数,每页显示条数
/**
* @param currentPage: 当前页数
* @param pageSize:每页显示条数
* @return List<Furn> 返回家具对象集合
* @author "卒迹"
* @description TODO
* @date 16:50
*/
public List<Furn> queryFurnList(int currentPage, int pageSize);
//得到所有家具集合对象,根据集合的长度-返回总记录数
public int queryAllFurnCount();
3.FurnDAOImpl 实体类
/**
* 分页查询-(当前页数-1)*每页显示条数,每页显示条数
*
* @param currentPage:当前页数
* @param pageSize:每页显示条数
* @return List<Furn>:分页查询-返回的家具对象集合
* @author "卒迹"
* @description TODO
* @date 16:50
*/
@Override
public List<Furn> queryFurnList(int currentPage, int pageSize) {
String startPage = Integer.toString((currentPage - 1) * pageSize);
String sql = "select `id`,`name`,`maker`,`price`,`sales`,`stock`,`img_path` AS imgPath from `furn` limit " + startPage + "," + Integer.toString(pageSize);
return queryMulti(sql, Furn.class);
}
/**显示所有家具的总数
* @param :
* @return int
* @author "卒迹"
* @description TODO
* @date 17:54
*/
@Override
public int queryAllFurnCount() {
String sql = "select * from `furn`";
List<Furn> furnList = queryMulti(sql, Furn.class);
return furnList.size();
}
4.FurnDAOImplTest 测试类
/**
* 测试-分页功能
*
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 17:35
*/
@Test
public void queryFurnList() {
List<Furn> furnList = furnDAO.queryFurnList(2, 3);
for (Furn furn : furnList) {
System.out.println(furn);
}
}
/**
* 测试查询-家具的总数
*
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 17:54
*/
@Test
public void queryAllFurnCount() {
System.out.println(furnDAO.queryAllFurnCount());
}
5.FurnService 层
/**
* 外部传入当前页数和显示条数
* 并调用DAO层查询的方法-得到查询分页的数据-和总的数量
* 初始化Page对象,并返回
*
* @param currentPage:
* @param pageSize:
* @return Page
* @author "卒迹"
* @description TODO
* @date 18:00
*/
public Page showPage(int currentPage, int pageSize);
6.FurnServiceImpl 实现类
/** 将查询到的数据封装到Page对象中并返回
* @param currentPage:
* @param pageSize:
* @return Page
* @author "卒迹"
* @description TODO
* @date 18:08
*/
@Override
public Page showPage(int currentPage, int pageSize) {
//查询分页的数据DAO
List<Furn> furnList = furnDAO.queryFurnList(currentPage, pageSize);
//查询家具总数量DAO
int furnCount = furnDAO.queryAllFurnCount();
return new Page(currentPage, pageSize, furnCount, furnList);
}
7.FurnServiceImplTest 测试类
/**
* page对象包含的属性
* 1.currentPage:当前页数
* 2.pageSize:每页显示几页
* 3.totalPageSize:总的家具数量
* 4.返回的分页的家具对象集合
*
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 18:13
*Page{currentPage=2, pageSize=3, totalPageSize=15,
furnList=[Furn{id=4, name='温馨风格盆景架', maker='蚂蚁家居', price=180.00, sales=666, stock=7, imgPath='assets/images/product-image/16.jpg'}
*/
@Test
public void showPage() {
Page page = furnService.showPage(2, 3);
System.out.println(page);
}
8.FurnServlet-Web 层
/**
* 分页显示
*
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 22:01
*/
public void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.得到两个参数,1.当前处于第几页,
//参数有误-默认显示第1页
int currentPage = DataUtils.parseInt(request.getParameter("currentPage"), 1);
System.out.println("currentPage="+currentPage);
//2.每页显示多少条记录
//参数有误/不传该参数,默认一页显示3条数据
int pageSize = DataUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
//得到Page对象
Page<Furn> page = furnService.showPage(currentPage, pageSize);
//System.out.println("page=" + page);
//使用request域请求转发
request.setAttribute("page",page);
request.getRequestDispatcher("/views/manage/furn_manage.jsp").forward(request, response);
}
前端(manage_menu.jsp)
修改 a 标签,并通过分页进行显示
action=page 调用 page 方法
currentPage=1 从第 1 页开始显示
pageSize 默认不传,则 按照 Page 对象设置的常量的给定的默认值 3
测试-点击家居管理-看后台是否请求到了数据
使用请求转发的方式-将 Page 对象传入该页面
furn_manage.jsp
取出 page 对象中的 items 属性 List<Furn>
9. 后台分页导航条
分析:把主页 index 的模拟分页的样式的导航栏,拷贝到显示数据
<!-- Pagination Area Start -->
<div class="pro-pagination-style text-center mb-md-30px mb-lm-30px mt-6" data-aos="fade-up">
<ul>
<li><a href="#">上页</a></li>
<li><a class="active" href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li><a href="#">下页</a></li>
</ul>
</div>
<!-- Pagination Area End -->
上一页和下一页、共多少页 (83)
分析: 上一页= 当前页-1 并且当前页>1 才进行显示
下一页= 当前页+1 并且当前页<总页数 才进行显示
当前页-显示对应家具的信息(84)
修改家具-返回原页(85)
furn_manage.jsp
点击修改家具-需要将当前分页所在页 currentPage 传入
修改家居页面 furn_update.jsp
currentPage 参数会传给 showFurn 这个方法,
如果是是请求转发-直接在 furn_upate.jsp 页面中通过
当用户点击修改家居按钮后,将该 数传递给 update 方法,
如果是请求转发直接使用{param.currentPage}接收
update 方法-接收 currentPage 属性
当前用户修改成功后,根据当 currentPage 属性显示分页数据
删除、添加返回原页(86)
点击删除按钮,把当前页 currentPage 参数,
传递到 deleteFurnByID 这个方法
在deleteFurnByID 接收到该参数,
删除成功后调用分页的功能进行展示
添加后返回原页-furn_manage.jsp
1.点击添加家居后,把当前页的参数带过去
2.当用户点击添加家居后,把当前页的参数 currentPage 带过去
因为是使用表单 from 进行提交的,所以需要设置 input 表单将数据带过去
在 add 方法中接收该数据
3.在 add 方法中接收该数据
添加成功后,重定向会到分页页面
10. 首页分页
需求分析
核心框架图
渲染首页数据-分页
有两个 index.jsp
1.在 Web 根路径下
2.在views/customers/路径下
默认启动项目,访问根路径下的 index.jsp
通过请求转发-访问 CustomerFurnServlet
index.jsp
<%--使用循环遍历,取出page对象中集合items,取出当前furn对象--%>
<c:forEach items="${requestScope.page.items}" var="furn">
<div class="col-lg-3 col-md-6 col-sm-6 col-xs-6 mb-6" data-aos="fade-up"
data-aos-delay="200">
<!-- Single Prodect -->
<div class="product">
<div class="thumb">
<a href="shop-left-sidebar.html" class="image">
<img src="${furn.imgPath}" alt="Product"/>
<img class="hover-image" src="assets/images/product-image/5.jpg"
alt="Product"/>
</a>
<span class="badges">
<span class="new">New</span>
</span>
<div class="actions">
<a href="#" class="action wishlist" data-link-action="quickview"
title="Quick view" data-bs-toggle="modal" data-bs-target="#exampleModal"><i
class="icon-size-fullscreen"></i></a>
</div>
<button title="Add To Cart" class=" add-to-cart">Add
To Cart
</button>
</div>
<div class="content">
<h5 class="title">
<a href="shop-left-sidebar.html">${furn.name} </a></h5>
<span class="price">
<span class="new">家居:${furn.name}</span>
</span>
<span class="price">
<span class="new">厂商: ${furn.maker}</span>
</span>
<span class="price">
<span class="new">价格: ¥${furn.price}</span>
</span>
<span class="price">
<span class="new">销量: ${furn.sales}</span>
</span>
<span class="price">
<span class="new">库存: ${furn.stock}</span>
</span>
</div>
</div>
</div>
</c:forEach>
分页-显示导航
首页搜索
根据家居名-分页
1.在 DAO 层-Service 层 增加查询的功能-根据名称查询后返回家居集合
2.在 DAO 层-Service 层 增加查询的功能-根据名称查询该家居的数量
1.FurnDAO 接口
//根据名字-查询家居的数量
public int queryCountByName(String name);
// //根据名字-查询家居的数量-分页显示
public List<Furn> queryFurnListByName(int currentPage, int pageSize, String name);
2.FurnDAOImpl 实现类
/**
* 根据名字-获取数量
*
* @param name:
* @return int
* @author "卒迹"
* @description TODO
* @date 22:42
*/
@Override
public int queryCountByName(String name) {
String sql = "SELECT COUNT(*) FROM `furn` WHERE `name` LIKE ?";
return ((Number) (queryScalar(sql, "%" + name + "%"))).intValue();
}
/**
* 分页显示-指定名称的家居信息
*
* @param currentPage:
* @param pageSize:
* @param name:
* @return List<Furn>
* @author "卒迹"
* @description TODO
* @date 23:00
*/
@Override
public List<Furn> queryFurnListByName(int currentPage, int pageSize, String name) {
String sql = "SELECT `id`,`name`,`maker`,`price`,`sales`,`stock`,`img_path` AS imgPath" +
" FROM `furn` WHERE `name` LIKE ? LIMIT ?,?";
int startPage = (currentPage - 1) * pageSize;
return queryMulti(sql, Furn.class, "%" + name + "%", startPage, pageSize);
}
3.FurnDAOImplTest 测试类
/**测试-根据名字获取指定家居的数量
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 22:46
*/
@Test
public void queryCountByName(){
int queryNumber = furnDAO.queryCountByName("Name");
System.out.println(queryNumber);
}
/** 查询指定的家居,并返回1个对象集合
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 22:52
*/
@Test
public void queryAllFurnByName() {
List<Furn> furnList = furnDAO.queryFurnListByName(1,3,"Name");
for (Furn furn : furnList) {
System.out.println(furn);
}
}
4.FurnService 接口
/** 根据名字-查询家居-并且查询指定页数的家居
* @param currentPage:
* @param pageSize:
* @param name:
* @return Page<Furn>
* @author "卒迹"
* @description TODO
* @date 23:16
*/
public Page<Furn> showPageByName(int currentPage, int pageSize,String name);
5.FurnServiceImpl 实现类
//通过家居名分页显示,该家居名的家居信息
@Override
public Page<Furn> getPageByName(int pageNo, int pageSize, String name) {
Page<Furn> page = new Page<>();
//当前是第几页
page.setPageNo(pageNo);
//一页显示多少条记录
page.setPageSize(pageSize);
//一共多少条记录(改)
int totalRecord = furnDAO.queryCountByName(name);
page.setPageTotalCount(totalRecord);
//一共多少页
int totalPageNum = (totalRecord + pageSize - 1) / pageSize;
page.setPageTotalRow(totalPageNum);
//一页显示的每一个家居对象的信息的集合(改)
List<Furn> items = furnDAO.queryFurnListByName(pageNo, pageSize, name);
page.setItems(items);
//返回Page对象
return page;
}
6.FurnServiceImplTest 测试类
/**根据家居名分页显示,该家居名的家居信息
* @param :
* @return void
* @author "卒迹"
* @description TODO
* @date 17:18
*/
@Test
public void getPageByName(){
Page<Furn> page = furnService.getPageByName(1, 3,"Java");
System.out.println(page);
}
7.FurnServlet-Web 层(95)
/** 根据名字-查询指定页数家居的信息
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 23:50
*/
public void pageByName(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.得到两个参数,1.当前处于第几页,
//参数有误-默认显示第1页
int currentPage = DataUtils.parseInt(request.getParameter("currentPage"), 1);
//System.out.println("currentPage="+currentPage);
//2.每页显示多少条记录
//参数有误/不传该参数,默认一页显示3条数据
int pageSize = DataUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
//家居名
String name = request.getParameter("name");
//得到Page对象
Page<Furn> page = furnService.showPageByName(currentPage, pageSize,name);
//System.out.println("page=" + page);
//使用request域请求转发
request.setAttribute("page",page);
request.getRequestDispatcher("/views/customer/index.jsp").forward(request, response);
}
分析:如果 name 不带任何参数请求,默认是""空的字符串
相当于查询所有的数据-
SELECT * FROM ` furn ` WHERE ` name ` LIKE %%;
如果 name 都不带,name=null
相当于查询的数据为空-
SELECT * FROM ` furn ` WHERE ` name ` LIKE %null%;
需求:希望带不带都返回所有的数据,加入判断当 name=null name="";
8. 搜索框的检索
分析:前端通过搜索,点击后需要将提交的数据-其实还是在访问网址
只不过需要得到提交的数据带上即可
当用户搜索,输入数据,点击提交后:
表单则通过 action 提交数据
from 访问的 action 地址:manage/furnServlet
后面?的参数需要通过提交 input 表单设置 name 值将参数带过去
9.分页显示检索数据(97)
1.定位到下页按钮
分析:当点击下一页
访问的链接:
name 的值(搜索的数据)怎么得到?
方案 1:
判断 name 的值是否为空字符串,成立则将访问的地址通过字符串拼接
赋值给 page 的属性 url
在 request 中进行设置即可
index.jsp-将 url 替换字符串原地址
首页导航-page 方法-设置 url 地址-全局搜索
或者将 index.jsp 请求的地址为:不带 name,默认全局搜索
解决两个问题(98)
问题 1:
当我们访问 furn_manage.jsp 页面的时候,
发现 customerFurnServlet 被调用
原因是因为:在 furn_manage.jsp 页面中 img 图标设置了#导致的
#根据 base 标签的 herf 设置的路径访问
这会导致请求到项目根目录的 index.jsp 页面,
会导致 请求转发到了 如下 页面从而导致该 Servlet 的 page 方法的调用
解决方案:如果不使用,则将该标签进行注释即可
问题 2:
原因是因为:
currentPage 和 pageSize 没有请求该传参(地址带参数)导致的
在处理转换的时候出现了这个问题 null,正常的现象
登录成功显示-登录名
分析:
1.登录成功后,在 login.ok.jsp 页面显示登录的用户名以及其他信息
2。用户点击登录成功,返回首页 index.jsp
注意该 index.jsp 为网址根目录的 index,因为需要去后台拿到数据
再去显示到 views/customer/index.jsp
如果该用户登录过,则显示
如果没有登录过,则显示
核心框架图
<!-- Header Action Start -->
<div class="col align-self-center">
<div class="header-actions">
<div class="header-bottom-set dropdown">
<span>欢迎:${sessionScope.member.username}</span>
<%--<a href="pages/manager/manager.html"></a>--%>
</div>
<!-- Single Wedge Start -->
<div class="header-bottom-set dropdown">
<%--<a href="manage/furnServlet?action=list">家居管理</a>--%>
<a href="manage/furnServlet?action=page¤tPage=1">订单管理</a>
</div>
<div class="header-bottom-set dropdown">
<a href="pages/manager/manager.html">安全退出</a>
</div>
</div>
</div>
<!-- Header Action End -->
index.jsp 中
判断该会员的 session 是否为空来显示对应的标签
<!-- Header Logo End -->
<c:if test="${sessionScope.member==null}">
<!-- Header Action Start -->
<div class="col align-self-center">
<div class="header-actions">
<div class="header_account_list">
<a href="javascript:void(0)" class="header-action-btn search-btn"><i
class="icon-magnifier"></i></a>
<div class="dropdown_search">
<form class="action-form" action="customerFurnServlet">
<%--携带的参数--%>
<%--name携带的参数 value:值--%>
<input type="hidden" name="currentPage" value="1">
<input type="hidden" name="pageSize" value="3">
<%--搜索的关键字-name的值必须为name--%>
<input class="form-control" placeholder="Enter your search key" name="name"
type="text">
<%--调用的方法--%>
<%-- action:方法,value:方法名--%>
<input type="hidden" name="action" value="pageByName">
<button class="submit" type="submit"><i class="icon-magnifier"></i></button>
</form>
</div>
</div>
<!-- Single Wedge Start -->
<div class="header-bottom-set dropdown">
<a href="views/member/login.html">登录|注册</a>
</div>
<div class="header-bottom-set dropdown">
<a href="#">后台管理</a>
</div>
<!-- Single Wedge End -->
<a href="#offcanvas-cart"
class="header-action-btn header-action-btn-cart offcanvas-toggle pr-0">
<i class="icon-handbag"> 购物车</i>
<span class="header-action-num">88</span>
</a>
<a href="#offcanvas-mobile-menu"
class="header-action-btn header-action-btn-menu offcanvas-toggle d-lg-none">
<i class="icon-menu"></i>
</a>
</div>
</div>
<!-- Header Action End -->
</c:if>
<c:if test="${sessionScope.member!=null}">
<!-- Header Action Start -->
注销登录
1.核心思路-当用户点击注销按钮后,
根据 session 的用户名销毁对应的 session 对象即可
核心框架图
点击 a 超链接-调用 memberServlet 中的 loginOut 方法
/**
* 注销功能
*
* @param request:
* @param response:
* @return void
* @author "卒迹"
* @description TODO
* @date 20:31
*/
public void loginOut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (request.getSession() != null) {
request.getSession().invalidate();//删除/销毁当前用户的Session
//销毁成功后,重定向回到首页
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
}
注册码验证
程序框架图
1.浏览器→请求验证码-→服务器
服务器→响应验证码→浏览器
2.服务器→验证码(文本)→保存到 Session 中
3.前端的注册信息的校验完毕后,会将注册信息提交到服务器
4.服务器从 Session 中取出验证码,并立即删除,防止表单重复提交
5.与前端提交的验证码进行比较,如果相等则继续,不相等返回注册失败 的提示信息
验证码-配置 KapchaServlet(107-108)
1.配置 Web.xml
<servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptchaServlet</url-pattern>
</servlet-mapping>
2.img 标签配置KaptchaServlet
<input type="text" id="code" name="code" style="width: 50%"
placeholder="验证码"/> <img id="codeImg" alt="" src="kaptchaServlet"
style="width: 120px;height: 50px">
3.为验证码绑定点击事件-生成新的验证码
//对验证码图片进行处理, 绑定一个点击事件,可以获取新的验证码
$("#codeImg").click(function () {
//先死后活
//在url没有变化时候,图片不会发出新的请求
//为了防止不请求,不刷新, 可以携带一个变化参数
this.src = "<%=request.getContextPath()%>/kaptchaServlet?d=" + new Date();
})
4.前端验证码不为空
// 验证码:浏览器这里验证不能为空
var codeText = $("#code").val();
//去掉验证码前后空格
codeText = $.trim(codeText);
if (codeText == null || codeText == "") {
//提示
$("span.errorMsg").text("验证码不能为空!");
return false;
}
5.后台校验验证码
6.验证码处理前端显示(111)
验证不通过后,显示的还是登录页面,不是显示在注册页面并提示用户信息
验证不通过,后端通过传入 1 个域对象 active 的值判断
然后模拟点击注册按钮即可
7.验证码不正确-回显注册信息
设置域对象:验证码不通过
前端使用:requset 接收回显数据