失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 书城项目zzz

书城项目zzz

时间:2020-09-23 01:29:16

相关推荐

书城项目zzz

书城项目第一阶段---表单验证

验证用户名:必须由字母,数字下划线组成,并且长度为 5 到 12 位

验证密码:必须由字母,数字下划线组成,并且长度为 5 到 12 位

验证确认密码:和密码相同

验证邮箱:xxxxx@

验证码:现在只需要验证用户已输入。因为还没讲到服务器。验证码生成。

<script type="text/javascript" src="../../static/script/jquery-1.7.2.js"></script> <script type="text/javascript"> // 页面加载完成之后 $(function () {// 给注册绑定单击事件 $("#sub_btn").click(function () {// 验证用户名:必须由字母,数字下划线组成,并且长度为 5 到 12 位 //1 获取用户名输入框里的内容 var usernameText = $("#username").val(); //2 创建正则表达式对象 var usernamePatt = /^\w{5,12}$/; //3 使用 test 方法验证 if (!usernamePatt.test(usernameText)) {//4 提示用户结果$("span.errorMsg").text("用户名不合法!"); return false; }// 验证密码:必须由字母,数字下划线组成,并且长度为 5 到 12 位 //1 获取用户名输入框里的内容 var passwordText = $("#password").val(); //2 创建正则表达式对象 var passwordPatt = /^\w{5,12}$/; //3 使用 test 方法验证 if (!passwordPatt.test(passwordText)) {//4 提示用户结果 $("span.errorMsg").text("密码不合法!"); return false; }// 验证确认密码:和密码相同 //1 获取确认密码内容 var repwdText = $("#repwd").val(); //2 和密码相比较 if (repwdText != passwordText) {//3 提示用户 $("span.errorMsg").text("确认密码和密码不一致!"); return false; }// 邮箱验证:xxxxx@ //1 获取邮箱里的内容 var emailText = $("#email").val(); //2 创建正则表达式对象 var emailPatt = /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/; //3 使用 test 方法验证是否合法 if (!emailPatt.test(emailText)) {//4 提示用户 $("span.errorMsg").text("邮箱格式不合法!"); return false; }// 验证码:现在只需要验证用户已输入。因为还没讲到服务器。验证码生成。 var codeText = $("#code").val(); //去掉验证码前后空格 alert("去空格前:["+codeText+"]") codeText = $.trim(codeText);alert("去空格后:["+codeText+"]") if (codeText == null || codeText == "") {//4 提示用户 $("span.errorMsg").text("验证码不能为空!"); return false; }$("span.errorMsg").text(""); }); }); </script>

书城项目第二阶段---用户注册和登陆

需求 1:用户注册

需求如下:

1)访问注册页面

2)填写注册信息,提交给服务器

3)服务器应该保存用户

4)当用户已经存在----提示用户注册 失败,用户名已存在

5)当用户不存在-----注册成功

需求 2:用户登陆

需求如下:

1)访问登陆页面

2)填写用户名密码后提交

3)服务器判断用户是否存在

4)如果登陆失败 —>>>> 返回用户名或者密码错误信息

5)如果登录成功 —>>>> 返回登陆成功信息

一、JavaEE 项目的三层架构

分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。

二、创建书城需要的数据库和表

drop database if exists book; create database book; use book; create table t_user( `id` int primary key auto_increment, `username` varchar(20) not null unique, `password` varchar(32) not null, `email` varchar(200) );insert into t_user(`username`,`password`,`email`) values('admin','admin','admin@');select * from t_user;

三、编写数据库表对应的 JavaBean 对象

public class User {private Integer id; private String username; private String password; private String email;...}

四、编写工具类 JdbcUtils

4.1、导入需要的 jar 包(数据库和连接池需要)

druid-1.1.9.jar

mysql-connector-java-5.1.7-bin.jar

4.2、在 src 源码目录下编写 jdbc.properties 属性配置文件

username=root password=root url=jdbc:mysql://localhost:3306/book driverClassName=com.mysql.jdbc.Driver initialSize=5 maxActive=10//连接池最大数量

注意点

如在数据库中出现???,必要时在url后面加上?characterEncoding=UTF-8改变数据库编码集

4.3、编写 JdbcUtils 工具类

public class JdbcUtils {private static DruidDataSource dataSource; static {try {Properties properties = new Properties(); // 读取 jdbc.properties 属性配置文件 InputStreaminputStream=JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); // 从流中加载数据 properties.load(inputStream); // 创建数据库连接池 dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) {e.printStackTrace(); } }/*** 获取数据库连接池中的连接 * @return 如果返回 null,说明获取连接失败<br/>有值就是获取连接成功 */ public static Connection getConnection(){Connection conn = null; try {conn = dataSource.getConnection(); } catch (Exception e) {e.printStackTrace(); }return conn; }/**关闭连接,放回数据库连接池 @param conn */ public static void close(Connection conn){if (conn != null) {try {conn.close(); } catch (SQLException e) {e.printStackTrace(); } } } }

4.4、JdbcUtils 测试

public class JdbcUtilsTest {@Test public void testJdbcUtils(){for (int i = 0; i < 100; i++){Connection connection = JdbcUtils.getConnection(); System.out.println(connection); JdbcUtils.close(connection); } } }

五、编写 BaseDao

5.1、导入 DBUtils 的 jar 包

commons-dbutils-1.3.jar

5.2、编写 BaseDao

public abstract class BaseDao {//使用 DbUtils 操作数据库 private QueryRunner queryRunner = new QueryRunner(); /*** update() 方法用来执行:Insert\Update\Delete 语句 ** @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数 */ public int update(String sql, Object... args) {Connection connection = JdbcUtils.getConnection(); try {return queryRunner.update(connection, sql, args); }catch (SQLException e) {e.printStackTrace(); }finally {JdbcUtils.close(connection); }return -1; }/*** 查询返回一个 javaBean 的 sql 语句 @param type 返回的对象类型 @param sql 执行的 sql 语句 @param args sql 对应的参数值 @param <T> 返回的类型的泛型 * @return */ public <T> T queryForOne(Class<T> type, String sql, Object... args) {Connection con = JdbcUtils.getConnection(); try {return queryRunner.query(con, sql, new BeanHandler<T>(type), args); } catch (SQLException e) {e.printStackTrace(); } finally {JdbcUtils.close(con); }return null; }/**查询返回多个 javaBean 的 sql 语句 @param type 返回的对象类型 @param sql 执行的 sql 语句 @param args sql 对应的参数值 @param <T> 返回的类型的泛型 @return */ public <T> List<T> queryForList(Class<T> type, String sql, Object... args) {Connection con = JdbcUtils.getConnection(); try {return queryRunner.query(con, sql, new BeanListHandler<T>(type), args); } catch (SQLException e) {e.printStackTrace(); } finally {JdbcUtils.close(con); }return null; }/**执行返回一行一列的 sql 语句 @param sql 执行的 sql 语句 @param args sql 对应的参数值 @return */ public Object queryForSingleValue(String sql, Object... args){Connection conn = JdbcUtils.getConnection(); try {return queryRunner.query(conn, sql, new ScalarHandler(), args); } catch (Exception e) {e.printStackTrace(); } finally {JdbcUtils.close(conn); }return null; } }

六、编写 UserDao

UserDao 接口

public interface UserDao {/*** 根据用户名查询用户信息 @param username 用户名 @return 如果返回 null,说明没有这个用户。反之亦然 */ public User queryUserByUsername(String username); /** 根据用户名和密码查询用户信息 @param username @param password @return 如果返回 null,说明用户名或密码错误,反之亦然 */ public User queryUserByUsernameAndPassword(String username,String password); /**保存用户信息 @param user @return 返回-1 表示操作失败,其他是 sql 语句影响的行数 */ public int saveUser(User user); }

UserDaoImpl 实现类

public class UserDaoImpl extends BaseDao implements UserDao {@Override public User queryUserByUsername(String username) {String sql = "select `id`,`username`,`password`,`email` from t_user where username = ?"; return queryForOne(User.class, sql, username); }@Override public User queryUserByUsernameAndPassword(String username, String password) {String sql = "select `id`,`username`,`password`,`email` from t_user where username = ? and password = ?"; return queryForOne(User.class, sql, username,password); }@Overridepublic int saveUser(User user) {String sql = "insert into t_user(`username`,`password`,`email`) values(?,?,?)"; return update(sql, user.getUsername(),user.getPassword(),user.getEmail()); } }

七、编写 UserService

UserService 接口

public interface UserService {/**注册用户 @param user */public void registUser(User user); /**登录 @param user @return 如果返回 null,说明登录失败,返回有值,是登录成功 */ public User login(User user); /**检查用户名是否可用 @param username @return 返回 true 表示用户名已存在,返回 false 表示用户名可用 */ public boolean existsUsername(String username); }

UserServiceImpl 实现类

public class UserServiceImpl implements UserService {private UserDao userDao = new UserDaoImpl();@Overridepublic void registerUser(User user) {userDao.saveUser(user);}@Overridepublic User login(User user) {return userDao.queryUserByUsernameAndPassword(user.getUsername(),user.getPassword());}@Overridepublic boolean existsUsername(String username) {if(userDao.queryUserByUsername(username)==null){//等于null说明没查到,没查到表示可用return false;}return true;}}

八、编写 web 层

8.1、实现用户注册的功能

8.1.1、图解用户注册的流程

8.1.2、修改 regist.html 和 regist_success.html 页面

1、添加 base 标签

//写 base 标签,永远固定相对路径跳转的结果<base href="http://localhost:8080/book/">

2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)

3、修改注册表单的提交地址和请求方式

8.1.3、编写 RegistServlet 程序

public class RegistServlet extends HttpServlet {private UserService userService = new UserServiceImpl(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1、获取请求的参数 String username = req.getParameter("username"); String password = req.getParameter("password"); String email = req.getParameter("email"); String code = req.getParameter("code"); // 2、检查验证码是否正确 === 写死,要求验证码为:abcde if ("abcde".equalsIgnoreCase(code)) {// 3、检查 用户名是否可用 if (userService.existsUsername(username)) {// 不可用 System.out.println("用户名[" + username + "]已存在!"); // 跳回注册页面 req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);} else {// 可用 // 调用 Sservice 保存到数据库 userService.registUser(new User(null, username, password, email)); // 跳到注册成功页面 regist_success.html req.getRequestDispatcher("/pages/user/regist_success.html").forward(req, resp); } } else {System.out.println("验证码[" + code + "]错误"); req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp); } } }//注意点 getRequestDispatcher()中的参数是绝对路径 需要/开头 /表示web下的文件

8.2、用户登录功能的实现

8.2.1、图解用户登录

8.2.2、修改 login.html 页面和 login_success.html 页面

1、添加 base 标签

//写 base 标签,永远固定相对路径跳转的结果<base href="http://localhost:8080/book/">

2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)

3、修改注册表单的提交地址和请求方式

8.2.3、LoginServlet 程序

public class LoginServlet extends HttpServlet {private UserService userService = new UserServiceImpl(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1、获取请求的参数 String username = req.getParameter("username"); String password = req.getParameter("password"); // 调用 userService.login()登录处理业务 User loginUser = userService.login(new User(null, username, password, null)); // 如果等于 null,说明登录 失败! if (loginUser == null) {// 跳回登录页面 req.getRequestDispatcher("/pages/user/login.html").forward(req, resp); } else {//登录成功 //跳到成功页面 login_success.html req.getRequestDispatcher("/pages/user/login_success.html").forward(req, resp); } } }

书城项目第三阶段---代码优化

一、抽取页面中相同的内容

1.1、代码头部 css、jquery、base 标签整合到head.jsp中

<%//动态获取base标签的地址 可以使其他主机访问时能够正常显示页面String basePath=request.getScheme()//http协议+"://"+request.getServerName()//ip地址+":"+request.getServerPort()//端口号+request.getContextPath()//工程路径+"/";%>//写base路径永远固定绝对路径跳转的结果<base href="<%= basePath %>"><link type="text/css" rel="stylesheet" href="static/css/style.css"><script src="static/script/jquery-1.7.2.js"></script>

1.2、代码尾部整合到footer.jsp中

<div id="bottom"><span>尚硅谷书城.Copyright &copy;</span></div>

1.3、登录成功后的菜单

<div>//a标签默认doGet请求 需要在BaseServlet中重写doGet方法<a href="manager/bookServlet?action=list">图书管理</a><a href="order_manager.jsp">订单管理</a><a href="../../index.jsp">返回商城</a></div>

二、BaseServlet 的抽取

2.1、代码优化一:合并 LoginServlet 和 RegistServlet 程序为 UserServlet 程序

给 login.jsp 添加隐藏域和修改请求地址
给 regist.jsp 页面添加隐藏域和修改请求地址

添加隐藏域的作用

由于LoginServlet和RegistServlet 合并成了UserServlet ,如果不添加隐藏域,则无法判断当前执行的业务是什么,需要添加隐藏域,通过隐藏域中的文本值来判断当前执行的业务,然后传到from表单中的action属性所指向的地址去执行相应的业务。

2.2、代码优化二:使用反射优化大量 else if 代码

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String action = req.getParameter("action");//每添加一个业务就需要添加else if语句 很麻烦//优化:利用反射获取对象并执行相应的方法(前提是action字符串内容和方法名相同)try {//获取action所对应的业务,利用反射获取对象Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);//调用目标业务方法method.invoke(this,req,resp);} catch (Exception e) {e.printStackTrace();}// if ("login".equals(action)) {// login(req,resp);// } else if ("regist".equals(action)) {// regist(req,resp);// }}}

getParameter(“action”)方法会获取name为action的标签中的value值,即隐藏域的文本值(或value值),得到一个值为value的字符串,通过这个字符串使用反射来找到对应的方法。

2.3、代码优化三:抽取 BaseServlet 程序。

三、数据的封装和 BeanUtils 的使用

BeanUtils 工具类,它可以一次性的把所有请求的参数注入到 JavaBean 中。

BeanUtils 工具类,经常用于把 Map 中的值注入到 JavaBean 中,或者是对象属性值的拷贝操作。

BeanUtils 它不是 Jdk 的类。而是第三方的工具类。所以需要导包。

 1、导入需要的 jar 包:

commons-beanutils-1.8.0.jar

commons-logging-1.1.1.jar

 2、编写 WebUtils 工具类使用:

WebUtils 工具类

public class WebUtils {/**把 Map 中的值注入到对应的 JavaBean 属性中。 @param value* @param bean */ public static <T> T copyParamToBean( Map value , T bean ){try {/**把所有请求的参数都注入到 user 对象中 */ BeanUtils.populate(bean, value); } catch (Exception e) {e.printStackTrace(); }return bean; } }

在注册中会使用到

protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// //1、获取请求的参数// String username = req.getParameter("username");// String password = req.getParameter("password");// String email = req.getParameter("email");String code = req.getParameter("code");//优化上述代码,将参数值直接注入到JavaBean中User user = WebUtils.copyParamToBean(req.getParameterMap(), new User());//2、检查验证码是否正确 验证码写死abcdeif ("abcde".equalsIgnoreCase(code)) {//3、检查用户名是否可用if (userService.existsUsername(user.getUsername())) {//把回显信息保存到request域中req.setAttribute("msg", "用户名已存在!");req.setAttribute("username", user.getUsername());req.setAttribute("email", user.getEmail());//跳回注册页面//System.out.println("用户[" + username + "]已存在");req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);} else {//调用Service存到数据库userService.registerUser(new User(null, user.getUsername(), user.getPassword(), user.getEmail()));//跳到注册成功面regist_success.htmlreq.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req, resp);}} else {//把回显信息保存到request域中req.setAttribute("msg", "验证码错误!");req.setAttribute("username", user.getUsername());req.setAttribute("email", user.getEmail());// System.out.println("验证码[" + code + "]错误");//跳回注册页面req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);}}

书城项目第四阶段---使用 EL 表达式修改表单回显

以登录为例

若使用表达式脚本则这样写

<%= request.getAttribute(“msg”)==null?“请输入用户名和密码”:request.getAttribute(“msg”) %>

注意点一:

客户端没响应之前,request域中没有值,所以刚打开浏览器时,msg的值为null

注意点二:

EL表达式不会输出null,若值为null,则输出空串

书城项目第五阶段---图书模块

一、MVC 概念

 MVC 全称:Model 模型、 View 视图、 Controller 控制器。

 MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。

 View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作—— JSP/HTML。

 Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。 转到某个页面。或者是重定向到某个页面。

 Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码—— JavaBean/domain/entity/pojo。

 MVC 是一种思想

 MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了降低耦合度)。

 MVC 的作用还是为了降低耦合。让代码合理分层。方便后期升级和维护。

二、图书模块

2.1、编写图书模块的数据库表

create table t_book( `id` int primary key auto_increment, `name` varchar(100), `price` decimal(11,2), `author` varchar(100), `sales` int, `stock` int, `img_path` varchar(200) );## 插入初始化测试数据 insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'java 从入门到放弃' , '国哥' , 80 , 9999 , 9 , 'static/img/default.jpg'); insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13 , 'static/img/default.jpg'); insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '怎样拐跑别人的媳妇' , '龙伍' , 68, 99999 , 52 , 'static/img/default.jpg'); insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '木虚肉盖饭' , '小胖' , 16, 1000 , 50 , 'static/img/default.jpg'); ...## 查看表内容 select id,name,author,price,sales,stock,img_path from t_book;

2.2、编写图书模块的 JavaBean

public class Book {private Integer id; private String name; private String author; private BigDecimal price; private Integer sales; private Integer stock; private String imgPath = "static/img/default.jpg";//get、set方法省略}

2.3、编写图书模块的 Dao

Dao 接口

public interface BookDao {public int addBook(Book book); public int deleteBookById(Integer id); public int updateBook(Book book); public Book queryBookById(Integer id); public List<Book> queryBooks();}

BookDaoImpl 实现类

public class BookDaoImpl extends BaseDao implements BookDao {@Override public int addBook(Book book) {String sql = "insert into t_book(`name`,`author`,`price`,`sales`,`stock`,`img_path`) values(?,?,?,?,?,?)"; return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath()); }@Override public int deleteBookById(Integer id) {String sql = "delete from t_book where id = ?"; return update(sql, id); }@Override public int updateBook(Book book) {String sql = "update t_book set `name`=?,`author`=?,`price`=?,`sales`=?,`stock`=?,`img_path`=? where id = ?"; return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath(),book.getId()); }@Override public Book queryBookById(Integer id) {String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` imgPath from t_book where id = ?"; return queryForOne(Book.class, sql,id); }@Override public List<Book> queryBooks() {String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` imgPath from t_book"; return queryForList(Book.class, sql); } }

2.4、编写图书模块的 Service

BookService 接口

public interface BookService {public void addBook(Book book); public void deleteBookById(Integer id);public void updateBook(Book book); public Book queryBookById(Integer id); public List<Book> queryBooks(); }

BookServiceImpl 实现类

public class BookServiceImpl implements BookService {private BookDao bookDao = new BookDaoImpl();@Override public void addBook(Book book) {bookDao.addBook(book); }@Override public void deleteBookById(Integer id) {bookDao.deleteBookById(id); }@Override public void updateBook(Book book) {bookDao.updateBook(book); }@Override public Book queryBookById(Integer id) {return bookDao.queryBookById(id); }@Override public List<Book> queryBooks() {return bookDao.queryBooks(); } }

2.5、编写图书模块的 Web 层,和页面联调测试

2.5.1、图书列表功能的实现

列表功能流程图解
1.BookServlet 程序中添加 list 方法

protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1 通过 BookService 查询全部图书 List<Book> books = bookService.queryBooks(); //2 把全部图书保存到 Request 域中 req.setAttribute("books", books); //3、请求转发到/pages/manager/book_manager.jsp 页面 req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp); }

2.修改【图书管理】请求地址

注意点

跳转路径前加了manager/ 作用是便于权限管理

action=list表示会自动寻找方法名为list的方法并执行,

父类BaseServlet通过反射获取action字符串的内容,找到相应的方法,

该方法在父类BaseServlet中,子类BookServlet可通过继承BaseServlet得到

3.修改 pages/manager/book_manager.jsp 页面的数据遍历输出(利用JSTL)

<%@ taglib prefix="c" uri="/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>图书管理</title> <!-- 静态包含 base 标签、css 样式、jQuery 文件 --> <%@ include file="/pages/common/head.jsp"%> </head> <body> <div id="header"> <img class="logo_img" alt="" src="../../static/img/logo.gif" > <span class="wel_word">图书管理系统</span> <!-- 静态包含 manager 管理模块的菜单 --> <%@include file="/pages/common/manager_menu.jsp"%> </div> <div id="main"> <table> <tr><td>名称</td> <td>价格</td> <td>作者</td> <td>销量</td> <td>库存</td> <td colspan="2">操作</td> </tr> <c:forEach items="${requestScope.books}" var="book"> <tr><td>${book.name}</td> <td>${book.price}</td> <td>${book.author}</td> <td>${book.sales}</td> <td>${book.stock}</td> <td><a href="book_edit.jsp">修改</a></td> <td><a href="#">删除</a></td></tr> </c:forEach> <tr><td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td><a href="book_edit.jsp">添加图书</a></td> </tr> </table> </div> <!--静态包含页脚内容--> <%@include file="/pages/common/footer.jsp"%> </body> </html>

2.5.2、前后台的简单介绍

2.5.3、添加图书功能的实现

添加图书流程图解
1.BookServlet 程序中添加 add 方法

protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取请求的参数==封装成为Book对象Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());//2.调用BookService.addBook()保存图书bookService.addBook(book);//3.跳到图书列表界面/manager/bookServlet?action=list// req.getRequestDispatcher("/manager/bookServlet?action=list").forward(req,resp);//防止表单重复提交 采用重定向的方式resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=list");}

注意点

1.如果数据库和浏览器都出现???乱码,则应该是数据库编码集问题

解决方法:在配置文件中url后面加入?characterEncoding=UTF-8或者 通过命令行设置数据库编码集

2.如果出现非中文乱码,则采用 doPost 请求的中文乱码解决方式

解决方法:在doPost方法内第一行加上req.setCharacterEncoding("UTF-8");

3.请求转发的 / 表示到工程名

 请求重定向的 / 表示到端口号,需要通过req.getContextPath()获取工程名

2.修改 book_edit.jsp 页面

<!--更改跳转路径并更改请求方式为get(个人认为post也行) --><form action="manager/bookServlet" method="get"> <!--添加隐藏域,声明当前业务为add添加操作 --><input type="hidden" name="action" value="add" /><table> <tr><td>名称</td> <td>价格</td> <td>作者</td> <td>销量</td> <td>库存</td> <td colspan="2">操作</td> </tr> <tr><td><input name="name" type="text" value="时间简史"/></td> <td><input name="price" type="text" value="30.00"/></td> <td><input name="author" type="text" value="霍金"/></td> <td><input name="sales" type="text" value="200"/></td> <td><input name="stock" type="text" value="300"/></td> <td><input type="submit" value="提交"/></td> </tr> </table> </form>

2.5.4、删除图书功能的实现

删除流程图解
1.BookServlet 程序中的 delete 方法

protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取要删除图书的idint id = WebUtils.parseInt(req.getParameter("id"), 0);//2.调用bookService.deleteBookById()删除数据bookService.deleteBookById(id);//3.重定向到/manager/bookServlet?action=listresp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=list");}

2.给 WebUtils 工具类添加转换 int 类型的工具方法

/*把String类型的字符串转换成int型变量,若转换失败则返回默认值*/public static int parseInt(String str,int defaultValue){try {return Integer.parseInt(str);} catch (Exception e) {e.printStackTrace();}return defaultValue;}}

3.修改删除的连接地址

注意点

 BookDao通过id删除,需要传id属性

4.给删除添加确认提示操作

<script type="text/javascript"> $(function () {// 给删除的 a 标签绑定单击事件,用于删除的确认提示操作 $("a.deleteClass").click(function () {// 在事件的 function 函数中,有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象。 /**confirm 是确认提示框函数,参数是它的提示内容 它有两个按钮,一个确认,一个是取消。 返回 true 表示点击了,确认,返回 false 表示点击取消。 */ return confirm("你确定要删除【" + $(this).parent().parent().find("td:first").text() + "】?"); //返回true提交删除请求,返回false不提交删除请求}); }); </script>

2.5.5、修改图书功能的实现

修改图书图解

注意点

 解决方案一:在各自的标签中添加method参数,例如method=add,然后在book_edit页面中的隐藏域中使用value="${ param.method }",其中param为EL表达式11个隐含对象其中之一

 解决方案二:在book_edit页面中的隐藏域中使用value="${ empty param.id ? "add" : "update" }"

 解决方案三:在book_edit页面中的隐藏域中使用value="${ empty requestScope.books ? "add" : "update" }"

1.更新【修改】的请求地址
2.BookServlet 程序中添加 getBook 方法

protected void getBook(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取请求的参数 图书idint id = WebUtils.parseInt(req.getParameter("id"), 0);//2.调用查询图书Book book = bookService.queryBookById(id);//3.保存图书到request域中req.setAttribute("books",book);//4.请求转发到pages/manager/book_edit.jsp页面req.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(req,resp);}

注意点

 此方法中把book信息保存到了request域中,因为需要在 book_edit.jsp 页面中回显要修改图书的信息,在原来信息的基础上进行修改,如下一节所示

3.在 book_edit.jsp 页面中显示修改的数据

<tr><td><input name="name" type="text" value="${requestScope.book.name}"/></td> <td><input name="price" type="text" value="${requestScope.book.price}"/></td> <td><input name="author" type="text" value="${requestScope.book.author}"/></td> <td><input name="sales" type="text" value="${requestScope.book.sales}"/></td> <td><input name="stock" type="text" value="${requestScope.book.stock}"/></td><td><input type="submit" value="提交"/></td> </tr>

4.在 BookServlet 程序中添加 update 方法

protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取请求的参数==封装成为Book对象Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());//2.调用bookService.updateBook()修改图书bookService.updateBook(book);//3.重定向回到图书列表管理页面resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=list");}

5.动态解决 book_edit.jsp 页面,即要实现添加,又要实现修改操作

<%--动态的改变value的值,由于此界面需要实现两个功能1、修改图书信息2、添加图书--%><input type="hidden" name="action" value="${ empty param.id ? "add" : "update" }"><%--BookDaoImpl中的update方法需要通过id查询Book类信息,所以需要传进去一个id参数--%><input type="hidden" name="id" value="${ requestScope.books.id }">

此节采用了第二种方法

书城项目第五阶段-图书分页

三、分页模块

图书分页图解

3.1、分页模型 Page 类的抽取(当前页数,总页数,总记录数, 当前页数据,每页记录数)

public class Page<T> {public static final Integer PAGE_SIZE=4;//当前页码private Integer pageNo;//总页码private Integer pageTotal;//当前页显示数量private Integer pageSize=PAGE_SIZE;//总记录数private Integer pageTotalCount;//当前页数据private List<T> items;public Integer getPageNo() {return pageNo;}//在BookServiceImpl会调用此方法public void setPageNo(Integer pageNo) {//数据边界的有效检查if(pageNo<1){pageNo=1;}if(pageNo>pageTotal){pageNo=pageTotal;}this.pageNo = pageNo;}...

3.2、分页的初步实现

BookDaoImpl 代码

@Overridepublic Integer queryForPageTotalCount() {String sql="select count(*) from t_book";Number count = (Number) queryForSingleValue(sql);return count.intValue();}@Overridepublic List<Book> queryForItems(int begin,int pageSize) {String sql="select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from t_book limit ?,?";return queryForList(Book.class, sql, begin, pageSize);}

BookServiceImpl 代码

@Overridepublic Page<Book> page(int pageNo, int pageSize) {Page<Book> page = new Page<>();//当前页显示数量page.setPageSize(pageSize);Integer pageTotalCount = bookDao.queryForPageTotalCount();//总记录数page.setPageTotalCount(pageTotalCount);Integer pageTotal=pageTotalCount/pageSize;if(pageTotalCount%pageSize>0){pageTotal+=1;}//总页码page.setPageTotal(pageTotal);//设置当前页码page.setPageNo(pageNo);//begin为当前页第一条数据的索引int begin=(page.getPageNo()-1)*pageSize;List<Book> items = bookDao.queryForItems(begin,pageSize);//当前页数据page.setItems(items);return page;}

注意点

 由于需要对数据边界进行有效检查,page.setPageNo(pageNo)必须在pageTotal的值计算出之后再调用

BookServlet 代码

protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取请求的参数pageNo和PageSizeint pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);//初始值是第一页int pageSize = WebUtils.parseInt(req.getParameter("PageSize"), Page.PAGE_SIZE);//默认值是4条数据//2.调用bookService.page(pageNo,pageSize)返回Page对象Page<Book> page = bookService.page(pageNo,pageSize);//3.保存Page对象到request域中req.setAttribute("page",page);//4.请求转发到/pages/manager/book_manager.jsp页面req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);}

manager_menu.jsp 中【图书管理】请求地址的修改

<div><a href="manager/bookServlet?action=page">图书管理</a><%-- a标签默认doGet请求 需要在BaseServlet中重写doGet方法--%><a href="order_manager.jsp">订单管理</a><a href="../../index.jsp">返回商城</a></div>

book_manager.jsp 修改

<table> <tr><td>名称</td> <td>价格</td> <td>作者</td> <td>销量</td> <td>库存</td> <td colspan="2">操作</td> </tr> <c:forEach items="${requestScope.page.items}" var="book"> <tr><td>${book.name}</td> <td>${book.price}</td> <td>${book.author}</td> <td>${book.sales}</td> <td>${book.stock}</td> <td><a href="manager/bookServlet?action=getBook&id=${book.id}">修改</a></td> <td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删 除</a></td></tr> </c:forEach> <tr><td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td><a href="pages/manager/book_edit.jsp">添加图书</a></td> </tr> </table> <div id="page_nav"> <a href="#">首页</a> <a href="#">上一页</a> <a href="#">3</a> 【${ requestScope.page.pageNo }】 <a href="#">5</a> <a href="#">下一页</a> <a href="#">末页</a> 共${ requestScope.page.pageTotal }页,共${ requestScope.page.pageTotalCount }条记录到第<input value="4" name="pn" id="pn_input"/>页 <input type="button" value="确定"> </div>

3.3、首页、上一页、下一页、末页实现

<div id="page_nav"><!-- 如果是第一页,则不显示上一页和首页--><c:if test="${requestScope.page.pageNo>1}"><a href="manager/bookServlet?action=page&pageNo=1">首页</a><a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a></c:if><!-- 如果是最后一页,则不显示下一页和末页--><c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}"><a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a><a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a></c:if>共${requestScope.page.pageTotal}页,共${requestScope.page.pageTotalCount}条记录到第<input value="${ param.pageNo }" name="pn" id="pn_input"/>页<input id="searchPageBtn" type="button" value="确定"></div>

3.4、分页模块中跳转到指定页数功能实现

head.jsp 添加basePath属性

<%<!--动态获取base标签的地址 可以使其他主机访问时能够正常显示页面 -->String basePath=request.getScheme()//http协议+"://"+request.getServerName()//ip地址+":"+request.getServerPort()//端口号+request.getContextPath()//工程路径+"/";<!--如果setAttribute()报红,则导入tomcat中lib下的jsp-api.jar包,pageContext在当前jsp页面有效-->pageContext.setAttribute("basePath",basePath);%>

该basePath属性在下文location.href中会用到

<div id="page_nav"><!-- 如果是第一页,则不显示上一页和首页--><c:if test="${requestScope.page.pageNo>1}"><a href="manager/bookServlet?action=page&pageNo=1">首页</a><a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a></c:if><!-- 如果是最后一页,则不显示下一页和末页--><c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}"><a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a><a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a></c:if>共${requestScope.page.pageTotal}页,共${requestScope.page.pageTotalCount}条记录到第<input value="${ param.pageNo }" name="pn" id="pn_input"/>页<input id="searchPageBtn" type="button" value="确定"><script type="text/javascript">$(function () {$("#searchPageBtn").click(function () {var pageNo=$("#pn_input").val();// javascript语言中提供了一个location地址栏对象// 它有一个属性href,它可以获取浏览器地址栏中的地址// href属性可读,可写location.href="${pageScope.basePath}manager/bookServlet?action=page&pageNo="+pageNo;});});</script></div>

3.5、分页模块中,页码 1,2,【3】,4,5 的显示,要显示 5 个页 码,并且页码可以点击跳转

 需求:显示 5 个连续的页码,而且当前页码在中间。除了当前页码之外,每个页码都可以点击跳到指定页。

情况1 :如果总页码小于等于 5 的情况,页码的范围是:1—总页码

1 页 1

2 页 1,2

3 页 1,2,3

4 页 1,2,3,4

5 页 1,2,3,4,5

情况 2:总页码大于 5 的情况。假设一共 10 页

小情况 1:当前页码为前面 3 个:1,2,3 的情况,页码范围是:1-5

 【1】2,3,4,5

1【2】3,4,5

1,2【3】4,5

小情况 2:当前页码为最后 3 个,8,9,10,页码范围是:总页码减 4 - 总页码

6,7【8】9,10

6,7,8【9】10

6,7,8,9【10】

小情况 3:4,5,6,7,页码范围是:当前页码减 2 - 当前页码加 2

2,3,4,5,6

3,4,5,6,7

4,5,6,7,8

5,6,7,8,9

<!--页码输出的开始--><c:choose><!-- 情况一:总页码数小于等于5,页码范围是:1---总页码--><c:when test="${requestScope.page.pageTotal<=5}"><c:set var="begin" value="1"/><c:set var="end" value="${requestScope.page.pageTotal}"/></c:when><!-- 情况2:总页码大于5的情况--><c:when test="${requestScope.page.pageTotal>5}"><c:choose><!--小情况1:当前页码为前面3个:1,2,3的情况,页码范围是:1---5 --><c:when test="${requestScope.page.pageNo<=3}"><c:set var="begin" value="1"/><c:set var="end" value="5"/></c:when><!-- 小情况2:当前页码为最后3个:8,9,10的情况,页码范围是:总页码-4---总页码 --><c:when test="${requestScope.page.pageNo>requestScope.page.pageTotal-3}"><c:set var="begin" value="${requestScope.page.pageTotal-4}"/><c:set var="end" value="${requestScope.page.pageTotal}"/></c:when><!-- 小情况3:4,5,6,7, 页码范围是:当前页码-2---当前页码+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"><c:if test="${i == requestScope.page.pageNo}">【${i}】</c:if><c:if test="${i != requestScope.page.pageNo}"><a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a></c:if></c:forEach>

3.6、修改分页后,增加,删除,修改图书信息的回显页面

在修改的请求地址上追加当前页码参数
在book_manager下将pageNo传到book_edit.jsp页面

<c:forEach items="${ requestScope.page.items }" var="books"><tr><td>${books.name}</td><td>${books.price}</td><td>${books.author}</td><td>${books.sales}</td><td>${books.stock}</td><td><a href="manager/bookServlet?action=getBook&id=${books.id}&pageNo=${requestScope.page.pageNo}">修改</a></td><td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${books.id}&pageNo=${requestScope.page.pageNo}">删除</a></td></tr></c:forEach><tr><td></td><td></td><td></td><td></td><td></td><td></td><td><a href="pages/manager/book_edit.jsp?PageNo=${requestScope.page.pageTotal}">添加图书</a></td></tr>

注意点

 添加操作使用PageNo=${requestScope.page.pageTotal原因,添加后需要把当前页码定位到最后一页

 修改和删除操作pageNo=${requestScope.page.pageNo}原因,修改和删除后需停留在当前页码

特别注意

 如果最后一页已满,则增加一条信息之后总页码会多出一页,会溢出不会跳到最后一页,此时pageNo+=1(也可以无限放大),保证添加之后在最后一页(数据边境有效检查)

 如果最后一页只剩下一条数据,则删除一条数据之后,当前页码会大于最大页码,由于数据边境有效检查,会自动跳转到最后一页

在 book_edit.jsp 页面中使用隐藏域记录下 pageNo 参数

说明

通过param输出请求参数pageNo,传到后台定位到最后一页找到最后添加的图书信息

修改add方法

protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 如果增加一条信息之后多出一页,则会溢出不会跳到最后一页,此时pageNo+=1,保证添加之后在最后一页(数据边境有效检查)int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 0);pageNo+=1;//1.获取请求的参数==封装成为Book对象Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());//2.调用bookService.addBook()保存图书bookService.addBook(book);//3.跳到图书列表界面/manager/bookServlet?action=list//请求转发的/表示到工程名// req.getRequestDispatcher("/manager/bookServlet?action=list").forward(req,resp);//请求重定向的/表示到端口号,需要通过req.getContextPath()获取工程名//防止表单重复提交 采用重定向的方式resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=page&PageNo="+pageNo);}

修改delete和update方法

protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取要删除图书的idint id = WebUtils.parseInt(req.getParameter("id"), 0);//2.调用bookService.deleteBookById()删除数据bookService.deleteBookById(id);//3.重定向到/manager/bookServlet?action=listresp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=page&pageNo="+req.getParameter("pageNo"));}protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取请求的参数==封装成为Book对象Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());//2.调用bookService.updateBook()修改图书bookService.updateBook(book);//3.重定向回到图书列表管理页面resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=page&pageNo="+req.getParameter("pageNo"));}

四、首页 index.jsp 的跳转

五、分页条的抽取

5.1、抽取分页条中请求地址为 url 变量

5.1.1、在 page 对象中添加 url 属性和相应get、set方法

public class Page<T> {public static final Integer PAGE_SIZE=4;//当前页码private Integer pageNo;//总页码private Integer pageTotal;//当前页显示数量private Integer pageSize=PAGE_SIZE;//总记录数private Integer pageTotalCount;//当前页数据private List<T> items;//分页条的请求地址private String url;public void setUrl(String url) {this.url = url;}public String getUrl() {return url;}

5.1.2、在 Servlet 程序的 page 分页方法中设置 url 的分页请求地址

5.1.3、修改分页条中请求地址为 url 变量输出,并抽取一个单独的 jsp 页面

<div id="page_nav"><!-- 如果是第一页,则不显示上一页和首页 --><c:if test="${requestScope.page.pageNo>1}"><a href="${ requestScope.page.url }&pageNo=1">首页</a><a href="${ requestScope.page.url }&pageNo=${requestScope.page.pageNo-1}">上一页</a></c:if>

六、首页价格搜索

6.1、ClientBookServlet层代码

protected void pageByPrice(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取请求的参数pageNo和PageSizeint pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);//初始值是第一页int pageSize = WebUtils.parseInt(req.getParameter("PageSize"), Page.PAGE_SIZE);//默认值是4条数据int min = WebUtils.parseInt(req.getParameter("min"), 0);int max = WebUtils.parseInt(req.getParameter("max"), Integer.MAX_VALUE);//2.调用bookService.page(pageNo,pageSize)返回Page对象Page<Book> page = bookService.pageByPrice(pageNo,pageSize,min,max);StringBuilder sb = new StringBuilder("client/bookServlet?action=pageByPrice");if(req.getParameter("min")!=null){sb.append("&min=").append(req.getParameter("min"));}if(req.getParameter("max")!=null){sb.append("&max=").append(req.getParameter("max"));}//导航栏page_nav.jsp中的地址page.setUrl(sb.toString());//3.保存Page对象到request域中req.setAttribute("page",page);//4.请求转发到/pages/client/index.jsp页面req.getRequestDispatcher("/pages/client/index.jsp").forward(req,resp);}

6.2、BookDaoImpl层代码

@Overridepublic Integer queryForPageTotalCountByPrice(int min, int max) {String sql="select count(*) from t_book where price between ? and ?";Number count = (Number) queryForSingleValue(sql,min,max);return count.intValue();}@Overridepublic List<Book> queryForItemsByPrice(int begin, int pageSize, int min, int max) {String sql="select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath " +"from t_book where price between ? and ? order by price limit ?,?";return queryForList(Book.class, sql , min , max , begin , pageSize);}

6.3、前端页面回显页码

<form action="client/bookServlet" method="get"><input type="hidden" name="action" value="pageByPrice"><!-- 地址栏中有对应参数就可以使用param -->价格:<input id="min" type="text" name="min" value="${param.min}"> 元 -<input id="max" type="text" name="max" value="${param.max}"> 元<input type="submit" value="查询"/></form>

书城项目第六阶段---验证码模块

一、登录后显示用户信息

UserServlet 程序中保存用户登录的信息

为什么使用Session?

 因为Session在一个会话范围内有效(打开浏览器访问服务器,直到关闭浏览器),跳转到有些界面可能是通过重定向方式跳转,使用Request域保存会丢失数据

注意点

 loginUser为登陆成功的User对象

修改 login_succuess_menu.jsp
还要修改首页 index.jsp 页面的菜单

二、登出—注销用户

 1、销毁 Session 中用户登录的信息(或者销毁 Session)

 2、重定向到首页(或登录页面)。

UserServlet 程序中添加 logout 方法

/*** 注销*/ protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1、销毁 Session 中用户登录的信息(或者销毁 Session)req.getSession().invalidate(); // 2、重定向到首页(或登录页面)。 resp.sendRedirect(req.getContextPath()); }

修改【注销】的菜单地址

<a href="userServlet?action=logout">注销</a>

三、表单重复提交之-----验证码

表单重复提交有三种常见的情况:

 一:提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键 F5,就会发起最后一次的请求。 造成表单重复提交问题。解决方法:使用重定向来进行跳转

 二:用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败, 就会着急,然后多点了几次提交操作,也会造成表单重复提交。

 三:用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复提交。

验证码解决表单重复提交图解

四、谷歌 kaptcha 图片验证码的使用

谷歌验证码 kaptcha 使用步骤如下:

1、导入谷歌验证码的 jar 包

kaptcha-2.3.2.jar

2、在 web.xml 中去配置用于生成验证码的 Servlet 程序

<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>/kaptcha.jpg</url-pattern> <!--最好以.jpg结尾,因为是图片--></servlet-mapping>

3、在表单中使用 img 标签去显示验证码图片并使用它

<form action="http://localhost:8080/tmp/registServlet" method="get"> 用户名:<input type="text" name="username" > <br> 验证码:<input type="text" style="width: 80px;" name="code"> <img src="http://localhost:8080/tmp/kaptcha.jpg" alt="" style="width: 100px; height: 28px;"> <br> <input type="submit" value="登录"> </form>

4、在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用

@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取 Session 中的验证码,KAPTCHA_SESSION_KEY是谷歌jar包servlet类中的图片对应的key值String token = (String)req.getSession().getAttribute(KAPTCHA_SESSION_KEY); // 删除 Session 中的验证码 req.getSession().removeAttribute(KAPTCHA_SESSION_KEY); String code = req.getParameter("code"); // 获取用户名 String username = req.getParameter("username"); if (token != null && token.equalsIgnoreCase(code)) {System.out.println("保存到数据库:" + username); resp.sendRedirect(req.getContextPath() + "/ok.jsp"); } else {System.out.println("请不要重复提交表单"); } }

5、给图片绑定单击事件切换验证码

// 给验证码的图片,绑定单击事件 $("#code_img").click(function () {// 在事件响应的 function 函数中有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象 // src 属性表示验证码 img 标签的 图片路径。它可读,可写 this.src = "${basePath}kaptcha.jpg?d=" + new Date(); });

注意点

?d=" + new Date()在谷歌浏览器中可以不加,其他浏览器在刷新时会把前一张图片的地址值缓存,如果地址相同则不会刷新

书城项目第六阶段---购物车模块

一、购物车模块分析

二、购物车模型编写

2.1、购物车模型

2.1.1、购物车对象

public class CartItem {private Integer id;private String name;private Integer count;private BigDecimal price;private BigDecimal totalPrice;...

2.1.2、购物车商品项

public class Cart {// private Integer totalCount;// private BigDecimal totalPrice;private Map<Integer,CartItem> items=new LinkedHashMap<>();public Integer getTotalCount() {Integer totalCount=0;//遍历购物车中的所有商品得到商品数量for (Map.Entry<Integer,CartItem> entry : items.entrySet()){totalCount+=entry.getValue().getCount();}// 第二种遍历方法// for (CartItem value : items.values()){//// }return totalCount;}public BigDecimal getTotalPrice() {BigDecimal totalPrice=new BigDecimal(0);for (Map.Entry<Integer,CartItem> entry : items.entrySet()){totalPrice=totalPrice.add(entry.getValue().getTotalPrice());}return totalPrice;}public Map<Integer, CartItem> getItems() {return items;}public void setItems(Map<Integer, CartItem> items) {this.items = items;}@Overridepublic String toString() {return "Cart{" +"totalCount=" + getTotalCount() +", totalPrice=" + getTotalPrice() +", items=" + items +'}';}public void addItem(CartItem cartItem){//先查看购物车中是否已经添加过此商品,如果已添加,则数量累加,总金额更新,如果没有添加过,直接放到集合中即可CartItem item = items.get(cartItem.getId());if (item==null){items.put(cartItem.getId(),cartItem);}else{item.setCount(item.getCount()+1);//数量加一item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));//更新总金额}}public void deleteItem(Integer id){items.remove(id);}public void clear(){items.clear();}public void updateCount(Integer id,Integer count){//先查看购物车中是否有此商品。如果有,修改商品数量,更新总金额CartItem item = items.get(id);if (item!=null){item.setCount(item.getCount()+1);//数量加一item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));//更新总金额}}}

三、加入商品功能的实现

CartServlet 程序中的代码

public class CartServlet extends BaseServlet {private BookService bookService = new BookServiceImpl();protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求的参数商品编号int id = WebUtils.parseInt(req.getParameter("id"), 0);//调用bookService.queryBookById(id) : Book 得到图书的信息Book book = bookService.queryBookById(id);//把图书信息,转换成为CartItem商品项CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());//调用cart.addItem(cartItem);添加商品项Cart cart = (Cart) req.getSession().getAttribute("cart");if (cart == null) {cart = new Cart();req.getSession().setAttribute("cart", cart);}cart.addItem(cartItem);//把最后添加的图书名字添加到Session中req.getSession().setAttribute("lastName",cartItem.getName());//重定向回商品列表页面resp.sendRedirect(req.getHeader("Referer"));}}

index.jsp 页面 js 的代码

<script type="text/javascript"> $(function () {// 给加入购物车按钮绑定单击事件 $("button.addToCart").click(function () {/*** 在事件响应的 function 函数 中,有一个 this 对象,这个 this 对象,是当前正在响应事件的 dom 对象*/ var bookId = $(this).attr("bookId"); location.href = "http://localhost:8080/book/cartServlet?action=addItem&id=" + bookId; }); }); </script>

跳回添加商品的页面图解
![在这里插入图片描述](https://img-/e382979bd8c44682b5cbcec546bf119e.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAb2t-fg==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)

四、购物车的展示

<%@ taglib prefix="c" uri="/jsp/jstl/core" %><%@ page contentType="text/html;charset=UTF-8" language="java" %><!DOCTYPE html><html><head><meta charset="UTF-8"><title>购物车</title><!--静态包含base标签、css样式、jQuery--><%@ include file="/pages/common/head.jsp"%><script>$(function () {//给删除购物车商品绑定单击事件$("a.deleteItem").click(function () {return confirm("确定要删除【"+ $(this).parent().parent().find("td:first").text() +"】吗?");});//给清空购物车商品绑定单击事件$("#clear").click(function () {return confirm("你确定要清空购物车吗?")});//给输入框绑定onchange事件$(".updateCount").change(function () {//获取属性var name=$(this).parent().parent().find("td:first").text();var id=$(this).attr("bookId");var count=this.value;if(confirm("是否将商品【"+ name +"】数量修改为"+ count +"个吗?")){//保存数据到服务器location.href="http://localhost:8080/cartServlet?action=updateCount&id="+id+"&count="+count;}else{//defaultValue属性是表单顶Dom对象的属性。它表示默认的value属性值。this.value=this.defaultValue;}});});</script></head><body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">购物车</span><!--静态包含登录成功之后的页面--><%@ include file="/pages/common/login_success_menu.jsp" %></div><div id="main"><table><tr><td>商品名称</td><td>数量</td><td>单价</td><td>金额</td><td>操作</td></tr><c:if test="${ empty sessionScope.cart.items }"><tr><td colspan="5"><a href="index.jsp">亲,当前购物车为空!快跟小伙伴们去浏览商品吧!!!</a></td></tr></c:if><c:if test="${ not empty sessionScope.cart.items }"><c:forEach items="${sessionScope.cart.items}" var="entry"><tr><td>${entry.value.name}</td><td><input type="text" class="updateCount"bookId="${entry.value.id}" style="width: 80px" value="${entry.value.count}"></td><td>${entry.value.price}</td><td>${entry.value.totalPrice}</td><td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${ entry.value.id }">删除</a></td></tr></c:forEach></c:if></table><c:if test="${ not empty sessionScope.cart.items }"><div class="cart_info"><span class="cart_span">购物车中共有<span class="b_count">${sessionScope.cart.totalCount}</span>件商品</span><span class="cart_span">总金额<span class="b_price">${sessionScope.cart.totalPrice}</span>元</span><span class="cart_span"><a id="clear" href="cartServlet?action=clear">清空购物车</a></span><span class="cart_span"><a href="cartServlet?action=createOrder">去结账</a></span></div></c:if></div><!--静态包含页脚内容--><%@include file="/pages/common/footer.jsp" %></body></html>

五、删除购物车商品项

CartServlet 程序

protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取商品编号int id = WebUtils.parseInt("id", 0);// 获取购物车对象Cart cart = (Cart) req.getSession().getAttribute("cart");if (cart != null) {// 删除购物车商品项cart.deleteItem(id);//重定向回购物车页面resp.sendRedirect(req.getHeader("Referer"));}}

删除的请求地址
删除的确认提示操作

<script type="text/javascript">//给删除购物车商品绑定单击事件$("a.deleteItem").click(function () {return confirm("确定要删除【"+ $(this).parent().parent().find("td:first").text() +"】吗?");});</script>

六、清空购物车

CartServlet 程序

protected void clear(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Cart cart = (Cart) req.getSession().getAttribute("cart");if (cart != null) {cart.clear();//重定向回购物车页面resp.sendRedirect(req.getHeader("Referer"));}}

给清空购物车添加请求地址,和添加 id 属性
清空的确认提示操作

<script type="text/javascript">// 给清空购物车绑定单击事件 $("#clearCart").click(function () {return confirm("你确定要清空购物车吗?"); })</script>

七、修改购物车商品数量

CartServlet 程序

protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {int id = WebUtils.parseInt(req.getParameter("id"), 0);int count = WebUtils.parseInt(req.getParameter("count"), 0);Cart cart = (Cart) req.getSession().getAttribute("cart");if(cart != null){cart.updateCount(id,count);//重定向回购物车页面resp.sendRedirect(req.getHeader("Referer"));}}

修改 pages/cart/cart.jsp 购物车页面
修改商品数量 js 代码

<script type="text/javascript">//给输入框绑定onchange事件$(".updateCount").change(function () {//获取属性var name=$(this).parent().parent().find("td:first").text();var id=$(this).attr("bookId");var count=this.value;if(confirm("是否将商品【"+ name +"】数量修改为"+ count +"个吗?")){//保存数据到服务器location.href="http://localhost:8080/cartServlet?action=updateCount&id="+id+"&count="+count;}else{//defaultValue属性是表单顶Dom对象的属性。它表示默认的value属性值。this.value=this.defaultValue;}});</script>

八、首页,购物车数据回显

在添加商品到购物车的时候,保存最后一个添加的商品名称,在addItem中添加

protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求的参数商品编号int id = WebUtils.parseInt(req.getParameter("id"), 0);//调用bookService.queryBookById(id) : Book 得到图书的信息Book book = bookService.queryBookById(id);//把图书信息,转换成为CartItem商品项CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());//调用cart.addItem(cartItem);添加商品项Cart cart = (Cart) req.getSession().getAttribute("cart");if (cart == null) {cart = new Cart();req.getSession().setAttribute("cart", cart);}cart.addItem(cartItem);//把最后添加的图书名字添加到Session中req.getSession().setAttribute("lastName",cartItem.getName());//重定向回商品列表页面resp.sendRedirect(req.getHeader("Referer"));}

在 pages/client/index.jsp 页面中输出购物车信息

<c:if test="${ not empty sessionScope.cart.items}"><div style="text-align: center"><span>您的购物车中有${sessionScope.cart.totalCount}件商品</span><div>您刚刚将<span style="color: red">${sessionScope.lastName}</span>加入到了购物车中</div></div></c:if><c:if test="${ empty sessionScope.cart.items}"><div><span style="color: red">当前购物车为空</span></div></c:if>

书城项目第七阶段---订单模块

一、订单模块的分析

二、订单模块的实现

2.1、创建订单模块的数据库表

use book; create table t_order( `order_id` varchar(50) primary key, `create_time` datetime, `price` decimal(11,2), `status` int, `user_id` int, foreign key(`user_id`) references t_user(`id`) );create table t_order_item( `id` int primary key auto_increment, `name` varchar(100), `count` int, `price` decimal(11,2), `total_price` decimal(11,2),`order_id` varchar(50), foreign key(`order_id`) references t_order(`order_id`) );

2.2、创建订单模块的数据模型

2.2.1、订单对象

public class Order {private String orderId;private Date createTime;private BigDecimal price;//0未发货 1已发货 2已签收private Integer status = 0;private Integer userId;

2.2.1、订单项

public class OrderItem {private Integer id;private String name;private Integer count;private BigDecimal price;private BigDecimal totalPrice;private String orderId;...

2.3、编写订单模块的 Dao 程序

OrderDao 实现

public class OrderDaoImpl extends BaseDao implements OrderDao {@Overridepublic int saveOrder(Order order) {String sql="insert into t_order(`order_id`,`create_time`,`price`,`status`,`user_id`) values(?,?,?,?,?)";return update(sql,order.getOrderId(),order.getCreateTime(),order.getPrice(),order.getStatus(),order.getUserId());}}

OrderItemDao 实现

public class OrderItemDaoImpl extends BaseDao implements OrderItemDao {@Overridepublic int saveOrderItem(OrderItem orderItem) {String sql="insert into t_order_item(`name`,`count`,`price`,`total_price`,`order_id`) values(?,?,?,?,?)";return update(sql,orderItem.getName(),orderItem.getCount(),orderItem.getPrice(),orderItem.getTotalPrice(), orderItem.getOrderId());}}

2.4、编写订单模块的 Service

OrderService 实现类

public class OrderServiceImpl implements OrderService {private OrderDao orderDao = new OrderDaoImpl(); private OrderItemDao orderItemDao = new OrderItemDaoImpl(); @Override public String createOrder(Cart cart, Integer userId) {// 订单号===唯一性 String orderId = System.currentTimeMillis()+""+userId; // 创建一个订单对象 Order order = new Order(orderId,new Date(),cart.getTotalPrice(), 0,userId); // 保存订单 orderDao.saveOrder(order); // 遍历购物车中每一个商品项转换成为订单项保存到数据库 for (Map.Entry<Integer, CartItem>entry : cart.getItems().entrySet()){// 获取每一个购物车中的商品项 CartItem cartItem = entry.getValue(); // 转换为每一个订单项 OrderItem orderItem = new OrderItem(null,cartItem.getName(),cartItem.getCount(),cartItem.getPrice(),cartItem.getTotalPrice(), orderId); // 保存订单项到数据库 orderItemDao.saveOrderItem(orderItem); }// 清空购物车 cart.clear(); return orderId; } }

2.5、编写订单模块的 web 层和页面联调

修改 OrderService 程序

public class OrderServiceImpl implements OrderService {private OrderDaoImpl orderDao = new OrderDaoImpl();private OrderItemDaoImpl orderItemDao = new OrderItemDaoImpl();private BookDao bookDao = new BookDaoImpl();@Overridepublic String createOrder(Cart cart, Integer userId) {//生成订单String orderId=System.currentTimeMillis()+""+userId;//创建一个订单对象Order order = new Order(orderId,new Date(),cart.getTotalPrice(),0,userId);//保存订单orderDao.saveOrder(order);//遍历购物车中每一个商品项转换成为订单项保存到数据库for (Map.Entry<Integer, CartItem> entry : cart.getItems().entrySet()) {//获取每一个购物车中的商品项CartItem cartItem = entry.getValue();//转换为每一个订单项OrderItem orderItem = new OrderItem(null,cartItem.getName(),cartItem.getCount(),cartItem.getPrice(),cartItem.getTotalPrice(),orderId);//保存订单项到数据库orderItemDao.saveOrderItem(orderItem);//通过商品项id获取bookBook book = bookDao.queryBookById(cartItem.getId());//增加销量book.setSales(book.getSales()+1);//减少库存book.setStock(book.getStock()-1);//更新book信息bookDao.updateBook(book);}//提交订单后清空购物车cart.clear();return orderId;}}

OrderServlet 程序

public class OrderServlet extends BaseServlet{private OrderService orderService = new OrderServiceImpl();protected void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//从Session域中获取cart对象Cart cart = (Cart) req.getSession().getAttribute("cart");//从Session域中获取user对象User loginUser = (User) req.getSession().getAttribute("user");//如果用户没登录,需要登录if (loginUser==null){//转发到登录界面req.getRequestDispatcher("/page/user/login,jsp").forward(req,resp);//下面代码不需要执行return;}//获取用户idInteger userId = loginUser.getId();//传参,调用方法String orderId = orderService.createOrder(cart, userId);//保存orderId到Session域中req.getSession().setAttribute("orderId",orderId);//重定向到/pages/cart/checkout.jspresp.sendRedirect(req.getContextPath()+"/pages/cart/checkout.jsp");}}

修改 pages/cart/cart.jsp 页面,结账的请求地址
修改 pages/cart/checkout.jsp 页面,输出订单号

书城项目第八阶段---过滤请求模块

一、使用 Filter 过滤器拦截/pages/manager/所有内容,实 现权限检查

ManagerFilter 代码

public class ManagerFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;Object user = httpServletRequest.getSession().getAttribute("user");if(user==null){servletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest,servletResponse);}else{filterChain.doFilter(servletRequest,servletResponse);}}@Overridepublic void destroy() {}}

Filter在web.xml 中的配置

<filter><filter-name>ManagerFilter</filter-name><filter-class>com.atguigu.filter.ManagerFilter</filter-class></filter><filter-mapping><filter-name>ManagerFilter</filter-name><url-pattern>/pages/manager/*</url-pattern><url-pattern>/manager/bookServlet</url-pattern></filter-mapping>

二、ThreadLocal 的使用

ThreadLocal 的作用,它可以解决多线程的数据安全问题。

ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)

ThreadLocal 的特点:

 1、ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程)

 2、每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例。

 3、每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型

 4、ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。

三、使用 Filter 和 ThreadLocal 组合管理事务

3.1、使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完成

原理分析
JDBCUtils 工具类的修改

public class JDBCUtils {private static DruidDataSource dataSource;private static ThreadLocal<Connection> conns= new ThreadLocal<Connection>();static {try {Properties properties=new Properties();//读取 jdbc.properties属性配置文件InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");//从流中加载数据properties.load(is);//创建数据库连接池dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);System.out.println(dataSource.getConnection());} catch (Exception e) {e.printStackTrace();}}/*获取数据库连接池中的连接如果返回null,说明获取链接失败*/public static Connection getConnection(){Connection conn = conns.get();if(conn==null){try {conn = dataSource.getConnection();//从数据库连接池中获取连接conns.set(conn);//保存到threadLocal对象中,供后面的jdbc使用conn.setAutoCommit(false);//手动提交事务} catch (SQLException throwables) {throwables.printStackTrace();}}return conn;}/*提交事务并关闭连接,放回数据库连接池*/public static void commitAndClose(){Connection conn = conns.get();if(conn!=null){//不等于null,说明之前操作过数据库try {mit();//提交事务} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {conn.close();//关闭连接} catch (SQLException throwables) {throwables.printStackTrace();}}}//一定要执行remove(),否则会出错(因为Tomcat底层使用了线程池技术)conns.remove();}/*回滚事务并关闭连接,放回数据库连接池*/public static void rollbackAndClose(){Connection conn = conns.get();if(conn!=null){//不等于null,说明之前操作过数据库try {conn.rollback();//回滚事务} catch (SQLException throwables) {throwables.printStackTrace();}finally {try {conn.close();//关闭连接} catch (SQLException throwables) {throwables.printStackTrace();}}}//一定要执行remove(),否则会出错(因为Tomcat底层使用了线程池技术)conns.remove();}}

BaseDao 修改

public abstract class BaseDao {//使用DbUtils操作数据库private QueryRunner queryRunner = new QueryRunner();/*update() 该方法用来执行Insert、update、Delete语句*/public int update(String sql, Object... args) {Connection conn = JDBCUtils.getConnection();try {return queryRunner.update(conn, sql, args);} catch (SQLException throwables) {throwables.printStackTrace();throw new RuntimeException(throwables);}}/*查询返回一个javabean的sql语句*/public <T> T queryForOne(Class<T> type, String sql, Object... args) {Connection conn = JDBCUtils.getConnection();try {return queryRunner.query(conn, sql, new BeanHandler<T>(type), args);} catch (SQLException throwables) {throwables.printStackTrace();throw new RuntimeException(throwables);}}/*查询返回多个javabean的sql语句*/public <T> List<T> queryForList(Class<T> type, String sql, Object... args) {Connection conn = JDBCUtils.getConnection();try {return queryRunner.query(conn, sql, new BeanListHandler<T>(type), args);} catch (SQLException throwables) {throwables.printStackTrace();throw new RuntimeException(throwables);}}/*执行返回一行一列的sql语句*/public Object queryForSingleValue(String sql, Object... args) {Connection conn = JDBCUtils.getConnection();try {return queryRunner.query(conn, sql, new ScalarHandler(), args);} catch (SQLException throwables) {throwables.printStackTrace();throw new RuntimeException(throwables);}}}

3.2、使用 Filter 过滤器统一给所有的 Service 方法都加上 try-catch 来进行管理

TransactionFilter 代码

public class TransactionFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {try {filterChain.doFilter(servletRequest,servletResponse);mitAndClose();//提交事务并关闭} catch (Exception e) {JDBCUtils.rollbackAndClose();//回滚事务并关闭e.printStackTrace();throw new RuntimeException(e);//把异常抛给Tomcat管理,用来展示友好的界面}}@Overridepublic void destroy() {}}

Filter 在 web.xml 中的配置

<filter><filter-name>TransactionFilter</filter-name><filter-class>com.atguigu.filter.TransactionFilter</filter-class></filter><filter-mapping><filter-name>TransactionFilter</filter-name><!-- 拦截当前工程下所有请求--><url-pattern>/*</url-pattern></filter-mapping>

一定要记得把 BaseServlet 中的异常往外抛给 Filter 过滤器

public class BaseServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("UTF-8");resp.setContentType("text/html; charset=UTF-8");String action = req.getParameter("action");//每添加一个业务就需要添加else if语句 很麻烦//优化:利用反射获取对象并执行相应的方法(前提是action字符串内容和方法名相同)try {//获取action所对应的业务,利用反射获取对象Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);//调用目标业务方法method.invoke(this,req,resp);} catch (Exception e) {e.printStackTrace();//该try-catch会捕获子类所有方法抛出的异常因为try中代码会调用子类所有的方法throw new RuntimeException(e);//把异常抛给Filter过滤器}}}

四、将所有异常都统一交给 Tomcat,让 Tomcat 展示友好的错误信息页面

在 web.xml 中的配置

<!-- error-page 标签配置,服务器出错之后,自动跳转的页面--><error-page><!-- error-code 是错误类型--><error-code>404</error-code><!-- location 标签表示。要跳转去的页面路径--><location>/pages/error/error404.jsp</location></error-page><!-- error-page 标签配置,服务器出错之后,自动跳转的页面--><error-page><!-- error-code 是错误类型--><error-code>500</error-code><!-- location 标签表示。要跳转去的页面路径--><location>/pages/error/error500.jsp</location></error-page>

书城项目第九阶段---AJAX优化模块

一、使用 AJAX 验证用户名是否可用

UserServlet 程序中 ajaxExistsUsername 方法

protected void ajaxExistsUsername(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求的参数usernameString username = req.getParameter("username");//调用userService.existsUsername()boolean existsUsername = userService.existsUsername(username);//把返回的结果封装成Map对象Map<String, Object> resultMap = new HashMap<>();resultMap.put("existsUsername",existsUsername);Gson gson = new Gson();String json = gson.toJson(resultMap);resp.getWriter().write(json);}

regist.jsp 页面中的代码

$("#username").blur(function () {//1.获取用户名var username = this.value;$.getJSON("http://localhost:8080/book/userServlet","action=ajaxExistsUsername&username="+username,function (data) {if(data.existsUsername){$("span.errorMsg").text("用户名已存在!");} else{$("span.errorMsg").text("用户名可用!");}});});

二、使用 AJAX 修改把商品添加到购物车

CartServlet 程序

protected void ajaxAddItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求的参数商品编号int id = WebUtils.parseInt(req.getParameter("id"), 0);//调用bookService.queryBookById(id) : Book 得到图书的信息Book book = bookService.queryBookById(id);//把图书信息,转换成为CartItem商品项CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());//调用cart.addItem(cartItem);添加商品项Cart cart = (Cart) req.getSession().getAttribute("cart");if (cart == null) {cart = new Cart();req.getSession().setAttribute("cart", cart);}cart.addItem(cartItem);//把最后添加的图书名字添加到Session中req.getSession().setAttribute("lastName",cartItem.getName());//返回购物车总的商品数量和最后一个添加的商品名称Map<String,Object> resultMap = new HashMap<>();resultMap.put("totalCount",cart.getTotalCount());resultMap.put("lastName",cartItem.getName());Gson gson = new Gson();String resultMapJsonString = gson.toJson(resultMap);resp.getWriter().write(resultMapJsonString);}

pages/client/index.jsp 页面

html 代码

<div style="text-align: center"><c:if test="${ not empty sessionScope.cart.items}"><span id="cartTotalCount">您的购物车中有${sessionScope.cart.totalCount}件商品</span><div>您刚刚将<span style="color: red" id="cartLastName">${sessionScope.lastName}</span>加入到了购物车中</div></c:if><c:if test="${ empty sessionScope.cart.items}"><span id="cartTotalCount"></span><div><span style="color: red" id="cartLastName">当前购物车为空</span></div></c:if></div>

javaScript 代码

$(function () {//给购物车按钮加入单击事件$("button.addToCart").click(function () {var bookId = $(this).attr("bookId");// location.href = "http://localhost:8080/book/cartServlet?action=addItem&id=" + bookId;//发送ajax请求,添加商品到购物车$.getJSON("http://localhost:8080/book/cartServlet","action=ajaxAddItem&id="+bookId,function () {$("#cartTotalCount").text("您的购物车中有"+data.totalCount+"件商品");$("#cartLastName").text(data.lastName);});});});

如果觉得《书城项目zzz》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
相关阅读
书城-项目总结

书城-项目总结

2023-04-12

尚硅谷书城项目总结

尚硅谷书城项目总结

2023-10-13

书城项目总结

书城项目总结

2022-11-19

书城项目学习

书城项目学习

2024-03-07