使用 Spring Boot、ThymeLeaf、Spring Data JDBC、MySQL 进行分页和排序

 在前面的教程中,我们使用Spring Boot和 Thymeleaf 开发了一个 CRUD Web 应用程序。

在本教程中,我们将扩展spring boot CRUD web 应用程序,并使用 spring boot、thymeleaf、spring data JDBC和 MySQL 数据库实现分页和排序操作。

如您所知,分页允许用户一次查看一小部分数据(一页),排序允许用户以更有条理的方式查看数据。分页和排序都可以帮助用户更轻松、更方便地消费信息。

让我们从可以从本教程下载的员工管理系统项目开始,该项目基于 Spring Boot、Spring Data JDBC、Thymeleaf 和 MySQL 数据库。

先决条件 

MySQL 数据库

-- --------------------------------------------------------
-- 主机:                           127.0.0.1
-- 服务器版本:                        8.0.22 - MySQL Community Server - GPL
-- 服务器操作系统:                      Win64
-- HeidiSQL 版本:                  11.3.0.6295
-- --------------------------------------------------------

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;


-- 导出 testdb 的数据库结构
CREATE DATABASE IF NOT EXISTS `testdb` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `testdb`;

-- 导出  表 testdb.employees 结构
CREATE TABLE IF NOT EXISTS `employees` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `first_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `last_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  testdb.employees 的数据:~7 rows (大约)
/*!40000 ALTER TABLE `employees` DISABLE KEYS */;
INSERT IGNORE INTO `employees` (`id`, `email`, `first_name`, `last_name`) VALUES
	(1, '1', '1', '1'),
	(2, '2', '2', '2'),
	(3, '3', '3', '3'),
	(4, '4', '4', '4'),
	(5, '5', '5', '5'),
	(6, '6', '6', '6'),
	(7, '7', '7', '7'),
	(8, '8', '8', '8'),
	(9, '9', '9', '9');
/*!40000 ALTER TABLE `employees` ENABLE KEYS */;

/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

项目要求

这是实现员工列表页面(员工管理系统) 的分页和排序的高级项目需求。 

用户应该能够:

  • 在员工列表页面上执行分页
  • 在员工列表页面上进行排序

我们将建造什么?

我们将使用 Spring Boot、Thymeleaf、Spring Data JDBC和 MySQL 数据库实现分页和排序。

下面的截图总结了我们将在本教程中开发的分页和排序操作。

分页:

排序:


首先,我们将逐步实现分页操作,然后我们将完成排序操作。

一、分页实现

了解 Spring Data JDBC 的分页 API

要使用 Spring Data JDBC 提供的分页和排序 API,您的存储库接口必须扩展 PagingAndSortingRepository 接口,该接口定义了以下几个方法(T 指的是实体类):

@NoRepositoryBean
public interface PagingAndSortingRepository < T, ID > extends CrudRepository < T, ID > {

    /**
     * Returns all entities sorted by the given options.
     *
     * @param sort
     * @return all entities sorted by the given options
     */
    Iterable < T > findAll(Sort sort);

    /**
     * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
     *
     * @param pageable
     * @return a page of entities
     */
    Page < T > findAll(Pageable pageable);
}

例如,使用以下命令从数据库中获取第一页,每页 5 个项目:

int pageNumber = 1;
int pageSize = 5;
Pageable pageable = PageRequest.of(pageNumber, pageSize);
 
Page<Product> page = repository.findAll(pageable);

然后就可以得到实际内容如下:

List<Employee> listEmployees = page.getContent();

使用 Page 对象,您可以根据给定的页面大小了解数据库中的总行数和总页数:

long totalItems = page.getTotalElements();
int totalPages = page.getTotalPages();

此信息对于使用 Thymeleaf 模板在视图中实现分页很有用。

分页的后端更改

EmployeeService.java 接口更改

在此接口中添加以下方法:

Page<Employee> findPaginated(int pageNo, int pageSize);

完整代码:

package net.javaguides.springboot.service;

import java.util.List;

import org.springframework.data.domain.Page;

import net.javaguides.springboot.model.Employee;

public interface EmployeeService {
    List < Employee > getAllEmployees();
    void saveEmployee(Employee employee);
    Employee getEmployeeById(long id);
    void deleteEmployeeById(long id);
    Page < Employee > findPaginated(int pageNo, int pageSize);
}

EmployeeServiceImpl.java 类更改

将以下方法添加到 EmployeeServiceImpl 类:

@Override
public Page<Employee> findPaginated(int pageNo, int pageSize) {
 Pageable pageable = PageRequest.of(pageNo - 1, pageSize);
 return this.employeeRepository.findAll(pageable);
}

EmployeeController.java 类更改

将以下处理程序方法添加到 EmployeeController 类以执行分页:

@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable(value = "pageNo") int pageNo, Model model) {
    int pageSize = 5;

    Page < Employee > page = employeeService.findPaginated(pageNo, pageSize);
    List < Employee > listEmployees = page.getContent();

    model.addAttribute("currentPage", pageNo);
    model.addAttribute("totalPages", page.getTotalPages());
    model.addAttribute("totalItems", page.getTotalElements());
    model.addAttribute("listEmployees", listEmployees);
    return "index";
}

此外,我们需要对现有方法进行更改,如下所示:

// display list of employees
@GetMapping("/")
public String viewHomePage(Model model) {
 return findPaginated(1, model);  
}

分页的前端更改

将以下分页代码添加到 index.html:

<div th:if="${totalPages > 1}">
    <div class="row col-sm-10">
        <div class="col-sm-2">
            Total Rows: [[${totalItems}]]
        </div>
        <div class="col-sm-1">
            <span th:each="i: ${#numbers.sequence(1, totalPages)}">
      <a th:if="${currentPage != i}" th:href="@{'/page/' + ${i}}">[[${i}]]</a>
      <span th:unless="${currentPage != i}">[[${i}]]</span> &nbsp; &nbsp;
            </span>
        </div>
        <div class="col-sm-1">
            <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${currentPage + 1}}">Next</a>
            <span th:unless="${currentPage < totalPages}">Next</span>
        </div>

        <div class="col-sm-1">
            <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${totalPages}}">Last</a>
            <span th:unless="${currentPage < totalPages}">Last</span>
        </div>
    </div>
</div>

完整代码:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="ISO-8859-1">
    <title>Employee Management System</title>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

</head>

<body>

    <div class="container my-2">
        <h1>Employees List</h1>

        <a th:href="@{/showNewEmployeeForm}" class="btn btn-primary btn-sm mb-3"> Add Employee </a>

        <table border="1" class="table table-striped table-responsive-md">
            <thead>
                <tr>
                    <th>Employee First Name</th>
                    <th>Employee Last Name</th>
                    <th>Employee Email</th>
                    <th> Actions </th>
                </tr>
            </thead>
            <tbody>
                <tr th:each="employee : ${listEmployees}">
                    <td th:text="${employee.firstName}"></td>
                    <td th:text="${employee.lastName}"></td>
                    <td th:text="${employee.email}"></td>
                    <td> <a th:href="@{/showFormForUpdate/{id}(id=${employee.id})}" class="btn btn-primary">Update</a>
                        <a th:href="@{/deleteEmployee/{id}(id=${employee.id})}" class="btn btn-danger">Delete</a>
                    </td>
                </tr>
            </tbody>
        </table>

        <div th:if="${totalPages > 1}">
            <div class="row col-sm-10">
                <div class="col-sm-2">
                    Total Rows: [[${totalItems}]]
                </div>
                <div class="col-sm-1">
                    <span th:each="i: ${#numbers.sequence(1, totalPages)}">
      <a th:if="${currentPage != i}" th:href="@{'/page/' + ${i}}">[[${i}]]</a>
      <span th:unless="${currentPage != i}">[[${i}]]</span> &nbsp; &nbsp;
                    </span>
                </div>
                <div class="col-sm-1">
                    <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${currentPage + 1}}">Next</a>
                    <span th:unless="${currentPage < totalPages}">Next</span>
                </div>

                <div class="col-sm-1">
                    <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${totalPages}}">Last</a>
                    <span th:unless="${currentPage < totalPages}">Last</span>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

2.排序实现

了解 Spring Data JPA 的排序 API

Spring Data JDBC提供了 PagingAndSortingRepository 接口,该接口支持使用以下 API 进行排序和分页:

@NoRepositoryBean
public interface PagingAndSortingRepository < T, ID > extends CrudRepository < T, ID > {

    /**
     * Returns all entities sorted by the given options.
     *
     * @param sort
     * @return all entities sorted by the given options
     */
    Iterable < T > findAll(Sort sort);

    /**
     * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
     *
     * @param pageable
     * @return a page of entities
     */
    Page < T > findAll(Pageable pageable);
}

用户将能够通过单击表格的列标题对员工列表进行排序。 

首先,像这样创建一个 Sort 对象:

Sort sort = Sort.by(“fieldName”).ascending();

这将按 fieldName 升序对结果进行排序。 fieldName 必须与实体类中声明的字段名称匹配。 

我们还可以按多个字段排序,例如:

Sort sort = Sort.by("fieldName1").ascending().and(Sort.by("fieldName2").descending());

然后我们通过 Sort 对象来创建一个 Pageable ,如下所示:

Pageable pageable = PageRequest.of(pageNum - 1, pageSize, sort);

让我们在示例部分了解更多。

用于排序的后端更改

EmployeeService.java 接口更改

让我们在现有方法中添加两个字段:

Page<Employee> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection);

完整代码:

package net.javaguides.springboot.service;

import java.util.List;

import org.springframework.data.domain.Page;

import net.javaguides.springboot.model.Employee;

public interface EmployeeService {
    List < Employee > getAllEmployees();
    void saveEmployee(Employee employee);
    Employee getEmployeeById(long id);
    void deleteEmployeeById(long id);
    Page < Employee > findPaginated(int pageNo, int pageSize, String sortField, String sortDirection);
}

EmployeeServiceImpl.java 类更改

以下方法中实现的排序逻辑:

@Override
public Page<Employee> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
    Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
     Sort.by(sortField).descending();
 
    Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
    return this.employeeRepository.findAll(pageable);
}

完整代码:

package net.javaguides.springboot.service;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.repository.EmployeeRepository;

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Override
    public List < Employee > getAllEmployees() {
        return employeeRepository.findAll();
    }

    @Override
    public void saveEmployee(Employee employee) {
        this.employeeRepository.save(employee);
    }

    @Override
    public Employee getEmployeeById(long id) {
        Optional < Employee > optional = employeeRepository.findById(id);
        Employee employee = null;
        if (optional.isPresent()) {
            employee = optional.get();
        } else {
            throw new RuntimeException(" Employee not found for id :: " + id);
        }
        return employee;
    }

    @Override
    public void deleteEmployeeById(long id) {
        this.employeeRepository.deleteById(id);
    }

    @Override
    public Page < Employee > findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
        Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
            Sort.by(sortField).descending();

        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
        return this.employeeRepository.findAll(pageable);
    }
}

EmployeeController.java 更改

让我们更改现有方法以提供对排序的支持:

@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable(value = "pageNo") int pageNo,
    @RequestParam("sortField") String sortField,
    @RequestParam("sortDir") String sortDir,
    Model model) {
    int pageSize = 5;

    Page < Employee > page = employeeService.findPaginated(pageNo, pageSize, sortField, sortDir);
    List < Employee > listEmployees = page.getContent();

    model.addAttribute("currentPage", pageNo);
    model.addAttribute("totalPages", page.getTotalPages());
    model.addAttribute("totalItems", page.getTotalElements());

    model.addAttribute("sortField", sortField);
    model.addAttribute("sortDir", sortDir);
    model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");

    model.addAttribute("listEmployees", listEmployees);
    return "index";
}

还为主页提供默认的排序字段和排序方向:

// display list of employees
    @GetMapping("/")
    public String viewHomePage(Model model) {
        return findPaginated(1, "firstName", "asc", model);
    }

完整代码:

package net.javaguides.springboot.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.service.EmployeeService;

@Controller
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    // display list of employees
    @GetMapping("/")
    public String viewHomePage(Model model) {
        return findPaginated(1, "firstName", "asc", model);
    }

    @GetMapping("/showNewEmployeeForm")
    public String showNewEmployeeForm(Model model) {
        // create model attribute to bind form data
        Employee employee = new Employee();
        model.addAttribute("employee", employee);
        return "new_employee";
    }

    @PostMapping("/saveEmployee")
    public String saveEmployee(@ModelAttribute("employee") Employee employee) {
        // save employee to database
        employeeService.saveEmployee(employee);
        return "redirect:/";
    }

    @GetMapping("/showFormForUpdate/{id}")
    public String showFormForUpdate(@PathVariable(value = "id") long id, Model model) {

        // get employee from the service
        Employee employee = employeeService.getEmployeeById(id);

        // set employee as a model attribute to pre-populate the form
        model.addAttribute("employee", employee);
        return "update_employee";
    }

    @GetMapping("/deleteEmployee/{id}")
    public String deleteEmployee(@PathVariable(value = "id") long id) {

        // call delete employee method 
        this.employeeService.deleteEmployeeById(id);
        return "redirect:/";
    }


    @GetMapping("/page/{pageNo}")
    public String findPaginated(@PathVariable(value = "pageNo") int pageNo,
        @RequestParam("sortField") String sortField,
        @RequestParam("sortDir") String sortDir,
        Model model) {
        int pageSize = 5;

        Page < Employee > page = employeeService.findPaginated(pageNo, pageSize, sortField, sortDir);
        List < Employee > listEmployees = page.getContent();

        model.addAttribute("currentPage", pageNo);
        model.addAttribute("totalPages", page.getTotalPages());
        model.addAttribute("totalItems", page.getTotalElements());

        model.addAttribute("sortField", sortField);
        model.addAttribute("sortDir", sortDir);
        model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");

        model.addAttribute("listEmployees", listEmployees);
        return "index";
    }
}

排序的前端更改

索引.html

我们通过使用以下代码添加超链接来使表格的标题列可排序:

<th>
      <a th:href="@{'/page/' + ${currentPage} + '?sortField=firstName&sortDir=' + ${reverseSortDir}}">
       Employee First Name</a>
     </th>
     <th>
     <a th:href="@{'/page/' + ${currentPage} + '?sortField=lastName&sortDir=' + ${reverseSortDir}}">
       Employee Last Name</a>
     </th>
     <th>
     <a th:href="@{'/page/' + ${currentPage} + '?sortField=email&sortDir=' + ${reverseSortDir}}">
       Employee Email</a>
     </th>
     <th> Actions </th>

我们还需要更改分页部分以提供排序支持,例如:

<div th:if = "${totalPages > 1}">
   <div class = "row col-sm-10">
    <div class = "col-sm-2">
     Total Rows: [[${totalItems}]] 
    </div>
    <div class = "col-sm-1">
     <span th:each="i: ${#numbers.sequence(1, totalPages)}">
      <a th:if="${currentPage != i}" th:href="@{'/page/' + ${i}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">[[${i}]]</a>
      <span th:unless="${currentPage != i}">[[${i}]]</span>  &nbsp; &nbsp;
     </span>
    </div>
    <div class = "col-sm-1">
     <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${currentPage + 1}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Next</a>
     <span th:unless="${currentPage < totalPages}">Next</span>
    </div>
    
     <div class="col-sm-1">
        <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${totalPages}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Last</a>
     <span th:unless="${currentPage < totalPages}">Last</span>
        </div>
   </div>
  </div>

完整代码:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Employee Management System</title>

<link rel="stylesheet"
 href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
 integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
 crossorigin="anonymous">
 
</head>
<body>

 <div class="container my-2">
 <h1>Employees List</h1>
 
 <a th:href = "@{/showNewEmployeeForm}" class="btn btn-primary btn-sm mb-3"> Add Employee </a>
 
  <table border="1" class = "table table-striped table-responsive-md">
   <thead>
    <tr>
     <th>
      <a th:href="@{'/page/' + ${currentPage} + '?sortField=firstName&sortDir=' + ${reverseSortDir}}">
       Employee First Name</a>
     </th>
     <th>
     <a th:href="@{'/page/' + ${currentPage} + '?sortField=lastName&sortDir=' + ${reverseSortDir}}">
       Employee Last Name</a>
     </th>
     <th>
     <a th:href="@{'/page/' + ${currentPage} + '?sortField=email&sortDir=' + ${reverseSortDir}}">
       Employee Email</a>
     </th>
     <th> Actions </th>
    </tr>
   </thead>
   <tbody>
    <tr th:each="employee : ${listEmployees}">
     <td th:text="${employee.firstName}"></td>
     <td th:text="${employee.lastName}"></td>
     <td th:text="${employee.email}"></td>
     <td> <a th:href="@{/showFormForUpdate/{id}(id=${employee.id})}" class="btn btn-primary">Update</a>
         <a th:href="@{/deleteEmployee/{id}(id=${employee.id})}" class="btn btn-danger">Delete</a>
     </td>
    </tr>
   </tbody>
  </table>
  
  <div th:if = "${totalPages > 1}">
   <div class = "row col-sm-10">
    <div class = "col-sm-2">
     Total Rows: [[${totalItems}]] 
    </div>
    <div class = "col-sm-1">
     <span th:each="i: ${#numbers.sequence(1, totalPages)}">
      <a th:if="${currentPage != i}" th:href="@{'/page/' + ${i}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">[[${i}]]</a>
      <span th:unless="${currentPage != i}">[[${i}]]</span>  &nbsp; &nbsp;
     </span>
    </div>
    <div class = "col-sm-1">
     <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${currentPage + 1}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Next</a>
     <span th:unless="${currentPage < totalPages}">Next</span>
    </div>
    
     <div class="col-sm-1">
        <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${totalPages}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Last</a>
     <span th:unless="${currentPage < totalPages}">Last</span>
        </div>
   </div>
  </div>
 </div>
</body>
</html>

EmployeeRepository.java

package net.javaguides.springboot.repository;

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

import net.javaguides.springboot.model.Employee;

@Repository
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {

}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>net.javaguides</groupId>
    <artifactId>springboot-thymeleaf-crud-web-app-spring-data-jdbc</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-thymeleaf-crud-web-app-spring-data-jdbc</name>
    <description>Demo project for Spring Boot and thymeleaf</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <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-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

GitHub 存储库链接

GitHub - allwaysoft/springboot-thymeleaf-crud-pagination-sorting-webapp-spring-data-jdbc

猜你喜欢

转载自blog.csdn.net/allway2/article/details/127192775
今日推荐