当前位置:   article > 正文

Java-SpringBoot:员工管理系统_创建一个sprinboot项目,利用lombok组件创建employee类,并在enployeeco

创建一个sprinboot项目,利用lombok组件创建employee类,并在enployeecontroller类

Java:SpringBoot-员工管理系统

参考的代码和资源链接:https://blog.csdn.net/qq_45173404/article/details/108934414?spm=1001.2014.3001.5501
学习视频:B站 狂神说Java – https://www.bilibili.com/video/BV1PE411i7CV

学习文档: 微信公众号 狂神说 –https://mp.weixin.qq.com/mp/homepage?__biz=Mzg2NTAzMTExNg==&hid=1&sn=3247dca1433a891523d9e4176c90c499&scene=18&uin=&key=&devicetype=Windows+10+x64&version=63020170&lang=zh_CN&ascene=7&fontgear=2

SpringBoot回顾

SpringBoot其实就是去整合了Spring、spring mvc框架,整合了里面的配置信息,“约定大于配置”。

  • xxxAutoConfiguration 完成自动配置文件,给容器中导入非常多的自动配置类。即给容器中导入配置好的组件 bean信息。

    使用时可以直接拿来使用,开箱即用。【不用再向原来mybatis、spring中那样写xml配置文件了】

  • 内嵌了服务器,比如Tomcat、Jetty。简化了web项目开发,不用再自己启动配置tomcat

  • 单体应用架构转化为微服务架构风格。

最重要的就是 SpringBoot自动装配原理了。

@SpringBootApplication
public class Springboot03WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot03WebApplication.class, args);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

开启SpringBoot应用的注解是 @SpringBootApplication,它里面包括:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • @ComponentScan 的作用是指定包加入Spring IOC的包扫描,自动扫描并加载符合条件的bean 或组件。将bean定义加载到 IOC容器中。
  • @SpringBootConfiguration 的作用是 表明这个类是springboot的配置类,里面保存的是配置信息,对应于spring中的xml配置文件
  • @EnableAutoConfiguration :开启自动配置功能。原来我们需要自己去添加配置的东西,在SpringBoot中可以直接自动地帮我们配置。

配置文件:

  • 自己定义 *.yaml 或者 *.properties 配置文件。然后可以去修改SpringBoot中默认的配置信息。

    yaml语法:key: 空格 value

    server:
      port: 8081
    #选择要激活那个环境块
    spring:
      profiles:
        active: prod
        
    ---
    server:
      port: 8082
    spring:
      profiles: dev # 设置环境的名称
      
    ---
    server:
      port: 8084
    spring:
      profiles: prod # 设置环境的名称
      
    person:
      name: alzn
      age: 18
      happy: true
      birth: 2002/01/01
      maps: {al: last name, zn: first name}
      lists:
        - learning
        - running
        - dancing
      dog:
        name: xiaohu
        age: 6
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • @ConfigurationProperties(加载配置文件的注解)对属性赋值
    • **@PropertySource :**加载指定的配置文件
  • 利用注解开发自定义配置文件,

    • 如mvc框架中的视图解析器:ViewResolver
    • 将自定义的配置文件信息 注入到Bean中,由SpringBoot帮我们去装配,组合所有的配置信息
    // 如果你想要 div 一些定制的功能,只要写这个组件,然后把它交给springboot,springboot就会帮我们装配
    // 扩展 springmvc  围绕着核心 dispatchservlet
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
        // ViewResolver 实现了视图解析器接口的类。 我们就可以把这个当作视图解析器
        @Bean //放到bean中。通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
        public ViewResolver myViewResolver(){
            return new MyViewResolver();
        }
    
        //我们写一个静态内部类,视图解析器就需要实现ViewResolver接口
        private static class MyViewResolver implements ViewResolver{
            @Override
            public View resolveViewName(String s, Locale locale) throws Exception {
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

thymeleaf模板引擎:同样也是为了简化web开发,直接继承 thymeleaf模板引擎。

模板引擎的作用就是帮助我们写了一个页面模板,比如有些值是动态的,我们写一些表达式。而这些值,从哪来呢,就是我们在后台封装一些数据。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。

在使用SpringBoot进行开发的时候,自己要解决的、参与的工作是:

  • 导入静态资源。配置资源文件:*.properties、 *.yaml
  • jsp、thymeleaf模板引擎。便于简化开发,动态的资源数据信息,代码去提供。thymeleaf模板引擎提供一个固定的页面
  • 装配和扩展 SpringMVC。自己定义的关于 WebMvcConfigurer 的mvcConfig配置信息;SpringBoot中包含的默认mvc配置信息。

@EnableWebMvc // 这个就是导入了一个类: DelegatingWebMvcConfiguration: 从容器中获取所有的 webmvcConfig

@EnableWebMvc 将 WebMvcConfigurationSupport 组件 导入进来了;而导入的 WebMvcConfigurationSupport 只是SpringMVC最基本的功能!此时我们全面接管SpringMVC。

SpringBoot:员工管理系统

SpringBoot web开发

jar:webapp

自动装配:

  1. 创建应用,选择模块

Springboot到底帮我们配置了什么? 我们能不能进行修改? 能修改哪些东西?能不能扩展?

  • xxxxAutoConfigurartion:自动配置类;给容器中添加组件

  • xxxxProperties: 封装配置文件中相关属性

其实SpringBoot的东西用起来非常简单,因为SpringBoot最大的特点就是自动装配。

使用SpringBoot的步骤:

1、创建一个SpringBoot应用,选择我们需要的模块,SpringBoot就会默认将我们的需要的模块自动配置好

2、手动在配置文件中配置部分配置项目就可以运行起来了

3、专注编写业务代码,不需要考虑以前那样一大堆的配置了。

我们在利用 Springboot的时候,能够利用和修改哪些配置,以及增加配置文件呢? 如下所示,我们最终是要通过这两个去进行 环境配置:

  • 向容器中自动配置组件 :xxxAutoconfiguration
  • 自动配置类,封装配置文件的内容:xxxProperties

我们在进行 springboot web 开发要解决的问题

  • 导入静态资源
  • 首页
  • jsp、模板引擎Thymeleaf
  • 装配扩展 SpringMVC
  • 增删查改
  • 拦截器
  • 国际化

参考的代码和资源链接:https://blog.csdn.net/qq_45173404/article/details/108934414?spm=1001.2014.3001.5501

1、环境搭建

新建一个SpringBoot项目:springboot-03-system。

image-20220421114225151

image-20220421114247795

选择pom依赖文件:SpringWeb 和Thymeleaf模板引擎

image-20220421114420737

导入静态资源

创建不存在的静态资源目录 public 和 resources,便于添加我们想要自定义的静态资源。

SpringBoot中的静态资源导入:【SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置类里面】

  • 两种映射规则:“classpath:/META-INF/resources/”、“classpath:/resources/”、“classpath:/static/”、“classpath:/public/”
  • 自定义静态资源路径

所有的 /webjars/** , 都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源
image-20220421161302785

静态资源文件:

image-20220421162232512

image-20220421162329494

把静态资源文件导入到对应文件夹中:

  • 将 html 静态资源放置 templates 目录下
  • 将 asserts 目录下的 css、img、js 等静态资源放置 static 目录下

image-20220421162456166

数据库的创建

创建一个伪数据库,进行数据库的模拟。

导入lombok,便于使用注解开发:

<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

实体类:pojo

创建实体类:Department 部门表

package com.al.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

// 部门表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
    private Integer id;
    private String departmentName;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

员工表:

  • 设置一对多和多对一。员工和部门的关系。
  • 性别没有使用char类型,使用Integr
package com.al.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

// 员工表
@Data
@NoArgsConstructor
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender; // 0:女  1:男。 用数字表示性别男女
    private Department department;
    private Date birth;

    public Employee(Integer id, String lastName, String email, Integer gender, Department department, Date birth) {
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
        this.department = department;
        // 默认的创建日期
        //this.birth = birth;
        this.birth = new Date();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

dao层

创建dao层:dao层,负责底层数据的操作。

Bean的注解开发:

  • @Component 通常是通过类路径扫描来⾃动侦测以及⾃动装配到Spring容器中。@Component 表明会将这个类交给 Spring管理装配,它与@Bean的不同之处在于,它作用在类上。注册一个类的实例到Spring IOC 容器。需要的时候,从里面去获取。

  • 为了更好的进行分层,在 web 开发中,会按照 mvc 三层架构分层。Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

    • @Controller:web层
    • @Service:service层
    • @Repository:dao层

Bean的自动装配配置

  • @Autowired: 自动装配 通过类型,名字
    • 如果@Autowired 不能唯一自动装配属性,则还需要通过 @Qualifier(value = “xxx”)
  • @Nullable: 字段标注了这个注解,表示这个字段可以为 null
  • @Resource:自动装配 通过名字, 类型。先去匹配byName,后去进行byType。

部门dao类:

DepartmentDao:将此类注入到Spring IOC容器中,dao层使用注解 @Repository。

  • 模拟数据库中的部门数据。采用map表示。

  • Map<Integer, Department> departments; // Integer代替数据库表id,Department表示数据库表中部门的具体信息。

package com.al.dao;


import com.al.pojo.Department;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

// 部门dao
@Repository
public class DepartmentDao {

    // 模拟数据库表中的数据
    private static Map<Integer, Department> departments = null;
    static {
        // 创建一个部门表
        departments = new HashMap<>();
        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, "礼部"));
        departments.put(106, new Department(106, "工部"));
    }

    // 获取所有的部门信息
    public Collection<Department> getDepartments(){
        return departments.values();
    }

    // 通过id获取部门信息
    public Department getDepartmentById(Integer id){
        return departments.get(id);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

员工dao类:

EmployeeDao类:需要去继承DepartmentDao 这个部门dao,获取这样的一个类实例,所以

  • 使用注解@Autowired 获取DapartmentDao的一个实例。

  • 模拟数据库中的员工表数据。采用map表示:private static Map<Integer, Employee> employees = null;

package com.al.dao;

import com.al.pojo.Department;
import com.al.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

// 员工Dao
@Repository
public class EmployeeDao {
    // 模拟数据库表中的员工数据信息
    private static Map<Integer, Employee> employees = null;
    // 员工所属部门
    @Autowired
    private static DepartmentDao departmentDao;

    static {
        employees = new HashMap<>();
        employees.put(1001, new Employee(1001, "AAA", "1356207897@qq.com", 1, new Department(101, "户部"), new Date()));
        employees.put(1002, new Employee(1002, "BBB", "1356207897@qq.com", 0, new Department(102, "兵部"), new Date()));
        employees.put(1003, new Employee(1003, "CCC", "1356207897@qq.com", 1, new Department(103, "吏部"), new Date()));
        employees.put(1004, new Employee(1004, "DDD", "1356207897@qq.com", 0, new Department(104, "刑部"), new Date()));
        employees.put(1005, new Employee(1005, "EEE", "1356207897@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 deleteEmployeeByID(int id) {
        employees.remove(id);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

2、首页实现

首页实现:自定义配置类 MyMvcConfig

首页控制,使导入的templates中的静态资源有效。利用该自定义的静态资源,需要提供静态资源配置文件。

  • 在 templates目录下的所有页面,只能通过 controller来跳转。这个需要模板引擎的支持。如 thymeleaf。

image-20220423174217358

在控制器 controller中,进行首页控制,IndexController:

package com.al.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
    @RequestMapping({"/", "/index.html"}) //这是一个集合
    public String index(){
        return "index";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

不过最好我们采用这种方式去进行对根目录下的访问:【采用自定义配置 资源文件的方式】

在主程序同级目录下新建 config 包用来存放自己的配置类:

  • 新建自定义配置类 MyMvcConfig,进行视图跳转
package com.al.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

启动浏览器测试,观察界面:界面可以访问,但是不理想

image-20220423173924576

想要直接去访问 template 文件夹路径下的静态资源,需要引入模板引擎。这里采用 SpringBoot中的 thymeleaf 。

  • 导入thymeleaf的依赖
  • 在 index中就需要导入 thymeleaf
  • 语法要使用thymeleaf语法,URL路径要用@{…}

回顾:静态资源映射规则

创建有效的静态资源路径 文件夹:而其中的classpath:/META-INF/resource/ 这个就对应着上面的那个webjars 资源路径。

"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
  • 1
  • 2
  • 3
  • 4
  • classpath对应的文件夹等级就是 resources 资源文件夹。

  • 我们创建以下的几个文件夹,都能够存放我们的静态文件:【可以直接获取该路径下的静态资源文件】

image-20210408212018536

首页实现:thymeleaf模板引擎

上述测试的时候,发现页面中的图片没有加载出来,且css和js的样式并没有显示。 这是因为 想要直接去访问 template 文件夹路径下的静态资源,需要引入模板引擎。

在SpringBoot中,推荐使用Thymeleaf作为模板引擎,我们将其中的语法改为Thymeleaf,所有页面的静态资源都需要使用模板引擎接管,里面的动态数据由后端提供。前面页面需要符合 thymeleaf语法。

Selection Variable Expressions: *{…}:选择表达式:和${}在功能上是一样;
Message Expressions: #{…}:获取国际化内容
Link URL Expressions: @{…}:定义URL;
Fragment Expressions: ~{…}:片段引用表达式

在index.html页面中:

  • 引入thymeleaf:

    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
    • 1
  • 第一个/代表项目的classpath,也就是这里的resources目录。 直接获取 “classpath:/static/” 路径下的资源文件:

    image-20220423192313207

    image-20220423192155084

  • thymeleaf语法中 定义URL的方式:@{}

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<meta name="description" content="">
		<meta name="author" content="">
		<title>Signin Template for Bootstrap</title>
		<!-- Bootstrap core CSS -->
		<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
		<!-- Custom styles for this template -->
		<link th:href="@{/css/signin.css}" rel="stylesheet">
	</head>

	<body class="text-center">
		<form class="form-signin" action="dashboard.html">
			<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
			<label class="sr-only">Username</label>
			<input type="text" class="form-control" placeholder="Username" required="" autofocus="">
			<label class="sr-only">Password</label>
			<input type="password" class="form-control" placeholder="Password" required="">
			<div class="checkbox mb-3">
				<label>
          <input type="checkbox" value="remember-me"> Remember me
        </label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
			<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
			<a class="btn btn-sm">中文</a>
			<a class="btn btn-sm">English</a>
		</form>

	</body>

</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

测试访问结果:

image-20220423191354609

其它页面进行同样的修改。

配置首页的时候:可以在这里自己自行定义 目录, 此时 localhost:8080失效,必须是 localhost:8080/al

  • application.properties 配置资源文件:
server.servlet.context-path=/al
  • 1

此时的测试结果:ulr请求路径必须是:http://localhost:8080/al/
image-20220423195244892

3、页面国际化

IDEA中编码设置:UTF-8

首先在 setting -> FileEncodings中 必须要确保编码为 UTF-8, 不然写的东西会乱码。

image-20220423195912693

i18n国际化资源配置文件

在静态资源中创建一个 i18n文件,国际化:

在这其中分别创建两个配置文件:

  • 命名方式是下划线的组合:文件名_语言_国家.properties;

    以此方式命名,IDEA会帮我们识别这是个国际化配置包,自动绑定在一起转换成如下的模式:

  • login.properties 和 login_zh_CN.properties, 然后IDEA会自动帮你合并:

image-20220423200427431

如果再添加其它配置文件:选择Resource Bundle ‘login’,右键点击添加即可:

image-20220423200512109

此时只需要输入区域名即可创建成功,比如输入 en_US,就会自动识别

image-20220423200612791

此时的i18n文件下的配置资源文件:

image-20220423200647641

在编写login资源配置文件时:可以选择左下角的 Resource Bundle进行可视化编写

  • 进入到可视化编辑页面后,点击加号 + ,添加属性,首先新建一个 login.tip 代表首页中的提示

image-20220423201120032

然后对这种提示,去分别做语言配置:在输入框中写入相应的语言提示

image-20220423203613511

login.properties:

login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名
  • 1
  • 2
  • 3
  • 4
  • 5

login_zh_CN.properties:

login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名
  • 1
  • 2
  • 3
  • 4
  • 5

login_en_US.properties:

login.btn=login
login.password=password
login.remember=remember
login.tip=Please sigin in
login.username=suaername
  • 1
  • 2
  • 3
  • 4
  • 5

那么如何让我们配置的这些生效呢?

  • 在Spring程序中,国际化主要是通过ResourceBundleMessageSource这个类来实现的

  • Spring Boot通过MessageSourceAutoConfiguration为我们自动配置好了管理国际化资源文件的组件

查看源码MessageSourceAutoConfiguration

  • 寻找Meaasge, 找到它的前缀。@ConfigurationProperties(prefix = “spring.messages”)
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {

	private static final Resource[] NO_RESOURCES = {};

	@Bean
	@ConfigurationProperties(prefix = "spring.messages")
	public MessageSourceProperties messageSourceProperties() {
		return new MessageSourceProperties();
	}

	@Bean
	public MessageSource messageSource(MessageSourceProperties properties) {
		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
		if (StringUtils.hasText(properties.getBasename())) {
			messageSource.setBasenames(StringUtils
					.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
		}
		if (properties.getEncoding() != null) {
			messageSource.setDefaultEncoding(properties.getEncoding().name());
		}
		messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
		Duration cacheDuration = properties.getCacheDuration();
		if (cacheDuration != null) {
			messageSource.setCacheMillis(cacheDuration.toMillis());
		}
		messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
		messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
		return messageSource;
	}
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

关于 messageSource 方法中的参数:MessageSourceProperties properties。

MessageSourceProperties 类:

public class MessageSourceProperties {
	/**
	 * Comma-separated list of basenames (essentially a fully-qualified classpath
	 * location), each following the ResourceBundle convention with relaxed support for
	 * slash based locations. If it doesn't contain a package qualifier (such as
	 * "org.mypackage"), it will be resolved from the classpath root.
	 */
	private String basename = "messages";
	/**
	 * Message bundles encoding.
	 */
	private Charset encoding = StandardCharsets.UTF_8;
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注释的意思:

* 逗号分隔的基本名称列表(本质上是完全限定的类路径 location),每个都遵循 ResourceBundle 约定,并轻松支持
*基于斜线的位置。 如果它不包含包限定符(例如"org.mypackage"),它将从类路径根目录中解析。
  • 1
  • 2

意思是:如果你不在springboot配置文件中指定以.分隔开的国际化资源文件名称的话,它默认会去类路径下找messages.properties作为国际化资源文件。

自定义国际化资源文件,因此我们需要在SpringBoot配置文件application.properties中加入以下配置指定我们配置文件的名称

  • 我们对配置文件的真实位置(i18n.login),修改后:
#自定义配置:国际化资源文件
spring.messages.basename=i18n.login
  • 1
  • 2

Message Expressions: #{…}:获取国际化内容

对于国际化 message如何写:thymeleaf 中 表示message 的语法为 #{…}

  • <label>
              <input type="checkbox" value="remember-me" th:text="#{login.remember}">
        <input type="checkbox" value="remember-me"> [[#{login.remember}]]
            </label>
    
    • 1
    • 2
    • 3
    • 4
  • 修改的地方:【对应着前端页面显示的地方】

image-20220423222322894

此时的index.html 代码:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Signin Template for Bootstrap</title>
    <!-- Bootstrap core CSS -->
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
    <!-- Custom styles for this template -->
    <link th:href="@{/css/signin.css}" rel="stylesheet">
</head>

<body class="text-center">
<form class="form-signin" action="dashboard.html">
    <img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
    <label class="sr-only">Username</label>
    <input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
    <label class="sr-only">Password</label>
    <input type="password" class="form-control" th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
<!--            <input type="checkbox" value="remember-me" th:text="#{login.remember}"> Remember me-->
            <input type="checkbox" value="remember-me" th:text="#{login.remember}">
        </label>
    </div>
<!--    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>-->
    <button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn}]]</button>
    <p class="mt-5 mb-3 text-muted">© 2017-2018</p>
    <a class="btn btn-sm">中文</a>
    <a class="btn btn-sm">English</a>
</form>

</body>

</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

此时的测试:首页:

image-20220423222232836

国际化组件的中英文切换

切换方式

如何通过按钮 进行中英文的转换?在index.html页面中可以看到两个标签:

    <a class="btn btn-sm">中文</a>
    <a class="btn btn-sm">English</a>
  • 1
  • 2

在标签中加入跳转链接,点击后进行响应跳转,获取对应的信息资源参数:

<!--这里传入参数不需要使用?使用key=value-->
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>

  • 1
  • 2
  • 3
  • 4
自定义地区解析组件

Spring源码中关于国际化的类有:

  • Locale:代表地区,每一个Locale对象都代表了一个特定的地理、政治和文化地区
  • LocaleResolver:地区解析器

分析:

消息语言转换的类:AcceptHeaderLocaleResolver,实现接口 LocaleResolver

image-20220424093117184

LocaleResolver:

public interface LocaleResolver {
	Locale resolveLocale(HttpServletRequest request);

	void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在WebMvcAutoConfiguration 类中寻找 LocaleResolver:

image-20220424095759400

		@Override
		@Bean
		@ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
		public LocaleResolver localeResolver() {
            //如果用户配置了,则使用用户配置好的
			if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
				return new FixedLocaleResolver(this.webProperties.getLocale());
			}
            //用户没有配置,则使用默认的
			AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
			localeResolver.setDefaultLocale(this.webProperties.getLocale());
			return localeResolver;
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

经过寻找后发现只要完成了 接口实现类 LocalResolver,就能完成 地区语言转换。该方法就是获取LocaleResolver地区对象解析器:

  • 如果用户配置了则使用用户配置的地区解析器;
  • 如果用户没有配置,则使用默认的地区解析器

在消息语言转换的类 AcceptHeaderLocaleResolver中:

public class AcceptHeaderLocaleResolver implements LocaleResolver {

	private final List<Locale> supportedLocales = new ArrayList<>(4);

	@Nullable
	private Locale defaultLocale;
    ......
    @Override
	public Locale resolveLocale(HttpServletRequest request) {
		Locale defaultLocale = getDefaultLocale();
		if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
			return defaultLocale;
		}
		Locale requestLocale = request.getLocale();
		List<Locale> supportedLocales = getSupportedLocales();
		if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) {
			return requestLocale;
		}
		Locale supportedLocale = findSupportedLocale(request, supportedLocales);
		if (supportedLocale != null) {
			return supportedLocale;
		}
		return (defaultLocale != null ? defaultLocale : requestLocale);
	}
    ......
    @Override
	public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) {
		throw new UnsupportedOperationException(
				"Cannot change HTTP accept header - use a different locale resolution strategy");
	}
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

我们自定义一个地区解析器,继承实现LocaleResolver 接口,使我们的自定义国际化资源生效。去实现,前端界面 Index.html中点击链接传递参数,使得按钮生效,完成中英文切换。

  • 定义自己的地区配置解析器LocalResolver
  • 前端页面的请求链接参数

index.html:

<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
  • 1
  • 2

自定义地区配置解析器:MyLocaleResolver,完成实现接口LocalResolver。

  • 按照源码中那样编写, 我们要获取 index.html中的语言链接请求,然后进行对应的地区语言转换:
package com.al.config;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

// 地区解析器:获取链接上携带的参数信息
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[] split = language.split("_");//zh_CN(语言_地区)
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

我们的国际化 message 写完后, 为了使地区配置解析器的信息生效。该组件需要放入到 bean中。

  • 在配置类 MvcConofig 中添加bean:

    package com.al.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.LocaleResolver;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("index");
            registry.addViewController("/index.html").setViewName("index");
        }
    
        // 将自定义的国际化配置放入 Spring容器中,使组件生效
        @Bean
        public LocaleResolver localeResolver(){
            return new MyLocaleResolver();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

重启项目进行访问测试。

  • 点击中文按钮,url路径为:http://localhost:8080/index.html?l=zh_CN

  • 点击英文按钮,url路径为:http://localhost:8080/index.html?l=en_US

    image-20220424111302852

4、登录功能的实现

前端页面 index.html

在登录页面,即首页去实现登录, 实现跳转到 dashboard.html 页面。

  • 在 index.html页面, 使用 thymeleaf 语法,改写提交动作 submit 跳转路径到 /user/login
  • 给定 username 名字和password密码时 添加 name 属性,便于传参
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Signin Template for Bootstrap</title>
    <!-- Bootstrap core CSS -->
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
    <!-- Custom styles for this template -->
    <link th:href="@{/css/signin.css}" rel="stylesheet">
</head>

<body class="text-center">
<!--<form class="form-signin" action="dashboard.html">-->
<form class="form-signin" th:action="@{/user/login}">
    <img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
    <label class="sr-only">Username</label>
    <input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
    <label class="sr-only">Password</label>
    <input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required="">
    <p style="color: red" th:text="${msg}"></p>
    <div class="checkbox mb-3">
        <label>
<!--            <input type="checkbox" value="remember-me" th:text="#{login.remember}"> Remember me-->
            <input type="checkbox" value="remember-me" th:text="#{login.remember}">
        </label>
    </div>
<!--    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>-->
    <button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn}]]</button>
    <p class="mt-5 mb-3 text-muted">© 2017-2018</p>
<!--    <a class="btn btn-sm">中文</a>-->
<!--    <a class="btn btn-sm">English</a>-->
    <!--这里传入参数不需要使用?使用key=value-->
    <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>

</form>

</body>

</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

image-20220424114139205

控制器Controller

我们创建一个控制器用于 响应这个请求 servlet,完成跳转。

LoginController 控制器:

package com.al.controller;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@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 "dashboard";
        }else {
            model.addAttribute("msg", "用户名或密码错误");
            return "index";
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

然后我们在index.html首页中加一个标签用来显示controller返回的错误信息:

<p style="color: red" th:text="${msg}"></p>
  • 1

测试结果:

image-20220424115727423

在密码不对的情况下:http://localhost:8080/user/login?username=ahoh&password=123

url路径里面显示了用户的名字和密码,这暴漏了用户的信息。我们编写一个映射 去解决这个问题。

  • 在自定义的配置类MyMvcConfig 中添加映射。在url=/main.html时,页面为 登录后的页面 dashboard.html

    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("index");
            registry.addViewController("/index.html").setViewName("index");
            registry.addViewController("/main.html").setViewName("dashboard");
        }
    
        // 将自定义的国际化配置放入 Spring容器中,使组件生效
        @Bean
        public LocaleResolver localeResolver(){
            return new MyLocaleResolver();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 对应的,我们需要在 登录控制器 LoginController中进行重定向:redirect:/main.html

    @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 "index";
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

测试:输入正确的用户名和密码登陆成功后,浏览器不再泄露信息。

但此时的结果虽然可以在登录后的后台界面进去了,但是你直接在url路径输入 /main.html 也能进去, 这就需要 拦截器了。

登录拦截器

创建一个登录拦截器 LoginHandlerInterceptor。

拦截器去完成登录拦截的功能:【用户登录后,后台会得到用户的信息;如果没有登录,则不会有任何的用户信息】。

  • 重写拦截器接口 HandlerInterceptor的方法。有用户的 session 即用户信息()才让其允许通过。
  • 在登录时的控制器 LoginController 中去加入用户的 session

LoginController控制器

  • 添加登录用户的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) && "123456".equals(password)){
            session.setAttribute("LoginUser", username);
            //return "dashboard";
            return "redirect:/main.html"; // 重定向
        }else {
            model.addAttribute("msg", "用户名或密码错误");
            return "index";
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

登录拦截器 LoginHandlerInterceptor:

编写一个自定义的登录拦截器 LoginHandlerInterceptor,继承HandlerInterceptor接口

  • 去获取用户信息 session:如果没有,让其跳转到首页 index.html,且提示消息 msg;
  • 如果不为空,放行
package com.al.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 用户如果登录成功了,应该获取到用户的 session 信息
        Object session = request.getSession().getAttribute("LoginUser");
        if (session == null) {
            request.setAttribute("msg", "权限不够,请先登录");
            request.getRequestDispatcher("/index.html").forward(request, response);
            return false;
        } else {
            return true;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

注册拦截器 LoginHandlerInterceptor 到spring 容器中,Srping Boot配置中添加自定义配置资源

定义完拦截器后,我们需要进行在 bean中进行 注册配置:

  • 在MyMvcConfig配置类中,重写关于拦截器的方法,添加自定义的拦截器

  • 屏蔽:要拦截的以及不要拦截的 路径 资源,

  • 需要放行的:.excludePathPatterns(“/index.html”, “/”, “/user/login”, “/css/", "/js/”, “/img/**”)。才能将页面完全导入进来,页面加载出来

package com.al.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/main.html").setViewName("dashboard");
    }

    // 将自定义的国际化配置放入 Spring容器中,使组件生效
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/index.html", "/", "/user/login", "/css/**", "/js/**", "/img/**");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

如果不放行静态资源,页面无法完全导入:

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/index.html", "/", "/user/login");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

image-20220425105914821

修改后测试:此时直接访问http://localhost:8080/main.html 显示权限不够:

image-20220425095810428

重新写入username 和password,进入到dashboard页面。

如果再重新直接访问 http://localhost:8080/main.html ,也可以直接进入。因为此时的 session 里面存入了用户的信息,拦截器会放行通过。

5、员工信息:增删改查

展示员工列表

实现Customers视图跳转

目标,功能:点击dashboard.html页面中的Customers 按钮跳转到 list.html列表,展示所有的员工信息。

image-20220425162706553

我们想要 点击员工管理,就去跳转到 员工管理的界面:

  • 那么,我们把这个对应的员工管理点击 动作 对应的超链接改为 url地址就可以了:
  • 员工管理展示信息列表的 url 请求响应后,对应的 controller 去控制这个请求 servlet: EmployeeController
  • 可能随着项目扩展,员工变多, 所以将 list页面移动到 创建的 emp文件中,或许以后还有其它页面。

dashboard.html页面的代码修改:

image-20220425163326807

修改后:

					<li class="nav-item">
								<a class="nav-link" th:href="@{/emps}">
									<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users">
										<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
										<circle cx="9" cy="7" r="4"></circle>
										<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
										<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
									</svg>
									员工管理
								</a>
							</li>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

同样修改list.html对应该的代码为上述代码:

  • 我们在templates目录下新建一个包emp,用来放所有关于员工信息的页面,我们将list.html页面移入该包中

    image-20220425164141212

在这里插入图片描述
image-20220425163653903

员工管理控制器:EmployeeController:

  • 对于请求的controller,处理 /emps 这一url请求,在controller 包下,创建EmployeeController 类 去对 /emps 请求进行响应
package com.al.controller;

import com.al.dao.EmployeeDao;
import com.al.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Collection;

// 员工管理信息 controller
@Controller
public class EmployeeController {

    @Autowired
    private EmployeeDao employeeDao;

    @RequestMapping("/emps")
    public String list(Model model){
        Collection<Employee> employees = employeeDao.getAll();
        model.addAttribute("emps",employees);
        return "emps/list"; // 返回到 list界面
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

测试:点击Customers,成功跳转到/emps

image-20220425170827413

改进的地方

  • 点击员工管理,我们想要实现高亮的效果
  • 需要对侧边栏进行修改, 对应着员工管理这些选项。类似的,我们也将顶部导航栏 进行插入, 实现代码的复用:
提取页面公共部分:侧边栏、顶部栏

对于这些公共的地方, 我们创建一个 commons文件夹去进行存放, commons.html 实现代码复用。

image-20220425202839075

commons.html

commons.html 公共部分的复用代码:顶部导航栏与侧边导航栏:

  • 顶部导航栏,利用th:fragment提取出来,命名为topbar【th:fragment=“topbar”】
  • 侧边栏,利用th:fragment提取出来,命名为sidebar【th:fragment=“siderbar”】
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<!--顶部导航栏,利用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/#">Company
        name</a>
    <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
    <ul class="navbar-nav px-3">
        <li class="nav-item text-nowrap">
            <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a>
        </li>
    </ul>
</nav>

<!--侧边栏,利用th:fragment提取出来,命名为sidebar-->
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="siderbar">
    <div class="sidebar-sticky">
        <ul class="nav flex-column">
            <li class="nav-item">
                <a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-home">
                        <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                        <polyline points="9 22 9 12 15 12 15 22"></polyline>
                    </svg>
                    Dashboard <span class="sr-only">(current)</span>
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-file">
                        <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path>
                        <polyline points="13 2 13 9 20 9"></polyline>
                    </svg>
                    Orders
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-shopping-cart">
                        <circle cx="9" cy="21" r="1"></circle>
                        <circle cx="20" cy="21" r="1"></circle>
                        <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
                    </svg>
                    Products
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" th:href="@{/emps}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-users">
                        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                        <circle cx="9" cy="7" r="4"></circle>
                        <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                        <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
                    </svg>
                    员工管理
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-bar-chart-2">
                        <line x1="18" y1="20" x2="18" y2="10"></line>
                        <line x1="12" y1="20" x2="12" y2="4"></line>
                        <line x1="6" y1="20" x2="6" y2="14"></line>
                    </svg>
                    Reports
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-layers">
                        <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
                        <polyline points="2 17 12 22 22 17"></polyline>
                        <polyline points="2 12 12 17 22 12"></polyline>
                    </svg>
                    Integrations
                </a>
            </li>
        </ul>

        <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
            <span>Saved reports</span>
            <a class="d-flex align-items-center text-muted"
               href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
                     stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
                     class="feather feather-plus-circle">
                    <circle cx="12" cy="12" r="10"></circle>
                    <line x1="12" y1="8" x2="12" y2="16"></line>
                    <line x1="8" y1="12" x2="16" y2="12"></line>
                </svg>
            </a>
        </h6>
        <ul class="nav flex-column mb-2">
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-file-text">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    Current month
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-file-text">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    Last quarter
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-file-text">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    Social engagement
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-file-text">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    Year-end sale
                </a>
            </li>
        </ul>
    </div>
</nav>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
list.html 和 dashboard.html

删除掉 list.html 和 dashboard.html 中的侧边栏和顶部导航栏的代码:

  • dashboard.html中删除后剩下的:

    image-20220425172750822

    <!DOCTYPE html>
    <!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ -->
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    	<head>
    		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    		<meta name="description" content="">
    		<meta name="author" content="">
    
    		<title>Dashboard Template for Bootstrap</title>
    		<!-- Bootstrap core CSS -->
    		<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
    
    		<!-- Custom styles for this template -->
    		<link th:href="@{/css/dashboard.css}" rel="stylesheet">
    		<style type="text/css">
    			/* Chart.js */
    			
    			@-webkit-keyframes chartjs-render-animation {
    				from {
    					opacity: 0.99
    				}
    				to {
    					opacity: 1
    				}
    			}
    			
    			@keyframes chartjs-render-animation {
    				from {
    					opacity: 0.99
    				}
    				to {
    					opacity: 1
    				}
    			}
    			
    			.chartjs-render-monitor {
    				-webkit-animation: chartjs-render-animation 0.001s;
    				animation: chartjs-render-animation 0.001s;
    			}
    		</style>
    	</head>
    
    	<body>
    
    		<div class="container-fluid">
    			<div class="row">
    
    				<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
    					<div class="chartjs-size-monitor" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;">
    						<div class="chartjs-size-monitor-expand" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
    							<div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0"></div>
    						</div>
    						<div class="chartjs-size-monitor-shrink" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
    							<div style="position:absolute;width:200%;height:200%;left:0; top:0"></div>
    						</div>
    					</div>
    					<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
    						<h1 class="h2">Dashboard</h1>
    						<div class="btn-toolbar mb-2 mb-md-0">
    							<div class="btn-group mr-2">
    								<button class="btn btn-sm btn-outline-secondary">Share</button>
    								<button class="btn btn-sm btn-outline-secondary">Export</button>
    							</div>
    							<button class="btn btn-sm btn-outline-secondary dropdown-toggle">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-calendar"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
                    This week
                  </button>
    						</div>
    					</div>
    
    					<canvas class="my-4 chartjs-render-monitor" id="myChart" width="1076" height="454" style="display: block; width: 1076px; height: 454px;"></canvas>
    
    
    				</main>
    			</div>
    		</div>
    
    		<!-- Bootstrap core JavaScript
        ================================================== -->
    		<!-- Placed at the end of the document so the pages load faster -->
    		<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js" ></script>
    		<script type="text/javascript" src="asserts/js/popper.min.js" ></script>
    		<script type="text/javascript" src="asserts/js/bootstrap.min.js" ></script>
    
    		<!-- Icons -->
    		<script type="text/javascript" src="asserts/js/feather.min.js" ></script>
    		<script>
    			feather.replace()
    		</script>
    
    		<!-- Graphs -->
    		<script type="text/javascript" src="asserts/js/Chart.min.js" ></script>
    		<script>
    			var ctx = document.getElementById("myChart");
    			var myChart = new Chart(ctx, {
    				type: 'line',
    				data: {
    					labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
    					datasets: [{
    						data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
    						lineTension: 0,
    						backgroundColor: 'transparent',
    						borderColor: '#007bff',
    						borderWidth: 4,
    						pointBackgroundColor: '#007bff'
    					}]
    				},
    				options: {
    					scales: {
    						yAxes: [{
    							ticks: {
    								beginAtZero: false
    							}
    						}]
    					},
    					legend: {
    						display: false,
    					}
    				}
    			});
    		</script>
    
    	</body>
    
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
  • list.html 中删除后剩下的:

    image-20220425173129865

    <!DOCTYPE html>
    <!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ -->
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
    	<head>
    		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    		<meta name="description" content="">
    		<meta name="author" content="">
    
    		<title>Dashboard Template for Bootstrap</title>
    		<!-- Bootstrap core CSS -->
    		<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
    
    		<!-- Custom styles for this template -->
    		<link th:href="@{/css/dashboard.css}" rel="stylesheet">
    		<style type="text/css">
    			/* Chart.js */
    			
    			@-webkit-keyframes chartjs-render-animation {
    				from {
    					opacity: 0.99
    				}
    				to {
    					opacity: 1
    				}
    			}
    			
    			@keyframes chartjs-render-animation {
    				from {
    					opacity: 0.99
    				}
    				to {
    					opacity: 1
    				}
    			}
    			
    			.chartjs-render-monitor {
    				-webkit-animation: chartjs-render-animation 0.001s;
    				animation: chartjs-render-animation 0.001s;
    			}
    		</style>
    	</head>
    
    	<body>
    
    
    		<div class="container-fluid">
    			<div class="row">
    				<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>
    								<tr>
    									<th>#</th>
    									<th>Header</th>
    									<th>Header</th>
    									<th>Header</th>
    									<th>Header</th>
    								</tr>
    							</thead>
    							<tbody>
    								<tr>
    									<td>1,001</td>
    									<td>Lorem</td>
    									<td>ipsum</td>
    									<td>dolor</td>
    									<td>sit</td>
    								</tr>
    								<tr>
    									<td>1,002</td>
    									<td>amet</td>
    									<td>consectetur</td>
    									<td>adipiscing</td>
    									<td>elit</td>
    								</tr>
    								<tr>
    									<td>1,003</td>
    									<td>Integer</td>
    									<td>nec</td>
    									<td>odio</td>
    									<td>Praesent</td>
    								</tr>
    								<tr>
    									<td>1,003</td>
    									<td>libero</td>
    									<td>Sed</td>
    									<td>cursus</td>
    									<td>ante</td>
    								</tr>
    								<tr>
    									<td>1,004</td>
    									<td>dapibus</td>
    									<td>diam</td>
    									<td>Sed</td>
    									<td>nisi</td>
    								</tr>
    								<tr>
    									<td>1,005</td>
    									<td>Nulla</td>
    									<td>quis</td>
    									<td>sem</td>
    									<td>at</td>
    								</tr>
    								<tr>
    									<td>1,006</td>
    									<td>nibh</td>
    									<td>elementum</td>
    									<td>imperdiet</td>
    									<td>Duis</td>
    								</tr>
    								<tr>
    									<td>1,007</td>
    									<td>sagittis</td>
    									<td>ipsum</td>
    									<td>Praesent</td>
    									<td>mauris</td>
    								</tr>
    								<tr>
    									<td>1,008</td>
    									<td>Fusce</td>
    									<td>nec</td>
    									<td>tellus</td>
    									<td>sed</td>
    								</tr>
    								<tr>
    									<td>1,009</td>
    									<td>augue</td>
    									<td>semper</td>
    									<td>porta</td>
    									<td>Mauris</td>
    								</tr>
    								<tr>
    									<td>1,010</td>
    									<td>massa</td>
    									<td>Vestibulum</td>
    									<td>lacinia</td>
    									<td>arcu</td>
    								</tr>
    								<tr>
    									<td>1,011</td>
    									<td>eget</td>
    									<td>nulla</td>
    									<td>Class</td>
    									<td>aptent</td>
    								</tr>
    								<tr>
    									<td>1,012</td>
    									<td>taciti</td>
    									<td>sociosqu</td>
    									<td>ad</td>
    									<td>litora</td>
    								</tr>
    								<tr>
    									<td>1,013</td>
    									<td>torquent</td>
    									<td>per</td>
    									<td>conubia</td>
    									<td>nostra</td>
    								</tr>
    								<tr>
    									<td>1,014</td>
    									<td>per</td>
    									<td>inceptos</td>
    									<td>himenaeos</td>
    									<td>Curabitur</td>
    								</tr>
    								<tr>
    									<td>1,015</td>
    									<td>sodales</td>
    									<td>ligula</td>
    									<td>in</td>
    									<td>libero</td>
    								</tr>
    							</tbody>
    						</table>
    					</div>
    				</main>
    			</div>
    		</div>
    
    		<!-- Bootstrap core JavaScript
        ================================================== -->
    		<!-- Placed at the end of the document so the pages load faster -->
    		<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js"></script>
    		<script type="text/javascript" src="asserts/js/popper.min.js"></script>
    		<script type="text/javascript" src="asserts/js/bootstrap.min.js"></script>
    
    		<!-- Icons -->
    		<script type="text/javascript" src="asserts/js/feather.min.js"></script>
    		<script>
    			feather.replace()
    		</script>
    
    		<!-- Graphs -->
    		<script type="text/javascript" src="asserts/js/Chart.min.js"></script>
    		<script>
    			var ctx = document.getElementById("myChart");
    			var myChart = new Chart(ctx, {
    				type: 'line',
    				data: {
    					labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
    					datasets: [{
    						data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
    						lineTension: 0,
    						backgroundColor: 'transparent',
    						borderColor: '#007bff',
    						borderWidth: 4,
    						pointBackgroundColor: '#007bff'
    					}]
    				},
    				options: {
    					scales: {
    						yAxes: [{
    							ticks: {
    								beginAtZero: false
    							}
    						}]
    					},
    					legend: {
    						display: false,
    					}
    				}
    			});
    		</script>
    
    	</body>
    
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230

    测试,此时访问dashboard.html 或者 list.html(/emps)页面的结果:http://localhost:8080/emps

    没有了侧边栏和顶部导航栏

image-20220425174858729

插入侧边栏和导航栏:

  • 插入: 并注意使用 thymeleaf 语法 ~ 波浪号
<!--顶部导航栏-->
<div th:replace="~{commons/commons::topbar}"></div>

<!--侧边栏-->
<div th:replace="~{commons/commons::siderbar}"></div>
  • 1
  • 2
  • 3
  • 4
  • 5

image-20220425175331768

测试:登陆后,成功导入侧边栏和导航栏。

image-20220425175456185

点击显示高亮

那么对于 高亮而言:就是 active 这里的动作:

commons页面:

  • 在公共页面commons.html相应标签部分利用thymeleaf接收参数active,利用三元运算符判断决定是否高亮
            <li class="nav-item">
                <a th:class="${active=='dashboard.html'?'nav-link active':'nav-link'}" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-home">
                        <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                        <polyline points="9 22 9 12 15 12 15 22"></polyline>
                    </svg>
                    Dashboard <span class="sr-only">(current)</span>
                </a>
            </li>
...
            <li class="nav-item">
                <a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                         stroke-linejoin="round" class="feather feather-users">
                        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                        <circle cx="9" cy="7" r="4"></circle>
                        <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                        <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
                    </svg>
                    员工管理
                </a>
            </li>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

image-20220425180918763

dashboard.html页面:

image-20220425180800589

				<!--侧边栏-->
				<div th:replace="~{commons/commons::siderbar(active='dashboard.html')}"></div>
  • 1
  • 2

同样在list.html的侧边栏标签传递参数activelist.html:

image-20220425180745215

		<!--侧边栏-->
				<div th:replace="~{commons/commons::siderbar(active='list.html')}"></div>
  • 1
  • 2

测试:

此时点击员工管理,员工管理高亮,成功。

image-20220425202706654

显示员工信息

修改 list.html 页面,展示员工的信息。

员工管理控制器 EmployeeController:

  • url请求 http://localhost:8080/emps 访问所有的员工信息。
  • 返回结果 集合 employees 封装在 emps中,利用 Model 将数据传递到前端
package com.al.controller;

import com.al.dao.EmployeeDao;
import com.al.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Collection;

// 员工管理信息 controller
@Controller
public class EmployeeController {

    @Autowired
    private EmployeeDao employeeDao;

    @RequestMapping("/emps")
    public String list(Model model){
        Collection<Employee> employees = employeeDao.getAll();
        model.addAttribute("emps",employees);
        return "emps/list"; // 返回到 list界面
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

修改表格的展示信息:获取参数的信息表示:【从emps中获取,遍历表示】

  • <td th:text="${emp.getId()}"></td>
    
    • 1
  • <td>[[${emp.getLastName()}]]</td>
    
    • 1

image-20220426100858585

上述修改后,重新启动程序,可以展示员工的信息列表。不过此时的

  • gender 性别显示的是 0 与 1,而不是男/女。需要进行修改。使用三元运算符解决。
  • 且对于操作一栏,添加 编辑和删除的 按钮功能。
  • 日期一栏的格式,修改为 yyyy-MM-dd HH:mm:ss 的形式
<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>
								<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()}"></td>-->
									<td th:text="${emp.getId()}"></td>
									<td>[[${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="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
									<td>
										<a class="btn btn-sm btn-primary">编辑</a>
										<a class="btn btn-sm btn-danger">删除</a>
									</td>
								</tr>
							</tbody>
						</table>
					</div>
				</main>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

小结:

在登录过程中:

  • 用户名和密码的判断,决定是否成功登录。
  • 只有用户成功登录后,有了用户 session,才能进行访问特定界面。 拦截器【拦截请求,还需放行资源】

在展示员工列表的时候:

  • 提取页面公共部分,代码复用。

    <!--定义: 顶部导航栏,利用th:fragment提取出来,命名为topbar-->
    <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
        
        <!-- 插入:   顶部导航栏-->
    <div th:replace="~{commons/commons::topbar}"></div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 员工信息在列表中循环/遍历获取 进行展示。

增加员工实现

员工管理的增加:

  • 在员工管理界面 添加 员工增加的请求url,按钮选项。
  • add 添加员工这一请求servlet 对应的控制器 controller。跳转到 add.html页面,进行添加
  • 创建 添加页面:add.html
添加员工选项

在员工信息展示列表 list.html 界面中添加 员工增加(add)选项,和对应的url请求路径:

<h2><a class="btn btn-sm btn-success" th:href="@{/add}">添加员工</a></h2>
  • 1

image-20220426220955115

效果:

image-20220426220409062

EmployeeController 控制器

编写 add 添加员工这一请求的 controller。

  • 通过Get方式提交,返回add.html页面(在该页面完成员工信息添加)
    @GetMapping("/add")
    public String toAddPage(Model model) {
        //查出所有的部门信息,添加到departments中,用于前端接收
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "emps/add";//返回到添加员工页面
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里,toAddPage函数传递 部门信息给前端,便于在添加员工信息时 去选择所在部门信息。

  • model.addAttribute(“departments”, departments);
增加员工信息界面:add.html

此时的department 信息 传递给前端, 在前端显示出来所有部门的信息。 add.thml 页面的修改:

add.html :

  • 复制list.html中的内容,把员工表格信息展示的部分修改为:
<!--顶部导航栏-->
<div th:replace="~{commons/commons::topbar}"></div>
<div class="container-fluid">
    <div class="row">
        <!--侧边栏-->
        <div th:replace="~{commons/commons::siderbar(active='list.html')}"></div>

        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
            <div class="form-group">
                <label>LastName</label>
                <input type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
            </div>
            <div class="form-group">
                <label>Email</label>
                <input type="email" name="email" class="form-control" placeholder="email:xxxxx@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">
                    <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>
                <!--注意这里的name是department.id,因为传入的参数为id-->
                <select class="form-control" name="department.id">
                    <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
                </select>
            </div>
            <div class="form-group">
                <label>Birth</label>
                <!--springboot默认的日期格式为yy/MM/dd-->
                <input type="text" name="date" class="form-control" placeholder="birth:yyyy/MM/dd">
            </div>
            <button type="submit" class="btn btn-primary">添加</button>
        </main>
    </div>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

点击添加员工,成功跳转到add.html页面:

image-20220426222007053

add.html页面完成员工信息添加功能

在前面增加员工实现的功能,已经完成了:

  • 在list.html这一员工信息展示列表 添加了 增加员工 选项,以及响应 增加员工请求(/add) 的控制器 controller
  • 员工控制器EmployeeController 中添加 toAddPage 方法,前往增加员工界面 add.html,且传递 部门信息
  • 增加员工界面 add.html 的代码编写,展示员工的 lastName、email、department、gender的信息,且添加 ‘name’ 属性,便于将相应的属性值由前端传递给后端,添加数据到底层。这里是添加到 模拟数据库表中的员工数据信息的 Map<Integer, Employee> employees 中。

那么增加员工实现 功能完成后,跳转页面到 list.html中,展示所有的员工信息。

  • 增加选项动作提交时的url响应。这里也同样使用 /add 请求路径,不过请求方式改为 Post。【使用Resful风格】
  • 编写对应的 controller,并在该控制器中 获取此时的员工map,传递给 list.html界面,展示添加员工后的 员工信息列表。

add.html:

image-20220427161111403

<!--顶部导航栏-->
<div th:replace="~{commons/commons::topbar}"></div>
<div class="container-fluid">
    <div class="row">
        <!--侧边栏-->
        <div th:replace="~{commons/commons::siderbar(active='list.html')}"></div>

        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
            <form th:action="@{/add}" method="post">
            <div class="form-group">
                <label>LastName</label>
                <input type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
            </div>
            <div class="form-group">
                <label>Email</label>
                <input type="email" name="email" class="form-control" placeholder="email:xxxxx@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">
                    <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>
                <!--注意这里的name是department.id,因为传入的参数为id-->
                <select class="form-control" name="department.id">
                    <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
                </select>
            </div>
            <div class="form-group">
                <label>Birth</label>
                <!--springboot默认的日期格式为yy/MM/dd-->
                <input type="text" name="date" class="form-control" placeholder="birth:yyyy/MM/dd">
            </div>
            <button type="submit" class="btn btn-primary">添加</button>
            </form>
        </main>
    </div>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

编写对应的controller,同样在EmployeeController中添加一个方法addEmp用来处理点击添加按钮的操作

package com.al.controller;

import com.al.dao.DepartmentDao;
import com.al.dao.EmployeeDao;
import com.al.pojo.Department;
import com.al.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Collection;

// 员工管理信息 controller
@Controller
public class EmployeeController {

    @Autowired
    private EmployeeDao employeeDao;
    @Autowired
    private DepartmentDao departmentDao;

    @RequestMapping("/emps")
    public String list(Model model){
        Collection<Employee> employees = employeeDao.getAll();
        model.addAttribute("emps",employees);
        return "emps/list"; // 返回到 list界面
    }

    @GetMapping("/add")
    public String toAddPage(Model model) {
        //查出所有的部门信息,添加到departments中,用于前端接收
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "emps/add";//返回到添加员工页面
    }
    
    @PostMapping("/add")
    public String addEmp(Employee employee){
        System.out.println("save=>" +employee);
        employeeDao.addEmployee(employee); //添加一个员工
        return "redirect:/emps";//重定向到/emps,刷新列表,返回到list页面
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

重启程序,进行测试,选择添加员工进入到 add.html界面:【注意:此时的日期是yyyy/MM/dd 的形式】

image-20220427161927229

点击添加选项,url请求 /add,resful风格的 Post请求提交方式,响应的控制器是 addEmp,完成员工信息添加,跳转到list.html界面。展示的所有员工信息为:这时候,发现错误,无法跳转到 list.html界面。错误的原因:

image-20220427174356120

在dao层的 EmployeeDao 发生了错误:

  • 增加员工时的:employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId())); 不正确
    // 增加一个员工
    public void addEmployee(Employee employee){
        if (employee.getId() == null){
            employee.setId(initId++);
        }
        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
        employees.put(employee.getId(), employee);
        System.out.println(employee);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

经过测试发现:

  • employee.getDepartment().getId() 没有问题,获取的是 department部门的id 102
  • departmentDao.getDepartmentById(102) 这里出错了

关于 departmentDao 中的方法无法调用。???错误还没解决掉

但是如果有问题的话,在EmployeeController 控制器 中的 toAddPage里面的

Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
  • 1
  • 2

这两行代码应该生效才对。 还是不对劲。…

修改员工信息

界面中,关于员工信息列表展示的时候,操作栏添加了 编辑 和 删除 的选项。同样的,对于编辑选项:【和添加员工信息的步骤类似】

  • 对于编辑选项,添加请求路径,url: /emp/{id} ,请求的时候携带员工id,便于查询对应的员工信息,进行展示 和修改
  • /emp/{id} 这一请求 servlet 对应的控制器 controller,跳转到更新界面 update.html
  • 在 update.html 点击 修改 按钮的一瞬间,我们需要返回到list页面,更新员工信息,因此我们需要添加 href 属性,实现点击按钮时发起一个请求 /edit
list.html界面中的编辑选项

点击编辑选项,

  • 增加 url 请求路径。
  • 且前端需要传递给后端的参数:携带员工id
<a class="btn btn-sm btn-primary" th:href="@{/emp/{id}(id=${emp.getId()})}">编辑</a>
  • 1
EmployeeController 控制器

响应 url请求,获取前端传递过来的员工 id 信息,跳转到 update.html 页面,展示员工 目前的信息:

    @GetMapping("/emp/{id}")
    public String toUpdatePage(@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 "emps/update";
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
updat.html 页面

这里的信息展示 和 add.html页面类似,复制进行修改:

  • 不能每次修改一次员工信息,就让id+1, 需要id隐藏域标签

    <input type="hidden" name="id" th:value="${emp.getId()}">
    
    • 1
  • 显示员工的原有部门信息,以及所有的部门信息

                        <select class="form-control" name="department.id">
                            <option th:selected="${emp.getDepartment().getId() == department.getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
                        </select>
    
    • 1
    • 2
    • 3
  • 规定一下显示的日期格式

    <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd">
    
    • 1

update.html:

<!--顶部导航栏-->
<div th:replace="~{commons/commons::topbar}"></div>
<div class="container-fluid">
    <div class="row">
        <!--侧边栏-->
        <div th:replace="~{commons/commons::siderbar(active='list.html')}"></div>

        <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="lastname:zsr">
                </div>
                <div class="form-group">
                    <label>Email</label>
                    <input th:value="${emp.getEmail()}" type="email" name="email" class="form-control" placeholder="email:xxxxx@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>
                    <!--注意这里的name是department.id,因为传入的参数为id-->
                    <select class="form-control" name="department.id">
                        <option th:selected="${emp.getDepartment().getId() == department.getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
                    </select>
                </div>
                <div class="form-group">
                    <label>Birth</label>
                    <!--springboot默认的日期格式为yy/MM/dd-->
<!--                    <input th:value="${emp.getBirth()}" type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd"> -->
<!--                    <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm')}" type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd">-->
                    <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd">
                </div>
                <button type="submit" class="btn btn-primary">修改</button>
            </form>
        </main>
    </div>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
update.html页面完成员工信息修改功能

员工信息修改后, 需要重新跳转 或者重定向到 员工列表的页面。

在 update.html 这个修改员工信息页面完成后, 提交动作需要执行 发送 更新页面完成这个请求, 然后才能重定向到员工列表页面:

update.html:

<form th:action="@{/updateEmp}" method="post">
  • 1

对应的控制器 Controller:

  • 处理点击修改按钮的请求。同样在EmployeeController中添加一个方法EditEmp用来处理edit页面点击添加的操作
    @PostMapping("/updateEmp")
    public String updataEmp(Employee employee){
        employeeDao.addEmployee(employee);//添加一个员工
        return "redirect:/emps";//添加完成重定向到/emps,刷新列表
    }
  • 1
  • 2
  • 3
  • 4
  • 5

进行测试,进入添加页面,填写相关信息

image-20220428132617117

点击修改,返回list.html页面:【我取消掉关于employeeDao中增加员工中的departmentDao 才有如下结果】

image-20220428132712878

同样的错误:?????????????????????????????

  • employee.getDepartment().getId() 没有问题,获取的是 department部门的id 102
  • departmentDao.getDepartmentById(102) 这里出错了

关于 departmentDao 中的方法无法调用。???错误还没解决掉

但是如果有问题的话,在EmployeeController 控制器 中的 toAddPage里面的

Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
  • 1
  • 2

这两行代码应该生效才对。 还是不对劲。…

删除员工信息

对于操作栏的删除,响应删除请求,url请求对应的controller,直接返回list.html界面。

删除选项增加超链接:【和编辑选项类似,超链接属性 href,且传递参数应携带员工id】

<a class="btn btn-sm btn-danger" th:href="@{/delemp/{id}(id=${emp.getId()})}">删除</a>
  • 1

EmployeeController 控制器:

响应url请求 /delemp/{id},移除员工信息,返回重定向到 list.html 页面。

    @GetMapping("/delemp/{id}")
    public String deleteEmp(@PathVariable("id")Integer id, Model model){
        employeeDao.deleteEmployeeByID(id);
        return "redirect:/emps";//添加完成重定向到/emps,刷新列表
    }
  • 1
  • 2
  • 3
  • 4
  • 5

测试,成功删除。

6、404页面定制和注销操作

404页面定制

只需要在templates目录下新建一个error包,然后将404.html放入其中,报错SpringBoot就会自动找到这个页面。
image-20220428190151951

注销操作

员工管理系统进行注销,即移除掉 session。

注销的功能在 commons.html文件中:

  • 顶部导航栏处中的标签添加href属性,实现点击发起请求/user/logout
<li class="nav-item text-nowrap">
    <a class="nav-link" th:href="@{/user/logout}">Sign out</a>
</li>
  • 1
  • 2
  • 3

控制器controller,响应 /user/logout 请求,在LoginController中编写对应的方法,清除session,并重定向到首页。

    @RequestMapping("/user/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/index.html";
    }
  • 1
  • 2
  • 3
  • 4
  • 5

测试,重启程序,成功注销。

如何写一个网站呢?

  • 前端

    • 模板:利用别人写好的,进行改写成自己想要的
    • 框架:去进行拼接。Bootstrap,layui,semantic-ui。 完成 栅格系统,导航栏,侧边栏,表单
  • 设计数据库【数据库表的设计、持久性、保护、备份】

  • 后端操作数据库。【事务、设计模式,提高开发效率的】

  • 后端和前端的数据交互。数据接口对接:json,对象 all in one

SpringBoot: 整合Mybatis-员工管理系统

对于这个员工管理系统,整合mybatis去完成对数据库操作。

创建数据库表

image-20220501094630816

数据库排序规则:

  • utf8_general_ci 不区分大小写
  • utf8_general_cs 区分大小写

建立的数据库表:

department 部门表:

SHOW CREATE DATABASE school;
create table `department`(
`id` int(4) not null auto_increment comment'部门id',
`departmentName` VARCHAR(30) NOT NULL DEFAULT '机密部门' COMMENT '姓名',
primary key(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

insert into `department`(`id`,`departmentName`)
values(101,'户部'),(102,'兵部'),(103,'吏部'),(104,'刑部'),(105,'礼部'),(106,'工部')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

外键

概念:如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表

在实际操作中,将一个表的值放入第二个表来表示关联,所使用的值是第一个表的主键值(在必要时可包括复合主键值)。此时,第二个表中保存这些值的属性称为外键(foreign key)。

作用:保持数据一致性完整性,主要目的是控制存储在外键表中的数据,约束。使两张表形成关联,外键只能引用外表中的列的值或使用空值。

创建表的时候指定外键的约束

方式一:在创建表的时候,增加约束(麻烦,复杂)

CREATE TABLE IF NOT EXISTS `school`.`grade` (
`gradeid` INT(10) NOT NULL auto_increment COMMENT '年级编号',
`gradename` VARCHAR(10) not null COMMENT '年级名字',
PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

CREATE TABLE if not EXISTS`student` (
`id` int(4) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` varchar(20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`pwd` varchar(20) NOT NULL DEFAULT '123456' COMMENT '密码',
`sex` varchar(2) NOT NULL DEFAULT '男' COMMENT '性别',
`gradeid` INT(10) NOT NULL auto_increment COMMENT '年级编号',
`birthday` datetime DEFAULT NULL COMMENT '出生日期',
`address` varchar(60) DEFAULT NULL COMMENT '住址',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`),
  KEY `FK_gradeid` (`gradeid`),
  CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

创建表之后再去修改

方式二:创建表之后再创建外键关系

alter table `student`
add constraint `FK_gradeid` foreign key(`gradeid`) references `grade` (`gradeid`);
-- alter table `表名`
-- add constraint `约束名` foreign key(作为外键的列) references 哪个表(哪个字段);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

删除有外键关系的表的时候,必须要先删除引用别人的表(从表),再删除自己的表()。即先删除子表,再删除主表。

以上的操作都是物理外键,数据库级别的外键,,我们不建议使用,避免数据库过多造成困扰。

最佳实践

  • 数据库就是单纯的表,只用来存数据,只有行和列
  • 我们想使用多张表的数据,想使用外键,程序去实现

员工表:employee

create table `employee`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT'员工id',
`lastName` VARCHAR(30) NOT NULL COMMENT '姓名',
`email` VARCHAR(30) NOT NULL DEFAULT '*.com' COMMENT '邮箱',
`gender` INT(4) NOT NULL DEFAULT '0' COMMENT '性别',
`department` INT(4) NOT NULL COMMENT '部门id',
`birth`DATETIME DEFAULT NULL COMMENT '生日',
primary key(`id`),
key `FK_id`(`department`),
constraint `FK_id` foreign key(`department`) references `department`(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

insert into `employee`(`id`,`lastName`,`email`,`gender`,`department`,`birth`)
values(1001,'AAA','1356207897@qq.com',0,101,'2020-04-05'),(1002,'BBB','1356207897@qq.com',0,102,'2020-04-06'),
(1003,'CCC','1356207897@qq.com',1,103,'2020-05-05'),(1004,'DDD','1356207897@qq.com',1,104,'2020-10-05'),
(1005,'EEE','1356207897@qq.com',1,103,'2020-11-05'),(1006,'FFF','1356207897@qq.com',0,106,'2021-04-05')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

数据库表成功创建后的结果:
image-20220501111419863
image-20220501111401905

创建SpringBoot项目

新建一个SpringBoot项目:springboot-employee-management-system-mybatis。

image-20220501192125912

选择的springBoot配置有 web、JDBC、MySQL的:

image-20220501192218369

注意:我们需要导入 thymeleaf 模板引擎的maven依赖:

       <!--thymeleaf. 我们都是基于 3.x 开发的-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

项目建立之后,将原来的静态资源文件,导入到现在的这个项目中。

建立数据库连接

对数据库配置文件进行连接的时候,导入 JDBC、mysql、Mybatis的maven依赖。

  • mybatis 的依赖
  • 静态资源导出的依赖

pom.xml :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.AL</groupId>
    <artifactId>springboot-employee-management-system-mybatis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-employee-management-system-mybatis</name>
    <description>Employee Management System Mybatis for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--thymeleaf. 我们都是基于 3.x 开发的-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--mybatis的maven依赖-->
        <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
        </dependency>
        <!-- Druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
        <!-- log4j日志-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <!--静态资源导出问题-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

数据库配置资源文件:

我使用 application.properties 去进行数据信息配置 。

application.properties 资源配置文件:

# 自定义ul请求目录
#server.servlet.context-path=/al

#自定义配置:国际化资源文件
spring.messages.basename=i18n.login

# 数据库配置信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

进行测试,建立数据库连接成功。

测试类文件:

package com.AL;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootTest
class SpringbootEmployeeManagementSystemMybatisApplicationTests {

    // DI 注入数据源。从spring IOC 容器中获取数据配置信息
    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
        // 查看默认的数据源: com.zaxxer.hikari.HikariDataSource
        System.out.println(dataSource.getClass());
        // 获得数据库连接:
        Connection connection = dataSource.getConnection()
        System.out.println(connection); // HikariProxyConnection@522082506 wrapping com.mysql.cj.jdbc.ConnectionImpl@57f847af

        //DruidDataSource druidDataSource = (DruidDataSource) dataSource;
        //System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive());
        //System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize());

        connection.close(); // 关闭连接
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

显示出了默认的数据源:

image-20220501114255012

pojo层

创建实体类:Department 和 Employee 类。

Department:

package com.AL.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

// 部门表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
    private Integer id;
    private String departmentName;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Employee:

package com.AL.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

// 员工表
@Data
@NoArgsConstructor
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender; // 0:女  1:男。 用数字表示性别男女
    private Department department;
    private Date birth;

    public Employee(Integer id, String lastName, String email, Integer gender, Department department, Date birth) {
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
        this.department = department;
        // 默认的创建日期
        //this.birth = birth;
        this.birth = new Date();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

dao层

在这里,为了和上面的区分开来,创建Mapper目录以及对应Mapper接口、Mapper接口映射文件,完成对数据库的操作。

DepartmentMapper.java

package com.AL.mapper;

import com.AL.pojo.Department;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

//@Mapper : 表示本类是一个 MyBatis 的 Mapper
@Mapper
@Repository
public interface DepartmentMapper {

    // 获取所有部门信息
    List<Department> getDepartments();

    // 通过id获得部门
    Department getDepartment(Integer id);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

DepartmentMapper.xml:关于DepartmentMapper 接口的映射文件,实现类。 完成对数据库的操作。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace= 绑定一个对应的Dao/Mapper接口
<mapper namespace="org.mybatis.example.BlogMapper">
    <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
-->
<mapper namespace="com.AL.mapper.DepartmentMapper">

    <select id="getDepartments" resultType="com.al.pojo.Department">
       select * from department;
    </select>

    <select id="getDepartment" resultType="Department" parameterType="int">
       select * from department where id = #{id};
    </select>

</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

整合Myabtis

在配置文件中,整合myabtis。绑定关于dao层的接口和接口实现类 映射文件:

application.properties:

# 数据库配置信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

# 整合mybatis
mybatis.type-aliases-package=com.AL.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Controller层

关于DepartmentController 的代码:

package com.AL.controller;

import com.AL.mapper.DepartmentMapper;
import com.AL.pojo.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class DepartmentController {
    @Autowired
    DepartmentMapper departmentMapper;

    // 查询全部部门
    @GetMapping("/getDepartments")
    public List<Department> getDepartments(){
        return departmentMapper.getDepartments();
    }

    // 查询全部部门
    @GetMapping("/getDepartment/{id}")
    public Department getDepartment(@PathVariable("id") Integer id){
        return departmentMapper.getDepartment(id);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

启动主程序进行测试,发现错误:

org.thymeleaf.exceptions.TemplateInputException: Error resolving template [index], template might not exist or might not be accessible by any of the configured Template Resolvers
  • 1

检查后,错误的原因是 thymeleaf 的依赖重复了,写了两遍。

在新建项目之后,去进行关于 controller 层的测试,发现了错误

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'departmentController': Unsatisfied dependency expressed through field 'departmentMapper'; 
  • 1

错误的意思是:创建名为“departmentController”的bean时出错:通过字段“departmentMapper”表示的依赖关系不满足;

检查发现,是我的DepartmentMapper.xml中的代码写错了,修改后:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace= 绑定一个对应的Dao/Mapper接口
<mapper namespace="org.mybatis.example.BlogMapper">
    <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
-->
<mapper namespace="com.AL.mapper.DepartmentMapper">

    <select id="getDepartments" resultType="com.AL.pojo.Department">
       select * from department;
    </select>

    <select id="getDepartment" resultType="com.AL.pojo.Department" parameterType="int">
       select * from department where id = #{id};
    </select>

</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

启动主程序,进行测试:http://localhost:8080/getDepartments

image-20220501195553235

对于根据 id 访问部门信息:http://localhost:8080/getDepartment/102

image-20220501195839188

员工 Employee

那么对于员工信息Employee 的CRUD操作,同样和部门信息类似,去建立dao层、controller进行测试,完成相关的操作。

Mapper层

EmployeeMapper:

package com.AL.mapper;


import com.AL.pojo.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

//@Mapper : 表示本类是一个 MyBatis 的 Mapper
@Mapper
@Repository
public interface EmployeeMapper {

    // 获取所有员工信息
    List<Employee> getEmployees();

    // 新增一个员工
    int addEmployee(Employee employee);

    // 通过id获得员工信息
    Employee getEmployeeById(Integer id);

    // 通过id删除员工
    int deleteEmployeeByID(Integer id);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

EmployeeMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace= 绑定一个对应的Dao/Mapper接口
<mapper namespace="org.mybatis.example.BlogMapper">
    <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
-->
<mapper namespace="com.AL.mapper.EmployeeMapper">
    <select id="getEmployees" resultType="com.AL.pojo.Employee">
        select * from employee
    </select>
    <delete id="deleteEmployeeByID" parameterType="int">
        delete from employee where id=#{id}
    </delete>

</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Controller层

package com.AL.controller;


import com.AL.mapper.EmployeeMapper;
import com.AL.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class EmployeeController {

    @Autowired
    EmployeeMapper employeeMapper;

    // 获取所有员工信息
    @GetMapping("/getEmployees")
    public List<Employee> getEmployees(){
        return employeeMapper.getEmployees();
    }

    // 通过id删除员工
    @GetMapping("/delete/{id}")
    public int delete(@PathVariable("id") Integer id){
        return employeeMapper.deleteEmployeeByID(id);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

测试,访问 http://localhost:8080/getEmployees ,获取的所有 员工信息列表为:

image-20220501203942607

[{"id":1001,"lastName":"AAA","email":"1356207897@qq.com","gender":0,"department":101,"birth":"2020-04-05T00:00:00.000+00:00"},{"id":1002,"lastName":"BBB","email":"1356207897@qq.com","gender":0,"department":102,"birth":"2020-04-06T00:00:00.000+00:00"},{"id":1003,"lastName":"CCC","email":"1356207897@qq.com","gender":1,"department":103,"birth":"2020-05-05T00:00:00.000+00:00"},{"id":1004,"lastName":"DDD","email":"1356207897@qq.com","gender":1,"department":104,"birth":"2020-10-05T00:00:00.000+00:00"},{"id":1005,"lastName":"EEE","email":"1356207897@qq.com","gender":1,"department":103,"birth":"2020-11-05T00:00:00.000+00:00"},{"id":1006,"lastName":"FFF","email":"1356207897@qq.com","gender":0,"department":106,"birth":"2021-04-05T00:00:00.000+00:00"}]
  • 1

可以发现,能够成功的显示员工所有的信息。不过 我们会想要的是关于部门的具体信息,而不仅仅是部门id。对于这种的解决方法,可以采用resultMap,映射的方法去解决。

resultMap

在这里修改的时候发现,为了更好的 方便的展示信息,需要在实体类 Employee 中绑定部门Department 类的信息。如下所示:

Employee:

package com.AL.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

// 员工表
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender; // 0:女  1:男。 用数字表示性别男女
    private Integer department; //原有的属性,对应数据库的 department id, 但对应的想要的是一个表
    private Date birth;
    private Department eDepartment; // 冗余设计

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
按照查询嵌套处理

多对一: 关联。 一对多:集合。

如何让员工表格和 部门表格去建立连接, 关联 起来。 需要用到map 以及 association

思路:
1.查询所有的员工信息
2.根据查询出来的员工信息的 department(即部门id),寻找对应的部门信息

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace= 绑定一个对应的Dao/Mapper接口
<mapper namespace="org.mybatis.example.BlogMapper">
    <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
-->
<mapper namespace="com.AL.mapper.EmployeeMapper">
    <!--查询员工信息: 单纯的 department id-->
<!--    <select id="getEmployees" resultType="com.AL.pojo.Employee">-->
<!--        select * from employee-->
<!--    </select>-->
<resultMap id="EmployeeDepartment" type="Employee">
    <result property="id" column="id"/>
    <result property="lastName" column="LastName"/>
    <result property="email" column="email"/>
    <result property="gender" column="gender"/>
    <result property="birth" column="birth"/>
    <!--复杂的属性:我们需要单独处理 对象:association    集合:collection-->
    <!--复杂的属性:我们需要单独处理
对象 关联:association    集合:collection
association : 关联,多对一
column : 数据库对应的列名
property : 对应属性名
javaType : 多对一 字段对应的Java类型
select : 关联一个语句
-->
    <association property="eDepartment" column="department" javaType="Department" select="getDepartment"/>
</resultMap>
    <select id="getDepartment" resultType="Department">
        select * from department where id = #{id}
    </select>
        <select id="getEmployees" resultMap="EmployeeDepartment">
        select * from employee
    </select>

    <delete id="deleteEmployeeByID" parameterType="int">
        delete from employee where id=#{id}
    </delete>

</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

这时,重新启动程序进行测试,成功显示 department的所有信息。

image-20220501211708403

分析:

association property=“eDepartment” column=“department” javaType=“Department” select=“getDepartment” 在这里面的 property 对应实体类中的属性,column对应数据库表中的字段名,javaType表示多对一(该字段对应的Java类型)。从这里去绑定部门 Department 的信息。

按照结果嵌套处理

将查询到的结果进行封装到对象的属性中,重新进行展示。

    <!--    &lt;!&ndash;结果封装,将查询出来的列封装到对象属性中&ndash;&gt;-->
    <resultMap id="EmployeeDepartment2" type="Employee">
        <result property="id" column="eid"/>
        <result property="lastName" column="lastName"/>
        <result property="email" column="email"/>
        <result property="gender" column="gender"/>
        <result property="birth" column="birth"/>
        <association property="eDepartment" javaType="Department">
            <result property="id" column="did"/>
            <result property="departmentName" column="dname"/>
        </association>
    </resultMap>
    <select id="getEmployees" resultMap="EmployeeDepartment2">
        select e.id as eid,lastName,email,gender,birth,d.id as did,d.departmentName as dname
        from department d,employee e
        where d.id = e.department
    </select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

启动程序,进行测试,可以成功的查询到所有员工的信息以及对应的部门id和部门名称。

[{"id":1001,"lastName":"AAA","email":"1356207897@qq.com","gender":0,"birth":"2020-04-05T00:00:00.000+00:00","edepartment":{"id":101,"departmentName":"户部"}},{"id":1002,"lastName":"BBB","email":"1356207897@qq.com","gender":0,"birth":"2020-04-06T00:00:00.000+00:00","edepartment":{"id":102,"departmentName":"兵部"}},{"id":1003,"lastName":"CCC","email":"1356207897@qq.com","gender":1,"birth":"2020-05-05T00:00:00.000+00:00","edepartment":{"id":103,"departmentName":"吏部"}},{"id":1004,"lastName":"DDD","email":"1356207897@qq.com","gender":1,"birth":"2020-10-05T00:00:00.000+00:00","edepartment":{"id":104,"departmentName":"刑部"}},{"id":1005,"lastName":"EEE","email":"1356207897@qq.com","gender":1,"birth":"2020-11-05T00:00:00.000+00:00","edepartment":{"id":103,"departmentName":"吏部"}},{"id":1006,"lastName":"FFF","email":"1356207897@qq.com","gender":0,"birth":"2021-04-05T00:00:00.000+00:00","edepartment":{"id":106,"departmentName":"工部"}}]
  • 1

分析:【自己的理解】

在这里的

    <association property="eDepartment" javaType="Department">
        <result property="id" column="did"/>
        <result property="departmentName" column="dname"/>
    </association>
  • 1
  • 2
  • 3
  • 4

里面的 eDepartment 是Employee实体类的一个属性,这个属性对应的其实是多个字段(一个类Department)。 那么在这种 resulMap的map映射里面,将这些查询到的结果 字段值 重新封装到一个对应的表里面,对应着 想要的对象属性,然后去进行展示。

Employee的完善

对于剩下的CRUD操作代码的完善。

EmployeeMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace= 绑定一个对应的Dao/Mapper接口
<mapper namespace="org.mybatis.example.BlogMapper">
    <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
-->
<mapper namespace="com.AL.mapper.EmployeeMapper">
    <!--查询员工信息: 单纯的 department id-->
<!--    <select id="getEmployees" resultType="com.AL.pojo.Employee">-->
<!--        select * from employee-->
<!--    </select>-->

<!--按照查询嵌套处理-->
<!--<resultMap id="EmployeeDepartment" type="Employee">-->
<!--    <result property="id" column="id"/>-->
<!--    <result property="lastName" column="LastName"/>-->
<!--    <result property="email" column="email"/>-->
<!--    <result property="gender" column="gender"/>-->
<!--    <result property="birth" column="birth"/>-->
<!--    &lt;!&ndash;复杂的属性:我们需要单独处理 对象:association    集合:collection&ndash;&gt;-->
<!--    &lt;!&ndash;复杂的属性:我们需要单独处理-->
<!--对象 关联:association    集合:collection-->
<!--association : 关联,多对一-->
<!--column : 数据库对应的列名-->
<!--property : 对应属性名-->
<!--javaType : 多对一 字段对应的Java类型-->
<!--select : 关联一个语句-->
<!--&ndash;&gt;-->
<!--    <association property="eDepartment" column="department" javaType="Department" select="getDepartment"/>-->
<!--</resultMap>-->
<!--    <select id="getDepartment" resultType="Department">-->
<!--        select * from department where id = #{id}-->
<!--    </select>-->
<!--        <select id="getEmployees" resultMap="EmployeeDepartment">-->
<!--        select * from employee-->
<!--    </select>-->

    <!--    &lt;!&ndash;结果封装,将查询出来的列封装到对象属性中&ndash;&gt;-->
    <resultMap id="EmployeeDepartment2" type="Employee">
        <result property="id" column="eid"/>
        <result property="lastName" column="lastName"/>
        <result property="email" column="email"/>
        <result property="gender" column="gender"/>
        <result property="birth" column="birth"/>
        <association property="eDepartment" javaType="Department">
            <result property="id" column="did"/>
            <result property="departmentName" column="dname"/>
        </association>
    </resultMap>
    <select id="getEmployees" resultMap="EmployeeDepartment2">
        select e.id as eid,lastName,email,gender,birth,d.id as did,d.departmentName as dname
        from department d,employee e
        where d.id = e.department
    </select>

    <insert id="addEmployee" parameterType="Employee">
        insert into employee (lastName,email,gender,department,birth)
        values (#{lastName},#{email},#{gender},#{department},#{birth});
    </insert>

    <!--select * from employee where id = #{id}-->
    <select id="getEmployeeById" parameterType="int" resultMap="EmployeeDepartment2">
        select e.id as eid,lastName,email,gender,birth,d.id as did,d.departmentName as dname
        from department d,employee e
        where d.id = e.department and e.id = #{id}
    </select>

    <delete id="deleteEmployeeByID" parameterType="int">
        delete from employee where id=#{id}
    </delete>

</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

EmployeeController:

package com.AL.controller;


import com.AL.mapper.DepartmentMapper;
import com.AL.mapper.EmployeeMapper;
import com.AL.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.List;

@RestController
public class EmployeeController {

    @Autowired
    EmployeeMapper employeeMapper;

    @Autowired
    DepartmentMapper departmentMapper;

    // 获取所有员工信息
    @GetMapping("/getEmployees")
    public List<Employee> getEmployees(){
        return employeeMapper.getEmployees();
    }

    @GetMapping("/add")
    public int save(){
        Employee employee = new Employee();
        employee.setLastName("alzn");
        employee.setEmail("hhhhhh@qq.com");
        employee.setGender(1);
        employee.setEDepartment(departmentMapper.getDepartment(101));
        employee.setBirth(new Date());
        return employeeMapper.addEmployee(employee);
    }

    // 通过id获得员工信息
    @GetMapping("/get/{id}")
    public Employee getById(@PathVariable("id") Integer id){
        return employeeMapper.getEmployeeById(id);
    }

    // 通过id删除员工
    @GetMapping("/delete/{id}")
    public int delete(@PathVariable("id") Integer id){
        return employeeMapper.deleteEmployeeByID(id);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

重新启动程序,进行测试,成功展示员工的信息以及对应的部门信息。

整合前端界面

将前面的项目里面的资源配置文件复制到此时项目里面:

image-20220502093740134

关于url请求拦截、静态资源放行的配置文件和用户登录验证的控制器、首页显示控制器:

image-20220502094502950

登录首页实现

首页显示控制器:

  • 所有的访问界面需要从 index.html 界面进行登录。直接跳转到该界面
@Controller
public class IndexController {
    @RequestMapping({"/", "/index.html"}) //这是一个集合
    public String index(){
        return "index";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

去测试,看能否访问到该页面信息,去让用户进行登录。

错误信息:

 Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
     
     org.thymeleaf.exceptions.TemplateInputException: Error resolving template [index], template might not exist or might not be accessible by any of the configured Template Resolvers
  • 1
  • 2
  • 3

可以发现编译后的 targets里面没有我们的静态资源配置文件 templates:

image-20220502103312237

这属于静态资源导出时的问题,我们查找后,对pom.xml 配置资源文件进行修改:

  • 加入**/*.html 配置和其它的
  • src/main/resources **/.yml **/.properties **/.xml **/.html false
        <!--静态资源导出问题-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.yaml</include>
                    <include>**/*.xml</include>
                    <include>**/*.html</include>
                    <include>**/*.js</include>
                    <include>**/*.css</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

我们使静态资源能够导出,此时再进行测试的结果:

image-20220502110111923

image-20220502110136618

登录功能的实现

用户登录控制器和拦截器配置:

  • LoginController:
package com.Al.controller;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpSession;

@Controller
//@RestController
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) && "123456".equals(password)){
            session.setAttribute("LoginUser", username);
            //return "dashboard";
            return "redirect:/main.html"; // 重定向
        }else {
            model.addAttribute("msg", "用户名或密码错误");
            return "index";
        }
    }

    @RequestMapping("/user/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/index.html";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 登录时的拦截器:LoginHandlerInterceptor
package com.AL.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 用户如果登录成功了,应该获取到用户的 session 信息
        Object session = request.getSession().getAttribute("LoginUser");
        if (session == null) {
            request.setAttribute("msg", "权限不够,请先登录");
            request.getRequestDispatcher("/index.html").forward(request, response);
            return false;
        } else {
            return true;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

地域解析器配置:

package com.AL.config;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

// 地区解析器:获取链接上携带的参数信息
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[] split = language.split("_");//zh_CN(语言_地区)
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

MVC配置信息:

package com.AL.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/main.html").setViewName("dashboard");
    }

    // 将自定义的国际化配置放入 Spring容器中,使组件生效
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/index.html", "/", "/user/login", "/css/**", "/js/**", "/img/**");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

启动程序,进行测试,发现能够成功登录,且也可以进行拦截没有用户session的url请求。

image-20220502111548747

控制器修改

在整合mybatis时测试的控制器,直接返回json形式的数据。只管展示。

在这里,为了和原先模拟数据库显示前端界面,需要对控制器中后端传递给前端数据 的代码进行修改。

查询:员工信息列表展示

在controller包下创建 EmployeeControllerDemo02 控制器。查询员工所有的信息,后端程序将结果封装在 Model 中的 emps 中,传递给前端。前端的emps 进行foreach 进行展示员工的信息。

EmployeeControllerDemo02:

package com.AL.controller;

import com.AL.mapper.DepartmentMapper;
import com.AL.mapper.EmployeeMapper;
import com.AL.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

// 员工管理信息
@Controller
public class EmployeeControllerDemo02 {
    @Autowired
    EmployeeMapper employeeMapper;

    @Autowired
    DepartmentMapper departmentMapper;

    @RequestMapping("/emps")
    public String list(Model model){
        List<Employee> employeeList = employeeMapper.getEmployees();
        model.addAttribute("emps",employeeList);
        return "emps/list"; // 返回到 list界面
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

前端界面显示 员工的信息。

  • 需要注意的是,在这里,我们的实体类pojo中的 Employee 的属性为
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender; // 0:女  1:男。 用数字表示性别男女
    private Integer department; //原有的属性,对应数据库的 department id, 但对应的想要的是一个表
    private Date birth;
    private Department eDepartment; // 冗余设计
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 对应的数据库表 employee 的字段为:
create table `employee`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT'员工id',
`lastName` VARCHAR(30) NOT NULL COMMENT '姓名',
`email` VARCHAR(30) NOT NULL DEFAULT '*.com' COMMENT '邮箱',
`gender` INT(4) NOT NULL DEFAULT '0' COMMENT '性别',
`department` INT(4) NOT NULL COMMENT '部门id',
`birth`DATETIME DEFAULT NULL COMMENT '生日',
primary key(`id`),
key `FK_id`(`department`),
constraint `FK_id` foreign key(`department`) references `department`(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

image-20220502194530171

而前面的,我们编写的dao层中的 EmployeeMapper.xml中查询所有的员工信息,返回的结果为:

<resultMap id="EmployeeDepartment2" type="Employee">
    <result property="id" column="eid"/>
    <result property="lastName" column="lastName"/>
    <result property="email" column="email"/>
    <result property="gender" column="gender"/>
    <result property="birth" column="birth"/>
    <association property="eDepartment" javaType="Department">
        <result property="id" column="did"/>
        <result property="departmentName" column="dname"/>
    </association>
</resultMap>
<select id="getEmployees" resultMap="EmployeeDepartment2">
    select e.id as eid,lastName,email,gender,birth,d.id as did,d.departmentName as dname
    from department d,employee e
    where d.id = e.department
</select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

image-20220502195131803

我们需要在前端中修改的部分就是关于 获取部门信息 重新进行展示的部分。如下所示:

list.html :

<tr th:each="emp:${emps}">
<!--									<td th:text="${emp.getId()}"></td>-->
    <td th:text="${emp.getId()}"></td>
    <td>[[${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.getEDepartment().getDepartmentName()}"></td>
    <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
    <td>
        <a class="btn btn-sm btn-primary" th:href="@{/emp/{id}(id=${emp.getId()})}">编辑</a>
        <a class="btn btn-sm btn-danger" th:href="@{/delemp/{id}(id=${emp.getId()})}">删除</a>
    </td>
</tr>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

image-20220502195346683

测试,访问员工管理界面,查看是否成功显示所有的 员工信息。

image-20220502195609123

增加员工

增加员工实现的步骤:

  • 在list.html前端界面添加 员工增加的 选项及其超链接,响应url请求,跳转到toAddPage页面
  • toAdd控制器,响应 /add 这一url请求。后端需要传递前端一个部门信息(部门id、name名称),注意add.html显示部门选项 option
  • add.html 员工信息添加页面展示,尤其注意部门选项option这一部分的东西
  • 员工的信息添加完成后,重定向到 list.html页面,显示所有的员工信息。

EmployeeControllerDemo02:控制器中的 toadd 和add 方法,去响应对应着请求url

    @GetMapping("/add")
    public String toAddPage(Model model) {
        //查出所有的部门信息,添加到departments中,用于前端接收
        Collection<Department> departments = departmentMapper.getDepartments();
        model.addAttribute("departments", departments);
        return "emps/add";//返回到添加员工页面
    }
    @PostMapping("/add")
    public String addEmp(Employee employee){
        System.out.println("save=>" +employee);
        employeeMapper.addEmployee(employee); //添加一个员工
        return "redirect:/emps";//重定向到/emps,刷新列表,返回到list页面
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

add.html:员工信息添加,重点注意的是 部门信息展示

  • 注意: 我将这里的属性 name 值设置成了department,这样才和 employee中的属性对应:select class=“form-control” name=“department”
  • option中显示的信息是 部门的名称,所以 th:text=“${department.getDepartmentName()}”
  • 传递的是部门id,所以是 th:value=“${department.getId()}” 对应着name=“department” 传递给前端

image-20220502203014662

            <form th:action="@{/add}" method="post">
            <div class="form-group">
                <label>LastName</label>
                <input type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
            </div>
            <div class="form-group">
                <label>Email</label>
                <input type="email" name="email" class="form-control" placeholder="email:xxxxx@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">
                    <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>
                <!--注意这里的name是department.id,因为传入的参数为id-->
<!--                <select class="form-control" name="department.id">-->
                <!--注意: 我将这里的属性 name 值设置成了department,这样才和 employee中的属性对应-->
                <select class="form-control" name="department">
                    <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
                </select>
            </div>
            <div class="form-group">
                <label>Birth</label>
                <!--springboot默认的日期格式为yy/MM/dd-->
                <input type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd">
            </div>
            <button type="submit" class="btn btn-primary">添加</button>
            </form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

测试,查看跳转到 添加员工界面:

image-20220502203550588

点击添加选项,成功重定向到 list.html页面,展示所有员工信息:

image-20220502203341213

解决了这里的之后,那么在前面的那里,员工添加 信息 出错的原因,是不是也是这个呢?

我需要对springboot-03-system 项目中的add.html 的前端传递部门信息进行修改。查看这个问题

修改员工

修改员工的信息:和增加员工信息的步骤类似。

EmployeeControllerDemo02

    @GetMapping("/emp/{id}")
    public String toUpdatePage(@PathVariable("id")Integer id, Model model){
        //查出原来的数据
        Employee employee = employeeMapper.getEmployeeById(id);
        model.addAttribute("emp", employee);
        //查出所有部门的信息
        Collection<Department> departments = departmentMapper.getDepartments();
        model.addAttribute("departments", departments);
        return "emps/update";
    }
    @PostMapping("/updateEmp")
    public String updataEmp(Employee employee){
        employeeMapper.addEmployee(employee);//添加一个员工
        return "redirect:/emps";//添加完成重定向到/emps,刷新列表
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

update.html

  • 重要的同样是 部门信息展示的部分。我自己修改的部门信息
<div class="form-group">
    <label>department</label>
    <!--注意这里的name是department.id,因为传入的参数为id-->
<!--                    <select class="form-control" name="department.id">-->
    <select class="form-control" name="department">
<!--                        <option th:selected="${emp.getDepartment().getId() == department.getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>-->
        <option th:selected="${emp.getEDepartment().getId() == department.getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
    </select>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

image-20220502210456988

进行测试,查看是否能够修改员工信息:

image-20220502205518670

修改后重定向到 list.html 页面:

  • 发现有两个该员工。检查后,发现自己在这里使用的方法里面是 addEmployee,所以导致增加了一个员工信息。

image-20220502205603239

删除员工

直接删除员工信息,重定向到 list.html页面。

EmployeeControllerDemo02

    @GetMapping("/delemp/{id}")
    public String deleteEmp(@PathVariable("id")Integer id, Model model){
        employeeMapper.deleteEmployeeByID(id);
        return "redirect:/emps";//添加完成重定向到/emps,刷新列表
    }
  • 1
  • 2
  • 3
  • 4
  • 5

测试,删除员工。成功完成。

image-20220502210301345

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/561700
推荐阅读
相关标签
  

闽ICP备14008679号