project:springboot-02!!!
一、模拟一个数据库
pojo.Department
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String departmentName;
}
pojo.Employee
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;//0女1男
private Department department;
private Date birth;
}
dao.DepartmentDao
@Repository
public class DepartmentDao {
//模拟数据库中的数据
private static Map<Integer, Department> departments = null;
static {
//Map中的key代表数据库的索引,value代表索引对应的这一行记录
departments = new HashMap<Integer, Department>();//创建一个部门数据表
departments.put(101, new Department(101, "教学部"));
departments.put(102, new Department(102, "市场部"));
departments.put(103, new Department(103, "教研部"));
departments.put(104, new Department(104, "运营部"));
departments.put(105, new Department(105, "后勤部"));
}
//获取所有部门信息
public Collection<Department> getDepartments(){
return departments.values();
}
//通过id得到部门
public Department getDepartmentById(Integer id){
return departments.get(id);
}
}
dao.EmployeeDao
@Repository
public class EmployeeDao {
//模拟数据库中的数据
private static Map<Integer, Employee> employees = null;
@Autowired
private DepartmentDao departmentDao;
static {
//Map中的key代表数据库的索引,value代表索引对应的这一行记录
employees = new HashMap<Integer, Employee>();
employees.put(1001, new Employee(1001, "AA", "392851349@qq.com", 0, new Department(101, "教学部"), new Date()));
employees.put(1002, new Employee(1002, "BB", "378985149@qq.com", 0, new Department(102, "市场部"), new Date()));
employees.put(1003, new Employee(1003, "CC", "848695349@qq.com", 1, new Department(103, "教研部"), new Date()));
employees.put(1004, new Employee(1004, "DD", "648695349@qq.com", 1, new Department(104, "运营部"), new Date()));
employees.put(1005, new Employee(1005, "EE", "248695349@qq.com", 1, new Department(105, "后勤部"), new Date()));
}
//主键自增
private static Integer initId = 1006;
//增加一个员工
public void addEmployee(Employee employee){
if(employee.getId()==null){
employee.setId(initId++);
}
employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
employees.put(employee.getId(), employee);
}
//查询全部员工
public Collection<Employee> getAll(){
return employees.values();
}
//通过id查询员工
public Employee getEmployeeById(Integer id){
return employees.get(id);
}
//通过id删除员工
public void delete(Integer id){
employees.remove(id);
}
}
@Repository注解
@repository跟@Service,@Compent,@Controller这4种注解是没什么本质区别,都是声明作用,取不同的名字只是为了更好区分各自的功能.
- 对DepartmentDao注解上@Repository后,
- @Autowire
- private DepartmentDao departmentDao;
- 就可以这样自动注入了,而不需要new对象
二、导入静态资源
具体资源在java后端开发文件的狂神springboot静态资源目录
- 注意这里面的html放在templates下,css、js、图片放在static下
三、首页
controller层:
//首页
@Controller
public class IndexController {
@RequestMapping({"/", "/index", "/index.html"})
//不管是访问"/", "/index", "/index.html"中的哪一个,都可以跳转到index.html页面
public String index(){
return "index";
}
}
- 在这里终于搞明白了return的作用,也就是说当请求localhost:8080/index的时候,视图控制器跳转到resources/templates/index.html。
- return的字符串其实就是templates下的文件名
结果:
- 但是这里的index首页中一些css样式和图片没有加进来,下一步就要把html文件中添加css、img
- 通过thymeleaf模板
扩展MVC
除了通过上述方式跳转到主页index.html以外,还可以通过自己定义的方式去扩展mvc
config.MyMvcConfig类:
@Configuration
//@EnableWebMvc
public class MyMvcConfig implements WebMvcConfigurer{
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
}
- @Configuration表示是一个配置类
- 不能加上@EnableWebMvc,加上后表示springboot 的所有默认配置都失效了,但是我们只想更改部分默认配置
-
registry.addViewController("/").setViewName("index");//访问根目录时,跳转到index.html文件
但是这里的index首页中一些css样式和图片没有加载进来,下一步就要把html文件中添加css、img。
通过thymeleaf模板引擎来在html加载静态资源
1、在html中添加thymeleaf命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
2、 更改html中的超链接:
<link href="asserts/css/bootstrap.min.css" rel="stylesheet">
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
3、......
在application.properties中关闭模板引擎的缓存,否则更改的html会因为缓存而无效
spring.thymeleaf.cache=false
四、首页的国际化
1、首先在File-setting-Editor-File Encodings中确保都是utf8,否则会乱码
2、在resources下新建一个i18n文件夹(全称:internationalization,i和n之间有18个字母)
新建三个名为:login.properties、login_en_US.properties、login_zh_CN.properties的配置文件,springboot会根据文件名自动识别其功能,并把它们自动合并在Resource Bundle 'login'
login.properties(默认)
login.tip=请登录
login.password=密码
login.remember=记住我
login.username=用户名
login.btn=登录
login_en_US.properties(英文)
login.tip=Please sign in
login.password=password
login.remember=remember me
login.username=user name
login.btn=sign in
login_zh_CN.properties(中文)
login.tip=请登录
login.password=密码
login.remember=记住我
login.username=用户名
login.btn=登录
3、application.properties中添加配置:
#国际化的配置文件所在地
spring.messages.basename=i18n.login
4、修改对应的index.html文件
...............省略下面过程
五、首页的登录功能
功能描述:
首页输入用户名和密码,如果正确,则页面跳转,如果不正确,那么提示密码错误。
首先来看首页提交的表单部分:
- form标签的action属性表示表单提交后处理表单的url地址,用thymeleaf改写前这个form是这样的:但是这样只是静态的,无法和服务器交互
<form class="form-signin" action="dashboard.html">
注意thymeleaf中url用@{}来表示,改写后表示表单提交后, 将跳转到localhost:8080/user/login请求
-
要实现登录功能,input标签必须有name属性,这个name属性不仅仅是别名的作用,还作为可与服务器交互数据的HTML元素的服务器端的标示,比如input、select、textarea、和button的name属性都有这个功能。我们可以在服务器端根据其Name通过@RequestParam取得元素提交的值
controller层:
在controller文件夹下新建一个类:
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
//获取到提交的表单的username、password后,进行具体的业务(这里是验证用户名密码是否正确业务)
if(username!=null && password.equals("123456")){
return "dashboard";//登录成功,跳转到dashboard.html页面
}else{
//显示登陆失败
//通过model向页面传递值
model.addAttribute("msg","用户名密码错误!");
return "index";
}
}
}
- 这里要结合index.html的表单部分来看
- form表单提交后,<form class="form-signin" th:action="@{/user/login}">,这个表示会产生一个localhost:8080/user/login的请求,从而这个请求会被上面这个controller层的login方法接收。(action="@{/user/login}和@RequestMapping("/user/login")相互对应)这里你也可以仔细品味一下@RequestMapping,翻译为中文就是请求映射,把一个请求映射到另外一个路径上
-
@RequestParam("username") String username 就表示把name="username"的标签的内容(这里指的是input标签中,我们从网页输入的内容)获取到,并且就是这个String username
- Model model的作用是向前端页面传递值。
model.addAttribute("msg","用户名密码错误!");//即向下面这个p标签的msg传递值
-
<p th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
- p标签的作用是,如果msg不为空(即msg为用户名密码错误!),就将这个p标签显示出来,否则不显示(用户名密码正确)。
- 用户名密码错误时:
- 用户名密码正确时:
- 但是注意上面这个url:
http://localhost:8080/user/login?username=ZY2002529&password=123456
- 这个url并不是这个页面的地址:localhost:8080/dashboard.html
- 而是一个请求(从“?username=ZY2002529&password=123456”这些参数就可以很明显看出来)
- 因此就要介绍一下请求转发和重定向的区别了!
@RequestMapping注解所在的方法
- 如果是return " ****** "; 那么是请求转发
- 如果是return " redirect:/ ***** "; 是重定向
总结重定向和请求转发的区别
重定向的方式进入dashboard.html页面
-
上述方式是错误的,会404,找不到dashboard.html,没搞明白为啥!????????
- 必须在扩展MVC配置类中加入这个才能正确跳转到dashboard.html
六、登录拦截器
登录拦截器的作用就是必须在你登录了以后才可以进入到http://localhost:8080/dashboard,否则你直接输入这个网址也可以进入。
首先需要使用session
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){
//获取到提交的表单的username、password后,进行具体的业务(这里是验证用户名密码是否正确业务)
if(username!=null && password.equals("123456")){
session.setAttribute("loginUser", username);
return "redirect:/dashboard";//登录成功,跳转到dashboard.html页面
}else{
//显示登陆失败
//通过model向页面传递值
model.addAttribute("msg","用户名密码错误!");
return "index";
}
}
}
- 在login方法参数中加入 HttpSession session参数
- 当验证密码通过后,给session赋值: session.setAttribute("loginUser", username); 也就是给别名为loginUser的Session赋值为username
什么是session和cookie?
Session:记录一系列状态
Session与cookie功能效果相同。Session与Cookie的区别在于Session是记录在服务端的,而Cookie是记录在客户端的。
解释session:当访问服务器否个网页的时候,会在服务器端的内存里开辟一块内存,这块内存就叫做session,而这个内存是跟浏览器关联在一起的。这个浏览器指的是浏览器窗口,或者是浏览器的子窗口,意思就是,只允许当前这个session对应的浏览器访问,就算是在同一个机器上新启的浏览器也是无法访问的。而另外一个浏览器也需要记录session的话,就会再开启一个属于自己的session
原理:HTTP协议是非连接性的,取完当前浏览器的内容,然后关闭浏览器后,链接就断开了,而没有任何机制去记录取出后的信息。而当需要访问同一个网站的另外一个页面时(就好比如在第一个页面选择购买的商品后,跳转到第二个页面去进行付款)这个时候取出来的信息,就读不出来了。所以必须要有一种机制让页面知道原来页面的session内容。
问题:如何知道浏览器和这个服务器中的session是一一对应的呢?又如何保证不会去访问其它的session呢?
原理解答:就是当访问一个页面的时候给浏览器创建一个独一无二的号码,也给同时创建的session赋予同样的号码。这样就可以在打开同一个网站的第二个页面时获取到第一个页面中session保留下来的对应信息(理解:当访问第二个页面时将号码同时传递到第二个页面。找到对应的session。)。这个号码也叫sessionID,session的ID号码,session的独一无二号码。
————————————————
版权声明:本文为CSDN博主「广小白」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42217767/article/details/92760353
在config包下新建一个类:LoginHandlerInterceptor
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登录成功后,应该会有用户的session
Object loginUser = request.getSession().getAttribute("loginUser");//获取别名为loginUser的session中存储的内容
if(loginUser==null){
request.setAttribute("msg", "没有权限,请先登录");//给msg赋值,这个msg是index.html中的参数
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
}else{
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
- 在SpringMVC 或者SpringBoot中定义一个拦截器是比较非常简单,主要有两种方式:
- 1、类要实现Spring 的HandlerInterceptor 接口
- 2、类继承实现了HandlerInterceptor 接口的类,例如 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter
- HandlerInterceptor:Handler(处理程序)Interceptor(拦截器),即拦截器处理程序
-
HandlerInterceptor的三个方法
-
preHandle:该方法将在请求处理之前进行调用,只有该方法返回true,才会继续执行后续的Interceptor和Controller,当返回值为true 时就会继续调用下一个Interceptor的preHandle 方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller方法;预处理,可以进行编码、安全控制、权限校验等处理;(上面红字的“请求”就是,/user/login请求,即前端的表单提交后发送的一个请求,“请求处理之前”也就是被LoginController的login方法返回页面return处理之前)
-
postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。
-
afterCompletion:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);
-
-
request.getRequestDispatcher是请求转发的意思,具体可以看下面这个博客,写的很好请求重定向、请求转发、请求包含的特点与区别详解
接下来需要在主配置文件MyMvcConfig中进行配置
@Configuration
//@EnableWebMvc
public class MyMvcConfig implements WebMvcConfigurer{//实现WebMvcConfigurer接口的类会被springboot认为是配置类
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/dashboard.html").setViewName("dashboard");
registry.addViewController("/dashboard").setViewName("dashboard");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index", "/", "/user/login", "/css/**", "/img/**", "/js/**");
}
}
- 添加一个拦截器,把LoginHandlerInterceptor类的对象传参进去
- addPathPatterns("/**"):表示拦截所有请求
- excludePathPatterns("/index", "/", "/user/login", "/css/**", "/img/**", "/js/**"):表示排除以下请求
添加拦截器成功后,如果不登录,直接访问http://localhost:8080/dashboard就会:
登录成功后,会发现请求中多了一个sessionId
如果你登录后把页面关闭了,再去直接访问http://localhost:8080/dashboard的时候会发现竟然也可以直接打开,是因为session的存在。如果把浏览器所有历史记录包括cookie和session删除,就不能直接打开了。
七、展示员工列表
对应的dashboard.html:
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
员工管理
</a>
</li>
- href对应的是点击后跳转的地址连接(其实是一个请求),这个请求被Controller接收
修改为:
<li class="nav-item">
<a class="nav-link" th:href="@{/employee}">
员工管理
</a>
</li>
对应controller层
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao;
@RequestMapping("/employee")
public String selectAllEmployees(Model model){
Collection<Employee> AllEmployees = employeeDao.getAll();
model.addAttribute("emps", AllEmployees);
return "/employee/list";
}
}
- 点击员工管理,controller接收/employee请求,利用model向前端传递值,返回/employee/list页面
未修改前的/employee/list:
修改/employee/list.html
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h2>Section title</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead><!--thead是table head-->
<tr>
<th>id</th>
<th>lastname</th>
<th>email</th>
<th>gender</th>
<th>department</th>
<th>birth</th>
</tr>
</thead>
<tbody><!--table 部分-->
<tr th:each="emp:${emps}">
<td th:text="${emp.getId()}"></td>
<td th:text="${emp.getLastName()}"></td>
<td th:text="${emp.getEmail()}"></td>
<td th:text="${emp.getGender()}"></td>
<td th:text="${emp.getDepartment().getDepartmentName()}"></td>
<td th:text="${emp.getBirth()}"></td>
</tr>
</tbody>
</table>
</div>
</main>
-
<tr th:each="emp:${emps}">中的emps是EmployeeController中的model.addAttribute("emps", AllEmployees); each代表对集合emps进行遍历,emp是emps的别名
上面的页面的问题是1、gender,应该把01改为男女 2、后面应该添加对员工的删除操作
<thead><!--thead是table head-->
<tr>
<th>id</th>
<th>lastname</th>
<th>email</th>
<th>gender</th>
<th>department</th>
<th>birth</th>
<th>操作</th>
</tr>
</thead>
<tbody><!--table 部分-->
<tr th:each="emp:${emps}">
<td th:text="${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().getDepartmentName()}"></td>
<td th:text="${emp.getBirth()}"></td>
<td>
<button class="btn btn-sm btn-primary">编辑</button>
<button class="btn btn-sm btn-danger">删除</button>
</td>
</tr>
</tbody>
修改后:
这一节还有几个小问题我没弄:
- 菜单栏点击高亮
- 员工birth的Date日期形式转化
- 不同页面公共的部分应该抽取出来,便于后期维护
八、添加员工
在员工表单的顶部设置 添加员工 按钮
<h2>
<a class="btn btn-sm btn-success" th:href="@{/add}">添加员工</a>
</h2>
- class="btn btn-sm btn-success" 是按钮的样式
- 点击后发送请求: /add
点击添加员工按钮后我们应该跳转到一个添加员工的页面/employee/add.html
@GetMapping("/add")
public String addEmployee(){
return "/employee/add";
}
- @GetMapping和@RequestMapping的区别是前者只能处理get请求, 同理还有@PostMapping
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form class="form-horizontal">
<div class="form-group">
<label>id</label>
<div class="col-sm-10">
<input class="form-control" name="id" placeholder="1000">
</div>
</div>
<div class="form-group">
<label>lastName</label>
<div class="col-sm-10">
<input class="form-control" name="lastName" placeholder="韩">
</div>
</div>
<div class="form-group">
<label>email</label>
<div class="col-sm-10">
<input class="form-control" name="email" placeholder="392851349@qq.com">
</div>
</div>
<label>gender</label>
<select class="form-control" name="gender">
<option>男</option>
<option>女</option>
</select>
<label>department</label>
<select class="form-control" name="department">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
<div class="form-group">
<label>birth</label>
<div class="col-sm-10">
<input class="form-control" name="birth" placeholder="1998/09/22">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">添加</button>
</div>
</div>
</form>
</main>
接下来有一个问题是,添加员工时,在部门下拉框那我们需要知道有哪些部门,因为部门是可以动态添加删除的
改写:
@Autowired
DepartmentDao departmentDao;
@GetMapping("/add")
public String addEmployee(Model model){
//先需要查询出来所有的部门名称
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
return "/employee/add";
}
<label>department</label>
<select class="form-control" name="department">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
</select>
- text属性是这个下拉框的option标签显示出来的内容
- value属性时这个下拉框的option标签所携带的值,
- 设置value的目的是因为创建员工,为其添加对应的部门时,需要知道部门id才可以查询到部门对象
- 通过thymeleaf的th:each来遍历后端传递过来的departments
- 给表单加上action
<form class="form-horizontal" th:action="@{/add}" method="post">
- form的method是post,所以 controller层用:@PostMapping("/add"),表示只能接收到post方式的/add请求
@PostMapping("/add")
public String add(Employee employee){
//添加员工的操作
employeeDao.addEmployee(employee);
System.out.println(employee);
//表单提交后,需要重定向到展示员工列表的页面
return "redirect:/employee";//重定向到/employee请求,即selectAllEmployees这个方法
}
- 提交表单后,先调用后台的添加员工方法,再重定向(重新查询一下全部员工列表)
但是前端表单里输入的信息,springboot是怎么与employee对象的各个属性一一对应的?
add.html中表单部分:
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form class="form-horizontal" th:action="@{/add}" method="post">
<div class="form-group">
<label>lastName</label>
<div class="col-sm-10">
<input class="form-control" name="lastName" placeholder="韩">
</div>
</div>
<div class="form-group">
<label>email</label>
<div class="col-sm-10">
<input class="form-control" name="email" placeholder="392851349@qq.com">
</div>
</div>
<label>gender</label>
<select class="form-control" name="gender">
<option th:value="1">男</option>
<option th:value="0">女</option>
</select>
<label>department</label>
<select class="form-control" name="department.id">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
</select>
<div class="form-group">
<label>birth</label>
<div class="col-sm-10">
<input class="form-control" name="birth" placeholder="1998/09/22">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">添加</button>
</div>
</div>
</form>
</main>
- name属性功不可没, 表单中的name属性要与实体类的属性名称一致。这样springboot就能将表单的input标签的输入与实体类的属性一一对应。
- 在Springboot进行数据封装时,提交到后台的数据都是String型,对于基本的数据类型会自动转化
- 实体类employee的字段必须包含表单中所有input的name对应的值,并且在类中为对应的字段生成getter 与setter方法
- id不需要在前端填写,因为设置了自增
- placeholder属性表示框内提示的信息
下面重点说一说两个<select>下拉框中选择的内容是怎么与对象employee的属性一一匹配的
<select class="form-control" name="gender">
<option th:value="1">男</option>
<option th:value="0">女</option>
</select>
- 会把所选中的<option>中的value值与对应对象的gender属性匹配
<select class="form-control" name="department.id">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
</select>
- 会把 th:value="${department.getId()}" 中的值传递给 name="department.id" ,注意这里department的id属性虽然是private,但是也必须这么写,不能写成 department.getId();
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;//0女1男
private Department department;
private Date birth;
}
观察employee类就看出来,如果表单上输入与employee属性一一对应的话,上面表单里应该提交的是Department department,那为什么提交的是department.id。
纠正一个上面的错误观点,不是要求表单上输入与employee属性一一对应,而是表单上输入与name属性的内容一一对应。
再来看下面:
@PostMapping("/add")
public String add(Employee employee){
//添加员工的操作
employeeDao.addEmployee(employee);
System.out.println(employee);
//表单提交后,需要重定向到展示员工列表的页面
return "redirect:/employee";//重定向到/employee请求,即selectAllEmployees这个方法
}
- 注意add方法有个Employee employee参数, 这个对象就是前端传过来的
employeeDao的addEmployee方法:
//增加一个员工
public void addEmployee(Employee employee){
if(employee.getId()==null){
employee.setId(initId++);
}
employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
employees.put(employee.getId(), employee);
}
- 从表单上获取的department.id被传入到addEmployee方法中去
employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
九、修改员工信息
功能:点击对应员工的 编辑 按钮,进入编辑员工界面
list.html:
//跳转到编辑员工信息界面
@RequestMapping("/edit")
public String ToEdit(){
return "/employee/edit";
}
但是在controller跳转到edit.html页面之前,必须先要获取到这个被编辑的员工的信息,展示到edit.html中。
所以上面的代码需要调整为:
list.html:
- 向后台发送请求的同时,发送当前待编辑对象的id参数,这样的话后台就可以根据employee的id,把这个employee的信息查询出来,显示到编辑页面edit.html
//跳转到编辑员工信息界面
@GetMapping("/edit/{id}")
public String ToEdit(@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 "/employee/edit";
}
- @GetMapping("/edit/{id}")中的{id}是占位符,与th:href="@{/edit/}+${emp.id}"中的emp.id对应
-
@PathVariable("id")Integer id是在获取前端数据,Model是给前端传数据
-
model.addAttribute("emp", employee);和model.addAttribute("departments", departments);中的"emp"、"departments"都是只在return的html页面内可以获取到的,其他页面获取不到
edit.html
-
<input>中的th:value="${emp.getLastName()}"表示:框内显示的默认值
- <option>中的 th:selected="${表达式}",表示如果表达式为true,那么就默认选中该下拉框
- 注意看上面这个url:http://localhost:8080/edit/1001 ,很明显看出来这是一个请求,并且请求里携带employee的id参数
剩下就是提交该表单的业务了
<form class="form-horizontal" th:action="@{/edit}" method="post">
- 提交表单,发送 /edit 请求
- 该请求被下面controller接收
//员工信息修改提交
@RequestMapping("/edit")
public String edit(Employee employee){
employeeDao.addEmployee(employee);
return "redirect:/employee";
}
- 添加该员工,再重定向到/employee请求
但是上面的edit方法有一个问题是提交的员工不是修改原来的,而是增加了一个新的员工:
解决办法:
在表单中加上上id的标签
<div class="col-sm-10">
<input type="hidden" th:value="${emp.getId()}" class="form-control" name="id">
</div>
并设置隐藏,用户不能修改,这样的话新添加的employee就会覆盖原来的(从employeeDao中能看出来为啥这样可以覆盖原来的)
十、删除员工
list.html
//删除员工
@RequestMapping("/delete/{id}")
public String delete(@PathVariable("id")Integer id){
employeeDao.delete(id);
return "redirect:/employee";
}
不多赘述
十一、404页面
springboot中,只需要在templates文件下添加一个名为404.html的页面,在404的时候会自动定位到这个页面并显示。