RESTful CRUD
1 前置准备
1.0 配置文件
- pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ifox.hgx</groupId>
<artifactId>springMVC_1</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springMVC_1 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>springMVC_1</finalName>
</build>
</project>
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置org.springframework.web.filter.HiddenHttpMethodFilter:可以把POST请求转为DELETE 或 POST 请求-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置DispatcherServlet初始化参数:springMVC配置文件的位置和参数-->
<!--也可以不通过contextConfigLocation来配置springMVC的配置文件,
而使用默认的配置文件: /WEB-INF/views/<servlet-name>-servlet.xml
即:springDispatcherServlet-servlet.xml
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置自动扫描的包-->
<context:component-scan base-package="com.ifox"></context:component-scan>
<!--配置视图解析器:如何把handler 方法返回值解析为实际的物理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
1.1 DAO
- DepartmentDao.java
import com.ifox.restful_CRUD.entities.Department;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@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);
}
}
- EmployeeDao.java
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.ifox.restful_CRUD.entities.Department;
import com.ifox.restful_CRUD.entities.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@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", "[email protected]", 1, new Department(101, "D-AA")));
employees.put(1002, new Employee(1002, "E-BB", "[email protected]", 1, new Department(102, "D-BB")));
employees.put(1003, new Employee(1003, "E-CC", "[email protected]", 0, new Department(103, "D-CC")));
employees.put(1004, new Employee(1004, "E-DD", "[email protected]", 0, new Department(104, "D-DD")));
employees.put(1005, new Employee(1005, "E-EE", "[email protected]", 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);
}
}
1.2 entities
- Department.java
public class Department {
private Integer id ;
private String departmentName ;
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;
}
public Department(Integer id, String departmentName) {
this.id = id;
this.departmentName = departmentName;
}
public Department() {
}
@Override
public String toString() {
return "Department{" +
"id=" + id +
", departmentName='" + departmentName + '\'' +
'}';
}
}
- Employee.java
public class Employee {
private Integer id ;
private String lastName ;
private String email ;
private Integer gender ;
private Department department ;
private Date birth ;
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
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 Employee() {
}
public Employee(Integer id, String lastName, String email, Integer gender, Department department) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
}
public Employee(Integer id, String lastName, String email, Integer gender, Department department, Date birth) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
this.birth = birth;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
", department=" + department +
", birth=" + birth +
'}';
}
}
2 显示所有员工信息(R)
- URI:emps
- 请求方式:GET
- 显示效果
1.1 handlers
@Controller
public class EmployeeHandler {
@Autowired
private EmployeeDao employeeDao;
@Autowired
private DepartmentDao departmentDao ;
@RequestMapping("/emps")
public String list(Map<String, Object> map) {
map.put("employees", employeeDao.getAll());
return "list";
}
}
1.2 index.jsp
<a href="/emps">list page</a>
1.3 list.jsp 显示页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>restful crud list</title>
</head>
<body>
</form>
<c:if test="${employees == null || employees.size()==0}">
<p style="color: red;">没有任何内容</p>
</c:if>
<c:if test="${employees != null && employees.size() != 0}">
<table border="1" cellspacing="0" cellpadding="10">
<tr>
<td>ID</td>
<td>LastName</td>
<td>Email</td>
<td>Gender</td>
<td>Department</td>
<td>Edit</td>
<td>Delete</td>
</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>
<a href="emp">Add New Employee</a>
</body>
</html>
- pom.xml 加入:
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
此页面用到了SpringMVC的表单标签:
- 通过 SpringMVC 的表单标签可以实现将模型数据中的属性和 HTML 表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显
1.4 form 标签
- 一般情况下,通过 GET 请求获取表单页面,而通过POST 请求提交表单页面,因此获取表单页面和提交表单页面的 URL 是相同的。只要满足该最佳条件的契约,
<form:form>
标签就无需通过 action 属性指定表单提交的 URL 可以通过 modelAttribute 属性指定绑定的模型属性,若没有指定该属性,则默认从 request 域对象中读取command 的表单 bean,如果该属性值也不存在,则会发生错误。
SpringMVC 提供了多个表单组件标签,如
<form:input/>
、<form:select/>
等,用以绑定表单字段的属性值,它们的共有属性如下:
path
:表单字段,对应 html 元素的 name 属性,支持级联属性
htmlEscape
:是否对表单值的 HTML 特殊字符进行转换,默认值为 true
cssClass
:表单组件对应的 CSS 样式类名
cssErrorClass
:表单组件的数据存在错误时,采取的 CSS 样式
form:input、form:password、form:hidden、form:textarea
:对应 HTML 表单的 text、password、hidden、textarea标签
form:radiobutton
:单选框组件标签,当表单 bean 对应的属性值和 value 值相等时,单选框被选中
form:radiobuttons
:单选框组标签,用于构造多个单选框
items:可以是一个 List、String[] 或 Map
itemValue:指定 radio 的 value 值。可以是集合中 bean 的一个属性值
itemLabel:指定 radio 的 label 值
delimiter:多个单选框可以通过 delimiter 指定分隔符表单标签
form:checkbox
:复选框组件。用于构造单个复选框
form:checkboxs
:用于构造多个复选框。使用方式同form:radiobuttons 标签
form:select
:用于构造下拉框组件。使用方式同form:radiobuttons 标签
form:option
:下拉框选项组件标签。使用方式同form:radiobuttons 标签
form:errors
:显示表单组件或数据校验所对应的错误 – <form:errors path= “ *” />
:显示表单所有的错误
<form:errors path= “ user*” />
:显示所有以 user 为前缀的属性对应的错误
<form:errors path= “ username” />
:显示特定表单对象属性的错误
3 添加新的员工(C)
3.1 Edit:显示添加员工页面
显示添加页面:
- URI:emp
- 请求方式:GET
示例:
- jsp页面:
<a href="emp">Add New Employee</a>
- handlers:EmployeeHandler.java中加入
@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
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>restful CRUD input</title>
</head>
<body>
<form:form action="${pageContext.request.contextPath}/emp" method="post" modelAttribute="employee">
LastName: <form:input path="lastName"></form:input>
<br>
Email:<form:input path="email"></form:input>
<br>
<%
Map<String,String> genders = new HashMap<>() ;
genders.put("0","Male") ;
genders.put("1","Female") ;
request.setAttribute("genders",genders);
%>
Gender:<form:radiobuttons path="gender" items="${genders}" delimiter=" "></form:radiobuttons>
<br>
Department:<form:select path="department.id" items="${departments}" itemLabel="departmentName" itemValue="id" ></form:select>
<br>
Birth:<form:input path="birth"></form:input>
<br>
<input type="submit" value="Submit">
</form:form>
</body>
</html>
点击后跳转到如下input.jsp页面
3.2 添加员工信息:
- URI:emp
- 请求方式:POST
- 显示效果:完成添加,重定向到 list 页面。
- handlers:EmployeeHandler.java加入:
@RequestMapping(value = "/emp",method = RequestMethod.POST)
public String save(Employee employee , Map<String,Object> map){
System.out.println("save:"+employee);
employeeDao.save(employee);
return "redirect:/emps" ;
}
点击提交,重定向到/emps 。
4 更新员工信息(U)
显示修改页面:
- URI:emp/{id}
- 请求方式:GET
显示效果:回显表单。
handlers:EmployeeHandler.java中添加:
@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" ;
}
list页面Edit:
<td><a href="emp/${emp.id}">Edit</a></td>
点击跳转到修改页面,为了简化,修改和添加员工用一个页面,跳转到input.jsp,但是也有不同的地方,修改操作不允许修改员工名字,和 id 。
修改员工信息:
- URI:emp
- 请求方式:PUT
显示效果:完成修改,重定向到 list 页面。
注意我们在web.xml中加入的:
<!--配置org.springframework.web.filter.HiddenHttpMethodFilter:可以把POST请求转为DELETE 或 POST 请求-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- handlers:EmployeeHandler.java中添加:
@ModelAttribute
public void getEmployee(@RequestParam(value = "id",required = false) Integer id , Map<String,Object> map){
if (id != null) {
Employee e = employeeDao.get(id);
map.put("employee",e) ;
}
}
@RequestMapping(value = "/emp",method = RequestMethod.PUT )
public String update(Employee employee){
// employee.setLastName(employeeDao.get(employee.getId()).getLastName());
employeeDao.save(employee);
return "redirect:/emps" ;
}
- input.jsp页面:
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>restful CRUD input</title>
</head>
<body>
<form:form action="${pageContext.request.contextPath}/emp" method="post" modelAttribute="employee">
<c:if test="${employee.id == null}">
LastName: <form:input path="lastName"></form:input>
<form:errors path="lastName" cssStyle="color: red"></form:errors>
</c:if>
<br>
<c:if test="${employee.id != null}">
<form:hidden path="id"></form:hidden>
<input type="hidden" name="_method" value="PUT">
</c:if>
Email:<form:input path="email"></form:input>
<br>
<%
Map<String,String> genders = new HashMap<>() ;
genders.put("0","Male") ;
genders.put("1","Female") ;
request.setAttribute("genders",genders);
%>
Gender:<form:radiobuttons path="gender" items="${genders}" delimiter=" "></form:radiobuttons>
<br>
Department:<form:select path="department.id" items="${departments}" itemLabel="departmentName" itemValue="id" ></form:select>
<br>
Birth:<form:input path="birth"></form:input>
<br>
<input type="submit" value="Submit">
</form:form>
</body>
</html>
- 在pom.xml中加入:
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
注意在其中我们写了一个@ModelAttribute注解修饰的方法,该方法会使得我们的employee是从数据库查到的,使得表单修改直接修改这个数据库查到的数据,从而避免了LastName在修改后为空的情况。
- 修改完成,submit后,重定向到/emps 。
5 删除员工(D)
删除操作
- URL:emp/{id}
- 请求方式:DELETE
删除后效果:对应记录从数据表中删除
删除员工1001后:
list页面Delete:
<td><a class="delete" href="emp/${emp.id}">Delete</a></td>
注意:我们需要DELETE请求,但是只能由POST请求转为DELETE,这里是GET请求,所以我们使用js来实现,选用Jquery来做。
- list.jsp修改为:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>restful crud list</title>
<%--处理静态资源--%>
<%--添加--%>
<script type="text/javascript" src="/scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){
$(".delete").click(function () {
var href = $(this).attr("href") ;
$("form").attr("action",href).submit() ;
return false ;
}) ;
})
</script>
</head>
<body>
<%--添加--%>
<form name="form" method="post" action="">
<input type="hidden" name="_method" value="DELETE">
</form>
<c:if test="${employees == null || employees.size()==0}">
<p style="color: red;">没有任何内容</p>
</c:if>
<c:if test="${employees != null && employees.size() != 0}">
<table border="1" cellspacing="0" cellpadding="10">
<tr>
<td>ID</td>
<td>LastName</td>
<td>Email</td>
<td>Gender</td>
<td>Department</td>
<td>Edit</td>
<td>Delete</td>
</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>
<a href="emp">Add New Employee</a>
</body>
</html>
- handlers:Employee.java加入:
@RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
public String delete(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/emps" ;
}
删除后直接跳重定向到/emps 。
注意: 此时你会发现DELETE不行,这是因为引入jquery是静态资源。我们将 DispatcherServlet 请求映射配置为le /, Spring MVC会将捕获WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理,因找不到对应处理器将导致错误。
所以我们需要在springmvc.xml配置文件中加入:
<!--静态资源处理-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>