参考王振国老师的笔记
1、MVC概念
MVC 全称:Model 模型、 View 视图、 Controller 控制器。
MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。MVC 是一种思想,MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了降低耦合度)
View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作——JSP/HTML。
Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。转到某个页面。或者是重定向到某个页面。
Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean类,其中不掺杂任何与数据处理相关的代码——JavaBean/domain/entity/pojo。
2、编写图书模块
2.1编写图书模块的数据库
2.2编写图书模块的 JavaBean
public class Book {
private Integer id;//图书id
private String name;//图书姓名
private BigDecimal price;//图书价格
private String author;//图书作者
private Integer sales;//图书库存
private Integer stock;//图书销量
private String imgPath="static/img/default.jpg";//图书封面
public Book() {
}
public Book(Integer id, String name, BigDecimal price, String author, Integer sales, Integer stock, String imgPath) {
this.id = id;
this.name = name;
this.price = price;
this.author = author;
this.sales = sales;
this.stock = stock;
if(imgPath!=null&&"".equals(imgPath)){
this.imgPath = imgPath;
}
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
", author='" + author + '\'' +
", sales=" + sales +
", stock=" + stock +
", imgPath='" + imgPath + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
if(imgPath!=null&&"".equals(imgPath)){
this.imgPath = imgPath;
}
}
}
2.3编写图书模块的Dao接口 和Dao的实现类及测试 Dao
- BookDao
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接口和Service的实现类及测试 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();
Page<Book> page(int pageNo, int pageSize);
}
- 编写BookServiceImpl
public class BookServiceImpl implements BookService {
//service一般都是依赖于dao,去操作访问数据库
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();
}
@Override
public Page<Book> page(int pageNo, int pageSize) {
Page<Book> page = new Page<>();
//设置当前页码
page.setPageNo(pageNo);
//设置每页显示的数量
page.setPageSize(pageSize);
//设置总记录数
Integer pageTotalCount=bookDao.queryForPageTotalCount();
page.setPageTotalCount(pageTotalCount);
//求总页码
Integer pageTotal=pageTotalCount/pageSize;
if(pageTotalCount%pageSize>0){
pageTotal+=1;
}
//设置总页码
page.setPageTotal(pageTotal);
//设置当前页数据
//球当前页数据的开始索引
int begin=(page.getPageNo()-1)*pageSize;
List<Book> items=bookDao.queryForPageItems(begin,pageSize);
page.setItems(items);
return page;
}
}
- 一定要测试!一定要测试!一定要测试!只有测试才能检查自己的代码有没有错误
3、编写图书模块的 Web 层,和页面联调测试
3.1查找图书
- 分析
JSP页面不可以直接访问到持久层,必须由web层控制service层去访问持久层
- 1、通过Servlet程序访问bookService.queryBooks()查询全部图书,把全部图书保存到Request域中
protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过bookService查询全部图书
List<Book> books = bookService.queryBooks();
//把全部图书保存到Request域中
req.setAttribute("books",books);
//请求转发到/pages/manager/book_manager.jsp
req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
}
- 2、从Request域中获取全部图书信息,通过JSTL标签库遍历输出
<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="manager/bookServlet?action=getBook&id=${book.id}&method=update">修改</a></td>
<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
</tr>
</c:forEach>
- 3、修改请求地址
<a href="manager/bookServlet?action=list">图书管理</a>
- 关于manager的解释
为了更好地区分地址,在配置web.xml时,给servlet添加上相应的标志
3.2添加图书
- 分析
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、跳到图书列表页面
//问题----->表单重复提交:当用户提交完请求,浏览器惠济路最后一次请求的全部信息,当用户按下功能键F5后,就会发起浏览器记录的最后一次请求
// req.getRequestDispatcher("/");所以此时不可以使用请求转发,请求转发是一次请求
resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=list");
/**
* 2.那么一个完整的url地址,基本格式如下:
* https://host:port/path?xxx=aaa&ooo=bbb
* --http/https:这个是协议类型,如图中1所示
* --host:服务器的IP地址或者域名,如图中2所示
* --port:HTTP服务器的默认端口是80,这种情况下端口号可以省略。
* 如果使用了别的端口,必须指明,例如:192.168.3.111:8080,这里的8080就是端口
* --path:访问资源的路径,如图中3所示/s (图中3是把path和请求参数放一起了)
* --?:url里面的?这个符号是个分割线,用来区分问号前面的是path,问号后面的是参数
* --url-params:问号后面的是请求参数,格式:xxx=aaa,如图4区域就是请求参数
* --&:多个参数用&符号连接
*/
}
- 表单重复提交问题
当用户提交完请求,浏览器会记录最后一次请求的全部信息,当用户按下功能键F5后,就会发起浏览器记录的最后一次请求
- 隐藏域的引入
作用:
1 、隐藏域在页面中对于用户是不可见的,在表单中插入隐藏域的目的在于收集或发送信息,以利于被处理表单的程序所使用。浏览者单击发送按钮发送表单的时候,隐藏域的信息也被一起发送到服务器。2、有些时候我们要给用户一信息,让他在提交表单时提交上来以确定用户身份,如sessionkey,等等.当然这些东西也能用cookie实现,但使用隐藏域就简单的多了.而且不会有浏览器不支持,用户禁用cookie的烦恼。
3、有些时候一个form里有多个提交按钮,怎样使程序能够分清楚到底用户是按那一个按钮提交上来的呢?我们就可以写一个隐藏域,然后在每一个按钮处加上οnclick=”document.form.command.value=”xx”“然后我们接到数据后先检查command的值就会知道用户是按的那个按钮提交上来的。
4 、有时候一个网页中有多个form,我们知道多个form是不能同时提交的,但有时这些form确实相互作用,我们就可以在form中添加隐藏域来使它们联系起来。
5 、javascript不支持全局变量,但有时我们必须用全局变量,我们就可以把值先存在隐藏域里,它的值就不会丢失了
- 修改访问地址
<table>
<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}&method=update">修改</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?method=add">添加图书</a></td>
</tr>
</table>
<form action="manager/bookServlet" method="get">
<%-- 隐藏域,告诉服务器调用哪个方法--%>
<input type="hidden" name="action" value="${param.method}">
<%-- 方法二 ${ empty param.id ?"add" : "update"} --%>
<%-- 方法三 ${ empty requestScope.book ?"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>
由于有时候一个网页中有多个form,我们知道多个form是不能同时提交的,但有时这些form确实相互作用,我们就可以在form中添加隐藏域来使它们联系起来。
3.3修改图书
- 分析
- 得到要修改的图书的信息,并保存到Request域中
/**
* 得到要修改的图书的信息
* @param req
* @param resp
* @return void
* @author 许宝华
* @creed: Talk is cheap,show me the code
* @date 2021/4/24 16:30
*/
protected void getBook(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的参数
int id = WebUtils.parseInt(req.getParameter("id"), 0);
//调用bookService.queryById查询图书
Book book = bookService.queryBookById(id);
//保存到图书到request域中
req.setAttribute("book",book);
//请求转发到 pages/manager/book_edit.jsp页面
req.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(req,resp);
}
- 在页面显示要修改的数据
<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>
- 实现update方法
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的参数,封装成为book对象
Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());
//调用bookService.updateBook(book)方法
bookService.updateBook(book);
//重定向回到图书管理页面
resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=list");
}
req.getContextPath()+"/manager/bookServlet?action=list",在地址的前边加req.getContextPath()的原因是getContextPath()能解决相对路径的问题,可返回站点的根路径。
- 解决 book_edit.jsp 页面,即要实现添加,又要实现修改操作。
<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}&method=update">修改</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?method=add">添加图书</a></td>
</tr>
- 分别给add、update添加一个method属性
方法一:
<input type="hidden" name="action" value="${param.method}">
- 判断id值是否为空
方法二:
<input type="hidden" name="action" value="${ empty param.id ?"add" : "update"} ">
方法一:
<input type="hidden" name="action" value="${ empty requestScope.book?"add" : "update"}">
3.4删除图书
- 分析
- 在BookServlet中实现delete方法
protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的参数
int id = WebUtils.parseInt(req.getParameter("id"), 0);
//调用bookService
bookService.deleteBookById(id);
//重定向返回图书管理页面
resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=list");
}
- 由于req.getParameter(“id”)的返回值是一个字符串类型,每次都需要类型转换,大大增加了代码的冗余量,所以把类型转换写进工具类之中
/**
* 将字符串类型转换成为int类型
* @param strInt
* @param defaultValue 默认值
* @return void
* @author 许宝华
* @creed: Talk is cheap,show me the code
* @date 2021/4/22 10:07
*/
public static int parseInt(String strInt,int defaultValue){
try {
return Integer.parseInt(strInt);
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}
- 修改删除地址
<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}&method=update">修改</a></td>
<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
</tr>
</c:forEach>
- 与用户界面进行交互
<script type="text/javascript">
$(function (){
//在事件的function函数中,有一个this对象,这个this对象,是正在响应事件的dom对象
//给删除的a标签绑定单击事件,用于删除的确认提示操作
$("a.deleteClass").click(function () {
return confirm("你确定删除["+$(this).parent().parent().find("td:first").text()+"]么?");
});
})
</script>