文章目录
Spring Boot - 03
员工管理系统
一、准备工作
- 准备静态资源(包括 css、img、js)和 html 页面,点此获取资源,提取码:3285;
- 编写对应的实体类;
- 编写 Dao 层(暂时不涉及到数据库,因此,数据是随便生成的,也没有 Service 层)。
提示:新建项目时,要导入 thymeleaf 和 web 的启动器以及 lombok 依赖。
1. 第一部分:导入静态资源和 html 页面
静态资源和 html 页面的放置位置:
- 静态资源(包括 css、img、js)放在 static 目录下;
- html 页面放在 templates 目录下,只能通过 controller 来跳转。
使用 Thymeleaf 模板引擎:
- 在头文件中导入约束:
xmlns:th="http://www.thymeleaf.org"
; - 使用 Thymeleaf 表达式或语法时,在该位置的 html 标签元素前加
th:
,让 Thymeleaf 替换接管。
Thymeleaf 表达式:Link URL Expressions :
@{/URL}
,一定注意:路径要以斜杠开头。
2. 第二部分:实体类
编写两个实体类:Department 和 Employee。
3. 第三部分:Dao 层
一个实体类对应一个 Dao,编写两个 Dao:DepartmentDao 和 EmployeeDao。
@Repository
public class DepartmentDao {
// 模拟数据库中的数据
private static Map<Integer, Department> departments = new HashMap<>();
static {
departments.put(1001, new Department(1001, "教学部"));
departments.put(1002, new Department(1002, "研发部"));
departments.put(1003, new Department(1003, "市场部"));
departments.put(1004, new Department(1004, "后勤部"));
}
// 查询所有的部门
public Collection<Department> selectAllDepartments() {
return departments.values();
}
// 根据 ID 查询部门
public Department selectDepartmentById(int id) {
return departments.get(id);
}
// 根据部门名称查询部门 ID
public Integer selectIdByDepartName(String departmentName) {
Collection<Department> values = departments.values();
for (Department value : values) {
if (departmentName.equals(value.getDepartmentName())) {
return value.getId();
}
}
return 0;
}
}
@Repository
public class EmployeeDao {
@Autowired
private DepartmentDao departmentDao;
// 模拟数据库中的数据
private static Map<Integer, Employee> employees = new HashMap<>();
static {
employees.put(1, new Employee(1, "小明", "123456@qq.com", 1, new Department(1001, "教学部")));
employees.put(2, new Employee(2, "小强", "100211@qq.com", 1, new Department(1002, "研发部")));
employees.put(3, new Employee(3, "大杜", "789456@qq.com", 0, new Department(1003, "市场部")));
employees.put(4, new Employee(4, "张三", "000111@qq.com", 0, new Department(1004, "后勤部")));
employees.put(5, new Employee(5, "月光", "448833@qq.com", 1, new Department(1002, "研发部")));
}
private int initIndex = 6;
// 增加员工
public boolean addEmployee() {
int index = initIndex++;
employees.put(index, new Employee(index, "员工" + index, "111222@qq.com",
new Random().nextInt(2),
departmentDao.selectDepartmentById(1001 + (new Random().nextInt(4)))));
return true;
}
// 修改员工信息
public boolean updateEmployee(Employee employee) {
// 使员工的部门 id 与部门名称对应起来
employee.setDepartment(new Department(
departmentDao.selectIdByDepartName(employee.getDepartment().getDepartmentName()),
employee.getDepartment().getDepartmentName())
);
// 覆盖原来的数据
employees.put(employee.getId(), employee);
System.out.println(employees.get(employee.getId()));
return true;
}
// 查询全部员工
public Collection<Employee> selectAllEmployees() {
return employees.values();
}
// 根据 ID 查询员工
public Employee selectEmployeeById(int id) {
return employees.get(id);
}
// 删除员工
public boolean deleteEmployee(int id) {
employees.remove(id);
return true;
}
}
二、首页实现
方式一:可以在 controller 中配置
@RequestMapping({"/", "/index.html"})
public String index() {
return "index";
}
说明:访问
/
和/index.html
都会跳转到首页。
方式二:可以在扩展配置类中配置,使 url 与要跳转的页面一一映射
说明:配置首页时,一般用扩展配置类进行配置。
运行结果
三、登录功能
设置前端页面
编写后端代码
结果显示
注意:可以看到登录成功和失败都可以达到想要的效果,但是两种情况的 url 都一样,接下来进行一点改进。
改进:登录成功时,重定向到 /main.html
请求,再通过配置,使得 /main.html
请求与登录成功页面相映射。
效果
总结:
- 提交表单需要 name 属性,并且提交方式 method 要与 controller 中方法的请求方式一致;
- 为了安全,提交方式用 post,这样 url 中不会显示表单中提交的参数;
- 相同的 url 请求地址,使用不同的请求方法,得到的结果也不同,浏览器默认为 get 请求方法;
- 如果直接在地址栏输入
http://localhost:8080/Sun/main.html
,也可以直接跳转到成功页面,因此需要增加拦截器; - 可以在 Spring Boot 核心配置文件中:关闭模板引擎的缓存,设置项目虚拟路径:
# 关闭模板引擎的缓存
spring.thymeleaf.cache=false
# 设置项目虚拟路径
server.servlet.context-path=/Sun
四、登录拦截器实现
拦截器进行拦截操作的依据就是:是否进行了登录操作。因此,在登录成功后,在 session 中存放指定信息。
实现拦截器的方法:
- 自定义一个拦截器,实现
HandlerInterceptor
接口; - 重写 preHandle 方法,判断是否拦截,以及进行拦截操作;
- 在 WebMvc 扩展配置类中重写 addInterceptors 方法,进行配置。
如果未经登录,直接在地址栏输入 http://localhost:8080/Sun/main.html
,会被拦截,效果
五、增删改查
1. 查询员工
后端
@Controller
public class MyController {
@Autowired
private EmployeeDao employeeDao;
// 查询全部员工
@RequestMapping("/select")
public String select(Model model) {
Collection<Employee> employees = employeeDao.selectAllEmployees();
model.addAttribute("employees", employees);
return "list";
}
}
注意:
- 注解 @RequestMapping 中的路径会改变 url;
- model 对象将数据存放在 request 域中,所以在请求转发时会携带 model 对象的数据;
- 这里的
return "list"
为请求转发,会经过视图解析器。
前端
效果
2. 增加员工
后端
// 增加员工
@RequestMapping("/add")
public String add() {
employeeDao.addEmployee();
return "redirect:/select";
}
前端
效果
3. 修改员工信息
后端
// 跳转到修改员工信息页面
@RequestMapping("/update/{employeeId}")
public String update(@PathVariable("employeeId") int id, Model model) {
Employee employee = employeeDao.selectEmployeeById(id);
model.addAttribute("employee", employee);
return "update";
}
// 修改员工信息
@PostMapping("/update")
public String updateEmployee(Employee employee) {
employeeDao.updateEmployee(employee);
return "redirect:/select";
}
前端:展示信息页面
前端:修改员工信息页面
说明:
- 跳转到修改员工信息页面时,会携带该员工的所有信息,通过 value 属性可以显示初始值;
- 后端接收的参数为 Employee 对象,但是员工的 id 不需要被修改,因此,将 id 属性放在隐藏域中;
- 前端在提交属性的值时,如果该属性是一个对象,无法直接提交,必须提交的是对象的单个属性,如:
department.departmentName
。
效果
4. 删除员工
后端
// 删除员工
@RequestMapping("/delete/{employeeId}")
public String delete(@PathVariable("employeeId") int id) {
employeeDao.deleteEmployee(id);
return "redirect:/select";
}
前端
效果
六、退出登录
通过在 session 中移除登录信息,实现退出登录的功能。
后端
// 注销
@RequestMapping("/user/logout")
public String logout(HttpSession session) {
session.removeAttribute("loginUser");
return "redirect:/index.html";
}
前端
效果
七、总结
-
在修改员工信息后提交表单时,出现
Failed to convert from type [java.lang.String] to type [java.util.Date]
错误:- 原因:前端表单提交的时间字段是 String 类型,后端无法将其解析为 Date 类型的数据;
- 解决方法:在实体类时间字段上加注解
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
声明。
-
请求转发和重定向区别:
区别 请求转发 重定向 行为 服务端行为 客户端行为 速度 快 慢 请求个数 一个请求 两个不同的请求 URL 地址 不改变 改变 Request 域的信息 可以访问(因为是同一个请求) 不能访问 WEB-INF 文件夹 可以访问 不能访问 说明:
- 请求转发:客户端 – 请求 – 服务端(请求转发) – 同一个请求 – 目标页面;
- 重定向:客户端 – 请求 – 服务端 – 重定向响应 – 客户端(重定向) – 新的请求 – 服务端 – 目标页面。
-
前端在提交属性的值时,如果该属性是一个对象,无法直接提交,必须提交的是对象的单个属性;
-
Spring 用自动装配(从容器中取对象)代替了 new 对象。因此,创建对象时,可以 new 对象,也可以自动装配对象;
-
自动装配 @Autowired 不能在静态变量上使用,因为当创建类,类加载器加载静态变量时,Spring 上下文尚未加载,所以类加载器不会在 bean 中正确注入静态类,注入结果为 null ;
-
定制错误界面的方法:在 templates 包下新建 error 文件夹,将错误界面如
404.html
、500.html
放到 error 文件夹下即可。
- 编写一个网站,需要先分析需求,设计数据库,然后设计前端页面,前端需要能够独立运行,后端接收数据,完成功能的实现:
- 前端:用模板(别人已经写好的)或者框架(组件,需要自己手动组合拼接);
- 后端:要有一套自己熟悉的后台模板。
注意:
- 获取本项目:点此进入,提取码:3285;
- 学习时,多问几个为什么,刨根问底,看源码,但也不能钻牛角尖,适度。