书城项目
几个月前刚接触JavaWeb的项目,现在回过头来对所学知识进行总结
1.项目介绍
本项目是基于原生底层技术,核心技术点主要有:
Servlet程序、Filter过滤器、Listener监听器、jsp页面、EL表达式、JSTL标签库、jQuery框架、Cookie技术、Session会话、JSON使用、Ajax请求。
目标:、理解、会用。并为后期框架、框架的学习打下坚实的基础
第一阶段–表单验证
用于在用户登录和注册时使用的逻辑
验证用户名:必须由字母,数字下划线组成,并且长度为 5 到 12位验证密码:必须由字母,数字下划线组成,并且长度为 5 到 12 位
验证确认密码:和密码相同
邮箱验证:xxxxx@xxx.com
验证码:现在只需要验证用户已输入。因为还没讲到服务器。验证码生成。
1、新建一个模块
2、把书城的静态资源拷贝到 05_book_static 工程下:
3、验证实现如下:
<script type="text/javascript">
//给注册绑定单击事件
$("#sub_btn").click(function () {
//验证用户名:必须由字母,数字下划线组成,并且长度为5到12位
//1 获取用户名输入框里的内容
var usernameTest = $("#username").val();
//2 创建正则表达式对象
var usernamePatt = /^\w{5,12}$/;
//3 使用test方法验证
if (!usernamePatt.test(usernameTest)) {
//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.length != passwordText.length) {
//提示用户
$("span.errorMsg").text("确认密码和密码不一致!");
return false;
}
//邮箱验证:xxxx@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 codeTest = $("#code").val();
//去掉验证码前后空格
codeTest = $.trim(codeTest);
if (codeTest == null || codeTest == "") {
//提示用户
$("span.errorMsg").text("验证码不能为空!");
return false;
}
//去掉错误信息
$("span.errorMsg").text("");
});
});
</script>
第二阶段–用户注册和登陆
1.JavaEE项目的三层架构
分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。
web 层 | com.atguigu.web/servlet/controller | 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 |
搭建书城项目开发环境:
1、先创建书城需要的数据库和表。
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;
2、编写数据库表对应的 JavaBean
对象
/**
* @outhor Mr.JK
* @create 2020-04-03 20:49
*/
public class User {
private Integer id;
private String username;
private String password;
private String email;
public User() {
}
public User(Integer id, String username, String password, String email) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
3、编写工具类 JdbcUtils
3.1、导入需要的jar 包(数据库和连接池需要):
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
以下是测试需要:
hamcrest-core-1.3.jar
junit-4.12.jar
3.2、在 src 源码目录下编写 jdbc.properties 属性配置文件
username=root
password=123456
url=jdbc:mysql://localhost:3306/book?characterEncoding=UTF-8
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
3.3、编写jdbcUtils工具类:
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);
//测试代码
//System.out.println(dataSource.getConnection());
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
} finally {
}
return conn;
}
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.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);
}
}
}
4、编写BaseDao
4.1、导入 DBUtils 的 jar 包
commons-dbutils-1.3.jar
4.2、编写 BaseDao:
/**
* @outhor Mr.JK
* @create 2020-04-03 21:17
*/
public abstract class BaseDao {
//使用DbUtils操作数据库
private QueryRunner queryRunner = new QueryRunner();
/**
* update()方法用来执行,Insert/Update/Delete语句
* @return 如果返回-1,说明执行失败 返回其他表示影响的行数
*/
public int update(String sql,Object ...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.update(connection,sql,args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 查询返回一个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 connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new BeanHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 查询返回多个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 connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection, sql, new BeanListHandler<T>(type), args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 执行返回一行一列的sql语句
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @return
*/
public Object queryForSingleValue(String sql,Object ...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new ScalarHandler(),args);
} catch (SQLException e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
5、编写 UserDao 和测试
UserDao 接口:
/**
* @outhor Mr.JK
* @create 2020-04-03 21:34
*/
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 返回-1表示操作失败,其他是sql语句影响的行数
*/
public int saveUser(User user);
}
UserDaoImpl 实现类:
/**
* @outhor Mr.JK
* @create 2020-04-03 21:40
*/
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 测试:
/**
* @outhor Mr.JK
* @create 2020-04-03 21:49
*/
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","admin") == null){
System.out.println("用户名或密码错误,登陆失败");
} else {
System.out.println("查询成功");
}
}
@Test
public void saveUser() {
System.out.println(userDao.saveUser(new User(null,"lin","123456","lin@qq.com")));
}
}
6、编写 UserService 和测试
UserService 接口:
/**
* @outhor Mr.JK
* @create 2020-04-04 10:08
*/
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实现类:
/**
* @outhor Mr.JK
* @create 2020-04-04 10:11
*/
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 测试:
/**
* @outhor Mr.JK
* @create 2020-04-04 10:15
*/
public class UserServiceImplTest {
UserService userService = new UserServiceImpl();
@Test
public void registUser() {
userService.registUser(new User(null,"lin","123456","123@qq.com"));
}
@Test
public void login() {
System.out.println(userService.login(new User(null,"lin","123456","1")));
}
@Test
public void existUsername() {
if (userService.existUsername("lin")){
System.out.println("用户名已存在!");
}else {
System.out.println("用户名可用!");
}
}
}
7、编写 web 层
7.1、实现用户注册的功能
7.1.1、图解用户注册的流程:
7.1.2、修改 regist.html 和 regist_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、修改注册表单的提交地址和请求方式
7.1.3、编写 RegistServlet 程序
/**
* @outhor Mr.JK
* @create 2020-04-05 16:33
*/
public class RegistServlet extends BaseServlet {
private UserService userService = new UserServiceImpl();
/**
* 处理注册的功能
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
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");
User user = WebUtils.copyParmToBean(req.getParameterMap(), new User());
// 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);
}
}
7.2、用户登录功能的实现
7.2.1、图解用户登录
7.2.2、修改 login.html 页面和 login_success.html 页面
1、添加 base 标签
<!--写base 标签,永远固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">
2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)
3、修改 login.html 表单的提交地址和请求方式
2,3如上
7.2.3、LoginServlet 程序
/**
* @outhor Mr.JK
* @create 2020-04-05 16:33
*/
public class LoginServlet extends BaseServlet {
private UserService userService = new UserServiceImpl();
/**
* 处理登录的功能
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void login(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));
if (loginUser == null){
//调回登录页面
req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
}else {
//登录成功页面
//System.out.println("登录成功");
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req,resp);
}
}
}
第三阶段–前后端整合
1.页面 jsp 动态化
1、在html 页面顶行添加page 指令。
2、修改文件后缀名为:.jsp
3、使用 IDEA 搜索替换.html 为.jsp(快捷键:Ctrl+Shift+R)
2.抽取页面中相同的内容
1、head 中 css、jquery、base 标签
<%
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":"
+ request.getServerPort()
+ request.getContextPath() + "/";
%>
<%=basePath%>
<!--写base 标签,永远固定相对路径跳转的结果-->
<base href="<%=basePath%>">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
2、每个页面的页脚
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
3、登录成功后的菜单
<div>
<span>欢迎<span class="um_span">韩总</span>光临尚硅谷书城</span>
<a href="../order/order.jsp">我的订单</a>
<a href="../../index.jsp">注销</a>
<a href="../../index.jsp">返回</a>
</div>
4、manager 模块的菜单
<div>
<a href="book_manager.jsp">图书管理</a>
<a href="order_manager.jsp">订单管理</a>
<a href="../../index.jsp">返回商城</a>
</div>
3.登录,注册错误提示,及表单回显
以登录回显为示例:
Servlet 程序端需要添加回显信息到Request 域中
jsp 页面,需要输出回显信息
4.BaseServlet的抽取
在实际的项目开发中,一个模块,一般只使用一个 Servlet
程序。
代码优化一:代码优化:合并 LoginServlet 和 RegistServlet 程序为 UserServlet 程序
UserServlet 程序:
public class UserServlet extends BaseServlet {
private UserService userService = new UserServiceImpl();
/**
* 处理登录的功能
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void login(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));
if (loginUser == null){
//调回登录页面
//把错误信息,和回显的表单项信息,保存到Request域中
req.setAttribute("msg","用户名或密码错误!");
req.setAttribute("username",username);
//System.out.println("登录失败");
req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
}else {
//登录成功页面
//System.out.println("登录成功");
//将登陆成功的信息保存报Session域
req.getSession().setAttribute("user",loginUser);
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req,resp);
}
}
/**
* 处理注册的功能
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Session中的验证码
String token = (String)req.getSession().getAttribute(KAPTCHA_SESSION_KEY);
//删除Session中的验证码
req.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
//1.获取请求的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String code = req.getParameter("code");
User user = WebUtils.copyParmToBean(req.getParameterMap(), new User());
//2.检查 验证码是否正确 ==== 用谷歌验证码
if (token != null && token.equalsIgnoreCase(code)){
//3.检查 用户名是否可用
if (userService.existUsername(username)){
//不可用
System.out.println("用户名[" + username + "]已存在!");
//把回显信息,保存到Request域中
req.setAttribute("msg","用户名已存在!");
req.setAttribute("username",username);
req.setAttribute("email",email);
//跳回注册页面
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);
}else{
//可用,调用Service保存到数据库
userService.registUser(new User(null,username,password,email));
req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req,resp);
}
}else {
//把回显信息,保存到Request域中
req.setAttribute("msg","验证码错误!");
req.setAttribute("username",username);
req.setAttribute("email",email);
System.out.println("验证码[" + code +"]错误");
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
String action = req.getParameter("action"); if ("login".equals(action)) {
login(req, resp);
} else if ("regist".equals(action)) {
regist(req, resp);
}
}
还要给
login.jsp 添加隐藏域和修改请求地址
给 tegist.jsp 页面添加隐藏域 action,和修改请求地址
优化代码二:使用反射优化大量 else if 代码:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");//关键 解决post乱码问题
//解决响应的中文乱码
resp.setContentType("text/html; charset=UTF-8");
String action = req.getParameter("action");
try {
Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
method.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
}
代码优化三:抽取BaseServlet 程序。
BaseServlet 程序代码:
/**
* @outhor Mr.JK
* @create 2020-04-05 18:01
*/
public abstract class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");//关键 解决post乱码问题
//解决响应的中文乱码
resp.setContentType("text/html; charset=UTF-8");
String action = req.getParameter("action");
try {
Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
method.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
修改 UserServlet 程序继承BaseServlet 程序。
5.数据的封装和抽取 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 工具类:
**
* @outhor Mr.JK
* @create 2020-04-05 19:57
*/
public class WebUtils {
public static <T> T copyParmToBean(Map values, T bean){
try {
//使用第三方jar包,将数据封装到Bean对象
BeanUtils.populate(bean,values);
}catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
第四阶段–修改回显
以登录为示例:
第五阶段–图书模块
MVC 概念
MVC 全称:Model 模型、 View 视图、 Controller 控制器。
MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。
View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作——
JSP/HTML。
Controller控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。转到某个页面。或者是重定向到某个页面。
Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码——
JavaBean/domain/entity/pojo。
MVC 是一种思想
MVC的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了降低耦合度)
MVC 的作用还是为了降低耦合。让代码合理分层。方便后期升级和维护。
1、图书模块
1.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');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'C++编程思想' , '刚哥' , 45.5 , 14 , 95 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '蛋炒饭' , '周星星' , 9.9, 12 , 53 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '赌神' , '龙伍' , 66.5, 125 , 535 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'Java 编程思想' , '阳哥' , 99.5 , 47 , 36 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'JavaScript 从入门到精通' , '婷姐' , 9.9 , 85 , 95 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'cocos2d-x 游戏编程入门' , '国哥' , 49, 52 , 62 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'C 语言程序设计' , '谭浩强' , 28 , 52 , 74 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'Lua 语言程序设计' , '雷丰阳' , 51.5 , 48 , 82 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '西游记' , '罗贯中' , 12, 19 , 9999 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '水浒传' , '华仔' , 33.05 , 22 , 88 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '操作系统原理' , '刘优' , 133.05 , 122 , 188 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '数据结构 java 版' , '封大神' , 173.15 , 21 , 81 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'UNIX 高级环境编程' , '乐天' , 99.15 , 210 , 810 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , 'javaScript 高级编程' , '国哥' , 69.15 , 210 , 810 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '大话设计模式' , '国哥' , 89.15 , 20 , 10 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`) values(null , '人月神话' , '刚哥' , 88.15 , 20 , 80 , 'static/img/default.jpg');
## 查看表内容
select id,name,author,price,sales,stock,img_path from t_book;
1.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";
}
1.3、编写图书模块的 Dao 和测试 Dao
Dao 接口
/**
* @outhor Mr.JK
* @create 2020-04-06 11:28
*/
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 实现类:
/**
* @outhor Mr.JK
* @create 2020-04-06 11:31
*/
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` 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` from t_book";
return queryForList(Book.class,sql);
}
}
BookDao 的测试:
/**
* @outhor Mr.JK
* @create 2020-04-06 11:42
*/
public class BookDaoImplTest {
private BookDao bookDao = new BookDaoImpl();
@Test
public void addBook() {
bookDao.addBook(new Book(null, "帅哥", "坤哥", new BigDecimal(999), 1, 1, null));
}
@Test
public void deleteBookById() {
bookDao.deleteBookById(21);
}
@Test
public void updateBook() {
bookDao.updateBook(new Book(21, "帅哥秘籍", "坤哥", new BigDecimal(999), 1, 1, null));
}
@Test
public void queryBookById() {
System.out.println(bookDao.queryBookById(21));
}
@Test
public void queryBooks() {
for (Book querybook : bookDao.queryBooks()) {
System.out.println(querybook);
}
}
}
1.4、编写图书模块的 Service 和测试 Service
BookService 接口
/**
* @outhor Mr.JK
* @create 2020-04-06 13:09
*/
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 实现类:
/**
* @outhor Mr.JK
* @create 2020-04-06 13:11
*/
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();
}
}
BookService 的测试:
/**
* @outhor Mr.JK
* @create 2020-04-06 13:37
*/
public class BookServiceImplTest {
private BookService bookService = new BookServiceImpl();
@Test
public void addBook() {
bookService.addBook(new Book(null, "帅哥", "坤哥", new BigDecimal(999), 1, 1, null));
}
@Test
public void deleteBookById() {
bookService.deleteBookById(27);
}
@Test
public void updateBook() {
bookService.updateBook(new Book(21, "帅哥秘籍", "坤哥", new BigDecimal(999), 1, 1, null));
}
@Test
public void queryBookById() {
System.out.println(bookService.queryBookById(27));
}
@Test
public void queryBooks() {
for (Book querybook : bookService.queryBooks()) {
System.out.println(querybook);
}
}
}
1.5、编写图书模块的 Web 层,和页面联调测试
1.5.1、图书列表功能的实现
1、图解列表功能流程:
2、BookServlet 程序中添加 list 方法
protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.通过BookServlet查询全部图书
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);
}
3、修改【图书管理】请求地址
4、修改 pages/manager/book_manager.jsp 页面的数据遍历输出
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图书管理</title>
</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.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}&pageNo=${requestScope.page.pageNo}">修改</a></td>
<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.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>
</table>
<%--静态包含分页条--%>
<%@include file="/pages/common/page_nav.jsp"%>
</div>
<%-- 静态包含页脚内容--%>
<%@include file="/pages/common/footer.jsp"%>
</body>
</html>
1.5.2、前后台的简单介绍
1.5.3、添加图书功能的实现
1.5.3.1、添加图书流程细节:
1.5.3.2、问题说明:表单重复提交:
当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键 F5,就会发起浏览器记录的最后一次请求。
1.5.3.3、BookServlet 程序中添加 add 方法
protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"),0);
pageNo+=1;
//1.获取请求的参数==封装成为Book对象
Book book = WebUtils.copyParmToBean(req.getParameterMap(), new Book());
//2.调用BookService.addBook()保存图书
bookService.addBook(book);
//3.跳出图书列表页面
// /manager/bookServlet?action=list
//这里跳转要用重定向,不能用请求转发,会有bug,F5刷新,会一直添加
resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + pageNo);
}
1.5.3.4、修改 book_edit.jsp 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="cmn-hans">
<head>
<meta charset="UTF-8">
<title>编辑图书</title>
<%-- 静态包含base标签,css样式,jQuery文件 --%>
<%@include file="/pages/common/head.jsp"%>
<style type="text/css">
h1 {
text-align: center;
margin-top: 200px;
}
h1 a {
color:red;
}
input {
text-align: center;
}
</style>
</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">
<form action="manager/bookServlet" method="post" >
<input type="hidden" name="pageNo" value="${param.pageNo}" />
<input type="hidden" name="action" value="${empty param.id ? "add":"update"}" />
<input type="hidden" name="id" value="${requestScope.book.id}" />
<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="${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>
</table>
</form>
</div>
<%-- 静态包含页脚内容--%>
<%@include file="/pages/common/footer.jsp"%>
</body>
</html>
1.5.4、删除图书功能的实现
1.5.4.1、图解删除流程:
1.5.4.2、BookServlet 程序中的 delete 方法:
protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求的参数id,图书编号
int id = WebUtils.parseInt(req.getParameter("id"),0);
//2.调用bookServlet.deleteBookById();删除图书
bookService.deleteBookById(id);
//3.重定向回图书列表管理页面
resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + req.getParameter("pageNo"));
}
1.5.4.3、给 WebUtils 工具类添加转换 int 类型的工具方法
/**
* 将字符串转换为int类型的
* @param strInt
* @param defaultValue
* @return
*/
public static int parseInt(String strInt,int defaultValue){
if (strInt == null){//前台客户端传参一定会是null,null转integer会报错
return defaultValue;
}
try {
return Integer.parseInt(strInt);
} catch (Exception e) {
//e.printStackTrace();
}
return defaultValue;
}
1.5.4.4、修改删除的连接地址:
1.5.4.5、给删除添加确认提示操作:
<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() + "】?");
// return false// 阻止元素的默认行为===不提交请求
});
});
</script>
1.5.5、修改图书功能的实现
1.5.5.1:图解修改图书细节:
1.5.5.2、更新【修改】的请求地址:
1.5.5.3、BookServlet 程序中添加 getBook 方法:
protected void getBook(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求的参数图书编号
int id = WebUtils.parseInt(req.getParameter("id"),0);
//2.调用bookServlet.queryBookId查询图书编号
Book book = bookService.queryBookById(id);
//3.保存图书到Request域中
req.setAttribute("book",book);
//4.请求转发到 pages/manager/book_edit.jsp页面
req.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(req,resp);
}
1.5.5.4、在 book_edit.jsp 页面中显示修改的数据
<div id="main">
<form action="manager/bookServlet" method="post" >
<input type="hidden" name="pageNo" value="${param.pageNo}" />
<input type="hidden" name="action" value="${empty param.id ? "add":"update"}" />
<input type="hidden" name="id" value="${requestScope.book.id}" />
<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="${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>
</table>
</form>
</div>
1.5.5.5、在 BookServlet 程序中添加 update 方法:
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求的参数==封装称为Book对象
Book book = WebUtils.copyParmToBean(req.getParameterMap(), new Book());
//2.调用BookServlet.update
bookService.updateBook(book);
//3.重定向回图书列表管理页面
resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + req.getParameter("pageNo"));
}
1.5.5.6、解决 book_edit.jsp页面,即要实现添加,又要实现修改操作
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!