一、项目开发流程
程序框架图
项目具体分层方案
MVC
1、说明是MVC
MVC全称: Mode模型、View视图、Controller控制器。
MVC最早出现在JavaEE三层中的Web层,它可以有效的指导WEB层的代码如何有效分离,单独工作。
View视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工(Vue/JSP/Thymeleaf/HTML)
Controller控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个"调度者"的角色(Servlet)
Model模型:将与业务逻辑相关的数据封装为具体的JavaBean类,其中不掺杂任何与数据处理相关的代码(JavaBean/Domain/Pojo)
2、MVC是一种思想
MVC的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了解耦合) ,也有很多落地的框架比如SpringMVC
二、实现1 正确加载静态页面
前端人员给的静态页面
三、实现功能02 会员注册前端JS校验
1.验证用户名:必须字母,数字下划线组成,并且长度为6到10位 => JQuery + 正则表达式
2.验证密码:必须由字母,数字下划线组成,并且长度为6到10位
3.邮箱格式验证:常规验证即可
4.验证码:后面实现
<script type="text/javascript" src = "../../script/jquery-3.6.0.min.js"></script>
<script>
$(function () {//页面加载完成后执行function
//绑定点击事件
$("#sub-btn").click(function (){
//获取输入的用户名 => 自己看前端给的页面
var $userName = $("#username").val();
// alert("usernameVal = " + $userName)
//使用正则表达式进行验证
var usernamePattern = /^\w{6,10}$/;
if(!usernamePattern.test($userName)){
//查看前端给的代码是否有错误提示信息
// <span class="errorMsg"
// style="float: right; font-weight: bold; font-size: 20pt; margin-left: 10px;"></span>
//jquery的属性过滤器
$("span[class = 'errorMsg']").text("用户格式不对 需要6-10字符");
return false;//不提交 需要返回false 不返回false会导致页面跳转
}
var password = $("#password").val();
var passwordPattern = /^\w{6,10}$/;
if(!passwordPattern.test(password)){
//jquery的基本过滤器
$("span.errorMsg").text("密码格式不对 需要6-10字符");
return false;
}
//验证两次密码是否正确
var repwd = $("#repwd").val();
if(repwd != password){
$("span.errorMsg").text("输入的两次密码不同");
return false;
}
var email = $("#email").val();
var emailPattern = /^[\w-]+@([a-zA-Z]+\.)+[a-zA-Z]+$/;
if(!emailPattern.test(email)){
$("span[class = 'errorMsg']").text("电子邮件格式不对");
return false;
}
//到这里就全部过关 => 我们暂时不提交
$("span.errorMsg").text("验证通过");
return false;
})
})
</script>
四、实现功能03 - 会员注册后端
导入mhl使用的BasicDAO以及JDBCbyDruid 测试是否成功
BasicDAO
package com.yinhai.furns.dao;
import com.yinhai.furns.utils.JDBCUtilsByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* @author 韩顺平
* @version 1.0
* 开发BasicDAO , 是其他DAO的父类
*/
public class BasicDAO<T> { //泛型指定具体类型
private QueryRunner qr = new QueryRunner();
//开发通用的dml方法, 针对任意的表
public int update(String sql, Object... parameters) {
Connection connection = null;
try {
//这里从数据库连接池获取connection
//老师说明: 每次从连接池中取出Connection , 不能保证是同一个
//1.我们目前已经是从当前线程关联的ThreadLocal获取的connection
//2.所以我们可以保证是同一个连接(在同一个线程/同一个请求)
connection = JDBCUtilsByDruid.getConnection();
int update = qr.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常->运行异常 ,抛出
}
}
//返回多个对象(即查询的结果是多行), 针对任意表
/**
*
* @param sql sql 语句,可以有 ?
* @param clazz 传入一个类的Class对象 比如 Actor.class
* @param parameters 传入 ? 的具体的值,可以是多个
* @return 根据Actor.class 返回对应的 ArrayList 集合
*/
public List<T> queryMulti(String sql, Class<T> clazz, Object... parameters) {
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new BeanListHandler<T>(clazz), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常->运行异常 ,抛出
}
}
//查询单行结果 的通用方法
public T querySingle(String sql, Class<T> clazz, Object... parameters) {
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常->运行异常 ,抛出
}
}
//查询单行单列的方法,即返回单值的方法
public Object queryScalar(String sql, Object... parameters) {
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new ScalarHandler(), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常->运行异常 ,抛出
}
}
}
JDBC工具类
package com.yinhai.furns.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;
//定义属性ThreadLocal, 这里存放一个Connection
private static ThreadLocal<Connection> threadLocalConn =
new ThreadLocal<>();
//在静态代码块完成 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();
//}
/**
* 从ThreadLocal获取connection, 从而保证在同一个线程中,
* 获取的是同一个Connection
* @return
* @throws SQLException
*/
public static Connection getConnection() {
Connection connection = threadLocalConn.get();
if (connection == null) {//说明当前的threadLocalConn没有连接
//就从数据库连接池中,取出一个连接放入, threadLocalConn
try {
connection = ds.getConnection();
//将连接设置为手动提交, 即不要自动提交
connection.setAutoCommit(false);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
threadLocalConn.set(connection);
}
return connection;
}
/**
* 提交事务, java基础 mysql事务+线程+过滤器机制+ThreadLocal
*/
public static void commit() {
Connection connection = threadLocalConn.get();
if(connection != null) {//确保该连接是有效
try {
connection.commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
connection.close();//关闭连接
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//老师说明
//1. 当提交后,需要把connection从 threadLocalConn 清除掉
//2. 不然,会造成 threadLocalConn 长时间持有该连接, 会影响效率
//3. 也因为我们Tomcat底层使用的是线程池技术
threadLocalConn.remove();
}
/**
* 老师说明: 所谓回滚,是回滚/撤销和 connection管理的操作删掉,修改,添加
*/
public static void rollback() {
Connection connection = threadLocalConn.get();
if(connection != null) {//保证当前的连接是有效
try {
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
threadLocalConn.remove();
}
//关闭连接, 老师再次强调: 在数据库连接池技术中,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);
}
}
}
实现Member的功能 查询等
一个规范接口
package com.yinhai.furns.dao;
import com.yinhai.furns.javabean.Member;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
public interface MemberDAO {
//小伙伴要自己分析,需要哪些方法
//提供一个通过用户名返回对应的Member
public Member queryMemberByUsername(String username);
//提供一个保存Member对象到数据库/表member表
public int saveMember(Member member);
/**
* 根据用户名和密码返回Member
* @param username 用户名
* @param password 密码
* @return 返回的对象,如果不存在,返回null
*
*/
public Member queryMemberByUsernameAndPassword
(String username, String password);
}
实现Member的功能 查询 添加等
package com.yinhai.furns.dao.impl;
import com.yinhai.furns.dao.BasicDAO;
import com.yinhai.furns.dao.MemberDAO;
import com.yinhai.furns.javabean.Member;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
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`\n" +
"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);
}
}
测试
package com.yinhai.furns.test;
import com.yinhai.furns.dao.MemberDAO;
import com.yinhai.furns.dao.impl.MemberDAOImpl;
import com.yinhai.furns.javabean.Member;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
public class MemberTest {
public static void main(String[] args) {
MemberDAO memberDAO = new MemberDAOImpl();
if (memberDAO.queryMemberByUsername("admin") == null){
System.out.println("null");
}else {
System.out.println("exist");
}
Member member = new Member(null,"jack","jack","135465@qq.com");
if(memberDAO.saveMember(member) == 0){
System.out.println("添加失败");
}else{
System.out.println("添加成功");
}
}
}
服务端 提供注册
package com.yinhai.furns.test;
import com.yinhai.furns.javabean.Member;
import com.yinhai.furns.service.MemberService;
import com.yinhai.furns.service.impl.MemberServiceImpl;
import com.yinhai.furns.utils.JDBCUtilsByDruid;
import org.junit.jupiter.api.Test;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
public class MemberServiceTest {
public static void main(String[] args) {
MemberService memberService = new MemberServiceImpl();
if (memberService.isExistsUsername("admin")){
System.out.println("用户存在");
}else{
System.out.println("用户不存在");
}
}
@Test
public void test(){
Member member = new Member(null, "tom", "tom", "643013242@qq.com");
MemberService memberService = new MemberServiceImpl();
boolean b = memberService.registerMember(member);
if (b){
System.out.println("注册成功");
}else{
System.out.println("注册失败");
}
JDBCUtilsByDruid.commit();
}
}
一个Servlet提供前后端交流
注册成功 请求转发到注册完成的页面
package com.yinhai.furns.web;
import com.yinhai.furns.javabean.Member;
import com.yinhai.furns.service.MemberService;
import com.yinhai.furns.service.impl.MemberServiceImpl;
import com.yinhai.furns.utils.JDBCUtilsByDruid;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
@WebServlet(name = "RegisterServlet",urlPatterns = "/registerServlet")
public class RegisterServlet extends HttpServlet {
private MemberService memberService = new MemberServiceImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
String email = request.getParameter("user-email");
if(!memberService.isExistsUsername(username)){
System.out.println(username + "用户可用" + password + email);
Member member = new Member(null,username,password,email);
if (memberService.registerMember(member)) {
request.getRequestDispatcher("/views/member/register_ok.jsp").forward(request,response);
JDBCUtilsByDruid.commit();
}else {
request.getRequestDispatcher("/views/member/login_ok.jsp").forward(request,response);
}
}else{
// System.out.println("用户不存在 返回注册页面");
request.getRequestDispatcher("/views/member/register_fail.jsp").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
五、实现功能04-会员登录
1.输入用户名、密码后提交
2.判断会员是否存在
3.会员存在(数据库),显示登录成功页面
在DAO类编写对数据库操作 也需要在父类以及接口内写该方法
public class MemberDAOImpl extends BasicDAO<Member> implements MemberDAO {
@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);
}
}
在Service调用该方法
public class MemberServiceImpl implements MemberService {
@Override
public Member login(Member member) {
//返回对象
return memberDAO.queryMemberByUsernameAndPassword
(member.getUsername(), member.getPassword());
}
}
LoginServlet 用于验证前端
package com.yinhai.furns.web;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.yinhai.furns.javabean.Member;
import com.yinhai.furns.service.MemberService;
import com.yinhai.furns.service.impl.MemberServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
@WebServlet(name = "LoginServlet",urlPatterns = "/loginServlet")
public class LoginServlet extends HttpServlet {
private MemberService memberService = new MemberServiceImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
if (!memberService.isExistsUsername(username)){
System.out.println("不存在 请重新登录");
request.getRequestDispatcher("/views/member/login.html").forward(request,response);
return;
}
if(memberService.login(new Member(null,username,password,null)) == null){
System.out.println("密码错误 重新登录");
request.getRequestDispatcher("/views/member/login.html").forward(request,response);
return;
}
System.out.println("登录成功");
request.getRequestDispatcher("/views/member/login_ok.jsp").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
六、实现功能05 - 登录错误提示 表单回显
在LoginServlet内添加回显以及消息提示
@WebServlet(name = "LoginServlet",urlPatterns = "/loginServlet")
public class LoginServlet extends HttpServlet {
private MemberService memberService = new MemberServiceImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
if (!memberService.isExistsUsername(username)){
System.out.println("用户名或者密码错误");
request.setAttribute("msg","用户名或者密码错误");
request.setAttribute("user-name",username);
request.getRequestDispatcher("/views/member/login.jsp").forward(request,response);
return;
}
if(memberService.login(new Member(null,username,password,null)) == null){
System.out.println("密码错误 重新登录");
request.setAttribute("msg","密码错误 重新登录");
request.setAttribute("user-name",username);
request.getRequestDispatcher("/views/member/login.jsp").forward(request,response);
return;
}
System.out.println("登录成功");
request.getRequestDispatcher("/views/member/login_ok.jsp").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
将前段页面改为jsp 并使用EL表达式
<div id="lg1" class="tab-pane active">
<div class="login-form-container">
<div class="login-register-form">
<span style="float: right; font-weight: bold; font-size: 20pt; margin-left: 10px;">
${msg}
</span>
添加动态显示用户名
在对应的地方使用el表达式即可
七、实现功能06 - 合并servlet
1.使用if else解决
使用switch - case进行判断
package com.yinhai.furns.web;
import com.yinhai.furns.javabean.Member;
import com.yinhai.furns.service.MemberService;
import com.yinhai.furns.service.impl.MemberServiceImpl;
import com.yinhai.furns.utils.JDBCUtilsByDruid;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
@WebServlet(name = "MemberServlet", urlPatterns = "/memberServlet")
public class MemberServlet extends HttpServlet {
private MemberService memberService = new MemberServiceImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
String email = request.getParameter("user-email");
String action = request.getParameter("action");
System.out.println(action);
switch (action) {
case "login":
if (!memberService.isExistsUsername(username)) {
System.out.println("用户不存在");
request.setAttribute("msg", "用户不存在");
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
return;
}
if (memberService.login(new Member(null, username, password, null)) == null) {
System.out.println("密码错误 重新登录");
request.setAttribute("msg", "密码错误 重新登录");
request.setAttribute("username", username);
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
return;
}
System.out.println("登录成功");
request.getRequestDispatcher("/views/member/login_ok.jsp").forward(request, response);
break;
case "register":
if (!memberService.isExistsUsername(username)) {
System.out.println(username + "用户可用" + password + email);
Member member = new Member(null, username, password, email);
if (memberService.registerMember(member)) {
request.getRequestDispatcher("/views/member/register_ok.jsp").forward(request, response);
JDBCUtilsByDruid.commit();
} else {
request.getRequestDispatcher("/views/member/register_fail.jsp").forward(request, response);
}
} else {
System.out.println("用户存在 无法注册");
request.setAttribute("msg", "用户存在 无法注册");
request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
}
break;
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
2.反射 + 模版设计 + 动态绑定
package com.yinhai.furns.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
public abstract class BasicServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决接收到的数据中文乱码问题
request.setCharacterEncoding("utf-8");
//System.out.println("BasicServlet doPost()");
//获取到action的值
//老韩提示:如果我们使用了模板模式+反射+动态绑定,要满足action的value 和 方法名一致!!!
String action = request.getParameter("action");
//System.out.println("action=" + action);
//使用反射,获取当前对象的方法
//老韩解读
//1.this 就是请求的Servlet
//2.declaredMethod 方法对象就是当前请求的servlet对应的"action名字" 的方法, 该方法对象(declaredMethod)
// 是变化的,根据用户请求
//3.使用模板模式+反射+动态机制===> 简化多个 if--else if---..
try {
Method declaredMethod =
this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
System.out.println("declaredMethod=" + declaredMethod);
//使用方法对象,进行反射调用
declaredMethod.invoke(this, request, response);
} catch (Exception e) {
//java基础->异常机制
//将发生的异常,继续throw
//老师心得体会: 异常机制是可以参与业务逻辑
throw new RuntimeException(e);
}
}
}
然后子类servlet继承该Servlet 随后只写login以及register方法即可
package com.yinhai.furns.web;
import com.yinhai.furns.javabean.Member;
import com.yinhai.furns.service.MemberService;
import com.yinhai.furns.service.impl.MemberServiceImpl;
import com.yinhai.furns.utils.JDBCUtilsByDruid;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
@WebServlet(name = "MemberServlet", urlPatterns = "/memberServlet")
public class MemberServlet extends BasicServlet {
private MemberService memberService = new MemberServiceImpl();
/**
* 处理会员的注册
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("======MemberServlet register()========");
//接收用户注册信息-> 一定要去看前端页面字段..
//用户名
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
String email = request.getParameter("user-email");
//获取用户提交验证码
String code = request.getParameter("code");
//从session中获取到验证码
// String token = (String) request.getSession().getAttribute(KAPTCHA_SESSION_KEY);
//立即删除session验证码->防止该验证码被重复使用
// request.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
//如果token为空,并且和用户提交的验证码一致,就继续
// if (token != null && token.equalsIgnoreCase(code)) {
//判断这个用户名是不是可用
if (!memberService.isExistsUsername(username)) {
//注册
//System.out.println("用户名 " + username + " 不存在, 可以注册");
//构建一个Member对象
Member member = new Member(null, username, password, email);
if (memberService.registerMember(member)) {
//请求转发
request.getRequestDispatcher("/views/member/register_ok.jsp")
.forward(request, response);
} else {
//请求转发
request.getRequestDispatcher("/views/member/register_fail.jsp")
.forward(request, response);
}
} else {
//返回注册页面
//后面可以加入提示信息...
request.getRequestDispatcher("/views/member/login.jsp")
.forward(request, response);
}
// } else { //验证码不正确
// request.setAttribute("msg", "验证码不正确~");
// //如果前端需要回显某些数据
// request.setAttribute("username", username);
// request.setAttribute("email", email);
// //带回一个信息 要显示到注册选项页
// request.setAttribute("active", "register");
// request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
// }
}
/**
* 处理会员登录
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("======MemberServlet login()========");
//System.out.println("LoginServlet 被调用..");
//老韩说明:如果在登录页面, 用户没有输入内容,就直接提交,后台接收到的是""
String username = request.getParameter("user-name");
String password = request.getParameter("user-password");
Member member = memberService.login(new Member(null, username, password, null));
if (member == null) { //用户没有在DB
//System.out.println(member + " 登录失败...");
//把登录错误信息,放入到request域 => 如果忘了,可以看servlet / jsp
request.setAttribute("msg", "用户名或者密码错误");
request.setAttribute("username", username);
//页面转发
request.getRequestDispatcher("/views/member/login.jsp")
.forward(request, response);
} else { //用户在DB
//System.out.println(member + " 登录成功~...");
//将得到member对象放入到session
request.getSession().setAttribute("member", member);
//老师做了一个简单处理
if ("admin".equals(member.getUsername())) {
request.getRequestDispatcher("/views/manage/manage_menu.jsp")
.forward(request, response);
} else {
request.getRequestDispatcher("/views/member/login_ok.jsp")
.forward(request, response);
}
}
}
}
八、功能07 - 后台管理家具
创建furn表
package com.yinhai.furns.web;
import com.yinhai.furns.dao.BasicDAO;
import com.yinhai.furns.javabean.Furn;
import com.yinhai.furns.service.FurnService;
import com.yinhai.furns.service.impl.FurnServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
*/
@WebServlet(name = "FurnServlet",urlPatterns = "/manager/furnServlet")
public class FurnServlet extends BasicServlet {
private FurnService furnService = new FurnServiceImpl();
/**
* 这里我们使用前面的模板设计模式+反射+动态绑定来的调用到list方法
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("FurnServlet list方法被调...");
List<Furn> furns = furnService.queryFurns();
//把furns集合放入到request域
req.setAttribute("furns", furns);
for (Furn furn : furns) {
System.out.println(furn);
}
//请求转发
req.getRequestDispatcher("/views/manage/furn_manage.jsp")
.forward(req, resp);
}
}
通过jstl标签取出并输出
<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}
</tr>
编写admin的DAO
public class AdminDAOImpl extends BasicDAO implements AdminDAO {
@Override
public int saveAdmin(Admin admin) {
String sql = "INSERT INTO `admin`(`username`,`password`) " +
"VALUES(?,MD5(?))";
return update(sql, admin.getUsername(),
admin.getPassword());
}
@Override
public Admin queryAdminByUsernameAndPassword(String username, String password) {
String sql = "SELECT `id`,`username`,`password` FROM `admin` " +
" WHERE `username`=? and `password`=md5(?)";
return (Admin) querySingle(sql, Admin.class,username,password);
}
}
编写对应的和前端交互的servlet
@WebServlet(name = "AdminServlet",urlPatterns = "/adminServlet")
public class AdminServlet extends BasicServlet {
private AdminService adminService = new AdminServiceImpl();
protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
Admin admin = new Admin(null, username, password);
if(adminService.login(admin) == null){
System.out.println("管理员不存在");
request.getRequestDispatcher("/views/manage/manage_login.jsp").forward(request,response);
return;
}
request.getRequestDispatcher("/views/manage/manage_menu.jsp").forward(request,response);
}
}
九、功能08 - 后台管理 添加家具
@WebServlet(name = "FurnServlet",urlPatterns = "/manage/furnServlet")
public class FurnServlet extends BasicServlet {
private FurnService furnService = new FurnServiceImpl();
protected void add(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
//获取家居信息
String uname = request.getParameter("name");
String maker = request.getParameter("maker");
String price = request.getParameter("price");
String sales = request.getParameter("sales");
String stock = request.getParameter("stock");
String defaultPath = "assets/images/product-image/default.jpg";
Furn furn = new Furn(null, uname, maker, new BigDecimal(price), new Integer(sales), new Integer(stock), defaultPath);
System.out.println(furn);
furnService.addFurn(furn);
request.getRequestDispatcher("/manage/furnServlet?action=list")
.forward(request, response);
}
public class FurnServiceImpl implements FurnService {
private FurnDAO furnDAO = new FurnDAOImpl();
//定义属性FurnDAO对象
/**
* 返回家居信息
* @return
*/
@Override
public List<Furn> queryFurns() {
return furnDAO.queryFurns();
}
@Override
public int addFurn(Furn furn) {
return furnDAO.addFurn(furn);
}
}
public class FurnDAOImpl extends BasicDAO implements FurnDAO {
@Override
public int addFurn(Furn furn) {
String sql = "INSERT INTO furn(`id`,`name`,maker,price,sales,stock,img_path)\n" +
"\tVALUES(NULL,?,?,?,?,?,?)";
return update(sql,furn.getName(),
furn.getMaker(),furn.getPrice(),furn.getSales(),furn.getStock(),furn.getImgPath());
}
}
细节 解决中文乱码问题 在BasicDAO上设置request response的文本格式
细节 解决表重复提交问题 使用重定向即可
细节 后端验证添加家具的合法性
//我们可以对获取的到数据, 进行一个校验
//1. 使用java的正则表达式来验证 sales是一个正整数
//2. 如果没有通过校验,则直接返回furn_add.jsp -> request.setAttribute("mes","xx")
//3. 这里可以直接进行转换
//try {
// int i = Integer.parseInt(sales);
//}catch (NumberFormatException e) {
// //System.out.println("转换异常...");
// req.setAttribute("mes", "销量数据格式不对...");
// //返回到furn_add.jsp
// req.getRequestDispatcher("/views/manage/furn_add.jsp")
// .forward(req, resp);
// return;
//}
//String stock = req.getParameter("stock");
//图片的路径 imgPath 使用默认即可
//Furn furn = null;
//try {
// furn = new Furn(null, name, maker, new BigDecimal(price),
// new Integer(sales), new Integer(stock), "assets/images/product-image/default.jpg");
//} catch (NumberFormatException e) {
// req.setAttribute("mes", "添加数据格式不对...");
// //返回到furn_add.jsp
// req.getRequestDispatcher("/views/manage/furn_add.jsp")
// .forward(req, resp);
// return;
//}
//后面我们会学习SpringMVC -> 专门的用于数据校验的规则/框架 JSR303... Hibernate Validator
细节 使用工具类DataUtils 完成自动封装JavaBean
1. BeanUtils工具类,它可以一次性的把所有请求的参数注入到JavaBean中。
2. BeanUtils工具类,经常用于把Map中的值注入到JavaBean中,或者是对象属性值的拷贝操作
3. BeanUtils不是Jdk的类,需要导入需要的jar包: commons-beanutils-1.8.0.jar
commons-logging-1.1.1.jar
//使用BeanUtils完成javabean对象的自动封装.
//
// Furn furn = new Furn();
// try {
// //讲 req.getParameterMap() 数据封装到furn 对象
// //使用反射将数据封装, 有一个前提就是表单提交的数据字段名
// //<input name="maker" style="width: 90%" type="text" value=""/>
// //需要和封装的Javabean的属性名一致
// BeanUtils.populate(furn, request.getParameterMap());
// } catch (Exception e) {
// e.printStackTrace();
// }
// 自动将提交的数据,封装到Furn对象
Furn furn =
DataUtils.copyParamToBean(request.getParameterMap(), new Furn());
System.out.println(furn);
furnService.addFurn(furn);
public class DataUtils {
//将方法,封装到静态方法,方便使用
public static <T> T copyParamToBean(Map value, T bean) {
try {
BeanUtils.populate(bean, value);
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}
在构造javabean的时候在Furn内赋予一个默认值 给这个类自动装入
十、功能09 后台管理 删除家具
前端对x图标进行绑定事件 弹出是否删除该名字的家具
Servlet增加功能
@WebServlet(name = "FurnServlet",urlPatterns = "/manage/furnServlet")
public class FurnServlet extends BasicServlet {
protected void del(HttpServletRequest request,HttpServletResponse response){
int id = DataUtils.parseInt(request.getParameter("id"), 0);
System.out.println(furnService.deleteFurnById(id));
}
}
Service增加功能
public int deleteFurnById(int id) {
return furnDAO.deleteFurnById(id);
}
DAO类增加功能
public class FurnDAOImpl extends BasicDAO implements FurnDAO {
public int deleteFurnById(int id) {
String sql = "DELETE FROM furn WHERE id = ?";
return update(sql,id);
}
}
十一、功能实现 10 后台管理 修改家具
查询对应的id的家具
protected void showFurn(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
int id = DataUtils.parseInt(request.getParameter("id"),0);
Furn furn = furnService.queryFurnById(id);
System.out.println(furn);
request.setAttribute("furn",furn);
//请求转发
request.getRequestDispatcher("/views/manage/furn_update.jsp").forward(request,response);
}
以上完成回显操作 下一步做修改的操作
@Override
public int updateFurn(Furn furn) {
String sql = "UPDATE furn SET `name` = ? , `maker` = ? , `price` = ? , sales = ?" +
" , stock = ? , `img_path` = ?" +
" WHERE id = ?";
return update(sql,furn.getName(),furn.getMaker(),
furn.getPrice(),furn.getSales(),furn.getStock(),furn.getImgPath(),furn.getId());
}
前端页面也调整 使用jstl标签拿出
十二、功能实现11 分页显示
将分页作为一个Bean模型
package com.yinhai.furns.javabean;
import java.util.List;
/**
* @author 银小海
* @version 1.0
* @email yinhai14@qq.com
* 一个分页的数据模型 包含了分页的各种信息
*/
//用一个泛型 分页模型对应的数据类型是不确定的
public class Page<T> {
//每页显示多少条记录可以在其他地方也用到 建议置为常量
//ctrl + shift + u 切换大小写
public static final Integer PAGE_SIZE = 3;
//表示显示当前页
private Integer pageNo;
//每页显示多少记录
private Integer pageSize = PAGE_SIZE;
//表示共有多少页
private Integer pageTotalCount;
//表示的是共有多少条记录 通过totalRow和pageSize计算得到pageTotalCount
private Integer totalRow;
//表示当前页要显示的数据
private List<T> items;
//分页导航的字符串
private String url;
public Integer getPageNo() {
return pageNo;
}
public void setPageNo(Integer pageNo) {
this.pageNo = pageNo;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getPageTotalCount() {
return pageTotalCount;
}
public void setPageTotalCount(Integer pageTotalCount) {
this.pageTotalCount = pageTotalCount;
}
public Integer getTotalRow() {
return totalRow;
}
public void setTotalRow(Integer totalRow) {
this.totalRow = totalRow;
}
public List<T> getItems() {
return items;
}
public void setItems(List<T> items) {
this.items = items;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
分析 page的那些属性可以从数据库获取 就把对应的属性交给DAO赋值
@Override
public int getTotalRow() {
String sql = "SELECT COUNT(*) FROM furn";
// return (int) queryScalar(sql); => 会报转换异常
return ((Number) queryScalar(sql)).intValue();
}
@Override
public List<Furn> getPageItems(int begin, int pageSize) {
String sql = "SELECT `id`,`name`,maker,price,sales,stock,img_path imgPath\n" +
" FROM furn ";
return null;
}
其他的交给Service处理
public class FurnServiceImpl implements FurnService {
@Override
public Page<Furn> page(int pageNo, int pageSize) {
Page<Furn> page = new Page<>();
page.setPageNo(pageNo);
page.setPageSize(pageSize);
int totalRow = furnDAO.getTotalRow();
page.setTotalRow(totalRow);
//pageTotalCount最大页数 需要通过算法计算得到
int pageTotalCount = totalRow / pageSize;
if(totalRow % pageSize > 0){
pageTotalCount += 1;
}
page.setPageTotalCount(pageTotalCount);
//private List<T> items
//计算begin
//private List<T> items
//老师开始计算begin-> 小小算法
//验证: pageNo = 1 pageSize = 3 => begin =0
//验证: pageNo = 3 pageSize = 2 => begin =4
//OK => 但是注意这里隐藏一个坑, 现在你看不到, 后面会暴露
int begin = (pageNo - 1) * pageSize;
List<Furn> pageItems = furnDAO.getPageItems(begin, pageSize);
page.setItems(pageItems);
//还差一个url => 分页导航,先放一放
return page;
}
}
Servlet调用
protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int pageNo = DataUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = DataUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
//调用service方法, 获取Page对象
Page<Furn> page = furnService.page(pageNo, pageSize);
//将page放入到request域
req.setAttribute("page", page);
//请求转发到furn_manage.jsp
req.getRequestDispatcher("/views/manage/furn_manage.jsp")
.forward(req, resp);
}
十三、功能实现 12 - 分页导航
<!-- Pagination Area Start 分页导航条 -->
<div class="pro-pagination-style text-center mb-md-30px mb-lm-30px mt-6" data-aos="fade-up">
<ul>
<%--如果当前页 > 1 , 就显示上一页--%>
<c:if test="${requestScope.page.pageNo > 1}">
<li><a href="manage/furnServlet?action=page&pageNo=${requestScope.page.pageNo - 1}">上一页</a></li>
</c:if>
<%--<li><a class="active" href="#">3</a></li>--%>
<%--<li><a href="#">4</a></li>--%>
<%--<li><a href="#">5</a></li>--%>
<%-- 显示所有的分页数, 先容易,再困难
老师思路: 先确定开始页数 begin 第1页
再确定结束页数 end 第pageTotalCount页
学生困惑:如果页数很多,怎么办? => 算法最多显示5页[这个规则可以由程序员决定.]
希望,小伙伴自己先想一想...=> 后面
老师分析
1. 如果总页数<=5, 就全部显示
2. 如果总页数>5, 按照如下规则显示(这个规则是程序员/业务来确定):
2.1 如果当前页是前3页, 就显示1-5
2.2 如果当前页是后3页, 就显示最后5页
2.3 如果当前页是中间页, 就显示 当前页前2页, 当前页 , 当前页后两页
这里的关键就是要根据不同的情况来初始化begin, end
--%>
<c:choose>
<%--如果总页数<=5, 就全部显示--%>
<c:when test="${requestScope.page.pageTotalCount <=5 }">
<c:set var="begin" value="1"/>
<c:set var="end" value="${requestScope.page.pageTotalCount}"/>
</c:when>
<%--如果总页数>5--%>
<c:when test="${requestScope.page.pageTotalCount > 5}">
<c:choose>
<%--如果当前页是前3页, 就显示1-5--%>
<c:when test="${requestScope.page.pageNo <= 3}">
<c:set var="begin" value="1"/>
<c:set var="end" value="5"/>
</c:when>
<%--如果当前页是后3页, 就显示最后5页--%>
<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotalCount - 3}">
<c:set var="begin" value="${requestScope.page.pageTotalCount - 4}"/>
<c:set var="end" value="${requestScope.page.pageTotalCount}"/>
</c:when>
<%--如果当前页是中间页, 就显示 当前页前2页, 当前页 , 当前页后两页--%>
<c:otherwise>
<c:set var="begin" value="${requestScope.page.pageNo - 2}"/>
<c:set var="end" value="${requestScope.page.pageNo + 2}"/>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<c:forEach begin="${begin}" end="${end}" var="i">
<%--如果i是当前页, 就使用class="active" 修饰--%>
<c:if test="${i == requestScope.page.pageNo}">
<li><a class="active" href="manage/furnServlet?action=page&pageNo=${i}">${i}</a></li>
</c:if>
<c:if test="${i != requestScope.page.pageNo}">
<li><a href="manage/furnServlet?action=page&pageNo=${i}">${i}</a></li>
</c:if>
</c:forEach>
<%--如果当前页 < 总页数 , 就显示下一页--%>
<c:if test="${requestScope.page.pageNo < requestScope.page.pageTotalCount}">
<li><a href="manage/furnServlet?action=page&pageNo=${requestScope.page.pageNo + 1}">下一页</a></li>
</c:if>
<li><a href="#">共 ${requestScope.page.pageTotalCount} 页</a></li>
</ul>
</div>
<!-- Pagination Area End -->
进行修改 删除 添加 家具之后 能够回显到原来操作所在的页面
修改一步步转发 到最后重定向到page页面
这几个都这么做都在修改请求转发地址上完成
@WebServlet(name = "FurnServlet", urlPatterns = "/manage/furnServlet")
public class FurnServlet extends BasicServlet {
protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取家居信息
// String uname = request.getParameter("name");
// String maker = request.getParameter("maker");
// String price = request.getParameter("price");
// String sales = request.getParameter("sales");
//
//
// String stock = request.getParameter("stock");
// String defaultPath = "assets/images/product-image/default.jpg";
// Furn furn = new Furn(null, uname, maker, new BigDecimal(price), new Integer(sales), new Integer(stock), defaultPath);
//我们可以对获取的到数据, 进行一个校验
//1. 使用java的正则表达式来验证 sales是一个正整数
//2. 如果没有通过校验,则直接返回furn_add.jsp -> request.setAttribute("mes","xx")
//3. 这里可以直接进行转换
//try {
// int i = Integer.parseInt(sales);
//}catch (NumberFormatException e) {
// //System.out.println("转换异常...");
// req.setAttribute("mes", "销量数据格式不对...");
// //返回到furn_add.jsp
// req.getRequestDispatcher("/views/manage/furn_add.jsp")
// .forward(req, resp);
// return;
//}
//String stock = req.getParameter("stock");
//图片的路径 imgPath 使用默认即可
//Furn furn = null;
//try {
// furn = new Furn(null, name, maker, new BigDecimal(price),
// new Integer(sales), new Integer(stock), "assets/images/product-image/default.jpg");
//} catch (NumberFormatException e) {
// req.setAttribute("mes", "添加数据格式不对...");
// //返回到furn_add.jsp
// req.getRequestDispatcher("/views/manage/furn_add.jsp")
// .forward(req, resp);
// return;
//}
//后面我们会学习SpringMVC -> 专门的用于数据校验的规则/框架 JSR303... Hibernate Validator
//这里我们使用第二种方式, 完成将前端提交的数据, 封装成Furn的Javabean对象
//使用BeanUtils完成javabean对象的自动封装.
//
// Furn furn = new Furn();
// try {
// //讲 req.getParameterMap() 数据封装到furn 对象
// //使用反射将数据封装, 有一个前提就是表单提交的数据字段名
// //<input name="maker" style="width: 90%" type="text" value=""/>
// //需要和封装的Javabean的属性名一致
// BeanUtils.populate(furn, request.getParameterMap());
// } catch (Exception e) {
// e.printStackTrace();
// }
// 自动将提交的数据,封装到Furn对象
Furn furn =
DataUtils.copyParamToBean(request.getParameterMap(), new Furn());
System.out.println(furn);
furnService.addFurn(furn);
//老师说明: 因为这里使用请求转发, 当用户刷新页面时, 会重新发出一次添加请求
// request.getRequestDispatcher("/manage/furnServlet?action=list")
// .forward(request, response);
//就会造成数据重复提交: 解决方案使用 重定向即可.
//因为重定向实际是让浏览器重新发请求, 所以我们回送的url , 是一个完整url
// response.sendRedirect(request.getContextPath() + "/manage/furnServlet?action=list");
//以分页的方式显示
response.sendRedirect(request.getContextPath()
+ "/manage/furnServlet?action=page&pageNo=" + request.getParameter("pageNo"));
}
protected void del(HttpServletRequest request, HttpServletResponse response) throws IOException {
int id = DataUtils.parseInt(request.getParameter("id"), 0);
System.out.println(furnService.deleteFurnById(id));
response.sendRedirect(request.getContextPath()
+ "/manage/furnServlet?action=page&pageNo=" + request.getParameter("pageNo"));
}
protected void showFurn(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int id = DataUtils.parseInt(request.getParameter("id"), 0);
Furn furn = furnService.queryFurnById(id);
System.out.println(furn);
request.setAttribute("furn", furn);
//再把获取到的showPage再传回去
// request.setAttribute("pageNo",request.getParameter("pageNo"));
// 如果是在同一个作用域request内的请求是不需要再转发的 在下个页面可以用param.pageNo获取
//请求转发
request.getRequestDispatcher("/views/manage/furn_update.jsp").forward(request, response);
}
protected void update(HttpServletRequest request, HttpServletResponse response) throws IOException {
Furn furn = DataUtils.copyParamToBean(request.getParameterMap(), new Furn());
System.out.println("update" + furn);
System.out.println(furnService.updateFurn(furn));
//请求重定向
// response.sendRedirect(request.getContextPath() + "/manage/furnServlet?action=list");
//这里考虑分页转发
System.out.println(request.getContextPath()
+ "/manage/furnServlet?action=page&pageNo=" + request.getParameter("pageNo"));
response.sendRedirect(request.getContextPath()
+ "/manage/furnServlet?action=page&pageNo=" + request.getParameter("pageNo"));
}
protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int pageNo = DataUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = DataUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
//调用service方法, 获取Page对象
Page<Furn> page = furnService.page(pageNo, pageSize);
if(page.getItems().size() == 0){
page = furnService.page(page.getPageTotalCount(), pageSize);
}
//将page放入到request域
req.setAttribute("page", page);
//请求转发到furn_manage.jsp
req.getRequestDispatcher("/views/manage/furn_manage.jsp")
.forward(req, resp);
}
}