[spring] Spring MVC - CRUD 操作
基础实现源自于这两篇笔记:
- [spring] Spring Boot REST API - 项目实现
- [spring] Spring Boot REST API - CRUD 操作
除了 Rest API 部分改成了 Controller 之外,其他没什么变化,还是使用 service --> DAO --> DB
这个实现方式,因此关于 CRUD 的部分——即 hibernate 实现的部分,这里不会过多涉及
也因此,这部分的代码实现应该还是挺快的,毕竟主要还是 HTML 模板+controller 实现
获取 employee
除了新增 HTML 模板之外,主要还是把 @RestController
换成 @Controller
,其他大方向没什么变化,实现如下:
@Controller
@RequestMapping("/employees")
public class EmployeeController {
private EmployeeService employeeService;
@Autowired
public EmployeeController (EmployeeService employeeService) {
this.employeeService = employeeService;
}
@GetMapping("/list")
public String listEmployees (Model model) {
// get employee from db
List<Employee> employeeList = employeeService.findAll();
// add employees to model
model.addAttribute("employees", employeeList);
return "list-employees";
}
}
这里先丢一个非常简单的 UI,保证逻辑对,数据可以正常显示即可:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Employee Directory</title>
</head>
<body>
<span th:text="${employees}" />
<script src="http://localhost:35729/livereload.js"></script>
</body>
</html>
结果如下:
数据可以正常显示
从 8080 端口重定向
现在直接访问 localhost:8080 会显示 white label 的问题
考虑到正常情况下都是会直接访问 80/8080 端口,这里做一个小小的优化。首先在 resources/static
下面创建一个 index.html
文件,代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<meta http-equiv="refresh" content="0; URL='employees/list'" />
</head>
<body>
<script src="http://localhost:35729/livereload.js"></script>
</body>
</html>
这个操作就是在访问 8080 端口时,直接重定向到 localhost/employees/list
,实现效果如下:
更新的 bootstrap 代码如下:
<tbody>
<tr th:each="employee: ${employees}">
<td th:text="${employee.firstName}"></td>
<td th:text="${employee.lastName}"></td>
<td th:text="${employee.email}"></td>
</tr>
</tbody>
这里的 bootstrap 是直接引用 cdn 的
添加 employee
这里会出现一个新的知识点,controller 部分实现如下:
@GetMapping("/showFormForAdd")
public String addEmployee (Model model) {
Employee employee = new Employee();
model.addAttribute("employee", employee);
return "employees/employee-form";
}
@PostMapping("/save")
public String saveEmployee(@ModelAttribute("employee") Employee employee) {
employeeService.save(employee);
// use a redirect to prevent duplicate submission
return "redirect:/employees/list";
}
⚠️:这里的 redirect:/employees/list
,这里主要告知 spring mvc,一个新的 GET 请求会被建立,这样可以有效避免重复提交数据
HTML 模板的实现如下,首先是添加一个按钮,重定向到新的 employee-form
页面:
<a th:href="@{/employees/showFormForAdd}" class="btn btn-primary btn-sm mb-3">
Add Employee
</a>
随后再是新建一个表单,用来保存用户信息:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Save Employee</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous"
/>
</head>
<body>
<div class="container">
<h3>Employee Directory</h3>
<hr />
<p class="h4 mb-4">Save Employee</p>
<form
action="#"
th:action="@{/employees/save}"
autocomplete="off"
th:object="${employee}"
method="post"
>
<label for="firstName">First Name: </label>
<input
type="text"
th:field="*{firstName}"
id="firstName"
class="form-control mb-4 w-25"
placeholder="First Name"
/>
<label for="lastName">Last Name: </label>
<input
type="text"
th:field="*{lastName}"
id="lastName"
class="form-control mb-4 w-25"
placeholder="Last Name"
/>
<label for="email">Email: </label>
<input
type="email"
th:field="*{email}"
id="email"
class="form-control mb-4 w-25"
placeholder="Email"
/>
<button type="submit" class="btn btn-info col-2">Save</button>
</form>
<br />
<a th:href="@{/employees/list}">Back to Employee List</a>
</div>
<script src="http://localhost:35729/livereload.js"></script>
</body>
</html>
最终实现效果如下:
更新 employee
这里基本可以复用添加 employee 部分的代码,主要也是因为 hibernate 在实现 save
的功能时,如果当前的 DAO 在数据库已经存在的话,就会实现更新,反之实现新增
因此这里只需要做一点的更新即可,首先是 controller 部分的更新:
@GetMapping("/showFormForUpdate")
public String showFormForUpdate(@RequestParam("employeeId") int id, Model model) {
// get employee from the service
Employee employee = employeeService.findById(id);
// set employee in the model to populate the form
model.addAttribute("employee", employee);
// send data over to form
return "employees/employee-form";
}
随后时添加新的 update 按钮,这部分添加到循环输出 employee 信息里的最后一行:
<td>
<a
th:href="@{/employees/showFormForUpdate(employeeId=${employee.id})}"
class="btn btn-info btn-sm"
>
Update
</a>
</td>
最后是新增一个隐藏的 id,这样可以让 HTML 模板中的对象和 controller 中实现正确的 mapping,毕竟 id
对于更新来说事必要的,如果没有这个隐藏的 id
选项,那么 findById
无法找到正确的对象,所有的更新操作都有可能成为新增操作:
<form
action="#"
th:action="@{/employees/save}"
autocomplete="off"
th:object="${employee}"
method="post"
>
<!-- add hidden form field to handle the update -->
<input type="hidden" th:field="*{id}" />
</form>
最终效果如下:
数据库中的信息:
删除 employee
这部分也非常的简单,首先在添加 update 按钮的旁边新增一个 delete:
<a
th:href="@{/employees/delete(employeeId=${employee.id})}"
class="btn btn-danger btn-sm"
onclick="if (!(confirm('Are you sure you want to delete this employee?'))) return false"
>
Delete
</a>
controller 的更新如下:
@GetMapping("/delete")
public String delete(@RequestParam("employeeId") int id) {
employeeService.deleteById(id);
return "redirect:/employees/list";
}
最终结果:
这样 spring mvc 的 crud 操作就都实现完成了