学习SpringMVC——你们要的REST风格的CRUD来了
来来来,让一下,客官,您要的REST清蒸CRUD来了,火候刚刚好,不油不腻,请慢用~~~
如果说前面是准备调料,洗菜,切菜,摆盘,那么今天就来完整的上道菜,主要说的是基于REST风格实现数据的增删改查(CRUD)操作。
用料
entity:Employee + Department
对于这样的实体类大家肯定很熟悉,一个Employee就是一个员工,一个Department就是一个部门,一个员工只属于一个部门,一个部门可以对应多个员工。
Employee.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | package com.jackie.springmvc.crud.entities; import java.util.Date; import javax.validation.constraints.Past; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.NumberFormat; public class Employee { private Integer id; @NotEmpty private String lastName; @Email private String email; //1 male, 0 female private Integer gender; private Department department; @Past @DateTimeFormat (pattern= "yyyy-MM-dd" ) private Date birth; @NumberFormat (pattern= "#,###,###.#" ) private Float salary; public Integer getId() { return id; } public void setId(Integer id) { this .id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this .lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this .email = email; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this .gender = gender; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this .department = department; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this .birth = birth; } public Float getSalary() { return salary; } public void setSalary(Float salary) { this .salary = salary; } @Override public String toString() { return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + ", department=" + department + ", birth=" + birth + ", salary=" + salary + "]" ; } public Employee(Integer id, String lastName, String email, Integer gender, Department department) { super (); this .id = id; this .lastName = lastName; this .email = email; this .gender = gender; this .department = department; } public Employee() { // TODO Auto-generated constructor stub } } |
Department.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package com.jackie.springmvc.crud.entities; public class Department { private Integer id; private String departmentName; public Department() { // TODO Auto-generated constructor stub } public Department( int i, String string) { this .id = i; this .departmentName = string; } public Integer getId() { return id; } public void setId(Integer id) { this .id = id; } public String getDepartmentName() { return departmentName; } public void setDepartmentName(String departmentName) { this .departmentName = departmentName; } @Override public String toString() { return "Department [id=" + id + ", departmentName=" + departmentName + "]" ; } } |
dao层:除了entity层,我们还需要有dao层,用于对于entity的增删改查的操作,这里对应的有EmployeeDao和DepartmentDao
EmployeeDao.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | package com.jackie.springmvc.crud.dao; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import com.jackie.springmvc.crud.entities.Department; import com.jackie.springmvc.crud.entities.Employee; @Repository public class EmployeeDao { private static Map<Integer, Employee> employees = null ; @Autowired private DepartmentDao departmentDao; static { employees = new HashMap<Integer, Employee>(); employees.put( 1001 , new Employee( 1001 , "E-AA" , "aa@163.com" , 1 , new Department( 101 , "D-AA" ))); employees.put( 1002 , new Employee( 1002 , "E-BB" , "bb@163.com" , 1 , new Department( 102 , "D-BB" ))); employees.put( 1003 , new Employee( 1003 , "E-CC" , "cc@163.com" , 0 , new Department( 103 , "D-CC" ))); employees.put( 1004 , new Employee( 1004 , "E-DD" , "dd@163.com" , 0 , new Department( 104 , "D-DD" ))); employees.put( 1005 , new Employee( 1005 , "E-EE" , "ee@163.com" , 1 , new Department( 105 , "D-EE" ))); } private static Integer initId = 1006 ; public void save(Employee employee){ if (employee.getId() == null ){ employee.setId(initId++); } employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId())); employees.put(employee.getId(), employee); } public Collection<Employee> getAll(){ return employees.values(); } public Employee get(Integer id){ return employees.get(id); } public void delete(Integer id){ employees.remove(id); } } |
该dao提供了:1. 添加保存一个employee——save方法
2. 获取所有员工信息——getAll方法
3. 根据指定员工id查找员工——get方法
4. 根据指定员工id删除员工——delete方法
DepartmentDao.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package com.jackie.springmvc.crud.dao; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Repository; import com.jackie.springmvc.crud.entities.Department; @Repository public class DepartmentDao { private static Map<Integer, Department> departments = null ; static { departments = new HashMap<Integer, Department>(); departments.put( 101 , new Department( 101 , "D-AA" )); departments.put( 102 , new Department( 102 , "D-BB" )); departments.put( 103 , new Department( 103 , "D-CC" )); departments.put( 104 , new Department( 104 , "D-DD" )); departments.put( 105 , new Department( 105 , "D-EE" )); } public Collection<Department> getDepartments(){ return departments.values(); } public Department getDepartment(Integer id){ return departments.get(id); } } |
该dao提供了:1. 查找所有部门信息——getDepartments
2. 根据指定id查找相应的部门信息——getDepartment
另外这里需要用到的所有jar包都在这里
http://pan.baidu.com/s/1qXT180c 提取码:z7q2
用料齐活了, 就开始做菜了,要做的分别是REST清蒸C(添加记录)、REST油炸R(查询记录)、REST红烧U(更新记录)和REST水煮D(删除记录)。
1. 查询记录
这个很简单,跟前几篇介绍的套路差不多,新建一个index.jsp,放入一个用于查询的超链接
1 | < a href="emps">list all employees</ a >< br /> |
相应的在Handler中需要添加映射支持,这里新建一个EmployeeHandler类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.jackie.springmvc.crud.handlers; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.jackie.springmvc.crud.dao.EmployeeDao; @Controller public class EmployeeHandler { @Autowired private EmployeeDao employeeDao; @RequestMapping ( "/emps" ) public String list(Map<String, Object> map){ map.put( "employees" , employeeDao.getAll()); return "list" ; } } |
注意:
这里RequestMapping中没有指明RequestMethod的请求类型,默认是GET方式。
最终我们需要将查询的数据解析出来放到页面上,这个页面就是list.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <! DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> < html > < head > < meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> < title >Insert title here</ title > </ head > < body > < c:if test="${empty requestScope.employees}"> 没有员工信息 </ c:if > < c:if test="${!empty requestScope.employees }"> < table border="1" cellpadding="10" cellspacing="0"> < tr > < th >ID</ th > < th >LastName</ th > < th >Email</ th > < th >Gender</ th > < th >Department</ th > < th >Edit</ th > < th >Delete</ th > </ tr > < c:forEach items="${requestScope.employees }" var="emp"> < tr > < td >${emp.id }</ td > < td >${emp.lastName }</ td > < td >${emp.email }</ td > < td >${emp.gender==0?'Female':'Male' }</ td > < td >${emp.department.departmentName }</ td > < td >< a href="emp/${emp.id}">Edit</ a ></ td > < td >< a class="delete" href="emp/${emp.id}">Delete</ a ></ td > </ tr > </ c:forEach > </ table > </ c:if > < br >< br > </ body > </ html > |
这里通过表格的方式显示,表格的每一行显示一个员工记录。
如果Handler层没有传入employee,则显示“没有员工信息”。
如果从Handler层传入查询到的employee的信息,则通过遍历的方式查询每条记录并显示在对应的表格中。
结果如下:
这里查询了所有的员工信息,为简便起见,这里的员工信息并不是从数据库中读取的,而是在EmployeeDao中通过静态模块声明构造的,将每个员工信息放入一个map集合中。
2. 添加记录
添加记录即在原有的数据集合基础上,再新建一个员工信息,我们需要为员工的每个属性填充值,所以我们需要一个页面来为这些字段赋值,这个页面就叫input.jsp。
添加员工信息的入口链接就放在list.jsp中
1 | < a href = "emp">add new employee</ a >< br /> |
同时在EmployeeHandler中添加映射
1 2 3 4 5 6 | @RequestMapping (value= "/emp" , method=RequestMethod.GET) public String input(Map<String, Object> map){ map.put( "departments" , departmentDao.getDepartments()); map.put( "employee" , new Employee()); return "input" ; } |
input.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <%@page import="java.util.HashMap"%> <%@page import="java.util.Map"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <! DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> < html > < head > < meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> < title >Insert title here</ title > </ head > < body > < form:form action="emp" method="POST" modelAttribute="employee"> <!-- path 属性对应html表单标签的name属性 --> LastName: < form:input path="lastName"></ form:input >< br > Email:< form:input path="email"></ form:input >< br > <% Map< String , String> genders = new HashMap< String , String>(); genders.put("1", "male"); genders.put("0", "female"); request.setAttribute("genders", genders); %> Gender:< form:radiobuttons path="gender" items="${genders }"/>< br > Department:< form:select path="department.id" items="${departments }" itemLabel="departmentName" itemValue="id"></ form:select >< br > < input type="submit" value="Submit"/> </ form:form > </ body > </ html > |
先不看代码,至此你就能看到这样的效果:
到这里还没完,我们还需要写一个保存的handler接口,在EmployeeHandler.java中
1 2 3 4 5 | @RequestMapping (value= "/emp" , method=RequestMethod.POST) public String save(Employee employee){ employeeDao.save(employee); return "redirect:/emps" ; } |
主要实现将新建的员工记录保存到employee集合中。
这样我们就能够成功的添加一条记录了
注意:
这里有几个问题需要声明
(1). 为什么使用form标签:可以更快速开发表单页面,而且可以方便的进行表单值的回显
(2) 为什么要在EmployeeHandler类中添加map.put("employee", employee),并且在input.jsp页面的form:form标签中添加modelAttribute="employee"
如果没有这样的声明代码,在运行中会出现报错信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | HTTP Status 500 - An exception occurred processing JSP page /WEB-INF/views/input.jsp at line 23 type Exception report message An exception occurred processing JSP page /WEB-INF/views/input.jsp at line 23 description The server encountered an internal error that prevented it from fulfilling this request. exception org.apache.jasper.JasperException: An exception occurred processing JSP page /WEB-INF/views/input.jsp at line 23 20 : --> 21 : <form:form action= "emp" method= "POST" > 22 : <!-- path 灞炴�у搴攈tml琛ㄥ崟鏍囩鐨刵ame灞炴�� --> 23 : LastName: <form:input path= "lastName" ></form:input><br> 24 : Email:<form:input path= "email" ></form:input><br> 25 : <% 26 : Map<String, String> genders = new HashMap<String, String>(); Stacktrace: org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java: 574 ) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java: 471 ) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java: 396 ) org.apache.jasper.servlet.JspServlet.service(JspServlet.java: 340 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java: 52 ) org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java: 209 ) org.springframework.web.servlet.view.AbstractView.render(AbstractView.java: 267 ) org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java: 1221 ) org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java: 1005 ) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java: 952 ) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java: 870 ) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java: 961 ) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java: 852 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 622 ) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java: 837 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java: 52 ) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java: 77 ) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java: 107 ) root cause java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java: 144 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java: 168 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java: 188 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java: 154 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java: 141 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java: 132 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java: 116 ) org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java: 422 ) org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java: 142 ) org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java: 84 ) org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java: 80 ) org.apache.jsp.WEB_002dINF.views.input_jsp._jspx_meth_form_005finput_005f0(input_jsp.java: 244 ) org.apache.jsp.WEB_002dINF.views.input_jsp._jspService(input_jsp.java: 168 ) org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java: 70 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java: 438 ) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java: 396 ) org.apache.jasper.servlet.JspServlet.service(JspServlet.java: 340 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java: 52 ) org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java: 209 ) org.springframework.web.servlet.view.AbstractView.render(AbstractView.java: 267 ) org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java: 1221 ) org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java: 1005 ) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java: 952 ) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java: 870 ) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java: 961 ) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java: 852 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 622 ) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java: 837 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java: 52 ) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java: 77 ) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java: 107 ) note The full stack trace of the root cause is available in the Apache Tomcat/ 8.0 . 36 logs. |
于是在handler中将employee放入map集合中,此时运行,仍然报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | HTTP Status 500 - java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute type Exception report message java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute description The server encountered an internal error that prevented it from fulfilling this request. exception org.apache.jasper.JasperException: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java: 555 ) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java: 471 ) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java: 396 ) org.apache.jasper.servlet.JspServlet.service(JspServlet.java: 340 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java: 52 ) org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java: 209 ) org.springframework.web.servlet.view.AbstractView.render(AbstractView.java: 267 ) org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java: 1221 ) org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java: 1005 ) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java: 952 ) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java: 870 ) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java: 961 ) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java: 852 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 622 ) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java: 837 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java: 52 ) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java: 77 ) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java: 107 ) root cause java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java: 144 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java: 168 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java: 188 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java: 154 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java: 141 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java: 132 ) org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java: 116 ) org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java: 422 ) org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java: 142 ) org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java: 84 ) org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java: 80 ) org.apache.jsp.WEB_002dINF.views.input_jsp._jspx_meth_form_005finput_005f0(input_jsp.java: 244 ) org.apache.jsp.WEB_002dINF.views.input_jsp._jspService(input_jsp.java: 168 ) org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java: 70 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java: 438 ) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java: 396 ) org.apache.jasper.servlet.JspServlet.service(JspServlet.java: 340 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java: 52 ) org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java: 209 ) org.springframework.web.servlet.view.AbstractView.render(AbstractView.java: 267 ) org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java: 1221 ) org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java: 1005 ) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java: 952 ) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java: 870 ) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java: 961 ) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java: 852 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 622 ) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java: 837 ) javax.servlet.http.HttpServlet.service(HttpServlet.java: 729 ) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java: 52 ) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java: 77 ) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java: 107 ) note The full stack trace of the root cause is available in the Apache Tomcat/ 8.0 . 36 logs. |
这时候在form:form中加入modelAttribute="employee",再次运行,就能够成功添加记录了。
这是为什么呢,因为表单在显示的时候会做一个检查,如果表单的字段在request的bean中不能一一对应,就会无法显示并报错,说白了就是这里加上了employee这个bean后,这个对象中的各个属性如email、gender和表单上的字段都能够对应起来了,校验通过,就能正常添加保存了。
注意input.jsp中modelAttribute的值要和handler中放进map集合的key值一致,默认在页面上这个modelAttribute的值为command,所以你会看到报错信息中频繁的出现command。
(3). <form:radiobuttons>这个标签中有一下属性:
items:存放具体的数据集合,可以是String、Map或List类型
itemValue:指定radio的value值,可以使集合中bean的一个属性值
itemLabel:radio的label值
3. 删除记录
其实从前面的list.jsp的表格中已经看出来了,在每条记录后面都有一个DELETE选项,用于删除该项记录,那么具体怎么实现
这里首先要该Delete项对应的href,在list.jsp中
1 | <td><a href= "emp/${emp.id}" >Delete</a></td> |
我们将href的值赋值为一个请求路径,显然我们需要在handler中添加映射处理:
1 2 3 4 5 | @RequestMapping (value= "/emp/{id}" , method=RequestMethod.DELETE) public String delete( @PathVariable ( "id" ) Integer id){ employeeDao.delete(id); return "redirect:/emps" ; } |
删除完记录后重定向到emps即list.jsp页面。
效果如下
注意:
(1). 这里要明白,实际上我们在页面上发送的是一个rest的delete请求,但是通过<a href>的超链接方式我们只能发送get请求,而且前面我们说过,对于form来说只能发get和post请求,如果要发delete和put,则需要伪装成post请求,再在handler处理类中进行解析处理。
对于这个问题,我们想到了jquery,于是引入jquery库到WebContent/scripts目录下,版本是1.9.1
首先需要一个form表单,因为无可厚非,只有表单才能提交post请求
1 2 3 | < form action="" method="POST"> < input type="hidden" name="_method" value="DELETE"/> </ form > |
在list.jsp中引入jquery库
1 | < script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></ script > |
在delete对应的<a>中加入class=“delete”,方便后面使用jquery进行元素定位
写入功能实现代码:
1 2 3 4 5 6 7 8 9 | <script type= "text/javascript" > $( function (){ $( ".delete" ).click( function (){ var href = $( this ).attr( "href" ); $( "form" ).attr( "action" , href).submit(); return false ; }); }) </script> |
这段代码的意思无非就是:找到delete删除元素,提取器href属性值,并将其赋给新添加的form表单中,当点击超链接后相当于提交form,并移交给定义好的hanlder接口。
(2). 又要啰嗦下,对于静态资源来说,因为我们在web.xml中声明spring是拦截了所有请求,所以对于静态资源我们也统统招收,拦截不误,但是显然我们没有与这个静态资源对应的映射,也就是说spring拦截下来了,却没有能力处理,这里介绍在springmvc.xml中添加两个标签就能解决问题
1 2 | < mvc:default-servlet-handler /> < mvc:annotation-driven ></ mvc:annotation-driven > |
在前面的章节中有介绍过其他的方法,就是在web.xml中声明各种静态资源的支持,比较繁琐傻瓜点^^
(3). 为什么添加了这个<mvc:default-servlet-handler/>就能解决问题:
Default-servlet-handler将在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler,它会对进入DispatcherServlet的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由WEB应用服务器默认的Servlet处理。如果不是静态资源的请求,才由DispatcherServlet继续处理。
一般WEB应用服务器默认的Servlet的名称都是default,若所使用的WEB服务器的默认Serlvet名称不是default,则需要通过default-servlet-name属性显示指定。
这里的小问题比较多,不知道你晕了没,但是到目前为止,好像一个简易的框架就有了,有了后台的java,还有模板jsp技术,以及对于dom操作的Jquery,当然我们还少了一个真正的数据库。
4. 更新记录
更新操作实际上是分为两步:查找要修改的记录 + 修改记录并保存
(1). 查找记录
通过get方式映射到emp/${id}
与delete选项类似,在list.jsp改为
1 | < td >< a href="emp/${emp.id}">Edit</ a ></ td > |
同时在Handler添加相应映射
1 2 3 4 5 6 | @RequestMapping(value="/emp/{id}", method=RequestMethod.GET) public String input(@PathVariable("id") Integer id, Map< String , Object> map){ map.put("employee", employeeDao.get(id)); map.put("departments", departmentDao.getDepartments()); return "input"; } |
这样就可以定位到选中的employee对象上,并跳转到input.jsp界面上。
(2). 修改并保存
这里要说清楚,进入input.jsp页面会有两种情况,一种是添加一条Employee记录,二是修改一条Employee记录,而且我们从前面的界面可以看出我们并不修改LastName这个字段,那我们如果让input.jsp具有辨识是从添加记录那还是修改记录那跳转过来的呢,于是我们需要在input.jsp中加入判断
1 2 3 4 5 6 7 8 9 | < c:if test="${employee.id == null }"> <!-- path 属性对应 html 表单标签的 name 属性值 --> LastName: < form:input path="lastName" /> < form:errors path="lastName"></ form:errors > </ c:if > < c:if test="${employee.id != null }"> < form:hidden path="id" /> < input type="hidden" name="_method" value="PUT" /> </ c:if > |
如果传过来的employee中id存在,那说明是修改更新,如果为空,说明是新建一条记录。
同时,老套路,这里还是利用隐藏域hidden来发送一个put请求到handler
在EmployeeHandler中
1 2 3 4 5 6 7 8 9 10 11 12 13 | @ModelAttribute public void getEmployee( @RequestParam (value= "id" ,required= false ) Integer id, Map<String, Object> map){ if (id != null ){ map.put( "employee" , employeeDao.get(id)); } } @RequestMapping (value= "/emp" , method=RequestMethod.PUT) public String update(Employee employee){ employeeDao.save(employee); return "redirect:/emps" ; } |
之所以这里有ModelAttribute前篇已经说过,如果没有话,更新完后这个LastName字段就会为空。
最终效果如下:
至此,我们学习了:
1. 基于REST风格查询记录
2. 基于REST风格添加记录
3. 基于REST风格删除记录
4. 基于REST风格更新记录
5. 引入JQuery改变请求方式
6. 明白了Request中bean必须要和请求表单中的属性对应
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架