13. Spring Boot CRUD
13.1 默认访问首页
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
}
# 关闭模板引擎的缓存
spring.thymeleaf.cache=false
# 配置项目虚拟目录 localhost:8080/indi
server.servlet.context-path=/indi
13.2 i18N
13.2.1 创建配置文件
-
设置编码格式
-
在resources目录创建i18n文件夹,新建
login.properties
、login_zh_CN.properties
-
此时在 i18N 目录下会出现一个Resource Bundle,右键创建一个新的配置文件
login_en_US.properties
-
批量修改配置文件
login.btn=登录 login.password=密码 login.remember=记住我 login.tip=请登录 login.username=用户名
13.2.2 配置国际化解析
WebMvcAutoConfiguration
中有一个LocaleResolver
(区域信息对象的解析器)
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
// 判断用户是否配置,如果配了就用用户的
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
// 默认配置:接收头国际化分解
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
AcceptHeaderLocaleResolver
@Override
public class AcceptHeaderLocaleResolver implements LocaleResolver {
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = this.getDefaultLocale();
// 默认是根据请求头带来的区域信息获取Locale进行国际化
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
} else {
Locale requestLocale = request.getLocale();
List<Locale> supportedLocales = this.getSupportedLocales();
if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
if (supportedLocale != null) {
return supportedLocale;
} else {
return defaultLocale != null ? defaultLocale : requestLocale;
}
} else {
return requestLocale;
}
}
}
}
13.2.3 自定义LocaleResolver
-
修改
login.html
按钮的跳转连接<a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">中文</a> <a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
-
config包下创建一个配置类
MyLocaleResolver
import org.springframework.util.StringUtils; public class MyLocaleResolver implements LocaleResolver{ // 解析请求 @Override public Locale resolveLocale(HttpServletRequest request){ String language = request.getParameter("l"); // 如果没有参数,就使用系统默认的 Locale locale = Locale.getDefault(); // 如果有参数,就处理参数 if(!StringUtils.isEmpty(language)){ String[] splits = language.split("_"); locale = new Locale(splits[0], splits[1]); } return locale; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { } }
-
MyMvcConfig
@Configuration public class MyMvcConfig implements WebMvcConfigurer{ // 使自定义配置的区域化信息生效 @Bean public LocaleResolver localResolver(){ return new MyLocaleResolver(); } }
-
application.properties
# 配置文件的真实位置 spring.messages.basename=i18n.login
13.3 登录
13.3.1 前端
login.html
<form class="form-signin" th:action="@{/user/login}">
<!--根据登录状态,判断是否显示提示信息-->
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
<input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
<input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required="">
13.3.2 后端
loginController.java
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Model model){
if (!StringUtils.isEmpty(username)&&"123456".equals(password)) {
return "redirect:/main.html";
}else {
model.addAttribute("msg","用户名或密码错误");
return "login";
}
}
}
MyMvcConfig.java
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// ...
registry.addViewController("/main.html").setViewName("dashboard");
}
}
13.4 拦截器
13.4.1 前端
-
dashboard.html
登录之后显示用户名<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a>
13.4.2 后端
-
LoginHandlerInterceptor.java
自定义拦截器类public class LoginHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser = request.getSession().getAttribute("loginUser"); if (loginUser == null) { request.setAttribute("msg", "请先登录"); request.getRequestDispatcher("/login.html").forward(request, response); return false; } return true; } }
-
MyMvcConfig.java
在配置类中配置拦截器@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/login.html","/","/user/login","/css/**","/js/**","/css/**","/img/**"); }
-
LoginController.java
登录成功之后添加session@Controller public class LoginController { @RequestMapping("/user/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){ if (!StringUtils.isEmpty(username)&&"1".equals(password)) { session.setAttribute("loginUser",username); return "redirect:/main.html"; }else { model.addAttribute("msg","用户名或密码错误"); return "login"; } } }
13.5 员工列表
13.5.1 前端
-
common.html
提取前端公共部分<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <body> <!--顶部栏--> <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar"> <!--...--> </nav> <!--侧边栏--> <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar"> <!--...--> <!--根据参数设置选中模块样式--> <a th:class="${active=='main.html'}?'nav-link active':'nav-link'" th:href="@{/main.html}"> 首页 <!--...--> <a th:class="${active=='list.html'}?'nav-link active':'nav-link'" th:href="@{/emps}"> 员工管理 <!--...--> </nav> </body> </html>
-
dashboard.html
前端引用公共组件<div th:replace="~{common/common::topbar}"></div> <!--调用模板引擎时,传递参数给组件,实现侧边栏选中模块是否高亮--> <div th:replace="~{common/common::sidebar(active='main.html')}"></div>
-
list.html
前端引用公共组件<div th:replace="~{common/common::topbar}"></div> <div th:replace="~{common/common::sidebar(active='list.html')}"></div> <!--设置列表显示信息--> <table class="table table-striped table-sm"> <thead> <tr> <th>id</th> <th>lastName</th> <th>email</th> <th>gender</th> <th>department</th> <th>birth</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="emp:${emps}"> <td th:text="${emp.getId()}">[[${emp.getId()}]]</td> <td th:text="${emp.getLastName()}"></td> <td th:text="${emp.getEmail()}"></td> <td th:text="${emp.getGender()==0?'女':'男'}"></td> <td th:text="${emp.getDepartment().getDepartName()}"></td> <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td> <td> <button class="btn btn-sm btn-primary">编辑</button> <button class="btn btn-sm btn-danger">删除</button> </td> </tr> </tbody> <!--...-->
13.5.2 后端
-
EmployeeController.java
@Controller public class EmployeeController { @Autowired EmployeeDao employeeDao; @RequestMapping("/emps") public String list(Model model){ Collection<Employee> employees = employeeDao.getAll(); model.addAttribute("emps",employees); return "emp/list"; } }
13.6 添加员工
13.6.1 前端
-
list.html
添加一个按钮<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2><a class="btn btn-primary" th:href="@{/emp}">添加员工</a> </h2>
-
新建
add.html
添加员工页面<!--核心表单--> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <form th:action="@{/emp}" method="post"> <div class="form-group"> <label>LastName</label> <input type="text" name="lastName" class="form-control" placeholder="zhangsan"> </div> <div class="form-group"> <label>Email</label> <input type="email" name="email" class="form-control" placeholder="zhangsan@qq.com"> </div> <div class="form-group"> <label>Gender</label><br/> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="gender" value="1" checked="checked"> <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> <!--因为后端接收的是一个Employee,所有需要提交的department中的一个属性--> <select class="form-control" name="department.id"> <option th:each="dept:${departments}" th:text="${dept.getDepartName()}" th:value="${dept.getId()}"></option> </select> </div> <div class="form-group"> <label>Birth</label> <input type="text" name="birth" class="form-control" placeholder="zhangsan"> </div> <button type="submit" class="btn btn-primary">添加</button> </form> </main>
13.6.2 后端
-
EmployeeController.java
@Controller public class EmployeeController { @Autowired DepartmentDao departmentDao; @GetMapping("/emp") public String toAddPage(Model model) { //查出部门信息 Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments", departments); return "emp/add"; } @PostMapping("/emp") public String addEmp(Employee employee) { employeeDao.save(employee); return "redirect:/emps"; } }
-
application.properties
# 时间日期格式化,添加员工生日时使用 spring.mvc.format.date=yyyy-MM-dd
13.7 修改员工
13.7.1 前端
-
list.html
<!--跳转到编辑页面--> <a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑</a>
-
update.html
大部分与add.html
一样,只修改了表单部分<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <form th:action="@{/updateEmp}" method="post"> <input type="hidden" name="id" th:value="${emp.getId()}"> <div class="form-group"> <label>LastName</label> <input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="zhangsan"> </div> <div class="form-group"> <label>Email</label> <input th:value="${emp.getEmail()}" type="email" name="email" class="form-control" placeholder="zhangsan@qq.com"> </div> <div class="form-group"> <label>Gender</label><br/> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==1}" 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 th:checked="${emp.getGender()==0}" 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> <!--因为后端接收的是一个Employee,所有需要提交的是其中的一个属性--> <select class="form-control" name="department.id"> <option th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDepartName()}" th:value="${dept.getId()}"></option> </select> </div> <div class="form-group"> <label>Birth</label> <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" type="text" name="birth" class="form-control" placeholder="zhangsan"> </div> <button type="submit" class="btn btn-primary">修改</button> </form> </main>
13.7.2 后端
-
EmployeeController.java
@Controller public class EmployeeController { @GetMapping("/emp/{id}") public String toUpdateEmp(@PathVariable("id") Integer id,Model model){ Employee employee = employeeDao.getEmployeeById(id); model.addAttribute("emp",employee); //查出部门信息 Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments", departments); return "emp/update"; } @PostMapping("/updateEmp") public String updateEmp(Employee employee){ //这里依旧调用save()是因为一旦map中的key重复了会直接覆盖,就是更新了 employeeDao.save(employee); return "redirect:/emps"; } }
13.8 删除员工
13.8.1 前端
-
list.html
<a th:onclick="if(confirm('确定要删除'+[[${emp.getLastName()}]]+'吗?')==false)return false;" class="btn btn-sm btn-danger" th:href="@{/deleteEmp/}+${emp.getId()}">删除</a>
13.8.2 后端
-
EmployeeController.java
@Controller public class EmployeeController { @GetMapping("/deleteEmp/{id}") public String delEmp(@PathVariable("id")int id){ employeeDao.delete(id);; return "redirect:/emps"; } }
13.9 404处理
只需要在templates目录下创建一个名称为error的文件夹,将404.html放入,Spring Boot将会自动使其生效。
13.10 注销
13.10.1 前端
common.html
<a class="nav-link" th:href="@{/user/logout}">注销</a>
13.10.2 后端
LoginController.java
@Controller
public class LoginController {
@RequestMapping("/user/logout")
public String logout(HttpSession session){
session.invalidate();
return "redirect:/index.html";
}
}