SpringBoot(三)——员工管理系统(无数据库)

个人博客:http://blog.kunpw.cn/

本节以一个员工管理系统的实例在前两节的基础上继续讲解一些功能。

1.初始化

  • 依然是上一个项目,但这次我们引入一个员工管理系统的模板,将页面和静态资源分别导入到项目中,并使用Thymeleaf模板解析器(模板资源链接:https://pan.baidu.com/s/15L8ByzIqhKzaCMo2Re_F3g 提取码:8lbu ):

    image-20210306102122875
    • 按照上一节在每一个页面引入Thymeleaf,并修改源html标签属性为Thymeleaf,以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>
      
    image-20210306102901100
  • MyMvcConfig文件中重写addViewControllers方法,也就是实现路由跳转功能(注:此功能不实现国际化跳转就会失败):

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

2.国际化

如上图所示,所谓国际化即所有文字能够拥有中文和英文两种格式,以及能够通过下方的链接刷新页面文字显示。

  1. resources目录下新建i18n目录,并在其下新建login.properties文件和login_zh_CN.properties文件和login_en_US.properties文件(IDEA会自动生成文件夹),如图:

    image-20210306104627196
  2. 并且点击图中资源包->左上角+号添加属性特性,如图所示建立以下属性对应index.html中所有文字显示(注:提前配置好IDEA编码设置,新建文件以UTF-8格式编码):

    image-20210306105116485
  3. 探索源码,在MessageResourceProperties文件中发现basename变量,默认值为message,在application.properties文件中修改该值,使其默认位置为i18n.login

    # 修改消息默认路径,即国际化语言路径配置
    spring.messages.basename=i18n.login
    
  4. 修改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}"></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}">
            </label>
    			</div>
    			<!-- [[]]是行内写法,#是消息语法 -->
    			<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>
    
  5. 查看源码,重写一个地区解析器:

    1. 查看WebMvcAutoConfiguration文件中LocaleResolver方法:

      @Bean
      @ConditionalOnMissingBean
      @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
      // 地区解析器,如果用户自己配置则使用用户配置,没有则使用默认配置
      public LocaleResolver localeResolver() {
          if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
              return new FixedLocaleResolver(this.mvcProperties.getLocale());
          }
          // 查看默认配置AcceptHeaderLocaleResolver类
          AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
          localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
          return localeResolver;
      }
      
    2. 查看默认配置AcceptHeaderLocaleResolver类,发现这个类继承自LocaleResolver接口,并且重写了接口中相关方法,所以我们类比该默认配置也配置自己的地区解析器并且重写相关方法;

    3. config目录下新建MyLocaleResolver文件并继承LocaleResolver接口重写resolveLocalesetLocale方法,重写过程参考AcceptHeaderLocaleResolver中对应重写方法:

      <!-- 设置链接,thymeleaf中使用括号传递参数lang -->
      <a class="btn btn-sm" th:href="@{/index.html(lang='zh_CN')}">中文</a>
      <a class="btn btn-sm" th:href="@{/index.html(lang='en_US')}">English</a>
      
      package com.kun.demoweb.config;
      
      import org.springframework.web.servlet.LocaleResolver;
      import org.thymeleaf.util.StringUtils;
      
      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) {
              // 获取请求中的语言参数lang
              String language = request.getParameter("lang");
              System.out.println("Debug==>"+language);
              // 先获取默认地区,如果没有语言参数则使用默认地区
              Locale locale = Locale.getDefault();
              // 如果有语言参数,则返回新地区
              if(!StringUtils.isEmpty(language)){
                  String[] langSplit = language.split("_");
                  locale = new Locale(langSplit[0],langSplit[1]);
              }
              return locale;
          }
      
          @Override
          public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
      
          }
      }
      
      • 并且在MyMvcConfig文件中将该配置注入@Bean(已生效不展示):
      package com.kun.demoweb.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");
          }
      
          // 将自定义的地区解析器注入Bean,即生效
          @Bean
          public LocaleResolver localeResolver(){
              return new MyLocaleResolver();
          }
      }
      

3.登录

  1. 修改登录按钮表单提交之后的动作:

    <!-- 提交表单并请求/user/login路径,由LoginController匹配接收 -->
    <form class="form-signin" th:action="@{/user/login}">
    
  2. 新建一个LoginController文件:

    package com.kun.demoweb.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.thymeleaf.util.StringUtils;
    import javax.servlet.http.HttpSession;
    
    @Controller
    public class LoginController {
    
        // model给视图返回属性数据,session设置会话属性并赋值为username用于dashboard页面显示
        // RequestMapping对请求的路径进行识别,/user/login路径则进入login方法
        @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);
                // 重定向到/main.html路径
                return "redirect:/main.html";
            }else {
                // 传递消息,登录失败
                model.addAttribute("msg","用户名或密码错误");
                return "index";
            }
        }
    }
    
  3. MyMvcConfig文件中增加视图映射:

    // 视图映射
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        // 将所有/main.html路径全部映射到dashboard页面,实质上网页显示路径只是映射视图,并不一定是真实页面名!
        registry.addViewController("/main.html").setViewName("dashboard");
    }
    
  4. 新建LoginHandlerInterceptor拦截器:

    package com.kun.demoweb.config;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    // 拦截器
    public class LoginHandlerInterceptor implements HandlerInterceptor {
        //重写preHandle方法,在进入跳转之前就拦截
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 登陆成功之后,获取用户session
            Object loginUser = request.getSession().getAttribute("loginUser");
    
            // 未登陆状态
            if(loginUser==null){
                // 设置属性提示
                request.setAttribute("msg","没有权限,请先登录");
                // 转发该请求回到首页
                request.getRequestDispatcher("/index.html").forward(request,response);
                return false;
            }else{
                return true;
            }
        }
    }
    
  5. MyMvcConfig文件中重写拦截器方法:

    // 重写拦截器方法,addPathPatterns方法拦截所有路径请求,excludePathPatterns方法放行一些请求
    // 注意,css等静态资源不放行时则页面无法加载该资源,该资源会被拦截
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
            .addPathPatterns("/**")
            .excludePathPatterns("/index.html","/","/user/login","/css/**","/js/**","/img/**");
    }
    
  6. dashboard.html文件中传递显示session属性,即登录者信息:

    <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a>
    

完成了登录设置、登录失败提示、跳转路由(取缔登录成功时参数可视)、拦截器(防止未登录直接进入主页)功能。

4.数据设置与显示

  • 由于尚未连接数据库,暂时在后台自定义一些数据(注:有些内容涉及Spring,暂未解释)。
  1. 新建daopojo两个包,dao包围数据访问层,即通过该包下类访问数据库(此处为自定义数据);pojo包为实体类,即对象存储区:

    添加依赖:

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    

    Department.java

    package com.kun.demoweb.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    // 部门表
    // 创建无参方法
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Department {
        private Integer id;
        private String departmentName;
    }
    

    Employee.java

    package com.kun.demoweb.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.Date;
    
    // 员工表
    @Data
    @NoArgsConstructor
    public class Employee {
        private Integer id;
        private String lastName;
        private String email;
        // 0 女 1 男
        private Integer sex;
        private Department department;
        private Date birth;
    
        // 自建一个有参构造,不传入出生日期,直接默认生成
        public Employee(Integer id, String lastName, String email, Integer sex, Department department) {
            this.id = id;
            this.lastName = lastName;
            this.email = email;
            this.sex = sex;
            this.department = department;
            this.birth = new Date();
        }
    }
    

    DepartmentDao.java

    package com.kun.demoweb.dao;
    
    import com.kun.demoweb.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<Integer,Department>();
    
            departments.put(101,new Department(101,"教学部"));
            departments.put(102,new Department(102,"市场部"));
            departments.put(103,new Department(103,"教研部"));
            departments.put(104,new Department(104,"运营部"));
            departments.put(105,new Department(105,"后勤部"));
        }
        // 获取所有部门信息
        public Collection<Department> getDepartments(){
            return departments.values();
        }
        // 通过id得到部门
        public Department getDepartmentById(Integer id){
            return departments.get(id);
        }
    }
    

    EmployeeDao.java

    package com.kun.demoweb.dao;
    
    import com.kun.demoweb.pojo.Department;
    import com.kun.demoweb.pojo.Employee;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
    
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    // 员工dao
    @Repository
    public class EmployeeDao {
        // 模拟数据库中数据
        private static Map<Integer, Employee> employees = null;
        // 员工有所属的部门
        @Autowired
        private DepartmentDao departmentDao;
        static {
            // 创建一个部门表
            employees = new HashMap<Integer,Employee>();
    
            employees.put(1001,new Employee(1001,"AA","A123456789@qq.com",1,new Department(101,"教学部")));
            employees.put(1002,new Employee(1002,"BB","B123456789@qq.com",0,new Department(102,"市场部")));
            employees.put(1003,new Employee(1003,"CC","C123456789@qq.com",1,new Department(103,"教研部")));
            employees.put(1004,new Employee(1004,"DD","D123456789@qq.com",0,new Department(104,"运营部")));
            employees.put(1005,new Employee(1005,"EE","E123456789@qq.com",1,new Department(105,"后勤部")));
        }
        // 主键自增
        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> getAllEmployees(){
            return employees.values();
        }
        // 通过id查询员工
        public Employee getEmployeeById(Integer id){
            return employees.get(id);
        }
        // 通过id删除员工
        public void deleteEmployee(Integer id){
            employees.remove(id);
        }
    }
    
  2. 新建EmployeeController文件,将员工数据传给前端list.html文件:

    templates目录下新建employee目录存储所有员工页面,并将list.html文件存入其中;

    EmployeeController.java

    package com.kun.demoweb.controller;
    
    import com.kun.demoweb.dao.EmployeeDao;
    import com.kun.demoweb.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
    public class EmployeeController {
        @Autowired
        EmployeeDao  employeeDao;
    
        @RequestMapping("/employees")
        public String list(Model model){
            Collection<Employee> employees = employeeDao.getAllEmployees();
            model.addAttribute("emps",employees);
            return "employee/list";
        }
    }
    
  3. 组件拆分与引用,在templates目录下新建common目录,用于存储组件,并创建common.html文件,从dashboard.html文件中将顶部导航栏和侧边栏作为组件引入:

    common.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <!-- 导航栏,设置为Thymeleaf fragment组件 -->
    <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/#">[[${session.loginUser}]]</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>
    
    <!-- 侧边栏 -->
    <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
        <div class="sidebar-sticky">
            <ul class="nav flex-column">
                <li class="nav-item">
                    <!-- 判断侧边栏传入的参数进行高亮标记,使用了三元运算符 -->
                    <a th:class="${active=='main'?'nav-link active':'nav-link'}" th:href="@{/main.html}">
                        <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>
                        首页 <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 th:class="${active=='employees'?'nav-link active':'nav-link'}" th:href="@{/employees}">
                        <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>
    

    将导航栏和侧边栏分别插入dashboard.html文件和list.html文件中相应位置(即复用组件代码,删除原先重复代码):

    <!-- 引入组件导航栏 -->
    <div th:replace="~{common/common::topbar}"></div>
    
    <!-- 引入组件侧边栏,并且传递参数给组件 -->
    <!-- 分别为dashboard和list,传参是为了让侧边栏组件判别出当前是在首页或者员工管理页,进而高亮侧边栏相应栏 -->
    <div th:replace="~{common/common::sidebar(active='main')}"></div>
    <div th:replace="~{common/common::sidebar(active='employees')}"></div>
    
  4. 删除list.html页面原本数据,并接收从EmployeeController传送过来的员工数据,进而展示,并且增加了两个按钮显示(暂不可用):

    <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>sex</th>
                        <th>department</th>
                        <th>birth</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <!-- 这里使用了Thymeleaf提供的工具类,方便调用数据 -->
                <tbody>
                    <tr th:each="emp:${emps}">
                        <td th:text="${emp.getId()}"></td>
                        <td th:text="${emp.getLastName()}"></td>
                        <td th:text="${emp.getEmail()}"></td>
                        <!-- 由于存储的数据只有0和1,这里要进行判断 -->
                        <td th:text="${emp.getSex()==0?'':''}"></td>
                        <td th:text="${emp.department.getDepartmentName()}"></td>
                        <!-- 获取的时间要使用工具类进行格式化输出 -->
                        <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}"></td>
                        <td>
                            <button class="btn btn-sm btn-primary">修改</button>
                            <button class="btn btn-sm btn-danger">删除</button>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </main>
    
  5. 增加员工操作:

    EmployeeController.java新增Get和Post获取跳转:

    @Autowired
    DepartmentDao departmentDao;    
    // 接收Get请求,并展示添加员工表单页面
    @GetMapping("/add_employee")
    public String toAddEmployeePage(Model model){
        // 查询所有部门
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("depts",departments);
        return "employee/add_employee";
    }
    
    // 接收Post请求,添加新员工之后跳转回员工页面
    @PostMapping("/add_employee")
    public String addEmployee(Employee employee){
        employeeDao.addEmployee(employee);
        return "redirect:/employees";
    }
    

    list.html文件中在main部分的最开始部分增加链接:

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

    employee目录下新建add_employee.html文件,内容完全复制list.html,修改中间main部分显示表单:

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <!-- 表单提交动作,且提交方法设置为Post -->
        <form th:action="@{/add_employee}" method="post">
            <!-- 姓名文本框 -->
            <div class="form-group">
                <label>LastName</label>
                <input type="text" class="form-control" name="lastName" placeholder="LastName">
            </div>
            <!-- 邮箱文本框 -->
            <div class="form-group">
                <label>Email</label>
                <input type="Email" class="form-control" name="email" placeholder="Email">
            </div>
            <!-- 性别单选框 -->
            <div class="radio">
                <label><input type="radio" name="sex" value="1"></label>
            </div>
            <div class="radio">
                <label><input type="radio" name="sex" value="0"></label>
            </div>
            <!-- 部门下拉框 -->
            <div class="form-group">
                <label>department</label>
                <!-- 部门value为id,name提交的必须是id -->
                <select class="form-control" th:name="department.id">
                    <option th:each="dept:${depts}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
                </select>
            </div>
            <!-- 出生日期 -->
            <div class="form-group">
                <label>Birth</label>
                <input type="date" class="form-control" name="birth" placeholder="Birth">
            </div>
            <button type="submit" class="btn btn-primary">添加</button>
        </form>
    </main>
    

    最后由于表单中date格式提交的数据是yyyy-MM-dd格式,而Spring中默认Date格式为dd/MM/yyyy,所以需要在application.properties文件中修改默认配置:

    # 修改默认Date格式
    spring.mvc.format.date=yyyy-MM-dd
    
  6. 修改员工操作:

    EmployeeController.java新增修改Get和Post获取跳转:

    // 接收Get请求,并展示修改员工表单页面,修改时要接收员工Id
    @GetMapping("/update_employee/{id}")
    public String toUpdateEmployeePage(@PathVariable("id")Integer id,Model model){
        // 根据Id查出员工数据
        Employee employee = employeeDao.getEmployeeById(id);
        model.addAttribute("emp",employee);
        // 查询所有部门
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("depts",departments);
    
        return "employee/update_employee";
    }
    
    // 接收Post请求,修改新员工之后跳转回员工页面
    @PostMapping("/update_employee")
    public String updateEmployee(Employee employee){
        employeeDao.addEmployee(employee);
        return "redirect:/employees";
    }
    

    list.html修改“修改”按钮为链接:

    <!-- 修改成链接格式,并且传参过去 -->
    <a class="btn btn-sm btn-primary" th:href="@{/update_employee/}+${emp.getId()}">修改</a>
    

    employee目录下新建update_employee.html文件,内容完全复制add_employee.html,修改中间main部分表单:

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <!-- 表单提交动作 -->
        <form th:action="@{/add_employee}" method="post">
            <!-- 增加一个隐藏域用于传id,这样才能修改原员工 -->
            <input type="hidden" name="id" th:value="${emp.getId()}">
            <!-- 姓名文本框 -->
            <div class="form-group">
                <label>LastName</label>
                <input th:value="${emp.getLastName()}" type="text" class="form-control" name="lastName" placeholder="LastName">
            </div>
            <!-- 邮箱文本框 -->
            <div class="form-group">
                <label>Email</label>
                <input th:value="${emp.getEmail()}" type="Email" class="form-control" name="email" placeholder="Email">
            </div>
            <!-- 性别单选框 -->
            <div class="radio">
                <label><input th:checked="${emp.getSex()==1}" type="radio" name="sex" value="1"></label>
            </div>
            <div class="radio">
                <label><input th:checked="${emp.getSex()==0}" type="radio" name="sex" value="0"></label>
            </div>
            <!-- 部门下拉框 -->
            <div class="form-group">
                <label>department</label>
                <!-- 部门value为id,name提交的必须是id -->
                <select class="form-control" th:name="department.id">
                    <option th:selected="${emp.getDepartment().getId()==dept.getId()}" th:each="dept:${depts}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
                </select>
            </div>
            <!-- 出生日期 -->
            <div class="form-group">
                <label>Birth</label>
                <!-- 由于日期读取出来格式不对,所以需要修改 -->
                <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}" type="date" class="form-control" name="birth" placeholder="Birth">
            </div>
            <button type="submit" class="btn btn-primary">修改</button>
        </form>
    </main>
    
  7. 删除员工操作:

    EmployeeController.java新增删除跳转:

    // 接收Get请求,删除员工,要接收员工Id
    @GetMapping("/delete_employee/{id}")
    public String deleteEmployee(@PathVariable("id")Integer id,Model model){
        employeeDao.deleteEmployee(id);
        return "redirect:/employees";
    }
    

    list.html文件修改删除按钮为链接:

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

5.404页面

只需要在templates文件夹下新建error文件夹并将404.html文件放入其中即可自动配置。

6.注销

LoginController.java文件中增加跳转:

// 注销
@RequestMapping("/user/logout")
public String logout(HttpSession session){
    // 使会话失效
    session.invalidate();
    return "redirect:/index.html";
}

common.html文件中修改导航栏的Sign out链接:

<a class="nav-link" th:href="@{/user/logout}">Sign out</a>

此处为后续添加内容,为该员工管理系统增加数据库连接及相关增删改查操作!

7.为员工管理系统增加数据库连接及操作

由于在原员工管理系统直接修改产生部分Bug,于是本人新建项目,依然使用原项目前端页面,具体步骤如下:

  1. 新建项目,导入依赖:

    • Developer Tools->Lombok,但可能需要修改版本,本人修改为1.18.12;
    • Web->Spring Web;
    • Template Engine->Thymeleaf;
    • SQL->JDBC API;
    • SQL->Spring Data JDBC;
    • SQL->MySQL Driver;
  2. 新建application.yml配置文件,代码为:

    spring:
      datasource:
        username: root
        password: password
        # serverTimezone=UTC是时区,
        url: jdbc:mysql://localhost:3306/springboot_mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
      thymeleaf:
        cache: false
      mvc:
        format:
          date: yyyy-MM-dd
    
    # 整合mybatis,分别为实体类位置和mapper实现层位置
    mybatis:
      type-aliases-package: com.kun.demowebmybatis.pojo
      mapper-locations: classpath:mybatis/mapper/*.xml
    
  3. 新增Mapper包,并新建DepartmentMapper.javaEmployeeMapper.java两个接口文件,代码分别为:

    DepartmentMapper.java

    @Mapper
    @Repository
    public interface DepartmentMapper {
        List<Department> getAllDepartments();
        Department getDepartmentById(Integer id);
    }
    

    EmployeeMapper.java

    @Mapper
    @Repository
    public interface EmployeeMapper {
        List<Employee> getAllEmployees();
        Employee getEmployeeById(Integer id);
        void addEmployee(Employee employee);
        void updateEmployee(Employee employee);
        void deleteEmployee(Integer id);
    }
    
  4. 导入原pojo包,并修改Employee.java实体类:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Employee {
        private Integer id;
        private String lastName;
        private String email;
        private Integer sex;
        private Integer departmentId;
        private Date birth;
        // 数据库中未存储,获取其它属性出来后临时获取
        private String departmentName;
    }
    
  5. 新建controller包,新建EmployeeController.java文件:

    @Controller
    public class EmployeeController {
        @Autowired
        DepartmentMapper departmentMapper;
        @Autowired
        EmployeeMapper employeeMapper;
        @GetMapping("/employees")
        public String getAllEmployees(Model model){
            List<Employee> employees = employeeMapper.getAllEmployees();
            // 重新设置Employee
            Department department = null;
            Employee employee = null;
            for (int i = 0; i < employees.size(); i++) {
                department = departmentMapper.getDepartmentById(employees.get(i).getDepartmentId());
                employee = employees.get(i);
                employee.setDepartmentName(department.getDepartmentName());
                employees.set(i,employee);
            }
            model.addAttribute("emps",employees);
            return "employee/list";
        }
        // 接收Get请求,并展示添加员工表单页面
        @GetMapping("/add_employee")
        public String toAddEmployeePage(Model model){
            // 查询所有部门
            List<Department> departments = departmentMapper.getAllDepartments();
            model.addAttribute("depts",departments);
            return "employee/add_employee";
        }
        // 接收Post请求,添加新员工之后跳转回员工页面
        @PostMapping("/add_employee")
        public String addEmployee(Employee employee){
            employeeMapper.addEmployee(employee);
            return "redirect:/employees";
        }
        // 接收Get请求,并展示修改员工表单页面,修改时要接收员工Id
        @GetMapping("/update_employee/{id}")
        public String toUpdateEmployeePage(@PathVariable("id")Integer id, Model model){
            // 根据Id查出员工数据
            Employee employee = employeeMapper.getEmployeeById(id);
            model.addAttribute("emp",employee);
            // 查询所有部门
            List<Department> departments = departmentMapper.getAllDepartments();
            model.addAttribute("depts",departments);
            return "employee/update_employee";
        }
        // 接收Post请求,修改新员工之后跳转回员工页面
        @PostMapping("/update_employee")
        public String updateEmployee(Employee employee){
            employeeMapper.updateEmployee(employee);
            return "redirect:/employees";
        }
        // 接收Get请求,删除员工,要接收员工Id
        @GetMapping("/delete_employee/{id}")
        public String deleteEmployee(@PathVariable("id")Integer id){
            employeeMapper.deleteEmployee(id);
            return "redirect:/employees";
        }
    }
    
  6. 导入原templates目录,并修改employee包下三个文件main内容:

    list.html:注意emp.getDepartmentName(),此处即上述员工实体类新增属性的使用

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <h2><a class="btn btn-sm btn-success" th:href="@{/add_employee}">添加员工</a></h2>
        <div class="table-responsive">
            <table class="table table-striped table-sm">
                <thead>
                    <tr>
                        <th>id</th>
                        <th>lastName</th>
                        <th>email</th>
                        <th>sex</th>
                        <th>department</th>
                        <th>birth</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <!-- 这里使用了Thymeleaf提供的工具类,方便调用数据 -->
                <tbody>
                    <tr th:each="emp:${emps}">
                        <td th:text="${emp.getId()}"></td>
                        <td th:text="${emp.getLastName()}"></td>
                        <td th:text="${emp.getEmail()}"></td>
                        <!-- 由于存储的数据只有0和1,这里要进行判断 -->
                        <td th:text="${emp.getSex()==0?'':''}"></td>
                        <td th:text="${emp.getDepartmentName()}"></td>
                        <!-- 获取的时间要使用工具类进行格式化输出 -->
                        <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}"></td>
                        <td>
                            <!-- 修改成链接格式,并且传参过去 -->
                            <a class="btn btn-sm btn-primary" th:href="@{/update_employee/}+${emp.getId()}">修改</a>
                            <a class="btn btn-sm btn-danger" th:href="@{/delete_employee/}+${emp.getId()}">删除</a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </main>
    

    add_employee.html:注意部门name改为departmentId

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <!-- 表单提交动作 -->
        <form th:action="@{/add_employee}" method="post">
            <!-- 姓名文本框 -->
            <div class="form-group">
                <label>LastName</label>
                <input type="text" class="form-control" name="lastName" placeholder="LastName">
            </div>
            <!-- 邮箱文本框 -->
            <div class="form-group">
                <label>Email</label>
                <input type="Email" class="form-control" name="email" placeholder="Email">
            </div>
            <!-- 性别单选框 -->
            <div class="radio">
                <label><input type="radio" name="sex" value="1"></label>
            </div>
            <div class="radio">
                <label><input type="radio" name="sex" value="0"></label>
            </div>
            <!-- 部门下拉框 -->
            <div class="form-group">
                <label>department</label>
                <!-- 部门value为id,name提交的必须是id -->
                <select class="form-control" th:name="departmentId">
                    <option th:each="dept:${depts}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
                </select>
            </div>
            <!-- 出生日期 -->
            <div class="form-group">
                <label>Birth</label>
                <input type="date" class="form-control" name="birth" placeholder="Birth">
            </div>
            <button type="submit" class="btn btn-primary">添加</button>
        </form>
    </main>
    

    update_employee.html:注意提交表单跳转/update_employee,部门name改为departmentId

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <!-- 表单提交动作 -->
        <form th:action="@{/update_employee}" method="post">
            <!--增加一个隐藏域用于传id,这样才能修改原员工-->
            <input type="hidden" name="id" th:value="${emp.getId()}">
            <!-- 姓名文本框 -->
            <div class="form-group">
                <label>LastName</label>
                <input th:value="${emp.getLastName()}" type="text" class="form-control" name="lastName" placeholder="LastName">
            </div>
            <!-- 邮箱文本框 -->
            <div class="form-group">
                <label>Email</label>
                <input th:value="${emp.getEmail()}" type="Email" class="form-control" name="email" placeholder="Email">
            </div>
            <!-- 性别单选框 -->
            <div class="radio">
                <label><input th:checked="${emp.getSex()==1}" type="radio" name="sex" value="1"></label>
            </div>
            <div class="radio">
                <label><input th:checked="${emp.getSex()==0}" type="radio" name="sex" value="0"></label>
            </div>
            <!-- 部门下拉框 -->
            <div class="form-group">
                <label>department</label>
                <!-- 部门value为id,name提交的必须是id -->
                <select class="form-control" th:name="departmentId">
                    <option th:selected="${emp.getDepartmentId()==dept.getId()}" th:each="dept:${depts}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
                </select>
            </div>
            <!-- 出生日期 -->
            <div class="form-group">
                <label>Birth</label>
                <!-- 由于日期读取出来格式不对,所以需要修改 -->
                <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}" type="date" class="form-control" name="birth" placeholder="Birth">
            </div>
            <button type="submit" class="btn btn-primary">修改</button>
        </form>
    </main>
    

    注:请注意其中一些细节的修改;

  7. resources目录下增加mybatis.mapper包结构,并新建对应的mapper.xml文件:

    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">
    <!-- 注意此处需要设置连接位置 -->
    <mapper namespace="com.kun.demowebmybatis.mapper.DepartmentMapper">
        <select id="getAllDepartments" resultType="Department">
            select * from department
        </select>
        <select id="getDepartmentById" resultType="Department">
            select * from department where id = #{id}
        </select>
    </mapper>
    

    EmployeeMapper.xml:此处注意数据库键名要对应,且参数名字也须一一对应,以及实现接口方法与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">
    <!-- 注意此处需要设置连接位置 -->
    <mapper namespace="com.kun.demowebmybatis.mapper.EmployeeMapper">
        <select id="getAllEmployees" resultType="Employee">
            select * from employees
        </select>
        <select id="getEmployeeById" resultType="Employee">
            select * from employees where id = #{id}
        </select>
        <insert id="addEmployee" parameterType="Employee">
            insert into employees (id,lastName,email,sex,departmentId,birth) values (#{id},#{lastName},#{email},#{sex},#{departmentId},#{birth})
        </insert>
        <update id="updateEmployee" parameterType="Employee">
            update employees set lastName = #{lastName},email = #{email},sex = #{sex},departmentId = #{departmentId},birth = #{birth} where id = #{id}
        </update>
        <delete id="deleteEmployee" parameterType="Integer">
            delete from employees where id = #{id}
        </delete>
    </mapper>
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值