第二个项目总结
1.写业务的思想:先分析页面实现的是增删改查哪个功能,参数是从前台向后台传还是从后台向前台传
2.先写控制层里面的方法,然后思考业务层写哪些方法,数据层写哪些方法,用了mybatis之后,如果数据层需要接收多种数据类型需要采用Map集合的方式传递,先在业务层将需要传递的参数封装到map集合中,再将这个map集合传递到数据层。
3.mybatis中常见的接收参数类型:单个参数(基本数据类型,如:Integer,String,Long等),对象(VO类的对象),Map集合等。
4.前台向后台参数传递(前台代码):
● 直接通过form表单传递:点击提交(submit)时,会全部提交表单中所有的参数,表单中所有的name属性的值就是所要传递的参数名称,传递的格式为
*.action?属性名称=值&属性名称=值&...,如果表单是get模式的话可以在网址栏中直接看到传递的参数,如果是post模式就看不到了。
<form class="searchform" action="pages/back/admin/resumes/resume_list.action" method="get"> <select name="status" class="form-control"> <option value="">状态</option> <option value="1" <c:if test="${status==1}">selected</c:if>>入档</option> <option value="2" <c:if test="${status==2}">selected</c:if>>通知面试</option> <option value="3" <c:if test="${status==3}">selected</c:if>>违约</option> <option value="4" <c:if test="${status==4}">selected</c:if>>录用</option> <option value="5" <c:if test="${status==5}">selected</c:if>>不录用</option> </select> <input type="text" class="form-control" name="keywords" placeholder="请输入姓名" value="${keywords}"/> <button type="submit" class="btn btn-primary">搜索</button> </form>
注意:表单中的隐藏域:经常用在编辑业务中,有时候我们需要修改每条记录,点击编辑之后需要向后台传递这条记录的id,但是这个id又不需要让用户看到,这个时候就可以使用隐藏域将其隐藏起来但是参数依然会传递。如下代码:
<input type="hidden" name="resid" value="${resume.resid}">
● 如果不使用表单提交,我们也可以使用*.action?参数=value&参数=value&参数=value&...的方式传递,这种方式适合参数比较少的情况,如下代码:
查看预览的时候需要将预览的这条数据的id传过去,所以就可以手动在后面加参数
<td><a href="pages/back/admin/resumes/resume_editPre.action?resid=${resume.resid}" target="_blank">查看预览</a></td>
● 使用ajax传递参数:现在我还没搞懂,待更新
5.前台向后台参数传递(后台代码):
● 可以直接在控制层的方法参数 的括号中直接接收(必须保证action括号中的参数名称与前台jsp参数name的值保持一致),此种传递适合传递参数比较少的情况,如下代码
@RequestMapping("resume_editPre.action") public ModelAndView editPre(String resid){ //这里的resid是从前台jsp页面中的name="resid"那里传过来的,name="resid"对应的value值就是这里resid的值 ModelAndView mav=new ModelAndView("back/admin/resumes/form"); Resume resume=this.resumeServiceClient.getIResumeService().findResumeByResid(Long.parseLong(resid)); List<WorkYears> allWorkYears=this.resumeServiceClient.getIResumeService().findWorkYears(); mav.addObject(resume); return mav;
● 也可以直接在控制层的方法中直接获取一个VO类对象,一定要保证前端表单中的name属性的值与vo类对应的数据表字段相同,如下代码:
@RequestMapping("resume_edit.action")
public String edit(Resume resume){
this.resumeServiceClient.getIResumeService().doEdit(resume);
return "forward:resume_list.action";
}
6.后台向前台参数传递:
● 如果传递单个参数(例如:字符串等):
● 后台:mav.addObject("keywords","你好");
● 前台:${keywords}
● 如果传递一个对象:
● 后台:Resume resume=this.resumeServiceClient.getIResumeService().findResumeByResid(Long.parseLong(resid));
mav.addObject(resume);
● 前台:${resume.字段1},${resume.字段2},${resume.字段3},${resume.字段4},...
● 如果传递一个List集合:
● 后台:mav.addObject("allEducation",this.resumeServiceClient.getIResumeService().findAll()); 说明:findAll()的返回值类型是一个List集合
● 前台:<c:forEach items="${allEducation}" var="education">${education.字段1},${education.字段2},${education.字段3},...</c:forEach>
● 如果传递一个Map集合:注意前面几个都是用的ModelAndView类中的addObject()方法,而传递map集合的时候用的是addAllObjects()方法
● 后台:Map<String,Object> map=this.resumeServiceClient.getIResumeService().findSplit(status,keywords,spu.getCurrentPage(),5,"state","name");
mav.addAllObjects(map);说明:在业务层的方法返回的map集合里面有两个key,分别为allResumes,allRecorders
● 前台:<c:forEach items="${allResumes}" var="resume">${resume.字段1},${resume.字段2},${resume.字段3},...</c:forEach>
7.前台中文传到数据库乱码问题:在数据库连接的资源文件(database.properties)中,数据库连接地址后面加上?useUnicode=true&characterEncoding=UTF8
db.druid.url=jdbc:mysql://inheart.club:3306/museum?useUnicode=true&characterEncoding=UTF8
8.分页组件的使用:
● 分页工具类:
package com.yootk.util.split; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; public class SplitPageUtil { private Long currentPage ;//当前页 private Integer lineSize ;//每页存放的数据记录数 private String keyword ;//查询关键字 private String column ;//查询列 private String columnData ;// private String url ;//在哪个控制层使用的此类,写上控制层的完整路径 public SplitPageUtil(String url) { this(url,null) ; } public SplitPageUtil(String url,String columnData) { this.url = url ; this.columnData = columnData ; this.splitHandle(); } private void splitHandle() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); try { this.currentPage = Long.parseLong(request.getParameter("cp")) ; } catch (Exception e) {} try { this.lineSize = Integer.parseInt(request.getParameter("ls")) ; } catch (Exception e) {} this.column = request.getParameter("col") ; this.keyword = request.getParameter("kw") ; request.setAttribute("currentPage",this.currentPage); request.setAttribute("lineSize",this.lineSize); request.setAttribute("column",this.column); request.setAttribute("keyword",this.keyword); request.setAttribute("url",this.url); request.setAttribute("columnData",this.columnData); } public Long getCurrentPage() { if (this.currentPage == null) { return 1L ; } return currentPage; } public String getColumn() { return column; } public String getKeyword() { return keyword; } public Integer getLineSize() { if (this.lineSize == null) { return 5 ; } return lineSize; } }
● 控制层要写的内容:下面这个是输入两个关键字查询两个字段
SplitPageUtil spu=new SplitPageUtil("/pages/back/admin/resumes/resume_list.action");//先实例化分页工具类实例 Map<String,Object> map=this.resumeServiceClient.getIResumeService().findSplit(status,keywords,spu.getCurrentPage(),5,"state","name");
说明:status,keywords分别为第一个关键字,和第二个关键字;"state","name"分别为两个查询列的名称
● 业务层要写的内容:使用分页查询一定要将数据的总记录数传递到前台
/**
* 分页查询全部的简历
* @param name "请输入姓名"框中输入的关键字
* @param state "状态"框中输入的关键字
* @param currentPage 当前页
* @param lineSize 每页显示的记录数
* @param columnone 要查的字段1,这里是状态
* @param columntwo 要查的字段,这里是姓名
* @return 返回Map集合
*/
public Map<String, Object> findSplit(String name, String state, Long currentPage, Integer lineSize, String columnone, String columntwo) {
Map<String,Object> params=new HashMap<>();
Map<String,Object> results=new HashMap<>();
params.put("name",name);
params.put("state",state);
params.put("start",(currentPage-1)*lineSize);
params.put("end",lineSize);
params.put("columnone",columnone);
params.put("columntwo",columntwo);
results.put("allResumes",this.resumeMapper.findAll(params));
results.put("allRecorders",this.resumeMapper.getAllRecorders(params));//这里的key一定要写成allRecorders,因为表单上接收的是allRecorders
return results;
}
● 数据层要写的内容:
<select id="findAll" parameterType="java.util.Map" resultType="Resume"><!--根据查询条件查询出符合条件的全部数据--> select <include refid="Base_Column_List" /> from resume <where> <if test="name != null and name !="""> ${columnone} like #{name} </if> <if test="state != null and state !="""> and ${columntwo} like #{state} <!--如果前面name==null,mybatis会自动去掉这个and关键字--> </if> </where> limit #{start},#{end} </select> <select id="getAllRecorders" resultType="java.lang.Long" parameterType="java.util.Map"><!--返回符合条件的全部记录数--> select count(*) from resume <where> <if test="name != null and name !="""> ${columnone} like #{name} </if> <if test="state != null and state !="""> and ${columntwo} like #{state} </if> </where> </select>
9.如果怀疑自己在映射文件中写的sql语句对不对:可以观察dubbo微服务中的日志:里面会有执行的SQL语句,自己观察即可,里面的Total只是返回的行数
10.运行的时候先启动dubbo微服务,再启动tomcat,如果修改提供端代码就需要重启对应的微服务(可能也需要重启tomcat,我不确定),如果修改消费端后台代码就需要重启tomcat。
11.采用dubbo的目录结构:
● api模块:写vo类和业务层接口
● 提供端模块:
● 写业务层实现子类,用dubbo中的@Service注解
● 写数据层接口
● 写数据层接口映射文件(写SQL语句)
● 消费端模块:
● 代理业务层接口(返回值类型为远程接口)
● 写代理业务层接口子类(在这个类中注入远程业务接口(api中的业务接口)实例)用dubbo中的@Reference注入,并且用spring中的@Service注解
● 写控制层:用spring中的@Autowired注入代理业务层实例,调用远程接口方法时,先通过代理业务层实例获得远程业务接口实例再调用其方法
● 修改web页面(jsp,js等)
12.要想查看自己的业务层接口有没有在zookeeper中注册可以在zookeeper服务器上查看,也可以在dubbo客户端上查看,dubbo客户端的地址:localhost:7001
dubbo客户端上显示的都是已经启动的东西。
13.action向action跳转:
● 如果不传递参数,直接返回字符串,返回要跳转的action的路径,如下代码:
@RequestMapping("resume_edit.action") public String edit(Resume resume){ this.resumeServiceClient.getIResumeService().doEdit(resume); return "forward:resume_list.action"; }
● 如果传递参数,需要返回ModelAndView类型,传递参数像后台向jsp传递参数一样。在接收参数的action中接收参数的方式和jsp向后台传递参数一样。
@RequestMapping("hello.action") public ModelAndView hello(){ ModelAndView mav=new ModelAndView("forward:resume_delete.action"); mav.addObject("hello","你好"); return mav; }
● 请求转发与路径重定向:上面代码中都用了forward关键字
● 请求转发(forward)即服务器端跳转(不改变网址输入框路径):浏览器向action发送一个请求,这个action再将这个请求转发到另外一个action,浏览器一共只发送一个请求;如果将jsp中传到action的参数再传给另外一个action,理论上下面hello.action中的参数hello可以直接传递到rubbish.action中,我没有测试过,写的时候尽量还是按照上面的做法传递
@RequestMapping("hello.action") public ModelAndView hello(String hello){ ModelAndView mav=new ModelAndView("forward:rubbish.action"); return mav; } @RequestMapping("rubbish.action") public void rubbish(String hello){ System.out.println(hello); }
● 路径重定向(redirect)即客户端跳转(改变网址输入框路径):浏览器向action发送一个请求,这个action处理完请求之后将这个请求返回到浏览器,浏览器再发送一个请求给redirect指定的action,浏览器一共发送了两个请求;如果一个action向另外一个action使用redirect跳转,将无法传递参数。
14.dubbo提供端又叫业务中心,又叫dubbo端,又叫微服务。
15.dubbo微服务与zookeeper的关系:zookeeper只是一个注册中心,微服务在zookeeper上注册,消费端在zookeeper上调用对应的接口方法时,zookeeper会提供给消费端微服务的主机信息接口及方法,接下来微服务就与消费端建立了关系就不再需要zookeeper了,即使zookeeper宕机了也不会影响消费端调用。zookeeper就像一个媒婆让引导双方认识,双方认识之后就和媒婆没有任何关系了。
16.<c:if>标签也可以用在<input>标签中(常用在表单回填的时候),如下代码:
<div class="form-group"> <label class="col-sm-2 col-sm-2 control-label">性别</label> <div class="col-sm-10"> <label class="radio-inline"> <input type="radio" name="sex" value="0" <c:if test="${resume.sex==0}">checked</c:if>><!--如果值为1,则选中男--> 男 </label> <label class="radio-inline"> <input type="radio" name="sex" value="1" <c:if test="${resume.sex==1}">checked</c:if>><!--如果值为2,则选中女--> 女 </label> </div> </div>
17.如果我现在想在页面上输出全部考勤人员的姓名,但是考勤表(checkork)中只有雇员eid字段,没有员工姓名字段,而雇员表中有雇员eid字段,也有雇员姓名字段。
这时就需要将全部的考勤信息查出来传到前台,并且将全部的员工信息也上传到前台,然后在jsp中写以下代码:
<c:forEach items="${allCheckorks}" var="checkork"> <!--遍历考勤表信息--> <c:forEach items="${allEmp}" var="emp"> <!--遍历雇员信息--> ${emp.eid==checkork.eid ? emp.name :""} <!--如果当前雇员的eid等于当前考勤表的eid,则输出该雇员的姓名--> </c:forEach> </c:forEach>
18.dubbo项目中消费端打包步骤:先打引用的模块,如:api模块,common模块,util模块。再打整个项目,用:clean install 。将消费端底下的target文件夹删除,重新打消费端的包,用:clean install package。消费端的部署:将此war包上传到tomcat服务器的usr/local/tomcat/webapps/文件夹下,然后启动tomcat服务,即可在浏览器中访问。微服务的部署:将微服务打的jar包随意上传到服务器的任何一个文件夹下,并且启动微服务(就是启动那个Main方法),一台服务器上可以部署多个微服务,这些微服务之间不冲突。
19.nginx反向代理,一台nginx服务器可以代理多个tomcat(是一个项目的多个tomcat,并不是部署不同项目的tomcat),部署一个项目的多个tomcat是为了提高并发访问的。