今天是跟着p222-225。
主要完成了将html文件转为jsp文件,完成服务器回显的基础。
这里就是先添加一个头文件代码,再改文件后缀。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
这里遗留了一个问题,好像页面布局出了一点问题。
然后在原jsp文件中使用静态包含
<%@ include file="/pages/common/head.jsp"%>
在sevlet文件中setAttribute
req.setAttribute("msg", "用户名已存在");
req.setAttribute("username", username);
req.setAttribute("email", email);
在jsp文件中获取
<%=request.getAttribute("msg")==null?"":request.getAttribute("msg")%>
今天是跟着p226-229。
主要完成代码结构的优化,利用到了反射和BeanUtils。
避免了代码冗余。这里涉及的操作就是整合方法,web.xml里面的配置,jsp里面的添加隐藏域和修改请求地址。
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
try {
// 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class,
HttpServletResponse.class);
// System.out.println(method);
// 调用目标业务 方法
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
public abstract class BaseServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
try {
// 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
// System.out.println(method);
// 调用目标业务 方法
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
为什么要使用?目的是避免req.getparameter的大量代码和创建对象的set的大量代码。
如何使用?
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 {
System.out.println("注入之前:" + bean);
/**
* 把所有请求的参数都注入到 user 对象中
*/
BeanUtils.populate(bean, value);
System.out.println("注入之后:" + bean);
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}
主要跟着p330-335
${requestScope.msg}$
1、MVC 概念
MVC 全称:Model 模型、 View 视图、 Controller 控制器。
MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。
View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作——JSP/HTML。
Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet转到某个页面。或者是重定向到某个页面。
Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码——JavaBean/domain/entity/pojo。
MVC 是一种思想
MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了降低耦合度)。
整个模块的开发流程:
1、编写图书模块的数据库表
2、编写图书模块的 JavaBean
3、编写图书模块的 Dao 和测试 Dao
4、编写图书模块的 Service 和测试 Service
5、编写图书模块的 Web 层,和页面联调测试
创建t_book表,并插入测试数据。
在pojo目录下创建Book类,对应表中属性,有参构造器,无参构造器,getter和setter以及tostring。这里注意imgPath的赋值。
先编写Dao接口,BookDao,增删改查。
再继承BaseDao,实现BookDao,编写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.ge
tImgPath(),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);
}
}
Test技巧,在BookDao内容中,按下shift+ctrl+T,选择所有方法,选择Test目录,选择Junit4。进行测试,
今日观看p236-239。
主要完成service功能和web的一部分。
即:
4、编写图书模块的 Service 和测试 Service
5、编写图书模块的 Web 层,和页面联调测试
很简单,在service定义增删改查的接口,BookService,然后对应实现类(通过创建bookdao(BookDao实现类对象)操纵),再进行test。
首先要用专门处理book的Servlet(这里配置xml,注意地址/manager/bookService,原因是区分前后台)。BookServlet(继承BaseServlet)依旧是增删改查(通过创建book()service(BookService实现类对象)操纵)。
manager_menu,点击图书管理,(如何请求bookservlet?)
来自manager_menu.jsp
<a href="/manager/bookServlet?action=list">图书管理</a>
来自servlet
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);
}
如何列表展示,利用JSTL
来自book_manager.jsp
<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="/pages/manager/book_edit.jsp">修改</a></td>
<td><a href="#">删除</a></td>
</tr>
</c:forEach>
当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键 F5,就会发起浏览器记录的最后一次请求。
请求转发:一次请求,到工程名。
重定向:两次请求,到端口号,因此要自己添加工程名。(防止F5刷新,重复提交业务)
jsp文件中要添加,隐藏标签。
<form action="manager/bookServlet" method="get">
<input type="hidden" name="action" value="add">
servlet
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");
}
今日完成240-242
把图书部分基本完成了。
主要解决,删除和修改。
1.1 manager_mene.jsp 不要再静态包含head.jsp。导致了duplicate basePath
1.2 herf = “pages” ,不要在前面+/
<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>
对应绑定单击事件
<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
第一种是在请求的时候增加method参数,然后通过param.method获取。
第二种和第三种,分别是根据param.id是否有,和servlet重定向的requestScope.book。
今日主要跟着视频p243-245。初步完成了分页部分。主要是利用到了泛型。
refacto-》rename,可以直接改为全大写等等命名
/**
* Page是分页的模型对象
* @param 是具体的模块的javabean类型,可以是user, book;
*/
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;
}
@Override
public Integer queryForPageTotalCount() {
String sql = "select count(*) from t_book";
Number count = (Number) queryForSingleValue(sql);
return count.intValue();
}
@Override
public List<Book> queryForPageItems(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);
}
@Override
public Page<Book> page(Integer pageNo, Integer pageSize) {
Page<Book> page = new Page<Book>();
// 设置每页显示的数量
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);
// 求当前页数据的开始索引
int begin = (page.getPageNo() - 1) * pageSize;
// 求当前页数据
List<Book> items = bookdao.queryForPageItems(begin,pageSize);
// 设置当前页数据
page.setItems(items);
return page;
}
protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1获取pageNo和pageSize
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
//2根据两个参数调用service
Page<Book> page = bookService.page(pageNo, pageSize);
//3保存到req
req.setAttribute("page", page);
//4请求转发
req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
}
到现在从p246-253
主要实现分页(首页分页,图书管理分页,修改删除增加跳转分页(具体哪一页)的展示)。
<%--分页条的开始--%>
<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>
<%--页码输出的开始--%>
<c:choose>
<%--情况1:如果总页码小于等于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="${ requestScope.page.url }&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
<%--页码输出的结束--%>
<%-- 如果已经 是最后一页,则不显示下一页,末页 --%>
<c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
<a href="${ requestScope.page.url }&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="${ requestScope.page.url }&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();
<%--var pageTotal = ${requestScope.page.pageTotal};--%>
<%--alert(pageTotal);--%>
// javaScript语言中提供了一个location地址栏对象
// 它有一个属性叫href.它可以获取浏览器地址栏中的地址
// href属性可读,可写
location.href = "${pageScope.basePath}${ requestScope.page.url }&pageNo=" + pageNo;
});
});
</script>
</div>
<%--分页条的结束--%>
p253-256
快捷键 alt+enter可以快速在相对应的文件中创建函数。
sql where price between min and max order by price;
保持查询状态,就是在ClientBookServlet文件中的pagebyPrice函数响应jsp时,设置page的url增加min和max;还有在jsp文件中,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.setUrl(sb.toString());
<div class="book_cond">
<form action="client/bookServlet" method="get">
<input type="hidden" name="action" value="pagebyPrice">
价格:<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>
</div>