Javaweb书城项目

目录

第一阶段:表单验证 

需求

流程

代码实现

第二阶段:用户登录和注册

需求

Java EE项目的三层结构

创建书城的数据库和表

POJO层

编写工具类JdbcUtils

DAO层

Service层

web层

用户登录功能的实现

第三阶段:代码优化

页面jsp动态化

 抽取页面中相同的内容

登录,注册错误提示,及表单回显 

BaseServlet的抽取

第四阶段:图书模块

MVC概念

图书模块

 第五阶段:图书分页

分页模块的分析

分页模型Page的抽取

前台分页的初步实现——首页 index.jsp 的跳转

分页条的抽取

首页价格搜索

第六阶段:cookie和session的应用

登陆---显示用户名

 登出---注销用户

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

谷歌 kaptcha 图片验证码的使用

第七阶段:购物车

购物车模块分析

购物车模型编写

第八阶段:订单

订单模块的分析

订单模块的实现

第九阶段:过滤器

ThreadLocal的使用

使用Filter和ThreadLocal组合管理事务

第十阶段:使用AJAX


温故知新,学有所获~~书城项目是整个Javaweb阶段的实操项目

Javaweb书城项目资源

第一阶段:表单验证 

需求

验证用户名:必须由字母,数字下划线组成,并且长度为 5 12
验证密码:必须由字母,数字下划线组成,并且长度为5到 12
验证确认密码:和密码相同
邮箱验证:xxxxx@xxx.com
验证码:现在只需要验证用户已输入。因为还没讲到服务器。验证码生成。

流程

1.在idea中新建模块book_static(静态工程)
2.将书城的静态资源拷贝到book_static工程下

分为购物车,后台管理,订单,用户登录注册模块 

3.在register.html中实现代码逻辑

代码实现

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>尚硅谷会员注册页面</title>
		<link type="text/css" rel="stylesheet" href="../../static/css/style.css" >
		<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@xxx.com
					//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>
	<style type="text/css">
		.login_form{
			height:420px;
			margin-top: 25px;
		}

	</style>
	</head>
	<body>
		<div id="login_header">
			<img class="logo_img" alt="" src="../../static/img/logo.gif" >
		</div>

			<div class="login_banner">

				<div id="l_content">
					<span class="login_word">欢迎注册</span>
				</div>

				<div id="content">
					<div class="login_form">
						<div class="login_box">
							<div class="tit">
								<h1>注册尚硅谷会员</h1>
								<span class="errorMsg"></span>
							</div>
							<div class="form">
								<form action="http://localhost:8080">
									<label>用户名称:</label>
									<input class="itxt" type="text" placeholder="请输入用户名"
										   autocomplete="off" tabindex="1" name="username" id="username" />
									<br />
									<br />
									<label>用户密码:</label>
									<input class="itxt" type="password" placeholder="请输入密码"
										   autocomplete="off" tabindex="1" name="password" id="password" />
									<br />
									<br />
									<label>确认密码:</label>
									<input class="itxt" type="password" placeholder="确认密码"
										   autocomplete="off" tabindex="1" name="repwd" id="repwd" />
									<br />
									<br />
									<label>电子邮件:</label>
									<input class="itxt" type="text" placeholder="请输入邮箱地址"
										   autocomplete="off" tabindex="1" name="email" id="email" />
									<br />
									<br />
									<label>验证码:</label>
									<input class="itxt" type="text" style="width: 150px;" id="code"/>
									<img alt="" src="../../static/img/code.bmp" style="float: right; margin-right: 40px">
									<br />
									<br />
									<input type="submit" value="注册" id="sub_btn" />
								</form>
							</div>

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

第二阶段:用户登录和注册

需求

需求1:用户注册
需求如下:1)访问注册页面
2)填写注册信息,提交给服务器
3)服务器应该保存用户
4)当用户已经存在----提示用户注册 失败,用户名已存在
5)当用户不存在-----注册成功
需求2:用户登陆
需求如下:
1)访问登陆页面
2)填写用户名密码后提交
3)服务器判断用户是否存在

Java EE项目的三层结构

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

项目的包分类

web层                 com.atguigu.web/servlet/controller
service层            com.atguigu.service                                 Service接口包
                           com.atguigu.service.impl                         Service接口实现类
dao持久层          com.atguigu.dao                                      Dao接口包
                           com.atguigu.dao.impl                              Dao接口实现类
实体bean对象    com.atguigu.pojo/entity/domain/bean      JavaBean 类
测试包                com.atguigu.test/junit
工具类                com.atguigu.utils

IDEA搭建书城项目开发环境 

创建动态java工程 file--new--module--勾选Web Application,在web中导入静态文件资源pages,static,index.html,在src下创建对应的包

创建书城的数据库和表

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@atguigu.com');

select * from t_user;

POJO层

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

public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    ...
    //使用ctrl+alt+S快速生成get/set,toString方法和构造器
}

编写工具类JdbcUtils

1导入需要的jar(数据库和连接池需要) 

druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
在WEB-INF文件夹下新建lib文件夹,用于存放jar包.(把相关jar复制进来).添加jar包方法如下:
file-project structure
1.

2.找到相关的jar包,先不绑定具体的module,更改libraries的名字

3. 把book_lib添加到book工程

4.添加到artifacts中

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

username=root
password=root
url=jdbc:mysql://localhost:3306/book
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10

3.编写JdbcUtils工具类

DruidDataSourceFactory.createDataSource(properties);

public class JdbcUtils {
    //创建数据库连接池
    private static DruidDataSource dataSource;
    static {
        try {
            Properties properties = new Properties();
            // 读取jdbc.properties属性配置文件
            InputStream inputStream = 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.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);
        }
    }
}

DAO层

编写BaseDao 

在dao包下,BaseDao是复用的,没有具体对象,使用抽象类型

使用dbutils操作数据库,导入DBUtils jar 包    commons-dbutils-1.3.jar。对如下操作陌生的可参考博文JDBC核心技术
public abstract class BaseDao {
    //使用dbutils操作数据库
    private QueryRunner queryRunner = new QueryRunner();

    /**
     * update()方法用来执行:Insert\Update\Delete语句
     * @param sql
     * @param args
     * @return 如果返回-1,说明执行失败,返回其他表示影响的行数
     */
    public int update(String sql,Object ... args) {
        Connection conn = null;
        try {
            conn = jdbcUtils.getConnection();
            return queryRunner.update(conn, sql, args);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            jdbcUtils.close(conn);
        }
        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 conn = null;
        try {
           conn = jdbcUtils.getConnection();
            return queryRunner.query(conn,sql,new BeanHandler<>(type),args);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            jdbcUtils.close(conn);
        }
        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 conn = null;
        try {
            conn = jdbcUtils.getConnection();
            return queryRunner.query(conn,sql,new BeanListHandler<>(type),args);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            jdbcUtils.close(conn);
        }
        return null;
    }

    /**
     * 返回执行一行一列的sql
      * @param sql 执行的sql语句
     * @param args sql对应的参数值
     ** @return
     */    
     public Object queryForSingleValue(String sql,Object...args){
         Connection conn = null;
         try {
             conn = jdbcUtils.getConnection();
             return queryRunner.query(conn,sql,new ScalarHandler(),args);
         } catch (SQLException throwables) {
             throwables.printStackTrace();
         } finally {
             jdbcUtils.close(conn);
         }
         return null;
     }
}

编写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);
}

UserDaoImlp实现类

实现UserDao的方法

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);
    }

    @Override
    public int saveUser(User user) {
        String sql ="insert into t_user (`username`,`password`,`email`) values(?,?,?)";
        return update(sql,user.getUsername(),user.getPassword(),user.getEmail());
    }
}

 UserDao测试类

自动生成测试类

public class UserDaoImplTest {
    UserDao userDao = new UserDaoImpl();
    @Test
    public void queryUserByUsername() {
        if(userDao.queryUserByUsername("admin")==null){
            System.out.println("用户名可用");
        }else{
            System.out.println("用户名已存在");
        }
    }

    @Test
    public void queryUserByUsernameAndPassword() {
        if ( userDao.queryUserByUsernameAndPassword("admin","admin1234") == null) {
            System.out.println("用户名或密码错误,登录失败");
        } else {
            System.out.println("查询成功");}
    }

    @Test
    public void saveUser() {
        System.out.println( userDao.saveUser(new User(null,"wzg168", "123456", "wzg168@qq.com")) );
    }
}

Service层

编写UserService和测试

service表示业务,一个业务一个方法.(业务包含登录/注册/检查用户是否可用)

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 existUsername(String username);
}

UserServiceImpl实现类

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();

    @Override
    public void registUser(User user) {
        userDao.saveUser(user);
    }

    @Override
    public User login(User user) {
        return userDao.queryUserByUsernameAndPassword(user.getUsername(),user.getPassword());
    }

    @Override
    public boolean existUsername(String username) {
        if(userDao.queryUserByUsername(username)==null){
            //等于null,没找到表示可用
            return false;
        }
        return true;
    }
}

 UserService测试

public class UserServiceImplTest {
    UserService userService = new UserServiceImpl();
    @Test
    public void registUser() {
        userService.registUser(new User(null,"bbj168","666666","bbj168@qq.com"));
        userService.registUser(new User(null,"abc168","666666","abc168@qq.com"));
    }

    @Test
    public void login() {
        System.out.println(userService.login(new User(null, "asd", "123456", null)));
        System.out.println(userService.login(new User(null, "abc168", "666666", "abc168@qq.com")));
    }

    @Test
    public void existUsername() {
        if(userService.existUsername("abc1608")){
            System.out.println("用户名已存在");
        }else{
            System.out.println("用户名可用");
        }
    }
}

web层

实现用户注册的功能
图解用户注册的流程

修改regist.htmlregist_success.html页面

路径的使用:web阶段使用base相对 框架之后使用绝对路径
1 、添加 base 标签
<!--写base标签,永远固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">

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

以下是几个修改的示例:
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script

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

编写RegistServlet程序

在web包下
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);
    }
    }
}

IDEADebug调试的使用

Debug调试代码,首先需要两个元素:断点+Debug启动服务器

1、断点,只需要在代码需要停的行的左边上单击,就可以添加和取消
2、Debug启动Tomcat运行代码:

 测试工具栏:

变量窗口

它可以查看当前方法范围内所有有效的变量。

方法调用栈窗口

1、方法调用栈可以查看当前线程有哪些方法调用信息

2、下面的方法调用上一行的方法其他常用调试相关按钮:

其他常用调试相关按钮:

用户登录功能的实现

图解用户登录

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

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

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

以下是几个修改的示例:
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
3、修改 login.html 表单的提交地址和请求方式

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);
    }
    }
}

第三阶段:代码优化

页面jsp动态化

为了实现页面的反馈效果,将静态html改为jsp

1、在html面顶行添加page指令。
        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2、修改文件后缀名为:.jsp(右键--refactor--rename)
3、使用IDEA搜索替换.html 为.jsp(edit--find --replace in path),将文件中出现的html内容改为jsp

 抽取页面中相同的内容

如上内容在多个页面中出现,我们将其抽取出为一个公共的JSP页面然后引用,减少代码维护量 

登录成功后的菜单
login_success_menu.jsp
<div>
    <span>欢迎<span class="um_span">韩总</span>光临尚硅谷书城</span>
    <a href="../order/order.jsp">我的订单</a>
    <a href="../../index.jsp">注销</a>&nbsp;&nbsp;
    <a href="../../index.jsp">返回</a>
</div>
order.jsp/login_success.jsp/cart.jsp/checkout.jsp
<%--静态包含登录成功之后的菜单--%>
<%@include file="/pages/common/login_success_menu.jsp"%>
head css jquery base 标签
<!--写base标签,永远固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">

<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
每个jsp文件中都改动
<%--静态包含base标签,css样式,iquery文件--%>	
<%@include file="/pages/common/head.jsp"%>

每个页面的页脚

<div id="bottom">
    <span>
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值