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>屬性元素裡,加入了分頁的效果,具體說明如下。
- 在第25行的程式碼,通過th:href="'/listByPage?pageNum=0'"程式碼,以url引數的形式,向控制器類的listByPage方法,傳遞了pageNum為0的引數,以展示首頁資料。
- 在顯示“上一頁”的效果前,先需要通過第27行的th:if程式碼判斷stocks物件裡是否包含了上一頁的資料,如果是,則通過第28行的程式碼展示“上一頁”連結,請注意這裡“上一頁”連結所對應的引數,這樣就能通過該連結,得到上一頁的資料。
- 展示“下一頁”的方法和展示“上一頁”的很相似,都是先通過th:if判斷是否有下一頁資料,然後再通過連結得到下一頁的資料。
- 在第34行的程式碼裡,通過th:href="'/listByPage?pageNum=' + ${stocks.getTotalPages() - 1}"的程式碼得到了尾頁的資料,請注意這裡是用url中pageNum的引數值,得到尾頁的資料。
8 觀察效果
編寫完成後,啟動該專案,此時如果在瀏覽器裡輸入http://localhost:8080/listByPage,就能看到如下圖所示的效果。
從中大家能看到,上圖裡每頁的資料是3條,而且在資料下方展示了對應的分頁連結,由於是第一頁,所以沒有包含“上一頁”的連結。如果點選上圖裡的“下一頁”連結,就能看到頁面跳轉的效果,如下圖所示。
從中大家不僅能看到頁面上的資料變化,而且還能看到在url裡,通過攜帶pageNum引數的方式,取到了下一頁資料。並且,由於引數stocks裡已經包含了“上一頁”的資料,所以還能看到對應的連結。同樣地,大家還能自行點選“首頁”、“下一頁”和“尾頁”等連結,以觀察對應的效