1. 程式人生 > 實用技巧 >SpringBoot-03-開發Web專案

SpringBoot-03-開發Web專案

5 SpringBoot開發web專案

5.1 靜態資源對映

靜態資源對映規則

  • 在開發一個web專案中,我們不可避免的要使用到許多靜態資源,如CSS、JS等,那麼SpringBoot是怎麼處理這些資源的呢?

  • 官方文件

  • 官方文件告訴了我們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>
        
      • 訪問測試

        紅色框出來的檔案都會被對映到url /webjars

5.2 首頁處理

  • 官方文件給出的說明是,SpringBoot會在靜態資源的目錄下查詢一個叫 index.html

    的檔案,將它作為首頁。如果這個找不到的話,就會在模板中找 index 的模板

  • 使用循序

    • index.html > index 模板

5.3 ThymeLeaf

模板引擎

  • Web開發的模板引擎是為了使使用者介面與業務資料(內容)分離而產生的,它可以生成特定格式的文件,用於網站的模板引擎就會生成一個標準的HTML文件。

  • 我們之前使用的jsp就是一種模板引擎,不過他已經過時了,我們就不在使用它了。

  • 這張圖可以告訴你模板引擎怎麼工作的

  • 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

  • 官方文件

  • 翻譯

    • 如果你在預設的配置上增加額外的配置,實現介面WebMvcConfigurer ,只給這個類加上 @Configuration 註解,千萬別用 @EnableWebMvc `
    • 如果想定製一些元件,就實現 WebMvcRegistrations 介面
    • 如果想全面接管SpringMVC,就給你的配置類同時加上 @Configuration@EnableWebMvc
  • 原始碼分析

    • 我們找到 WebMvcAutoConfiguration ,這個是SpringMVC的自動配置類。可以看到這個類的有這麼一個生效條件 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class),這個註解的意思是,當不存在 WebMvcConfigurationSupport 這個類的時候,預設配置才會生效

    • 下面我們再來看 @EnableWebMvc 的原始碼,可以看見他引入了一個叫 DelegatingWebMvcConfiguration 的類。字面意思就是託管WebMVC的自動配置。

    • 再深入檢視原始碼之後,這個 DelegatingWebMvcConfiguration 實際上繼承了 WebMvcConfigurationSupport ,這也就是說為什麼我們在擴充套件SpringMVC的時候,不要加上 @EnableWebMvc ` 的註解

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
        
    • 在首頁中使用

    • 效果展示

  • 原始碼分析

    • WebMvcAutoConfiguration 中有關於選擇 本地資源的內容,具體邏輯如下

      • 先判斷是否總是使用配置的語言環境
      • 如果不是的話,則根據請求頭中的 Accept-Language 解析使用什麼樣的語言資源

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("====================");
    }
    

    @Test
    public void EmployeeTest() {
        EMapper.addEmployee(new Employee(1, "布魯斯兒", "[email protected]", 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("=============");
    }
    

首頁

  • 在配置類中給首頁新增幾個對映

    @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
      
  • 在首頁中使用

  • 實現中英文切換

    • 給兩個連結新增地址

      <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();
      }
      
  • 效果展示

登入

  • 修改index.html中的相應元素,使其能將登入表單提交到相應的位置

  • 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";
    }
    
  • 測試

登入攔截器

  • 現在我們的主頁可以在沒有登入的情況下直接訪問,那麼我們就需要使用攔截器去攔截所有的未登入情況下的所有請求,這也就是在登入的時候給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";
    }
    
  • 效果展示

部門資訊展示

  • 頁面

    <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";
    }
    
  • 效果

員工資訊展示

  • 頁面

    <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";
    }
    
  • 展示

增、刪、改員工

增加

  • 跳轉連結

    <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";
    }
    
  • 展示

  • 注意點:

    • 日期格式的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";
    }
    
  • 展示

刪除

  • 跳轉連結

    <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)