1. 程式人生 > >Spring Boot和Thymeleaf整合,結合JPA實現分頁效果

Spring Boot和Thymeleaf整合,結合JPA實現分頁效果

    在專案裡,我需要做一個Spring Boot結合Thymeleaf前端模版,結合JPA實現分頁的演示效果。做的時候發現有些問題,也查了現有網上的不少文件,發現能全棧實現的不多,所以這裡我就把我的做法,全部程式碼和步驟貼出來供大家參考。

1 建立專案,用pom.xml引入依賴

    這裡將建立名為ThymeleafWithDB的Maven,在pom.xml裡引入如下的依賴包。

1	    <dependencies>
2	        <dependency>
3	            <groupId>org.springframework.boot</groupId>
4	            <artifactId>spring-boot-starter-web</artifactId>
5	        </dependency>
6	        <dependency>
7	            <groupId>org.springframework.boot</groupId>            
8	<artifactId>spring-boot-starter-thymeleaf</artifactId>
9	        </dependency>
10	    </dependencies>

    而在此專案裡,對應的Stock庫存表如下所示。

欄位名

型別

說明

id

int

主鍵

name

varchar

庫存貨物名

num

int

庫存數量

description

varchar

庫存貨物的描述

2 編寫啟動類

   這個類是中規中矩的,程式碼如下。

1	package prj;
2	import org.springframework.boot.SpringApplication;
3	import org.springframework.boot.autoconfigure.SpringBootApplication;
4	@SpringBootApplication
5	public class SpringBootApp {
6	    public static void main(String[] args) {
7	        SpringApplication.run(SpringBootApp.class, args);
8	    }
9	}

3 在控制器類裡,新增支援分頁的方法

1	    @RequestMapping("/listByPage")
2	    public ModelAndView listByPage(@RequestParam(value = "pageNum", defaultValue = "0") int pageNum,
3	@RequestParam(value = "pageSize", defaultValue = "3") int pageSize) {
4	        Page<Stock> stocks=stockService.getStockListByPage(pageNum, pageSize);
5	        System.out.println("total page:" + stocks.getTotalPages());
6	        System.out.println("current Page:" + pageNum);
7	        ModelAndView modelAndView = new ModelAndView("listByPage");
8	        //傳遞引數
9	        modelAndView.addObject("stocks",stocks);
10	        return modelAndView;
11	    }

    在第2行和第3行定義該方法的引數時,由於表示當前頁的pageNum和每頁資料個數的pageSize引數都是從url請求裡以get引數的形式得到,所以在之前要加@RequestParam註解,否則的話就無法從請求裡得到這兩個引數。

    在該方法的第4行裡,呼叫了stockService物件的getStockListByPage方法,在傳入分頁引數的情況下,得到了當前頁面中的資料。同時為了除錯,還在第5行和第6行裡,輸出了當前頁和每頁個數的資訊。

    在拿到當前頁面的資料後,該方法時通過第9行的方法,把它加到modelAndView物件裡,並在第10行裡,通過該物件,向listByPage檢視返回資料。

4 編寫業務邏輯方法

1	public Page<Stock> getStockListByPage(int pageNum, int pageSize) {
2	        Sort sort = new Sort(Sort.Direction.ASC , "ID");
3	        Pageable pageable = PageRequest.of(pageNum, pageSize, sort);
4	        Page<Stock> stocks = stockRepo.findAll(pageable);
5	        return stocks;
6	    }

    在這個方法的第2行裡,首先通過Sort物件,定義了“按ID進行升序排列”的排序方式,隨後通過第3行的PageRequest物件,定義的分頁的方式,這裡表示起始資料的pageNum和每頁展示資料的pageSize值,都是來自於外部傳入的引數。

    在確定好排序和分頁的方式後,本方法在第4行裡,通過呼叫PagingAndSortingRepository型別物件stockRepo的findAll方法,根據在引數pageable裡封裝好的分頁和排序的方式,向MySQL的stock資料表裡請求資料,並把得到的資料通過第5行的return語句返回。

5 編寫Repo類

1	package prj.repo;
2	import org.springframework.data.repository.PagingAndSortingRepository;
3	import org.springframework.stereotype.Component;
4	import prj.model.Stock;
5	@Component
6	public interface StockRepo extends PagingAndSortingRepository<Stock, Integer> {  }

    從第6行的程式碼裡大家能看到,該Repo類實現( implements)了JPA裡包含分頁和排序功能的PagingAndSortingRepository介面,由於在StockService裡呼叫的findAll方法已經封裝在該JPA接口裡了,所以這裡在StockRepo類裡,甚至不需要再寫程式碼。

6 在application.yml檔案裡編寫JPA和Thymeleaf的配置引數    

1	spring:
2	  jpa:
3	    show-sql: true
4	    hibernate:
5	      dll-auto: validate
6	  datasource:
7	    url: jdbc:mysql://localhost:3306/stock?serverTimezone=GMT
8	    username: root
9	    password: 123456
10	    driver-class-name: com.mysql.jdbc.Driver
11	  thymeleaf:
12	    enabled: true
13	    content-type: text/html
14	    check-template-location: true
15	    cache: false
16	    prefix: classpath:/templates/
17	    suffix: .html

    其中在第1行到第10行的程式碼裡,給出了JPA和MySQL的相關定義,而在第11行到第17行的程式碼裡,給出了Thymeleaf模板的引數。

    這裡用到的配置引數,其實在前文裡都已經說明過,不過請注意第2行和第11行的縮排,根據yml配置檔案的縮排格式,第11行的thymeleaf其實是和第2行的jpa同級,它們均屬於第1行的spring的子級配置。

7 新增listByPage.html頁面,實現分頁的效果

    根據配置,該檔案是需要放在resources/templates目錄裡,具體程式碼如下。

1	<!DOCTYPE html>
2	<html  lang="en" xmlns:th="http://www.thymeleaf.org">
3	<head>
4	    <meta charset="UTF-8">
5	    <title>庫存列表</title>
6	</head>
7	<body>
8	<table border="2">
9	    <tr>
10	        <td>庫存編號</td>
11	        <td>庫存貨物</td>
12	        <td>數量</td>
13	        <td>描述</td>
14	    </tr>
15	    <tr th:each="stock : ${stocks}">
16	        <td th:text="${stock.ID}"></td>
17	        <td th:text="${stock.name}"></td>
18	        <td th:text="${stock.num}"></td>
19	        <td th:text="${stock.description}"></td>
20	    </tr>
21	</table>
22	<div>
23	    <ul>
24	        <li>
25	            <a th:href="'/listByPage?pageNum=0'">首頁</a>
26	        </li>
27	        <li th:if="${stocks.hasPrevious()}">
28	            <a th:href="'/listByPage?pageNum=' + ${stocks.previousPageable().getPageNumber()}" th:text="上一頁"></a>
29	        </li>
30	        <li th:if="${stocks.hasNext()}">
31	            <a th:href="'/listByPage?pageNum=' + ${stocks.nextPageable().getPageNumber()}" th:text="下一頁"></a>
32	        </li>
33	        <li>
34	            <a th:href="'/listByPage?pageNum=' + ${stocks.getTotalPages() - 1}">尾頁</a>
35	        </li>
36	    </ul>
37	</div>
38	</body>
39	</html>

    在第22行到第37行的<div>屬性元素裡,加入了分頁的效果,具體說明如下。

  1. 在第25行的程式碼,通過th:href="'/listByPage?pageNum=0'"程式碼,以url引數的形式,向控制器類的listByPage方法,傳遞了pageNum為0的引數,以展示首頁資料。
  2. 在顯示“上一頁”的效果前,先需要通過第27行的th:if程式碼判斷stocks物件裡是否包含了上一頁的資料,如果是,則通過第28行的程式碼展示“上一頁”連結,請注意這裡“上一頁”連結所對應的引數,這樣就能通過該連結,得到上一頁的資料。
  3. 展示“下一頁”的方法和展示“上一頁”的很相似,都是先通過th:if判斷是否有下一頁資料,然後再通過連結得到下一頁的資料。
  4. 在第34行的程式碼裡,通過th:href="'/listByPage?pageNum=' + ${stocks.getTotalPages() - 1}"的程式碼得到了尾頁的資料,請注意這裡是用url中pageNum的引數值,得到尾頁的資料。

8 觀察效果

    編寫完成後,啟動該專案,此時如果在瀏覽器裡輸入http://localhost:8080/listByPage,就能看到如下圖所示的效果。

    從中大家能看到,上圖裡每頁的資料是3條,而且在資料下方展示了對應的分頁連結,由於是第一頁,所以沒有包含“上一頁”的連結。如果點選上圖裡的“下一頁”連結,就能看到頁面跳轉的效果,如下圖所示。

    從中大家不僅能看到頁面上的資料變化,而且還能看到在url裡,通過攜帶pageNum引數的方式,取到了下一頁資料。並且,由於引數stocks裡已經包含了“上一頁”的資料,所以還能看到對應的連結。同樣地,大家還能自行點選“首頁”、“下一頁”和“尾頁”等連結,以觀察對應的效