回顾:
浏览器发出请求——>Servlet处理请求——>DAO访问数据库——>数据库中的表
以查询学生信息为例:
浏览器发出请求:
请求什么资源?
静态资源:HTML,图片等
动态资源:Servlet,JSP
JSP:jsp主要负责数据的显示,显示的位置在哪?
Servlet:
获取请求数据
调用业务方法处理请求,并共享数据
控制页面跳转
在该请求中,应该准备一个Servlet。将数据准备好;再跳转到jsp中,在JSp中使用EL表达式和JSTL标准标签库中的数据呈现给用户。
使用什么作用域来共享数据?
需要在Servlet和JSP之间共享查询到的数据信息,哪一个作用域对象合适再进行选择。根据数据数据共享来选择作用域对象。
以后开发中,还可以根据跳转的资源所在位置来决定页面跳转的方式:
访问WEB-INF中的资源:请求转发;
访问WEB-INF外的资源:重定向;
在JSP中如何获得共享数据?EL+JSTL
使用EL表达式从Servlet中获得共享数据;
在使用JSTL来操作(遍历)获取到的信息。
MVC设计思想
模式1(Model 1):
以JSP为中心.
JSP + JavaBean.
JSP的职责:
界面输出.
接受请求参数
调用业务方法,处理请求
控制界面跳转.
优势: 适用于简单的,快速开发.
劣势: 没有体现出责任分离原则.

模式2(Model2):解决了Model1存在的责任不分离的问题.
以Servlet为中心(所有请求都发送给Servlet):
JSP + Servlet + JavaBean .
servlet/JSP/JavaBean的职责:
JSP: 界面输出.
Servlet:
接受请求参数
调用业务方法,处理请求
控制界面跳转.
JavaBean:
封装业务操作,可重复使用.

MVC模式:面试题
目的:责任分离,把业务代码从视图中剥离出来。
早期运用于CS领域(桌面程序).
M:Model:模型对象(封装业务操作,算法,可重复使用,JavaBean);
V:View:视图(界面,JSP,HTML);
C:Controler:控制器(控制界面跳转,Servlet.)

三层架构
Web开发中的最佳实践:分层开发模式(技术层面的"分而治之").
三层架构(3-tier architecture) :
通常意义上的三层架构就是将整个业务应用划分为:表现层、业务逻辑层、数据访问层。区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。
表现层(Predentation Layer):MVC,负责处理与界面交互的相关操作(Struts2/Spring MVC) 直接与用户交互的;
业务层(Business Layer):service,负责复杂的业务逻辑计算和判断(Spring)
持久层(Persistent Layer) :ORM,负责将业务逻辑数据进行持久化存储(Hibernate/MyBatis)

开发顺序:持久层——>业务层——>表现层
后台CRUD开发
操作数据库表

项目后台结构:

代码实现:
测试先行.(测试代码没有收集)
针对dao和service分别取测试.完成实现类,实现一个,测试一个.
IProductDAO.java
public interface IProductDAO {// 添加void insert(Product p);// 删除void delete(Long id);// 修改void update(Product p);// 查询Product selectOne(Long id);// 查询所有List<Product> selectAll();}
ProductImpl.java
public class ProductImpl implements IProductDAO {@Overridepublic void insert(Product pro) {// 获得会话对象,添加SqlSession session = MyBatisUtil.getSqlSession();session.insert("cn.wolfcode.pmis.maaper.ProductMapper.insert", pro);session.commit();session.close();}@Overridepublic void delete(Long id) {// 获得会话对象,添加SqlSession session = MyBatisUtil.getSqlSession();session.delete("cn.wolfcode.pmis.maaper.ProductMapper.delete", id);session.commit();session.close();}@Overridepublic void update(Product pro) {// 获得会话对象,添加SqlSession session = MyBatisUtil.getSqlSession();session.update("cn.wolfcode.pmis.maaper.ProductMapper.update", pro);session.commit();session.close();}@Overridepublic Product selectOne(Long id) {// 获得会话对象,添加SqlSession session = MyBatisUtil.getSqlSession();Product one = session.selectOne("cn.wolfcode.pmis.maaper.ProductMapper.selectOne", id);//查询不需要提交事务//session.commit();//一定要释放资源,否则连接池会被使用完session.close();return one;}@Overridepublic List<Product> selectAll() {// 获得会话对象,添加SqlSession session = MyBatisUtil.getSqlSession();List<Product> list = session.selectList("cn.wolfcode.pmis.maaper.ProductMapper.selectAll", null);//查询不需要提交事务//session.commit();session.close();return list;}}
Product.java
@AllArgsConstructor@NoArgsConstructor@Setter@Getter@ToStringpublic class Product {//字段名与数据库中的表列名相同private Long id;private String productName;private Long dir_id;private Double salePrice;private String supplier;private String brand;private Double cutoff;private Double costPrice;}
MyBatisUtil.java
public class MyBatisUtil {//私有化构造器private MyBatisUtil() {}static SqlSessionFactory factory = null;static {//获得sql会话工厂对象try {factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}//获得SQL会话对象的方法public static SqlSession getSqlSession() {//返回会话连接对象return factory.openSession();}}
ProductMapper.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace:区分不同Mapper中的项目id的SQL --><mapper namespace="cn.wolfcode.pmis.maaper.ProductMapper"><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into product(productName,dir_id,salePrice,supplier,brand,cutoff,costPrice)values(#{productName},#{dir_id},#{salePrice},#{supplier},#{brand},#{cutoff},#{costPrice})</insert><delete id="delete">delete from product where id = #{id}</delete><update id="update">update product set productName=#{productName},dir_id=#{dir_id},salePrice=#{salePrice},supplier=#{supplier},brand=#{brand},cutoff=#{cutoff},costPrice=#{costPrice}where id = #{id}</update><select id="selectOne" resultType="Product">select * from product where id = #{id}</select><select id="selectAll" resultType="Product">select * from product</select></mapper>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties resource="pd.properties"></properties><typeAliases><package name="cn.wolfcode"/></typeAliases><environments default="crud"><environment id="crud"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${driverClassName}" ><property name="url" value="${url}" ><property name="username" value="${username}" ><property name="password" value="${password}" ></dataSource></environment></environments><mappers><mapper resource="cn\wolfcode\pmis\maaper\ProductMapper.xml"/></mappers></configuration>
pd.properties
driverClassName=com.mysql.jdbc.Driverurl=jdbc:mysql:///java_webusername=rootpassword=root
前台CRUD开发
分析web的crud到底有哪些操作/请求?
1.查询商品列表
一定要使用一个servlet去查询所有的商品.
ProductListServlet
2.删除指定的商品
一定要使用servlet去删除数据库中的数据.
ProductDeleteServlet
3.点击编辑和添加都是进入到可编辑的界面
一定要使用servlet去跳转到编辑界面.
ProductInputServlet
4.在编辑界面,点击保存.
一定要使用一个servlet去处理保存或者更新操作.
ProductSaveOrUpdateServlet
上面的设计的问题:
如果我们操作非常多,可能会使用非常多的servlet去处理请求.
导致类的个数爆炸式增长.不好维护.
在实际开发中,我们通常是在一个Servlet中完成对一个实体(表)的增删改查的处理
解决方案:(请求分发)
使用一个类来处理一张表的操作.
在service方法中,想要去区分开到底是哪个请求,可以增加一个参数来区分.
cmd:使用该参数来区分是何种请求,不同的值,就表示不同的请求.
cmd=delete:删除操作
cmd=input:点击添加或者编辑进入到相同的输入界面
cmd=saveOrUpdate:在输入界面点击提交按钮的操作
默认就是列表界面
需要知道用户到底是发的什么请求
localhost/product?cmd=input 跳转到录入页面
localhost/product?cmd=save 保存商品信息
localhost/product?cmd=update 修改商品信息, 通常是和save操作合并在一起来处理
localhost/product?cmd=delete&a=1&b=2 删除指定的商品
localhost/product?cmd=list 查询所有商品信息
在Servlet中获取到用户请求中传递的cmd,根据cmd的值来调用响应的方法来处理请求
查询流程:

删除流程:

点击添加按钮进入编辑界面:

添加功能的流程(易错点)

在这个过程中,需要单独执行两个请求来完成
用户在点击添加按钮(超链接)时,是想到一个录入页面中去填写自己的数据,所以我们需要在这个时候去跳转到录入页面
/product?cmd=input-----> input.jsp
用户在录入完数据之后,再提交表单将数据保存到数据库中
/product?cmd=saveOrUpdate
接收用户表单中填写的数据,将数据封装到Product对象
调用业务方法处理保存操作:service.save(Product对象)
再重新查询一次,将最新数据显示到页面上
商品信息编辑的流程


该编辑操作需要两个请求来完成
点击编辑按钮,跳转到录入页面, 这里我们需要将编辑之前的数据填充到表单中
如果不填充原始数据,那么用户需要填写所有信息(包括他不修改的数据)
给表单元素设置数据使用他的value属性来实现
用户根据自己的需要修改表单中的数据,然后提交表单
此时需要把修改之后的数据给更新到数据库中
更新之后再将最新的数据显示到页面中
在saveOrUpdate方法中同时完成保存和更新功能,此时存在的问题是:如何区分用户保存还是更新?
保存时,用户不用传递id, 更新时,用户必须要传递id
所以在这里我们就可以根据id是否有值来判断用户是保存还是更新
1. 在表单中添加一个隐藏域来保存id的值2. 在servlet中接收id,判断是否有值1. 有:更新,记得将id封装到商品对象中2. 没有保存
点击提交按钮的流程:

错误定位:
找问题的原则:
对于web应用,出现了数据错误问题,一定要第一时间定位到错误的位置.第一时间确定问题是出在前台界面,或者是后台数据库.
在浏览器中打开F12,查看数据是否传递给了服务器,如果在请求中已经传递了数据,那么就可以不用看前台界面了.问题是后台的代码.
在servlet的service方法中,调用业务方法的地方打断点,确定到底是参数封装问题,还是调用业务的问题.这样可以将问题定位到是否是数据库问题.
注:
web应用是交给tomcat管理的,所以直接打断点,只要有请求过来,service就可以被断掉.
项目前台结构:

IProductService.java
public interface IProductService {// 添加void save(Product pro);// 删除void delete(Long id);// 修改void update(Product pro);// 查询Product get(Long id);// 查询所有List<Product> listAll();}
ProductServiceImpl.java
public class ProductServiceImpl implements IProductService {//获得DAO对象private IProductDAO dao = new ProductImpl();@Overridepublic void save(Product pro) {dao.insert(pro);}@Overridepublic void delete(Long id) {dao.delete(id);}@Overridepublic void update(Product pro) {dao.update(pro);}@Overridepublic Product get(Long id) {return dao.selectOne(id);}@Overridepublic List<Product> listAll() {return dao.selectAll();}}
ProductServlet.java
//在这里完成对表的CRUD@WebServlet("/product")public class ProductServlet extends HttpServlet {private static final long serialVersionUID = 1L;// 获得业务对象private IProductService service = new ProductServiceImpl();@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置编码方式req.setCharacterEncoding("UTF-8");// 获取用户请求中的cmd中的值String cmd = req.getParameter("cmd");// 请求分发:判断请求cmd的值,做不同的处理if ("delete".equals(cmd)) {delete(req, resp);} else if ("input".equals(cmd)) {input(req, resp);} else if ("saveOrUpdate".equals(cmd)) {saveOrUpdate(req, resp);} else {// 默认执行全部查询list(req, resp);}}// 处理删除的请求protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/** 删除操作流程: 1.调用删除方法 2.删除后要显示删除之后所有商品的信息(可以在调listAll())* 我们直接跳转到ProductServlet的product页面, product资源不在WEB-INF下,只能使用重定向的方式才能访问到*/// 接收用户传过来的idString id = req.getParameter("id");// 传参要将String的id转化成Longservice.delete(Long.valueOf(id));// 跳转// list(req, resp);// 删除成功以后,访问的资源不再WEB—INF下,用重定向resp.sendRedirect("/product");}// 处理页面跳转:在添加和修改的时候需要跳转到用户录入信息的界面protected void input(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 需要编辑的商品信息,根据用户传递的id来获取到对应的商品String id = req.getParameter("id");// 将获得到的id共享给input.jspif (hasLength(id)) {// 获得该id对象Product p = service.get(Long.valueOf(id));// 分享该对象req.setAttribute("p", p);}// 跳转到input.jsp// input.jsp在WEB-INF下,所以用请求转发req.getRequestDispatcher("/WEB-INF/views/product/input.jsp").forward(req, resp);}// 查询所有商品protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 查询所有,没有参数,不用接收// 调用业务方法List<Product> products = service.listAll();// 将查询的数据共享给JSp,因为数据只在当前请求中,所以用Requestreq.setAttribute("products", products);// 数据显示————>页面跳转,作用域是Request,所以用请求转发req.getRequestDispatcher("/WEB-INF/views/product/list.jsp").forward(req, resp);}// 保存或修改:处理保存或更新的请求protected void saveOrUpdate(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/** 保存操作的流程:(有两个请求) 1.点击添加按钮跳转 2.跳转到录入界面填写数据 3.填写完成后提交表单将数据保存到数据库中*/// 保存:拿到用户输入商品中的值,封装到Product对象中String productName = req.getParameter("productName");String salePrice = req.getParameter("salePrice");String supplier = req.getParameter("supplier");String brand = req.getParameter("brand");String cutoff = req.getParameter("cutoff");String costPrice = req.getParameter("costPrice");String dir_id = req.getParameter("dir_id");// 封装数据到对象Product p = new Product();//创建空对象// 封装数据非String的参数要做类型转换,所以需要先判断字符串是否为空(空字符串转化时出现数据转化异常)p.setProductName(productName);if (hasLength(salePrice)) {p.setSalePrice(Double.valueOf(salePrice));}p.setSupplier(supplier);p.setBrand(brand);if (hasLength(cutoff)) {p.setCutoff(Double.valueOf(cutoff));}if (hasLength(costPrice)) {p.setCostPrice(Double.valueOf(costPrice));}if (hasLength(dir_id)) {p.setDir_id(Long.valueOf(dir_id));}//共享这个对象到list.jsp中,最终呈现出来req.setAttribute("p", "p");//此处注意:修改和保存操作区分是看id是否有值来决定调用哪个业务方法的// 获取idString id = req.getParameter("id");if (hasLength(id)) {//编辑操作:// 更新之后要将id封装到对象中,才能保存到数据库p.setId(Long.valueOf(id));// 调用更新操作service.update(p);} else {//如果id为空,就执行保存业务// 保存操作// 调用业务保存方法service.save(p);}// 跳转到ProductServlet中的list,显示最后结果resp.sendRedirect("/product");}// 判断字符串是否为空private boolean hasLength(String str) {return str != null && str.trim().length() > 0;}}
list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!-- 导包JSTL --><%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><!DOCTYPE html><html><head><meta charset="UTF-8"><title>商品信息列表</title></head><body><h2>商品信息列表</h2><!-- 添加:由servlet的input方法指定跳转到input.jsp页面 --><a href="/product?cmd=input">添加</a><table width="80%" border="1" cellpadding="0" cellspacing="0"><tr><th>序号</th><th>编号</th><th>商品名称</th><th>商品类别</th><th>售价</th><th>生产商</th><th>品牌</th><th>折扣</th><th>成本价</th><th>操作</th></tr><!-- 遍历分享数据 products--><c:forEach items="${products}" var="p" varStatus="vs"><!-- 将商品信息放到tr中对应的td中 --><tr align="center"><td>${vs.count}</td><td>${p.id}</td><td>${p.productName}</td><td>${p.dir_id}</td><td>${p.salePrice}</td><td>${p.supplier}</td><td>${p.brand}</td><td>${p.cutoff}</td><td>${p.costPrice}</td><td><!-- 编辑:由servlet的input方法指定跳转到input.jsp页面 --><a href="/product?cmd=input&id=${p.id}">编辑</a> |<!-- 删除操作:跳转回servlet类中执行删除的方法,删除方法需要接收id --><a href="/product?cmd=delete&id=${p.id}">删除</a></td></tr></c:forEach></table></body></html>
input.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html><html><head><meta charset="UTF-8"><title>商品信息录入页面</title></head><body><h2>商品信息录入页面</h2><!-- 表单提交之后:要将数据保存到数据库 --><form action="/product?cmd=saveOrUpdate" method="post"><!-- 用户不关心id值,可没可以设置为只读不许修改,或者直接隐藏域让用户眼不见心不烦 --><input name="id" value="${p.id}" type="hidden"/><!-- 一定要设置name,为了Servlet里取值封装到对象中 --><!-- value是在编辑时在Servlet中get()的商品对象共享的,在此处获得对象的信息在对应的输入框中,让用户选择性修改-->名 称:<input name="productName" value="${p.productName}"/><br/>售 价:<input name="salePrice" value="${p.salePrice}"/><br/>生产商:<input name="supplier" value="${p.supplier}"/><br/>品 牌:<input name="brand" value="${p.brand}"/><br/>折 扣:<input name="cutoff" value="${p.cutoff}"/><br/>成 本:<input name="costPrice" value="${p.costPrice}"/><br/>分 类:<!-- select元素:提交到服务端的值是什么?如果option元素没有使用value来指定值,那么值就是option之间的文本如果option元素有使用value来指定值,那么值就是option的value属性的值根据商品的dir_id来判断:哪个option元素上应该添加selected属性--><select name="dir_id"><option value="1" ${p.dir_id == 1 ? "selected" : ""}>鼠标</option><option value="2" ${p.dir_id == 2 ? "selected" : ""}>有线鼠标</option><option value="3" ${p.dir_id == 3 ? "selected" : ""}>无线鼠标</option><option value="4" ${p.dir_id == 4 ? "selected" : ""}>其他</option></select><br/><!-- 提交按钮 --><input type="submit" value="提交保存"/></form></body></html>
———— e n d ————
觉得文章不错,欢迎点在看和转发,长按识别二维码关注公众号 java学途,了解更多精彩。

点在看,让更多看见。




