家具购-JavaWeb项目实战

项目结构

项目搭建

静态页面的搭建

配置 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. 定义 1 个 BasicServlet 抽象类,

           定义抽象方法 login 和 register

           定义 dopost 方法,在该方法中调用定义的抽象方法

  1. 当子类继承了抽象类,重写抽象方法
  2. 通过反射创建抽象类实例,调用 dopost 方法
  3. 根据动态绑定机制,调用子类的方法

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 域名+端口号

  1. 项目名:furniture
  2. 访问的 Servlet 路径:/manage/furnServlet
  3. get 请求: ?
  4. 调用的 Servlet 的哪个方法:list
  5. 看后台是否调用了该方法
请求转发

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 &copy; 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 furnset 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)

http://localhost:8080/furniture/customerFurnServlet?currentPage=1&pageSize=3&name=Java&action=pageByName

 /** 根据名字-查询指定页数家居的信息
     * @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. 搜索框的检索

分析:前端通过搜索,点击后需要将提交的数据-其实还是在访问网址

只不过需要得到提交的数据带上即可

当用户搜索,输入数据,点击提交后:

http://localhost:8080/furniture/customerFurnServlet?currentPage=1&pageSize=3&name=得到表单的数据&action=pageByName

表单则通过 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&currentPage=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 接收回显数据

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值