MVC的简单项目 客户信息-增删改查 崔希凡JavaWeb 之 day-19

目录

增删改查

以“增”举例

查询

查询与修改总结

删除

高级搜索


增删改查

以“增”举例


Servlet的功能:(MVC中的C,控制层,一般包名servlet或者web)
1)把用户提交到服务器的表单数据,封装(创建)为domain包中的 Customer对象(对应一张数据库表),所以对象是在运行在服务器上的Servlet时创建的(而不是在用户的浏览器的机器上)。(最终传到DAO层中的Customer对象就产生于此时)
2)对用户信息,生成 uuid,因为domain域的实体中有该字段,但没有值。
3)接着把第1)步中得到的Customer对象,作为调用service方法的参数传入。
4)回显到一个页面,提示成功信息。

浏览器:
1)表单数据相关检测。
2)发送增加条目的请求时,URL要带上method对应的方法,从而给servlet用于识别区分,通过这样来调用不同的增删改查Servlet

 

 

 

domain层(实体层,又称POJO),对应于数据库表,它是最底层,也是首先应该写出来的代码。(用excel表的思维去思考更简单)

注意,一个POJO类的对象,是代表对应数据库表中的一行,(每行都有表的结构中的所有列。)

             




DAO层: 这层的类中,应该都持有一个POJO类(domain包中的对应类)的对象作为属性,对于“增”对应的DAO方法void  add(Customer  c),是无返回值void的。

“删”对应DAO方法void  delete(String  cid);

“改”对应DAO方法void   edit(Customer   c); 改的时候,也需要按c.getCid,填到where  cid =  ? ,才能改。

"查"对应DAO方法List<Customer>   findAll(); 这是对应select *  from  t_customer语句,查询出表中所有的行(所有的对象)

下面是“增”的DAO代码。

 

业务层: (一般包名是service)
add增加业务
1)这里没有复杂的多个操作的业务,直接把servlet传过来的Customer对象,再次直接传递给底层的DAO中的add方法。
持久层 DAO层:持久层是为了增删改查数据库表(MVC中的M,模型层,直接使用JavaBean操作数据库表,一般包名是dao,也可以把domain中的实体也算作M,因为他们是对应的)
调用add方法,把Servlet(控制)层传来的Customer对象,按数据库的对应字段,填入相应的字段。
注意,如果是查询操作,那么是按照sql语句中要查询的字段,利用反射去调用JavaBean中相应的getXXX获取值。

add方法,类比下应该也类似,在DAO层中,使用Servlet封装好的、业务层传下来的Customer对象,去调用对应的getXXX方法得到返回值,作为数据值放入SQL代码中。

业务层代码:
持有一个底层的CustomerDao类引用,将控制层传入自己add方法的参数:Customer类引用,直接传给DAO层的add方法。
把数据(这里是Customer对象)看作水流,这里的业务层Service等于用方法的参数去接收了这个数据,再次将其作为参数放入DAO层方法中。
数据就被直接打入到DAO层的方法中。

servlet层代码(对应MVC中的C,控制层):注意,这里我们使用了继承BaseServlet类

附上BaseServlet代码:
package cn.itcast.web.servlet;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public abstract class BaseServlet extends HttpServlet {
    public void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        /*
         * 1. 获取参数,用来识别用户想请求的方法
         * 2. 然后判断是否哪一个方法,是哪一个我们就调用哪一个
         */
        String methodName = req.getParameter("method");
       
        if(methodName == null || methodName.trim().isEmpty()) {
            throw new RuntimeException("您没有传递method参数!无法确定您想要调用的方法!");
        }
       
        /*
         * 得到方法名称,是否可通过反射来调用方法?
         * 1. 得到方法名,通过方法名再得到Method类的对象!
         *   * 需要得到Class,然后调用它的方法进行查询!得到Method
         *   * 我们要查询的是当前类的方法,所以我们需要得到当前类的Class
         */
        Class c = this.getClass();   //得到继承自BaseServlet 的  某个Servlet类的class对象。
   // 注意上面,在Servlet中使用this的用法,将来哪个Servlet对象来跑这段代码,这个this就代表谁!
        Method method = null;
        try { 
/*下面,通过客户端请求的方法名字符串,得到方法对象 。 
原型是下面,从第二个参数开始,是要获取的方法的形参类型。因为仅仅一个方法名,无法唯一的确定下来一个方法,还需要参数。
返回值就不需要了,因为唯一确定一个方法的就是:方法名、形参的(列表),getMethod方法说明:
      Method	getMethod(String name, Class<?>... parameterTypes)
比如本例中,是要获取一个双参数,第一参数是HttpServletRequest类型,第二参数是HttpServletResponse类型的方法的对象。
实际上就是Servlet层里的add方法,上面有add方法的代码截图。
  */
          method = c.getMethod(methodName,  HttpServletRequest.class,  HttpServletResponse.class);
        } catch (Exception e) {
            throw new RuntimeException("您要调用的方法:" + methodName + "(HttpServletRequest,HttpServletResponse),它不存在!");
        }
       
        /*
         * 下面利用反射,调用起method方法对象
         */
        try {
       String result = (String)method.invoke(this, req, resp); //this是将来跑起来时的Servlet对象,req是将来的请求,resp是将来的响应
            /*
             * 获取请求处理方法执行后return的字符串,它表示转发或重定向的路径!可以参照上面Servlet层的代码截图
             * 帮它完成转发或重定向!
             */
            /*
             * 如果用户返回的是字符串为null,或为"",那么我们什么也不做!
             */
            if(result == null || result.trim().isEmpty()) {
                return;
            }
            /*
             * 查看返回的字符串中是否包含冒号,如果没有,表示转发
             * 如果有,使用冒号分割字符串,得到前缀和后缀!
             * 其中前缀如果是f,表示请求转发;
             * 如果是r表示重定向
             * 后缀就是要转发或重定向的路径了!
             */
            if(result.contains(":")) {
                // 使用冒号分割字符串,得到前缀和后缀
                int index = result.indexOf(":");//获取冒号的位置
                String s = result.substring(0, index);//截取出前缀,表示操作
                String path = result.substring(index+1);//截取出后缀,表示路径
                if(s.equalsIgnoreCase("r")) {//如果前缀是r,那么重定向!
                    resp.sendRedirect(req.getContextPath() + path);
                } else if(s.equalsIgnoreCase("f")) {
                    req.getRequestDispatcher(path).forward(req, resp);
                } else {
                    throw new RuntimeException("你指定的操作:" + s + ",当前版本还不支持!");
                }
            } else {//没有冒号,默认为转发!
                req.getRequestDispatcher(result).forward(req, resp);
            }
        } catch (Exception e) {
            System.out.println("您调用的方法:" + methodName + ", 它内部抛出了异常!");
            throw new RuntimeException(e);
        }
    }
}
CommonUtils类代码,在day14的资料中:
package cn.itcast.commons;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
/**
 * 小小工具
 * @author qdmmy6
 *
 */
public class CommonUtils {
 /**
  * 返回一个不重复的字符串
  * @return
  */
 public static String uuid() {
  return UUID.randomUUID().toString().replace("-", "").toUpperCase(); //UUID默认会带横线,替换掉即可,推荐使用UUID
 }
 /**
  * 把map转换成对象
  * @param map
  * @param clazz
  * @return
  *
  * 把Map转换成指定类型,该map一般来自于 request.getParakmeterMap()方法
  */
 @SuppressWarnings("rawtypes")
 public static <T> T toBean(Map map, Class<T> clazz) {
  try {
   /*
    * 1. 通过参数clazz创建实例
    * 2. 使用BeanUtils.populate把map的数据封闭到bean中
    */
   T bean = clazz.newInstance();
   ConvertUtils.register(new DateConverter(), java.util.Date.class);
   BeanUtils.populate(bean, map);
   return bean;
  } catch(Exception e) {
   throw new RuntimeException(e);
  }
 }
}

add.jsp  (对应MVC中的V)

这里要注意的用法,就是输入框的隐藏,就是用来传参数method的,CustomerServlet中有对其的处理。
这是POST提交表单的常见用法。
<input  type = "hidden"   name="method"    value="add">
如果是GET的请求,直接在    /CustomerServlet?method=add

 

msg.jsp(成功后,跳转到该页面)

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'msg.jsp' starting page</title>
    
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">    
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->

  </head>
  
  <body>
    <h1 style="color:green;" align="center">恭喜,${msg }</h1>
  </body>
</html>

 

domain域  实体代码:

package cn.itcast.cstm.domain;

/**
 * 领域对象 与表单和数据库表在设计上要对应
 *
 * @author cxf
 *
 */
public class Customer {
    /*
     * 对应数据库表
     */
    private String cid;                // 主键
    private String cname;            // 客户名称
    private String gender;                // 客户性别
    private String birthday;                // 客户生日
    private String cellphone;                    // 客户手机
    private String email;                    // 客户邮箱
    private String description;            // 客户的描述

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getCellphone() {
        return cellphone;
    }

    public void setCellphone(String cellphone) {
        this.cellphone = cellphone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Customer [cid=" + cid + ", cname=" + cname + ", gender="
                + gender + ", birthday=" + birthday + ", cellphone="
                + cellphone + ", email=" + email + ", description="
                + description + "]";
    }
}

 

查询

接下来是查询功能的几个实现模块:
top.jsp      这是主页,用户的输入,即要查询的人,就在这个页面上输入。
list.jsp     这是负责显示查询结果的页面

我们写代码的时候,从底层DAO开始写,之后写 Service层,最后写Servlet层。
但开始运行的时候,调用是最先Servlet;在Servlet中持有Service的对象;   在Service中持有DAO的对象。
查询的逻辑很简单:
1、Servlet上,一步步地利用各层中持有的底层对象,最终利用DAO层中的findAll()方法查到 List<Customer> ,一步步返回到Servlet
2、保存到request域的变量"cstmList"中
3、转发到list.jsp页面,在该页面上显示出request域的变量"cstmList"

DAO层查询模块,下面图是我拼出来的:DAO层需要持有 QueryRuner类的对象

import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.QueryRunner;
import cn.itcast.jdbc.TxQueryRunner;          //  TxQueryRunner extends QueryRunner  自己设计的类
这个SQL语句中没有问号,所以查询方法query中,没有第三参数。

 

这里的查询方法,在运行时,是不需要Service层 乃至Servlet层传入参数的。如果要接收用户指定信息的查询,还是需要带参数的DAO层查询方法。

 

 

Service层代码:  持有DAO层对象,并调用DAO层的方法。(  还有你;一切拜托你)

Servlet层代码:
1、持有Service层对象,并调用Service层方法(还有你;一切拜托你)
2、把得到的查询结果(一个List<Customer>对象),存到request域变量中,以便让jsp负责拿出显示,因为查询的最终目的是让jsp页面进行展示,这点与增加、修改、删除,Servlet层与jsp的交互处理不太一样。

top.jsp中,对应的查询部分,之前搭建时,写的是  <a href="<c:url  value='list.jsp'/>">查询客户</a>
 改为:                              <a href="<c:url  value='/CustomerServlet?method=findAll'/>">查询客户</a>
            以便用户点击后,转入执行    CustomerServlet   中的指定 findAll() 方法。

list.jsp 显示功能代码:    需要注意的是,list.jsp原本被设置为静态的,静态的显示效果好了之后,再往里添加显示域对象
<c:forEach>遍历显示

<c:forEach>标签用于遍历,items是要遍历的request域中对象,var是每次遍历时的对象中元素的赋值变量

list.jsp  完整代码

完整的前端list.jsp代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>客户列表</title>
    
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">    
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->

  </head>
  
  <body>
<h3 align="center">客户列表</h3>
<table border="1" width="70%" align="center">
<tr>
<th>客户姓名</th>
<th>性别</th>
<th>生日</th>
<th>手机</th>
<th>邮箱</th>
<th>描述</th>
<th>操作</th>
</tr>
<c:forEach items="${requestScope.cstmList}" var="cstm">
<tr>
<td>${cstm.cname }</td>
<td>${cstm.gender }</td>
<td>${cstm.birthday }</td>
<td>${cstm.cellphone }</td>
<td>${cstm.email }</td>
<td>${cstm.description }</td>
<td>
<a href="<c:url value='/CustomerServlet?method=preEdit&cid=${cstm.cid }'/>">编辑</a>
<a href="<c:url value='/msg.jsp'/>">删除</a>
</td>
</tr>
</c:forEach>
</table>
  </body>
</html>

 

 

编辑用户

编辑用户,需要两次请求过程:
1、在查询结果list.jsp 的基础上,点击要编辑的用户。这是一个request请求。对应的Servlet中处理的方法叫preEidt
     该preEdit方法查询到Customer对象,然后转发到edit.jsp,这是用户真正编辑的界面。

2、第二个过程才是真正编辑的过程,对应Servlet上的方法edit。

 

关键在第一个过程中,list.jsp怎么带出请求参数cid,这是通过在查询的那次请求request的结果时,就有的  ${cstm.cid} :
所以编辑的动作,要求先有查询。

CustomerServlet 代码:
调用Service层的load方法报错,因为这里是从上层往下层写的。
从list.jsp发来的请求中,抽取 cid 的请求参数值,将这个CID的字符串,往下经过Service层、层层传入DAO层对应的方法中。

这里Servlet层需要 request.setAttribute的原因,是因为要把预处理的查询得到的对象,通过请求转发给编辑页面edit.jsp

____ _________ ______ ________ ______________
CustomerService    中  load方法,一样因为底层没有写。
   该层的load要求传入的String  cid ,来自于Servlet层的传入,  再次调用DAO层方法,把 cid 传入下层DAO层。

___   ___________    ___________   _________________

CustomerDao类,持有TxQueryRunner类的属性;查询方法query中的结果集参数,是单行的BeanHandler;?号只有1处,为cid的值;

上面总体来说,Servlet  的preEdit方法加载到一个结果存在request域中后,将请求request转发到edit.jsp页面;
下面edit.jsp页面要承担显示load方法的加载当前结果到页面的任务;还要承担提供一个“保存、提交、编辑用户”按钮,把用户修改的值传入Servlet的edit方法。

下面就是edit.jsp的代码:

下面65、66两行,为了向/CustomerServlet发送参数 method、cid 
参数method为了指明调用Servlet中的edit方法;       参数cid指明了要修改(编辑)的数据库中的Customer对象。

————————————————————————————————————————————————

Servlet中 编辑 的第二步: edit方法的编写
接受用户请求中的数据,封装为Customer对象,用了CommonUtils类

那么接下来,再继续往下层的edit方法中,层层向下传递Customer对象:

数据库表叫做  t_customer
——————————————————————————————————

BaseServlet

查询与修改总结

如果分析几个Servlet方法中参数的流向:
1、首先,当用户在top.jsp页面输入好了值,点击”查询“按钮后,Servlet中的查询方法findAll() 被调用 ,从数据库中查找到一个Customer的对象存入request域的一个变量名中,再将这个request请求转发 (forward )到list.jsp,去显示出来。
<本过程的Service层中的findAll()方法是无参的,上层Servlet并不向底层DAO传入参数;因为这里要查全部的人员,以List的返回值得到对象的列表>

2、然后,用户可以点击list.jsp上某一个用户对应的"编辑"按钮,该操作会在requst中携带名为cid的参数,同时会调用Servlet中的preEdit方法(本项目中,Servlet中的方法名,不一定与下层的方法名一致!!),进而层层调用下层的load方法,查询到该cid为该值的Customer对象,放在request域的一个变量名中,再将这个请求转发到edit.jsp页面,去显示出来。
<本过程中,被层层传入下层的是一个字符串cid,因为Service层中的load(String  cid)是有参数的,得到的返回值是一个Customer对象类型>


3、再然后,用户填写对该Customer对象准备更新的所有数据,写完后点击 “保存、提交、编辑用户” 按钮,同时会调用Servlet的edit方法,该方法是把最新的Customer对象的值,传入底层的DAO,从而调用QueryRunner类 的update方法完成对数据库的更新,完成编辑功能。
<本过程中,被层层传入下层的是一个Customer对象,因为Service层中的edit(Customer   c)是有参数的 ,编辑(写入)方法返回值类型是void> 


所以,查询,是从数据库中查;  编辑,是编辑数据库。
Servlet类中方法的返回值都是String,是为了在执行后,可以把request请求转发到另外的页面,这是自定义的设计。
 
——————————————————————————————————

删除

删除也是建立在查询findAll()的结果集list.jsp之上的。
 在list.jsp上面,每一行查询结果记录右边,不仅有一个编辑按钮,还有一个删除按钮。
在删除按钮上做两个隐藏的输入框,各带一个参数。一个用来指明点击链接后调用的Servlet中的方法delete(String  cid),另一个用来携带cid号。
注意删除的方法名,在Servlet层、Service层、DAO层都是一样的。
Servlet层的方法还是:
1、持有Service类属性对象;调用void  delete(String  cid);
2、设置request域对象的值,在这里就是一个字符串“删除成功!”
3、返回一个字符串,转发到msg.jsp提示成功(msg.jsp中去读取上面设置的域对象);


Service层往下,都是void  delete(String  cid)

_________________________________________________

高级搜索

其实并不麻烦,多条件组合查询而已。
思路:
按照需求,在query.jsp页面上给出可以按何种字段查询的文本框,接收用户的输入。
每一个文本框,都自带一个参数name,在request请求中,传递给Servlet的query方法以备获取;
之后被Servlet获取到,压入调用组合查询的Service层的方法中,层层传递到DAO层的组合查询方法中。
<
Service、DAO层中query方法的参数就是一个Customer对象。对象的有些属性是默认值,非默认值的就是Servlet接收jsp页面的输入,构造而成的Customer对象;
非默认值属性的个数,应该与jsp页面上提供的可接收用户输入的字段个数相同,这是由CommonUtils.toBean()  方法支持的!
CommonUtils.toBean()  可以支持jsp页面上的输入字段,是domain的实体中字段的子集。
>

DAO层需要数据库多表查询的知识。在用户没有对某个字段有查询要求时,应该对该字段不给予where的筛选条件。

DAO层返回值当然与findAll查询一样,是一个List<Customer>数组,因为结果可能不是单行的。

测试时,可能需要造些写测试数据,添加进数据库:

造出300个人

 

高级搜索流程:

高级搜索的Servlet层:

高级搜索的Service层:

高级搜索的DAO层的query方法:


由于防止用户变更查询业务逻辑,我们使用StringBuiler来处理字符串,先给出一定不变SQL查询字符串的部分:
select  *   from    t_customer    where   1=1
之后往后追加where语句。追加时不需要考虑where会不会被执行,全部都是and了。因为上面的where一定会被执行,这是技巧经验!
<拿一个废条件把where占了,之后就一定都是and了,执行不执行都是and>

因为用户不一定会填写Jsp上的每个查询条件,所以,在DAO里要对jsp页面上有的字段做判断:
如果传过来的Customer对象的指定属性非空(并且不是空串),就要在sql模板上追加一个  “   and   对应属性   like  ? ”

注意,List<Object>   params作为参数列表
qr.query方法的执行,需要 sql模板、结果集、sql模板中的传入值。

最后List<Object>   params作为参数列表的处理中,cname甚至支持查到中间是输入的姓名的记录。其他字段也类似,只有性别是精确的。

如果不想模糊匹配,想精确匹配可以用下面:其他的左匹配、右匹配都是一个意思:

 

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值