CRUD
就是数据的增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete)四种操作。
总体设计
相关的类:
- 实体类:Employee、Department
- Handler:EmployeeHandler
- Dao:EmployeeDao、DepartmentDao
相关的页面:
- list.jsp
- input.jsp
- edit.jsp
显示员工信息
配置环境
- 新建maven-webapp工程
- pom.xml加入依赖包
- 配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 配置 SpringMVC的 DispatcherServlet -->
<!-- The front controller of this Spring Web application, responsible for handling
all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置 HiddenHttpMethodFilter : 把 POST 请求转为 DELETE、 PUT 请求 -->
<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>
</web-app>
主要是springmvc的配置文件、过滤器的配置。
4. 配置springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 配置 SpringMVC的 DispatcherServlet -->
<!-- The front controller of this Spring Web application, responsible for handling
all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置 HiddenHttpMethodFilter : 把 POST 请求转为 DELETE、 PUT 请求 -->
<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>
</web-app>
主要是自动扫描包、默认的视图解析器。
实体层和Dao层
编写实体(entities)类、Dao类
实体类:
public class Employee {
private int id;
private String lastName;
private String email;
private String gender;
public Employee() {
}
public Employee(int id, String lastName, String email, String gender) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
}
public int getId() {
return id;
}
public void setId(int 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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
public class Department {
private int id;
private String departMentName;
public Department(int id, String departMentName) {
this.id = id;
this.departMentName = departMentName;
}
public Department() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDepartMentName() {
return departMentName;
}
public void setDepartMentName(String departMentName) {
this.departMentName = departMentName;
}
}
Dao类:
@Repository
public class DepartmentDao {
private static Map<Integer, Department> departments = null;
static {
departments = new HashMap<>();
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);
}
}
@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类需要使用@Repository标识为Bean,且在EmployeeDao中的Department要使用@AutoWired注解进行自动注入。
控制器层(Handler)
@Controller
public class EmployeeHandler {
@Autowired
private EmployeeDao emplyoeeDao;
@RequestMapping(value = "/emps")
public String list(Map<String, Object> map) {
map.put("employees", emplyoeeDao.getAll());
return "list";
}
}
注意,该类使用了@Controller进行修饰,其中有一个成员变量employeeDao
,使用@Autowired进行修饰。
视图层(View)
list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!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>员工列表</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 ? 'Femail' : 'Male' }</td>
<td>${emp.department.departmentName }</td>
<td><a href="">Edit</td>
<td><a href="">Delete</td>
</tr>
</c:forEach>
</table>
</c:if>
</body>
</html>
效果
小结
整个流程都是前面所学内容,目前还没涉及到REST的请求方式,只是显示信息,属于GET请求,把目标集合放入到Map中,在JSP页面使用JSTL标签进行便利就可以了。
添加员工信息
URL:emp
请求方式:POST
两个步骤,一个显示添加页面、一个完成回显页面。
几个注意点:
1. 添加页面的URL是emp,请求方式是GET
;回显页面的URL也是emp,请求方式是POST
。
2. 添加页面中的Department必须来源于EmployeeDao,所以该界面必须经过Handler
。
3. 为了更快的完成开发,添加页面使用SpringMVC的标签库
。
4. 添加成功,回显时,使用的是重定向
。
编写JSP前端
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="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>添加员工</title>
</head>
<body>
<!--
1.Why 使用 form 标签呢?
可以更快速的开发出表单页面,而且可以更方便地进行表单值的回显.
2.注意
可以通过 modelAttribute 属性指定绑定的模型属性,
若没有指定该属性,则默认从 request 域对象中读取 command 的表单bean
如果该属性值也不存在,则会发生错误.
-->
<form:form action="emp" method="POST" modelAttribute="employee">
<!-- path 属性对应 html 表单标签的 name 属性值 -->
LastName:<form:input path="lastName" />
<br>
Email:<form:input path="email" />
<br>
<%
Map<String, String> genders = new HashMap<>();
genders.put("1", "Male");
genders.put("0", "Female");
request.setAttribute("genders", genders);
%>
Gender:<br>
<form:radiobuttons path="gender" items="${genders }" delimiter="<br>" />
<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>
path 属性对应 html 表单标签的 name 属性值。
注意:可以通过 modelAttribute 属性指定绑定的模型属性,若没有指定该属性,则默认从request域对象中读取command的表单bean,如果该属性值也不存在,则会发生错误。(与下注意对应)
编写Handler方法(Controller)
EmployeeHandler.java方法中加入如下代码:
@RequestMapping(value = "/emp", method = RequestMethod.POST)
public String save(Employee employee) {
emplyoeeDao.save(employee);
return "redirect:/emps";
}
@RequestMapping(value = "emp", method = RequestMethod.GET)
public String input(Map<String, Object> map) {
// 为了显示部门信息,所以从Handler方法中传到前端显示
map.put("departments", departmentDao.getDepartments());
// 为了存放新增的那个对象
map.put("employee", new Employee());
return "input";
}
注意:input方法中,需要在map中放一个存储输入界面的临时对象
,命名为“employee”,可以任意指定,但必须与JSP前端页面的<form:form action="emp" method="POST" modelAttribute="employee">
中的modelAttribute中的值相同。(如果JSP中没有指定ModelAttribute的名字,则默认为command
)。
注意2:为了前端显示目前的部门,我们需要从handler方法把所需的数据放入request域对象中。
删除员工信息
URL:emp/{id}
请求方式:DELETE
我们需要一个DELETE请求
,但是我们在前端页面只有一个普通的URL链接,属于GET请求,所以要使用JS
的方式去发起POST请求,然后通过HiddenHttpMethodFilter过滤器
将POST请求转换成DELETE请求(hidden域,name=”_method”,value=”DELETE”)。
(1)导入JQuery的包,配置到jsp中,并写一个Script的方法
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!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>员工列表</title>
<!--
SpringMVC 处理静态资源:
1. 为什么会有这样的问题:
优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀
若将 DispatcherServlet 请求映射配置为 /,
则 Spring MVC 将捕获 WEB 容器的所有请求, 包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理,
因找不到对应处理器将导致错误。
2. 解决: 在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>
-->
<!-- 导包 -->
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<!-- 写方法提交POST -->
<script type="text/javascript">
$(function() {
$(".delete").click(function() {
var href = $(this).attr("href");
$("form").attr("action", href).submit();
return false;
})
})
</script>
</head>
<body>
<form action="" method="POST">
<input type="hidden" name="_method" value="DELETE">
</form>
<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 ? 'Femail' : 'Male' }</td>
<td>${emp.department.departmentName }</td>
<td><a href="">Edit</td>
<td><a class="delete" href="emp/${emp.id}">Delete</td>
</tr>
</c:forEach>
</table>
</c:if>
<br>
<a href="emp">Add New Employee</a>
<br>
</body>
</html>
其中,涉及到静态资源的加载问题
,因为之前所有的请求都被DispatcherServlet
拦截,包括静态资源。
我们需要区分静态资源和请求,在springmvc.xml中,增加如下配置:
<!-- default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler, -->
<!-- 它会对进入 DispatcherServlet 的请求进行筛查, 如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用服务器默认的 -->
<!-- Servlet 处理. 如果不是静态资源的请求,才由 DispatcherServlet 继续处理 -->
<!-- 一般 WEB 应用服务器默认的 Servlet 的名称都是 default. -->
<!-- 若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定 -->
<mvc:default-servlet-handler />
<!-- 只配置上面,不配置下面,将会造成@RequestMapping不好用 -->
<mvc:annotation-driven></mvc:annotation-driven>
增加Handler方法
@RequestMapping(value = "/emp/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable("id") Integer id) {
emplyoeeDao.delete(id);
return "redirect:/emps";
}
在这里,使用重定向
的方法进行跳转。
修改员工信息
分为两个步骤:
1. 显示修改页面,表单回显。
URL:emp/{id}
请求方式:GET
2. 点提交按钮,提交修改操作。
URL:emp
请求方式:PUT
需求:lastName不能修改,修改完成重定向
到 list 页面。
表单回显
前端界面修改
编辑处修改成如下代码:
<form:form action="${pageContext.request.contextPath }/emp" method="POST"
modelAttribute="employee">
<td><a href="emp/${emp.id }">Edit</td>
注意,表单的路径应用绝对路径
,用相对路径会出错,实际开发用,也推荐使用绝对路径。
编写Handler方法
@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";
}
注意1:因为要进行模型的合成,所以要用@ModelAttribute
去修饰一个方法,去传递一个旧的模型,注意该方法会在每个Handler方法前执行,另由于handler方法中的参数没用@ModelAttribute
修饰,则此处必须使用参数类型首字符小写的字符串
。
注意2:一个要使用@PathVariable
进行参数的获取,另一个要加入一个Map,进行Request域对象的数据传递
,表单处要显示指定的员工信息以及部门列表,故将指定数据模型保存在Map中进行传递。