SpringBoot-03-开发Web项目

5 SpringBoot开发web项目

5.1 静态资源映射

静态资源映射规则

  • 在开发一个web项目中,我们不可避免的要使用到许多静态资源,如CSS、JS等,那么SpringBoot是怎么处理这些资源的呢?

  • 官方文档

    image-20201218153115463

  • 官方文档告诉了我们SpringBoot中默认存放静态资源的几个位置,/static/public/resources/META-INF/resources。或者是配置文件中指定的位置

  • 除了这些常规手段之外,还提到了一种特殊的静态资源 webjars,所有的 webjars 会被映射到路径 /webjars/**

    • 什么是 webjars

      • 按照 jar 的格式进行打包的静态资源文件。这种格式的文件管理和方便,只需要引入依赖即可使用。
    • 示例

      • 引入maven依赖

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.5.1</version>
        </dependency>
        
      • 访问测试

        image-20201218155717735

        红色框出来的文件都会被映射到url /webjars

        image-20201218155806349

5.2 首页处理

  • 官方文档给出的说明是,SpringBoot会在静态资源的目录下查找一个叫 index.html 的文件,将它作为首页。如果这个找不到的话,就会在模板中找 index 的模板

  • 使用循序

    • index.html > index 模板

5.3 ThymeLeaf

模板引擎

  • Web开发的模板引擎是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。

  • 我们之前使用的jsp就是一种模板引擎,不过他已经过时了,我们就不在使用它了。

  • 这张图可以告诉你模板引擎怎么工作的

    image-20201218161130289

  • SpringBoot原生支持的模板引擎:FreeMaker、Groovy、Thymeleaf、Mustache,其中官方建议我们使用Thymeleaf

Thymeleaf简介

  • Thymeleaf是用来开发Web和独立环境项目的服务器端的Java模版引擎

  • Thymeleaf与SpringMVC的视图技术,及SpringBoot的自动化配置集成非常完美,几乎没有任何成本,你只用关注Thymeleaf的语法即可。

  • 优点

    • 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
    • 开箱即用:它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每套模板、改jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
    • 多方言支持:Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
    • SpringBoot完美整合,SpringBoot提供了Thymeleaf的默认配置,并且为Thymeleaf设置了视图解析器,我们可以像以前操作jsp一样来操作Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别
  • 如何使用

    • 导入Thymeleaf启动器

      <!--thymeleaf-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>
      
    • 把模板文件放到template文件夹下

语法

参考:https://www.cnblogs.com/jiangbei/p/8462294.html

  • 创建HTML,这样的话才能在上下文中使用th标签

    <html xmlns:th="http://www.thymeleaf.org">
    
  • 变量${variable}, *{...}

    <p th:text="'Hello!, '+${name}+'!'">World</p>
    
    <div th:object="${session.user}">
        <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
        <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p> 
        <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
    </div> 
    <!--等价于-->
    <div>
        <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p> 
        <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p> 
        <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
    </div>
    
  • 链接表达式 @{…}

    <a href="details.html" th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a> 
    
  • 文本替换,在替换时不能进行条件判断

    <span th:text="'Welcome to our application, ' + ${user.name} + '!'"/>
    
  • 运算符

    • 算数运算符:+,-,*,/,%
    • 逻辑运算符: >, <, >=<===!=
    • 条件运算符:
      • If-then: (if) ? (then)
      • If-then-else: (if) ? (then) : (else)
      • Default: (value) ?: (defaultvalue)
  • 条件选择 th:ifth:unlessth:switch

    <a th:href="@{/login}" th:if=${session.user == null}>Login</a>
    <a th:href="@{/login}" th:unless=${session.user != null}>Login</a>
    <div th:switch="${user.role}">
      <p th:case="'admin'">User is an administrator</p>
      <p th:case="#{roles.manager}">User is a manager</p>
      <p th:case="*">User is some other thing</p>
    </div>
    
  • 循环 th:each

    <table>
        <tr>
            <th>ID</th>
            <th>NAME</th>
            <th>AGE</th>
        </tr>
        <tr th:each="emp : ${empList}">
            <td th:text="${emp.id}">1</td>
            <td th:text="${emp.name}">海</td>
            <td th:text="${emp.age}">18</td>
        </tr>
    </table>
    

5.4 配置SpringMVC

默认配置下的SpringMVC

  • 包含 ContentNegotiatingViewResolverBeanNameViewResolver
  • 映射了静态资源路径,包括支持了Webjars
  • 自动注册了一些类型转换器和格式转化器
  • 支持 Http 消息转换和 错误代码定制
  • 支持静态首页
  • 初始化数据绑定器(自动绑定数据到JavaBean上)

如何扩展SpringMVC

  • 官方文档

    image-20201222145117544

  • 翻译

    • 如果你在默认的配置上增加额外的配置,实现接口WebMvcConfigurer ,只给这个类加上 @Configuration 注解,千万别用 @EnableWebMvc `
    • 如果想定制一些组件,就实现 WebMvcRegistrations 接口
    • 如果想全面接管SpringMVC,就给你的配置类同时加上 @Configuration@EnableWebMvc
  • 源码分析

    • 我们找到 WebMvcAutoConfiguration ,这个是SpringMVC的自动配置类。可以看到这个类的有这么一个生效条件 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class),这个注解的意思是,当不存在 WebMvcConfigurationSupport 这个类的时候,默认配置才会生效

      image-20201222150215436

    • 下面我们再来看 @EnableWebMvc 的源码,可以看见他引入了一个叫 DelegatingWebMvcConfiguration 的类。字面意思就是托管WebMVC的自动配置。

      image-20201222150457546

    • 再深入查看源码之后,这个 DelegatingWebMvcConfiguration 实际上继承了 WebMvcConfigurationSupport ,这也就是说为什么我们在扩展SpringMVC的时候,不要加上 @EnableWebMvc ` 的注解

      image-20201222150758561

5.5 i18n国际化

  • SpringBoot支持本地化消息用于迎合不同用户的语言需求

  • 默认情况下,SpringBoot会在 classpath 下查找消息资源包,也可以通过配置文件指定位置

  • 示例

    • resources 目录下创建 i18n 文件夹,IDEA会自动识别这个文件夹为国际化的文件

    • 在SpringBoot中配置国际化资源路径

      spring:
        messages:
          basename: i18n.message
      
    • 创建中文的配置文件和英文的配置文件

      • zh_CN

        alert=请登录
        loginBtn=登录
        password=密码
        remember=记住密码(不建议在公共电脑上勾选)
        username=用户名
        
      • en_US

        alert=Please sign in
        loginBtn=SIGN IN
        password=Password
        remember=Remember Me (not recommended to set on public computers)
        username=username
        
    • 在首页中使用

      image-20201222202811758

    • 效果展示

      image-20201222202902252

  • 源码分析

    • WebMvcAutoConfiguration 中有关于选择 本地资源的内容,具体逻辑如下

      • 先判断是否总是使用配置的语言环境
      • 如果不是的话,则根据请求头中的 Accept-Language 解析使用什么样的语言资源

      image-20201222204001721

5.6 上手操作

准备工作

  • 把前端模板放到该放的地方

  • 导入Maven依赖

    <dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!--数据库-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        
        <!--配置工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
  • 实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Repository
    public class Department {
    
        private int id;
        private String name;
    
    }
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Repository
    public class Employee {
    
        private int id;
        private String name;
        private String email;
        private int gender;
        private Department department;
        private Date birth;
    
    }
    
  • 配置数据库连接信息

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123456
        url: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8&useSSL=true&useUnicode=true&characterEncoding=utf8
    

MyBatis整合

  • 配置MyBatis

    mybatis:
      mapper-locations: classpath:mybatis/mapper/*.xml
      type-aliases-package: com.pbx.pojo
    
  • mapper

    • DepartmentMapper.java

      @Mapper
      @Repository
      public interface DepartmentMapper {
          List<Department> getDepartmentList();
          Department getDepartmentById(@Param("departmentID") int id);
          int countByDepartmentId(@Param("departmentID")int id);
          int updateDepartment(Department department);
          int deleteDepartment(@Param("departmentID") int id);
          int insertDepartment(Department department);
      }
      
    • DepartmentMapper.xml

      <mapper namespace="com.pbx.mapper.DepartmentMapper">
      
          <select id="getDepartmentList" resultType="com.pbx.pojo.Department">
              select *
              from springboot.department
          </select>
      
          <select id="getDepartmentById" resultType="com.pbx.pojo.Department">
              select *
              from springboot.department
              where id = #{departmentID}
          </select>
          <select id="countByDepartmentId" resultType="java.lang.Integer">
              SELECT COUNT(id) num FROM springboot.employee WHERE department = #{departmentID}
          </select>
          <update id="updateDepartment" parameterType="com.pbx.pojo.Department">
              update springboot.department
              set name = #{name}
              where id = #{id};
          </update>
          <delete id="deleteDepartment">
              delete
              from springboot.department
              where id = #{departmentID};
          </delete>
          <insert id="insertDepartment" parameterType="com.pbx.pojo.Department">
              insert into springboot.department (id, name)
              values (#{id}, #{name})
          </insert>
      </mapper>
      
    • EmployeeMapper.java

      @Mapper
      @Repository
      public interface EmployeeMapper {
          List<Employee> getEmployeeList();
          Employee getOneById(@Param("employeeID") int id);
          int addEmployee(Employee employee);
          int updateEmployee(Employee employee);
          int deleteEmployee(int id);
      }
      
    • EmployeeMapper.xml

      <mapper namespace="com.pbx.mapper.EmployeeMapper">
          <!--因为实体类的字段和数据库的字段不一致,所以要配置这resultMap和parameterMap-->
          <resultMap id="resultMap" type="com.pbx.pojo.Employee">
              <!--有association之后就要配置所有属性的映射-->
              <result property="id" column="id"/>
              <result property="name" column="last_name"/>
              <result property="email" column="email"/>
              <result property="gender" column="gender"/>
              <result property="birth" column="birth"/>
              <association property="department" javaType="com.pbx.pojo.Department">
                  <result property="id" column="did"/>
                  <result property="name" column="dname"/>
              </association>
          </resultMap>
          <select id="getEmployeeList" resultMap="resultMap">
              select e.id, e.last_name, e.email, e.gender, e.department, e.birth, d.id did, d.name dname
              from springboot.employee e, springboot.department d
              where e.department = d.id
          </select>
          <select id="getOneById" resultMap="resultMap">
              select e.id, e.last_name, e.email, e.gender, e.department, e.birth, d.id did, d.name dname
              from springboot.employee e,  springboot.department d
              where e.id = #{employeeID} and e.department = d.id
          </select>
          <insert id="addEmployee" parameterType="com.pbx.pojo.Employee">
              insert into springboot.employee(last_name, email, gender, department, birth)
              values (#{name}, #{email}, #{gender}, #{department.id}, #{birth})
          </insert>
          <update id="updateEmployee" parameterType="com.pbx.pojo.Employee">
              update springboot.employee
              set last_name = #{name}, email=#{email}, gender=#{gender}, department=#{department.id}
              where id = #{id}
          </update>
          <delete id="deleteEmployee">
              delete
              from springboot.employee
              where id = #{id}
          </delete>
      </mapper>
      
  • 测试

    @Test
    public void DepartmentTest() {
        DMapper.insertDepartment(new Department(106, "干饭部门"));
        showDepartment();
        DMapper.updateDepartment(new Department(106, "打工部"));
        showDepartment();
        DMapper.deleteDepartment(106);
        showDepartment();
    }
    
    public void showDepartment() {
        for (Department department : DMapper.getDepartmentList()) {
            System.out.println(department);
        }
        System.out.println("====================");
    }
    

    image-20201222180625744

    @Test
    public void EmployeeTest() {
        EMapper.addEmployee(new Employee(1, "布鲁斯儿", "123@qq.com", 1,
                DMapper.getDepartmentById(101), new Date()));
        showEmployee();
        Map<String, Object> map = new HashMap<>();
        map.put("id", 1);
        map.put("name", "里卡呆");
        EMapper.updateEmployee(map);
        showEmployee();
        EMapper.deleteEmployee(1);
        showEmployee();
    }
    
    public void showEmployee() {
        for (Employee employee : EMapper.getEmployeeList()) {
            System.out.println(employee);
        }
        System.out.println("=============");
    }
    

    image-20201222184323736

首页

  • 在配置类中给首页添加几个映射

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/index").setViewName("index");
    }
    
  • 这样的话,访问上述任一路径都可以到达我们的首页,同时为了保证所有的静态资源能被正确引用到,这里我们要用 Thymeleaf 管理所有的静态资源

    • 具体表现为用 Thymeleaf 来引用

      <!-- 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">
      

i18n 国际化

  • 在SpringBoot中配置国际化资源路径

    spring:
      messages:
        basename: i18n.message
    
  • 创建中文的配置文件和英文的配置文件

    • zh_CN

      alert=请登录
      loginBtn=登录
      password=密码
      remember=记住密码(不建议在公共电脑上勾选)
      username=用户名
      
    • en_US

      alert=Please sign in
      loginBtn=SIGN IN
      password=Password
      remember=Remember Me (not recommended to set on public computers)
      username=username
      
  • 在首页中使用

    image-20201222202811758

  • 实现中英文切换

    • 给两个链接添加地址

      <a class="btn btn-sm" th:href="@{/zh/index}">中文</a>
      <a class="btn btn-sm" th:href="@{/en/index}">English</a>
      
    • 配置Controller

      @GetMapping("/{s}/index")
      public String changeLanguage(@PathVariable String s, HttpSession session) {
          // 为了保证语言信息能跟着一起走,所有要放到session里面
          session.setAttribute("language", s);
          return "index";
      }
      
    • 重新配置Locale

      public class LanguageConfig implements LocaleResolver {
          @Override
          public Locale resolveLocale(HttpServletRequest request) {
              String language = (String) request.getSession().getAttribute("language");
              if ("zh".equals(language)) {
                  return new Locale("zh", "CN");
              } else if ("en".equals(language)) {
                  return new Locale("en", "US");
              }
              return request.getLocale();
          }
      
          @Override
          public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
      
          }
      }
      
    • 在MVC配置类中注入

      @Bean
      public LocaleResolver localeResolver() {
          return new LanguageConfig();
      }
      
  • 效果展示

    image-20201222202902252

    image-20201223215737382

登录

  • 修改index.html中的相应元素,使其能将登录表单提交到相应的位置

    image-20201223224339252

  • User实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Repository
    public class User {
    
        private int id;
        private String username;
        private String password;
    
    }
    
  • mapper接口和xml

    @Mapper
    @Repository
    public interface UserMapper {
    
        User getUser(Map<String, String> map);
    
    }
    
    <mapper namespace="com.pbx.mapper.UserMapper">
    
        <select id="getUser" parameterType="map" resultType="com.pbx.pojo.User">
            select * from springboot.user
            where username = #{username} and password = #{password}
        </select>
    </mapper>
    
  • 登录服务

    @Service
    public class LoginService {
    
        @Autowired
        private UserMapper mapper;
    
        public void setMapper(UserMapper mapper) {
            this.mapper = mapper;
        }
    
        public boolean login(Map<String, String> map) {
            return mapper.getUser(map) != null;
        }
    }
    
  • Controller设置

    @Autowired
    private LoginService loginService;
    
    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        Map<String, String> map = new HashMap<>(2);
        map.put("username", username);
        map.put("password", password);
        if (loginService.login(map)) {
            session.setAttribute("user", username);
            session.setAttribute("login", true);
            return "redirect:/dashboard";
        }
        return "index";
    }
    
  • 测试

    image-20201223232754772

登录拦截器

  • 现在我们的主页可以在没有登录的情况下直接访问,那么我们就需要使用拦截器去拦截所有的未登录情况下的所有请求,这也就是在登录的时候给session加了一个状态的原因

  • 自定义拦截器

    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Object login = request.getSession().getAttribute("login");
            if (login == null || login == Boolean.FALSE) {
                request.getRequestDispatcher("/index").forward(request, response);
                return false;
            } else {
                return true;
            }
        }
    }
    
  • 注册拦截器

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        String[] exclude = {"/", "/index","/index.html","/login","/jss/**","/css/**","/js/**","/*/index"};
        registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(exclude);
    }
    

主页

  • 抽取公共组件,简化模板文件结构,提高代码复用,同时完成链接跳转功能

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
        <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" style="color:white;" th:text="${session.user}"></a>
            <ul class="navbar-nav px-3">
                <li class="nav-item text-nowrap">
                    <a class="nav-link" th:href="@{/quit}" th:text="#{main.signout}"></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="${current=='main' ? 'nav-link active' : 'nav-link'}" th:href="@{/main}"
                           th:text="#{main.main}">
                        </a>
                    </li>
                    <li class="nav-item">
                        <a th:class="${current=='employee' ? 'nav-link active' : 'nav-link'}" th:href="@{/employee}"
                           th:text="#{main.employee}">
                        </a>
                    </li>
                    <li class="nav-item">
                        <a th:class="${current=='department' ? 'nav-link active' : 'nav-link'}" th:href="@{/department}"
                           th:text="#{main.department}">
                        </a>
                    </li>
                </ul>
            </div>
        </nav>
        
    </html>
    
  • 主页页面

    <body>
    
    <div th:replace="~{component/part::topbar}"></div>
    
    <div class="container-fluid">
        <div class="row">
            <div th:replace="~{component/part::sidebar}"></div>
    
            <main class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4" role="main">
                <h1 th:text="'Hello ' + ${session.user}"></h1>
            </main>
        </div>
    </div>
    
    </body>
    
  • 主页Controller

    @RequestMapping({"/dashboard", "/main"})
    public String mainPage(Model model) {
        model.addAttribute("current", "main");
        return "detail/main";
    }
    @RequestMapping("/quit")
    public String quit(HttpSession session) {
        session.invalidate();
        return "index";
    }
    
  • 效果展示

    image-20201224165558744

部门信息展示

  • 页面

    <body>
    
    <div th:replace="~{component/part::topbar}"></div>
    
    <div class="container-fluid">
        <div class="row">
            <div th:replace="~{component/part::sidebar}"></div>
    
            <main class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4" role="main">
                <h1 th:text="#{main.department.title}">Department</h1>
                <div class="table-responsive">
                    <table class="table table-striped table-sm">
                        <thead>
                        <tr>
                            <th th:text="#{main.department.id}"></th>
                            <th th:text="#{main.department.name}"></th>
                            <th th:text="#{main.department.nums}"></th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr th:each="k:${map}">
                            <td th:text="${k.getKey().getId()}"></td>
                            <td th:text="${k.getKey().getName()}"></td>
                            <td th:text="${map.get(k.getKey())}"></td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </main>
        </div>
    </div>
    
    </body>
    
  • mapper

    • 接口

      @Mapper
      @Repository
      public interface DepartmentMapper {
          List<Department> getDepartmentList();
          int countByDepartmentId(@Param("departmentID")int id);
      }
      
    • 映射文件

      <mapper namespace="com.pbx.mapper.DepartmentMapper">
          <select id="getDepartmentList" resultType="com.pbx.pojo.Department">
              select *
              from springboot.department
          </select>
          <select id="countByDepartmentId" resultType="java.lang.Integer">
              SELECT COUNT(id) num FROM springboot.employee WHERE department = #{departmentID}
          </select>
      </mapper>
      
  • Service

    @Service
    public class DepartmentService {
        @Autowired
        private DepartmentMapper mapper;
    
        private List<Department> getDepartment() {
            return mapper.getDepartmentList();
        }
    
        public Map<Department, Integer> countDepartment() {
            Map<Department, Integer> map = new LinkedHashMap<>();
            List<Department> departmentList = getDepartment();
            for (Department department : departmentList) {
                map.put(department, mapper.countByDepartmentId(department.getId()));
            }
            System.out.println(map);
            return map;
        }
    }
    
  • Controller

    @GetMapping("/department")
    public String department(Model model) {
        model.addAttribute("current", "department");
        Map<Department, Integer> map = departmentService.countDepartment();
        model.addAttribute("map", map);
        return "detail/department";
    }
    
  • 效果

    image-20201224174429008

员工信息展示

  • 页面

    <body>
    
    <div th:replace="~{component/part::topbar}"></div>
    <div class="container-fluid">
        <div class="row">
            <div th:replace="~{component/part::sidebar}"></div>
            <main class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4" role="main">
                <h2 th:text="#{main.employee.title}"></h2>
                <div class="table-responsive">
                    <table class="table table-striped table-sm">
                        <thead>
                        <tr>
                            <th th:text="#{main.employee.id}"></th>
                            <th th:text="#{main.employee.name}"></th>
                            <th th:text="#{main.employee.email}"></th>
                            <th th:text="#{main.employee.gender}"></th>
                            <th th:text="#{main.employee.department}"></th>
                            <th th:text="#{main.employee.birth}"></th>
                            <th th:text="#{main.employee.action}"></th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr th:each="l:${list}">
                            <td th:text="${l.getId()}"></td>
                            <td th:text="${l.getName()}"></td>
                            <td th:text="${l.getEmail()}"></td>
                            <td th:text="${l.getGender()==0 ? '女' : '男'}"></td>
                            <td th:text="${l.getDepartment().getName()}"></td>
                            <td th:text="${l.getBirth()}"></td>
                            <td>
                                <button class="btn btn-sm btn-primary" th:text="#{main.employee.edit}"></button>
                                <button class="btn btn-sm btn-danger" th:text="#{main.employee.delete}"></button>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </main>
        </div>
    </div>
    
    </body>
    
  • mapper

    • 接口

      @Mapper
      @Repository
      public interface EmployeeMapper {
      
          List<Employee> getEmployeeList();
      
          Employee getOneById(@Param("employeeID") int id);
      
          int addEmployee(Employee employee);
      
          int updateEmployee(Map<String, Object> map);
      
          int deleteEmployee(int id);
      
      }
      
    • 映射文件

      <mapper namespace="com.pbx.mapper.EmployeeMapper">
          <!--因为实体类的字段和数据库的字段不一致,所以要配置这resultMap和parameterMap-->
          <resultMap id="resultMap" type="com.pbx.pojo.Employee">
              <!--有association之后就要配置所有属性的映射-->
              <result property="id" column="id"/>
              <result property="name" column="last_name"/>
              <result property="email" column="email"/>
              <result property="gender" column="gender"/>
              <result property="birth" column="birth"/>
              <association property="department" javaType="com.pbx.pojo.Department">
                  <result property="id" column="did"/>
                  <result property="name" column="dname"/>
              </association>
          </resultMap>
          <select id="getEmployeeList" resultMap="resultMap">
              select e.id, e.last_name, e.email, e.gender, e.department, e.birth, d.id did, d.name dname
              from springboot.employee e, springboot.department d
              where e.department = d.id
          </select>
          <select id="getOneById" resultMap="resultMap">
              select *
              from springboot.employee
              where id = #{employeeID}
          </select>
          <insert id="addEmployee" parameterType="com.pbx.pojo.Employee">
              insert into springboot.employee(id, last_name, email, gender, department, birth)
              values (#{id}, #{name}, #{email}, #{gender}, #{department.id}, #{birth})
          </insert>
          <update id="updateEmployee" parameterType="map">
              update springboot.employee
              <set>
                  <if test="name != null">
                      last_name = #{name}
                  </if>
                  <if test="email != null">
                      email = #{email}
                  </if>
                  <if test="gender != null">
                      gender = #{gender}
                  </if>
                  <if test="department != null">
                      department = #{department}
                  </if>
                  <if test="birth != null">
                      birth = #{birth}
                  </if>
              </set>
              where id = #{id}
          </update>
          <delete id="deleteEmployee">
              delete
              from springboot.employee
              where id = #{id}
          </delete>
      </mapper>
      
  • Service

    @Service
    public class EmployeeService {
    
        @Autowired
        private EmployeeMapper mapper;
    
        public List<Employee> getEmployList() {
            return mapper.getEmployeeList();
        }
    
    }
    
  • Controller

    @GetMapping("/employee")
    public String employee(Model model) {
        model.addAttribute("current", "employee");
        model.addAttribute("list", employeeService.getEmployList());
        return "detail/employee";
    }
    
  • 展示

    image-20201224181940236

增、删、改员工

增加

  • 跳转链接

    <a th:href="@{/delete/}+${l.getId()}"><button class="btn btn-sm btn-danger" th:text="#{main.employee.delete}"></button></a>
    
  • 页面

    <body>
    <div th:replace="~{component/part::topbar}"></div>
    <div class="container-fluid">
        <div class="row">
            <div th:replace="~{component/part::sidebar}"></div>
            <main class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4" role="main">
                <form action="/add" method="post">
                    <div class="form-group">
                        <label for="name">姓名</label>
                        <input type="text" class="form-control" id="name" placeholder="姓名" name="name">
                    </div>
                    <div class="form-group">
                        <label for="email">邮箱</label>
                        <input type="email" class="form-control" id="email" placeholder="邮箱" name="email">
                    </div>
                    <div class="form-group">
                        <label>性别</label>
                        <div class="form-check">
                            <input class="form-check-inline" type="radio" name="gender" value="0">
                            <label class="form-check-label">男</label>
                        </div>
                        <div class="form-check">
                            <input class="form-check-inline" type="radio" name="gender" value="1">
                            <label class="form-check-label">女</label>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="department">部门</label>
                        <select class="form-control" id="department" name="department.id">
                            <option th:each="dept:${list}" th:text="${dept.getName()}" th:value="${dept.getId()}"></option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="birth">出生日期</label>
                        <input type="date" class="form-control" id="birth" placeholder="出生日期" name="birth">
                    </div>
                    <button type="submit" class="btn btn-default">提交</button>
                </form>
            </main>
        </div>
    </div>
    </body>
    
  • Controller

    @GetMapping("/add")
    public String toAdd(Model model) {
        model.addAttribute("list", departmentService.getDepartment());
        return "detail/add";
    }
    
    @PostMapping("/add")
    public String add(Employee employee) {
        System.out.println(employee);
        employeeService.addEmployee(employee);
        return "redirect:/employee";
    }
    
  • 展示

    image-20201224204502791

    image-20201224204513311

  • 注意点:

    • 日期格式的Formatter要记得设置

      spring:
        mvc:
          format:
            date: yyyy-mm-dd
      

修改

  • 跳转链接

    <a th:href="@{/edit/}+${l.getId()}"><button class="btn btn-sm btn-primary" th:text="#{main.employee.edit}"></button></a>
    
  • 页面

    <body>
    <div th:replace="~{component/part::topbar}"></div>
    <div class="container-fluid">
        <div class="row">
            <div th:replace="~{component/part::sidebar}"></div>
            <main class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4" role="main">
                <form th:action="@{/edit/}+${employee.getId()}" method="post">
                    <div class="form-group">
                        <label for="name">姓名</label>
                        <input type="text" class="form-control" id="name" th:value="${employee.getName()}" th:name="${'name'}">
                    </div>
                    <div class="form-group">
                        <label for="email">邮箱</label>
                        <input type="email" class="form-control" id="email" th:value="${employee.getEmail()}" name="email">
                    </div>
                    <div class="form-group">
                        <label>性别</label>
                        <div class="form-check">
                            <input th:checked="${employee.getGender()==0}" class="form-check-inline" type="radio" name="gender" value="0">
                            <label class="form-check-label">男</label>
                        </div>
                        <div class="form-check">
                            <input th:checked="${employee.getGender()==1}" class="form-check-inline" type="radio" name="gender" value="1">
                            <label class="form-check-label">女</label>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="department">部门</label>
                        <select class="form-control" id="department" name="department.id">
                            <option th:selected="${dept.getId()==employee.getDepartment().getId()}" th:each="dept:${list}" th:text="${dept.getName()}" th:value="${dept.getId()}"></option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="birth">出生日期</label>
                        <input type="date" class="form-control" id="birth" th:value="${#dates.format(employee.getBirth(),'yyyy-mm-dd')}" name="birth">
                    </div>
                    <button type="submit" class="btn btn-default">提交</button>
                </form>
            </main>
        </div>
    </div>
    </body>
    
  • Controller

    @GetMapping("/edit/{id}")
    public String toUpdate(@PathVariable int id, Model model) {
        Employee employee = employeeService.getOneByID(id);
        System.out.println(employee);
        model.addAttribute("employee", employee);
        model.addAttribute("list", departmentService.getDepartment());
        return "detail/edit";
    }
    
    @PostMapping("/edit/{id}")
    public String udate(@PathVariable int id, Employee employee) {
        System.out.println(id);
        System.out.println(employee);
        employeeService.updateEmployee(employee);
        return "redirect:/employee";
    }
    
  • 展示

    image-20201224220434119

    image-20201224220446739

    image-20201224220501607

删除

  • 跳转链接

    <a th:href="@{/delete/}+${l.getId()}"><button class="btn btn-sm btn-danger" th:text="#{main.employee.delete}"></button></a>
    
  • Controller

    @GetMapping("/delete/{id}")
    public String delete(@PathVariable int id) {
        employeeService.deleteEmployee(id);
        return "redirect:/employee";
    }
    
  • 演示

![image-20201224220948227](https://gitee.com/primabrucexu/image/raw/main/image/20201224220948.png)

![image-20201224220954612](https://gitee.com/primabrucexu/image/raw/main/image/20201224220954.png)
posted @ 2020-12-24 22:20  PrimaBruceXu  阅读(56)  评论(0编辑  收藏  举报