Web开发(CRUD)
一、实验要求
1. 使用Rest风格:URI+请求方式
| 实验功能 | 请求URI | 请求方式 |
|---|---|---|
| 查询所有员工 | emps | GET |
| 查询员工(来到修改页面) | emp/{id} | GET |
| 来到添加页面 | emp | GET |
| 添加员工 | emp | POST |
| 来到修改页面(查出员工信息进行回写) | emp/{id} | GET |
| 修改员工 | emp | PUT |
| 删除员工 | emp/{id} | DELETE |
2. 实体类和Dao:
/entities/Employee:员工类
private Integer id;
private String lastName;
private String email;
//1 male, 0 female
private Integer gender;
private Department department;
private Date birth;
/entities/Department:部门类
private Integer id;
private String departmentName;
/dao/EmployeeDao:
@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;
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;
}
/dao/DepartmentDao:
@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);
}
}
二、查询所有员工
1. 创建EmployeeController类,处理各种请求
2. 在EmployeeController中定义list方法,发送emps请求+GET请求方式(GetMapping),查询所有员工,返回/templates/emp/list.html列表页面
具体代码:
EmployeeController:
@Controller
public class EmployeeController {
/*
* 将EmployeeDao自动注入,目前Dao没有涉及到数据库操作,直接在Dao中定义了一些Emp
*/
@Autowired
private EmployeeDao employeeDao;
/*
* 查询所有员工
*/
@GetMapping("/emps")
public String list(Model model){
// 查询所有员工
Collection<Employee> employees = employeeDao.getAll();
model.addAttribute("emps", employees);
return "emp/list";
}
}
(1)使用@GetMapping("/emps")发送emp+GET请求,返回"emp/list"页面
(2)将查询到的员工employees存储到Model中,用于在页面调用
注意:thymeleaf默认会进行拼串:找到classpath:/templates/emp/list.html页面
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
3. 抽取并引入页面的公共片段:顶部和侧边栏
(1)抽取公共片段(为公共片段命名):
- <div th:fragment="片段名">
- <nav id="片段名>
(2)从dashboard模板中引入公共片段:
- <div th:insert="~{模板名::片段名}">:将公共片段整个插入到需要引入的元素的div中
- <div th:replace="模板名::片段名">:将需要引入的元素(包括div)替换为公共片段
- th:include:将被引入的片段的内容包含进这个标签中
- 使用以上三种方式引入,可以不写~{ }
(3)将dashboard模板中定义的公共片段也抽取出来,单独放在/templates/commons/bar.html中
(4)对公共片段的侧边栏设置高亮显示
- 点击dashboard,url指向/main.html页面
- 点击员工管理,url发送/emps请求,指向list.html页面
具体代码:
/templates/commons/bar.html:抽取出来的公共片段
<!-- topbar -->
<!-- 将顶部公共页面抽取出来 th:fragment="topbar" -->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<!-- 显示登录的用户名 -->
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a>
<!-- ...省略... -->
</nav>
<!-- sidebar -->
<nav class="col-md-2 d-none d-md-block bg-light sidebar" id="sidebar">
...省略...
<li class="nav-item">
<a class="nav-link active" href="#" th:href="@{/main.html}"
th:class="${activeUri=='main.html'?'nav-link active': 'nav-link'}">
<!-- ...省略... -->
Dashboard <span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<!-- 点击员工管理,查询所有员工,发送/emps请求 -->
<!-- 将sidebar的员工管理点击后指向emps
th:class:判断Uri,如果是emps,那么就高亮
-->
<a class="nav-link active " href="#" th:href="@{/emps}"
th:class="${activeUri=='emps'?'nav-link active':'nav-link'}">
<!-- ...省略... -->
员工管理
</a>
</li>
(1)使用th:fragment="topbar"定义抽取出来的头部片段,使用id="sidebar"定义抽取出来的侧边栏片段
(2)在侧边栏sidebar中,使用th:href="@{/main.html}"来替换掉原生属性,使得点击侧边栏上的dashboard可以指向main.html,使用th:class="${activeUri=='main.html'?'nav-link active': 'nav-link'}"来进行判断,如果当前uri是main.html,那么高亮。
(3)在侧边栏sidebar中,使用th:href="@{/emps}"来替换掉原生属性,使得点击侧边栏上的员工管理可以发送emps请求,使用th:class="${activeUri=='emps'?'nav-link active': 'nav-link'}"来进行判断,如果当前uri是emps,那么高亮。
/templates/dashboard.html:引入带参数的公共片段,使之高亮
<body>
<div th:replace="commons/bar::topbar"></div>
<div class="container-fluid">
<div class="row">
<div th:replace="commons/bar::#sidebar(activeUri='main.html')"></div>
<!-- ...省略... -->
</div>
</div>
(1)使用<div th:replace="commons/bar::topbar"></div>来引入定义的头部公共片段
(2)使用<div th:replace="commons/bar::#sidebar(activeUri='main.html')"></div>来引入定义的侧边栏公共片段,并且带上了参数(activeUri='main.html'),这里的( )表示?,当前在dashboard页面下,uri=main.html,因此高亮。
/templates/emp/list.html:引入带参数的公共片段,使之高亮
<body>
<div th:replace="commons/bar::topbar"></div>
<div class="container-fluid">
<div class="row">
<div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>
<!-- ...省略... -->
</div>
</div>
(1)使用<div th:replace="commons/bar::topbar"></div>来引入定义的头部公共片段
(2)使用<div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>来引入定义的侧边栏公共片段,并且带上了参数(activeUri='emps'),这里的( )表示?,当前发送emps请求,在list.html页面下,uri=emps,因此高亮。
4. 将Controller中查询到的emps在页面遍历显示:
在EmployeeController中我们使用model.addAttribute("emps", employees);将查询到的员工数据都保存在了model里,那么在list页面可以使用${emps.属性名}来调用
list.html页面:
(1)列名:
<thead>
<tr>
<th>#</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>department</th>
<th>birth</th>
<th>操作</th>
</tr>
</thead>
(2)显示数据:
<tbody>
<tr th:each="emp:${emps}">
<td th:text="${emp.id}"></td>
<td>[[${emp.lastName}]]</td>
<td th:text="${emp.email}"></td>
<td th:text="${emp.gender}==0?'女':'男'"></td>
<td th:text="${emp.department.departmentName}"></td>
<td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
</tbody>
在tr标签里使用th:each="emp:${emps}"遍历存储在model里的emps数据,每次遍历到的emp,使用th:text="${emp.属性名}"来获得具体数据。
三、添加员工
1. 在list.html页面配置员工添加按钮,发送/emp+GET请求
2. 在EmployeeController中定义toAddPage方法,发送emp+GET请求来到添加员工的页面/templates/emp/add.html
具体代码:
list.html:
<h2><a class="btn btn-sm btn-success" href="emp" th:href="@{/emp}">员工添加</a></h2>
(1)使用th:href="@{/emp}"指向emp的请求
EmployeeController:
@GetMapping("/emp")
public String toAddPage(Model model){
// TODO 6.6.4 查出所有部门放到model里,在页面显示
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts", departments);
// 转到添加页面
return "emp/add";
}
(1)使用@GetMapping("/emp")发送emp+GET请求,返回"emp/add"页面
(2)为了方便在添加页面上部门信息选项的显示,在这里先将部门departments查询出来存储到Model中
3. 在add.html页面配置每个添加的信息项
4. 配置添加按钮,将表单设置成emp+POST请求
5. 在EmployeeController中定义addEmp方法,发送emp+POST请求,调用EmployeeDao.save方法保存添加的员工,添加完成后重定向到list.html/页面
具体代码:
add.html:
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<!-- 设置成emp+POST方式 -->
<form th:action="@{/emp}" method="post">
<div class="form-group">
<label>LastName</label>
<input name="lastName" type="text" class="form-control" placeholder="zhangsan" >
</div>
<div class="form-group">
<label>Email</label>
<input name="email" type="email" class="form-control" placeholder="zhangsan@Lemon.com" >
</div>
<div class="form-group">
<label>Gender</label>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="1" >
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="0" >
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>department</label>
<select class="form-control" name="department.id">
<!-- 显示每个部门,提交的是部门的id -->
<option th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input name="birth" type="text" class="form-control" placeholder="2020-6-14" >
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
</main>
(1)将表单设置成emp+POST方式:<form th:action="@{/emp}" method="post">
(2)对于部门选项,在页面上显示部门的名称,提交时使用部门id进行提交:
<option th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
其中depts是EmployeeController中的方法保存在Model中的数据
(3)添加按钮:<button type="submit" class="btn btn-primary">添加</button>
EmployeeController:
@PostMapping("/emp")
public String addEmp(Employee employee){
System.out.println("保存的员工的信息:" + employee);
// (3)调用dao方法保存添加的员工
employeeDao.save(employee);
return "redirect:/emps";
}
(1)使用@PostMapping("/emp")发送emp+POST请求,使用employeeDao.save(employee)来保存添加的员工信息
(2)添加完成后要求仍然返回到list,html页面,但是不能直接return "emp/list",这样的话数据不会更新,可以使用重定向/转发。
(3)这里使用重定向到/emps请求:"redirect:/emps",而/emps请求是list方法的请求,会返回list.html页面:return "emp/list";
(4)注意:SpringMVC自动将请求参数和入参对象的属性进行绑定,要求请求参数的名字和JavaBean入参对象的属性名相同
(5)对于自定义日期格式:可以在主配置文件application.properties中添加配置:spring.mvc.format.date=yyyy-MM-dd
四、修改员工
1. 在list.html页面配置编辑按钮,发送/emp/{id}+GET请求
2. 在EmployeeController中定义toEditPage方法,发送emp/{id}+GET请求来到修改页面/templates/emp/update.html
具体代码:
list.html:
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a>
(1)使用拼串的方式加上当前遍历到的emp的id值:th:href="@{/emp/}+${emp.id}"
EmployeeController:
@GetMapping("/emp/{id}")
public String toEditPage(@PathVariable("id") Integer id, Model model){
// 查询出当前员工
Employee employee = employeeDao.get(id);
// 保存查询到的员工数据
model.addAttribute("emp", employee);
// 页面要显示所有的部门列表
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts", departments);
// 回到change修改页面
return "emp/change";
}
(1)因为要从传入的参数获取id值,才能针对具体某个员工进行修改,所以使用@PathVariable("id")注解
3. 来到update.html页面(和add页面类似),需要回显信息
4. 配置修改按钮,将表单设置成emp+PUT请求
5. 在EmployeeController中定义updateEmployee方法,发送emp+PUT请求,调用EmployeeDao.save方法保存添加的员工,修改完成后重定向到list.html/页面
具体代码:
update.html:
<form th:action="@{/emp}" method="post">
<!-- 要发送PUT请求,修改员工数据 -->
<input type="hidden" name="_method" value="put">
<input type="hidden" name="id" th:value="${emp.id}">
<div class="form-group">
<label>LastName</label>
<!-- TODO 回写内容 -->
<input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp.lastName}">
</div>
<div class="form-group">
<label>Email</label>
<input name="email" type="email" class="form-control" placeholder="zhangsan@Lemon.com" th:value="${emp.email}">
</div>
<div class="form-group">
<label>Gender</label>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp.gender==1}">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp.gender==0}">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>department</label>
<select class="form-control" name="department.id">
<!-- TODO 显示每个部门,提交的是部门的id -->
<option th:selected="${dept.id==emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input name="birth" type="text" class="form-control" placeholder="zhangsan" th:value="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">
</div>
<button type="submit" class="btn btn-primary">修改</button>
</form>
(1)因为在toEditPage方法中根据发送的id信息获取到了该员工,并保存在了Model中:model.addAttribute("emp", employee);,所以只需要使用th:value="${emp.属性名}"即可回显查询到的员工信息
(2)对于gender属性,使用
th:checked="${emp.gender==1}"
来进行判断:如果查询到的员工性别为男,则选中。
(3)对于department属性,使用
th:selected="${dept.id==emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}"
th:each="dept:${depts}"遍历保存在Model中的depts所有部门信息,得到每一个部门deptth:value="${dept.id}"获得遍历到的当前部门idth:selected="${dept.id==emp.department.id}"判断当前遍历到的部门是否和要编辑查询到的emp的部门id相同th:text="${dept.departmentName}":如果判断成立,则回显部门名称
(4)对于birth属性,使用
th:value="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"
将编辑查询到的emp.birth属性使用dates.format来进行日期格式化
(5)对于id属性,并不需要显示在页面上,所以使用
<input type="hidden" name="id" th:value="${emp.id}">
来进行隐式输出
(6)创建一个input项,来发送PUT请求,name="_method"这个名称不能变!:<input type="hidden" name="_method" value="put">
(7)<button type="submit" class="btn btn-primary">修改</button>:修改按钮
(8)注意:一定要在主配置文件中配置spring.mvc.hiddenmethod.filter.enabled=true,否则修改后还是发送POST请求,而不是(6)中指定的PUT请求
EmployeeController:
@PutMapping("/emp")
public String updateEmployee(Employee employee){
System.out.println("修改的员工数据:" + employee);
// 修改后要保存,如果有id,map重新put进去
employeeDao.save(employee);
return "redirect:/emps";
}
(1)使用@PutMapping("/emp")发送emp+PUT请求,使用employeeDao.save(employee)来保存信息
(2)使用"redirect:/emps"来重定向到list.html页面
(3)注意:如果要把add.html和update.html合并在一起,那么可以加上一个判断th:if="${emp!=null}"来判断当前是修改还是添加,因为在修改时需要先查询id的员工信息,存储在Model中,所以emp不为空;而添加时不需要查询,emp为空。
五、删除员工
1. 在list.html页面配置删除按钮,发送/emp/{id}+DELETE请求
2. 在EmployeeController中添加deleteEmployee方法,发送emp/{id}+DELETE请求,调用EmployeeDao.delete方法删除指定id的员工,删除后重定向到list.html/页面
3. 美化删除按钮
具体代码:
list.html:
<div>
<div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<!-- 将list.html中的员工添加按钮设置可以跳转到添加页面 -->
<h2><a class="btn btn-sm btn-success" href="emp" th:href="@{/emp}">员工添加</a></h2>
<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button>
<!-- 省略 -->
</main>
<form id="deleteEmpForm" method="post">
<input type="hidden" name="_method" value="delete"/>
</form>
</div>
</div>
<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js" th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
<script type="text/javascript" src="asserts/js/popper.min.js" th:src="@{/webjars/popper.js/1.16.1/dist/popper.js}"></script>
<script type="text/javascript" src="asserts/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/4.5.0/js/bootstrap.js}"></script>
<!-- Icons -->
<script type="text/javascript" src="asserts/js/feather.min.js" th:src="@{/asserts/js/feather.min.js}"></script>
<script>
feather.replace()
</script>
<script>
$(".deleteBtn").click(function () {
$("#deleteEmpForm").attr("action", $(this).attr("del_uri")).submit();
return false;
})
</script>
(1)如果使用下面的配置,删除样式会挤到下一行去
<form th:action="@{/emp/}+${emp.id}" method="post">
<input type="hidden" name="_method" value="delete"/>
<button type="submit" class="btn btn-sm btn-danger">删除</button>
</form>
(2)如果样式丢失,要将三个script标签使用th:src="@{...}"替换掉原生属性
th:src="@{/webjars/jquery/3.3.1/jquery.js}"
th:src="@{/webjars/popper.js/1.16.1/dist/popper.js}"
th:src="@{/webjars/bootstrap/4.5.0/js/bootstrap.js}"
EmployeeController:
@DeleteMapping("/emp/{id}")
public String deleteEmployee(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/emps";
}
(1)使用@DeleteMapping("/emp/{id}")发送emp/{id{+DELETE请求,使用employeeDao.delete(id)来保存信息
(2)使用"redirect:/emps"来重定向到list.html页面
7438

被折叠的 条评论
为什么被折叠?



