SpringMVC学习笔记——REST风格的CRUD练习

CRUD

就是数据的增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete)四种操作。

总体设计

相关的类:
- 实体类:Employee、Department
- Handler:EmployeeHandler
- Dao:EmployeeDao、DepartmentDao
相关的页面:
- list.jsp
- input.jsp
- edit.jsp

显示员工信息

配置环境

  1. 新建maven-webapp工程
  2. pom.xml加入依赖包
  3. 配置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", "[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);
    }
}

注意,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中进行传递。

猜你喜欢

转载自blog.csdn.net/u012525096/article/details/81356875