Статьи

PagingAndSortingRepository — Как использовать с Thymeleaf

В этом уроке я покажу, как отобразить список клиентов бизнеса в Thymeleaf с нумерацией страниц.

1 — Структура проекта

У нас нормальная структура проекта Maven.

2 — Зависимости проекта

Помимо обычных зависимостей Spring, мы добавляем Thymeleaf и hsqldb, потому что мы используем встроенную базу данных.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?xml version="1.0" encoding="UTF-8"?>
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.michaelcgood</groupId>
    <artifactId>michaelcgood-pagingandsorting</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>
 
    <name>PagingAndSortingRepositoryExample</name>
    <description>Michael C  Good - PagingAndSortingRepository</description>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
 
</project>

3 — Модели

Мы определяем следующие поля для клиента:

  • уникальный идентификатор
  • имя клиента
  • адрес клиента
  • сумма задолженности по текущему счету

Получатели и установщики быстро генерируются в Spring Tool Suite.
Аннотация @Entity необходима для регистрации этой модели в @SpringBootApplication.

ClientModel.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.michaelcgood.model;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
 
@Entity
public class ClientModel {
     
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Integer getCurrentInvoice() {
        return currentInvoice;
    }
    public void setCurrentInvoice(Integer currentInvoice) {
        this.currentInvoice = currentInvoice;
    }
    private String name;
    private String address;
    private Integer currentInvoice;
 
}

PagerModel — это просто POJO (обычный старый Java-объект), в отличие от ClientModel. Нет импорта, следовательно, нет аннотаций. Эта PagerModel просто используется для помощи с нумерацией страниц на нашей веб-странице. Пересмотрите эту модель, как только прочитаете шаблон Thymeleaf и увидите демонстрационные картинки. PagerModel имеет больше смысла, когда вы думаете об этом в контексте.

PagerModel.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.michaelcgood.model;
 
public class PagerModel {
    private int buttonsToShow = 5;
 
    private int startPage;
 
    private int endPage;
 
    public PagerModel(int totalPages, int currentPage, int buttonsToShow) {
 
        setButtonsToShow(buttonsToShow);
 
        int halfPagesToShow = getButtonsToShow() / 2;
 
        if (totalPages <= getButtonsToShow()) {
            setStartPage(1);
            setEndPage(totalPages);
 
        } else if (currentPage - halfPagesToShow <= 0) {
            setStartPage(1);
            setEndPage(getButtonsToShow());
 
        } else if (currentPage + halfPagesToShow == totalPages) {
            setStartPage(currentPage - halfPagesToShow);
            setEndPage(totalPages);
 
        } else if (currentPage + halfPagesToShow > totalPages) {
            setStartPage(totalPages - getButtonsToShow() + 1);
            setEndPage(totalPages);
 
        } else {
            setStartPage(currentPage - halfPagesToShow);
            setEndPage(currentPage + halfPagesToShow);
        }
 
    }
 
    public int getButtonsToShow() {
        return buttonsToShow;
    }
 
    public void setButtonsToShow(int buttonsToShow) {
        if (buttonsToShow % 2 != 0) {
            this.buttonsToShow = buttonsToShow;
        } else {
            throw new IllegalArgumentException("Must be an odd value!");
        }
    }
 
    public int getStartPage() {
        return startPage;
    }
 
    public void setStartPage(int startPage) {
        this.startPage = startPage;
    }
 
    public int getEndPage() {
        return endPage;
    }
 
    public void setEndPage(int endPage) {
        this.endPage = endPage;
    }
 
    @Override
    public String toString() {
        return "Pager [startPage=" + startPage + ", endPage=" + endPage + "]";
    }
 
}

4 — Репозиторий

PagingAndSortingRepository является расширением CrudRepository. Разница лишь в том, что он позволяет делать нумерацию сущностей. Обратите внимание, что мы аннотируем интерфейс с помощью @Repository, чтобы сделать его видимым для @SpringBootApplication.

ClientRepository.java

01
02
03
04
05
06
07
08
09
10
11
package com.michaelcgood.dao;
 
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
 
import com.michaelcgood.model.ClientModel;
 
@Repository
public interface ClientRepository extends PagingAndSortingRepository<ClientModel,Long> {
 
}

5 — Контроллер

Мы определяем некоторые переменные в начале класса. Мы хотим показывать только 3 страничные кнопки одновременно. Начальная страница — это первая страница результатов, начальное количество элементов на странице — 5, и пользователь может иметь 5 или 10 результатов на страницу.

Мы добавляем некоторые примеры значений в наш репозиторий с помощью метода addtorepository (), который определен ниже в этом классе. С помощью метода addtorepository () мы добавляем несколько «клиентов» в наш репозиторий, и многие из них являются головными компаниями, потому что у меня закончились идеи.

ModelAndView используется здесь, а не Model. Вместо этого используется ModelAndView, поскольку он является контейнером как для ModelMap, так и для объекта представления. Это позволяет контроллеру возвращать оба значения как одно значение. Это желательно для того, что мы делаем.

ClientController.java

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package com.michaelcgood.controller;
 
import java.util.Optional;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.michaelcgood.model.PagerModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
 
import com.michaelcgood.dao.ClientRepository;
import com.michaelcgood.model.ClientModel;
 
@Controller
public class ClientController {
     
    private static final int BUTTONS_TO_SHOW = 3;
    private static final int INITIAL_PAGE = 0;
    private static final int INITIAL_PAGE_SIZE = 5;
    private static final int[] PAGE_SIZES = { 5, 10};
    @Autowired
    ClientRepository clientrepository;
     
    @GetMapping("/")
    public ModelAndView homepage(@RequestParam("pageSize") Optional<Integer> pageSize,
            @RequestParam("page") Optional<Integer> page){
         
        if(clientrepository.count()!=0){
            ;//pass
        }else{
            addtorepository();
        }
         
        ModelAndView modelAndView = new ModelAndView("index");
        //
        // Evaluate page size. If requested parameter is null, return initial
        // page size
        int evalPageSize = pageSize.orElse(INITIAL_PAGE_SIZE);
        // Evaluate page. If requested parameter is null or less than 0 (to
        // prevent exception), return initial size. Otherwise, return value of
        // param. decreased by 1.
        int evalPage = (page.orElse(0) < 1) ? INITIAL_PAGE : page.get() - 1;
        // print repo
        System.out.println("here is client repo " + clientrepository.findAll());
        Page<ClientModel> clientlist = clientrepository.findAll(new PageRequest(evalPage, evalPageSize));
        System.out.println("client list get total pages" + clientlist.getTotalPages() + "client list get number " + clientlist.getNumber());
        PagerModel pager = new PagerModel(clientlist.getTotalPages(),clientlist.getNumber(),BUTTONS_TO_SHOW);
        // add clientmodel
        modelAndView.addObject("clientlist",clientlist);
        // evaluate page size
        modelAndView.addObject("selectedPageSize", evalPageSize);
        // add page sizes
        modelAndView.addObject("pageSizes", PAGE_SIZES);
        // add pager
        modelAndView.addObject("pager", pager);
        return modelAndView;
         
    }
     
public void addtorepository(){
         
        //below we are adding clients to our repository for the sake of this example
                ClientModel widget = new ClientModel();
                widget.setAddress("123 Fake Street");
                widget.setCurrentInvoice(10000);
                widget.setName("Widget Inc");
                 
                clientrepository.save(widget);
                 
                //next client
                ClientModel foo = new ClientModel();
                foo.setAddress("456 Attorney Drive");
                foo.setCurrentInvoice(20000);
                foo.setName("Foo LLP");
                 
                clientrepository.save(foo);
                 
                //next client
                ClientModel bar = new ClientModel();
                bar.setAddress("111 Bar Street");
                bar.setCurrentInvoice(30000);
                bar.setName("Bar and Food");
                clientrepository.save(bar);
                 
                //next client
                ClientModel dog = new ClientModel();
                dog.setAddress("222 Dog Drive");
                dog.setCurrentInvoice(40000);
                dog.setName("Dog Food and Accessories");
                clientrepository.save(dog);
                 
                //next client
                ClientModel cat = new ClientModel();
                cat.setAddress("333 Cat Court");
                cat.setCurrentInvoice(50000);
                cat.setName("Cat Food");
                clientrepository.save(cat);
                 
                //next client
                ClientModel hat = new ClientModel();
                hat.setAddress("444 Hat Drive");
                hat.setCurrentInvoice(60000);
                hat.setName("The Hat Shop");
                clientrepository.save(hat);
                 
                //next client
                ClientModel hatB = new ClientModel();
                hatB.setAddress("445 Hat Drive");
                hatB.setCurrentInvoice(60000);
                hatB.setName("The Hat Shop B");
                clientrepository.save(hatB);
                 
                //next client
                ClientModel hatC = new ClientModel();
                hatC.setAddress("446 Hat Drive");
                hatC.setCurrentInvoice(60000);
                hatC.setName("The Hat Shop C");
                clientrepository.save(hatC);
                 
                //next client
                ClientModel hatD = new ClientModel();
                hatD.setAddress("446 Hat Drive");
                hatD.setCurrentInvoice(60000);
                hatD.setName("The Hat Shop D");
                clientrepository.save(hatD);
                 
                //next client
                ClientModel hatE = new ClientModel();
                hatE.setAddress("447 Hat Drive");
                hatE.setCurrentInvoice(60000);
                hatE.setName("The Hat Shop E");
                clientrepository.save(hatE);
                 
                //next client
                ClientModel hatF = new ClientModel();
                hatF.setAddress("448 Hat Drive");
                hatF.setCurrentInvoice(60000);
                hatF.setName("The Hat Shop F");
                clientrepository.save(hatF);
                 
    }
     
}

6 — Шаблон из тимьяна

В шаблоне Thymeleaf следует отметить две наиболее важные вещи:

  • Стандартный тимьяновый диалект
  • Javascript

Как и в CrudRepository, мы перебираем PagingAndSortingRepository с th: each = ”clientlist: $ {clientlist}». За исключением того, что каждый элемент в хранилище является Итерируемым, этот элемент является Страницей.

С помощью select class = ”page-control pagination” id = ”pageSizeSelect” мы позволяем пользователю выбрать размер своей страницы 5 или 10. Мы определили эти значения в нашем контроллере.

Далее идет код, который позволяет пользователю просматривать различные страницы. Это где наш PagerModel приходит на использование.

Функция changePageAndSize () — это функция JavaScript, которая будет обновлять размер страницы при ее изменении пользователем.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
    xmlns:th="http://www.thymeleaf.org">
 
<head>
<!-- CSS INCLUDE -->
<link rel="stylesheet"
    integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
    crossorigin="anonymous"></link>
 
<!-- EOF CSS INCLUDE -->
<style>
.pagination-centered {
    text-align: center;
}
 
.disabled {
    pointer-events: none;
    opacity: 0.5;
}
 
.pointer-disabled {
    pointer-events: none;
}
</style>
 
</head>
<body>
 
    <!-- START PAGE CONTAINER -->
    <div class="container-fluid">
        <!-- START PAGE SIDEBAR -->
        <!-- commented out     <div th:replace="fragments/header :: header"> </div> -->
        <!-- END PAGE SIDEBAR -->
        <!-- PAGE TITLE -->
        <div class="page-title">
            <h2>
                <span class="fa fa-arrow-circle-o-left"></span> Client Viewer
            </h2>
        </div>
        <!-- END PAGE TITLE -->
        <div class="row">
            <table class="table datatable">
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Address</th>
                        <th>Load</th>
                    </tr>
                </thead>
                <tbody>
                    <tr th:each="clientlist : ${clientlist}">
                        <td th:text="${clientlist.name}">Text ...</td>
                        <td th:text="${clientlist.address}">Text ...</td>
                        <td><button type="button"
                                class="btn btn-primary btn-condensed">
                                <i class="glyphicon glyphicon-folder-open"></i>
                            </button></td>
                    </tr>
                </tbody>
            </table>
            <div class="row">
                <div class="form-group col-md-1">
                    <select class="form-control pagination" id="pageSizeSelect">
                        <option th:each="pageSize : ${pageSizes}" th:text="${pageSize}"
                            th:value="${pageSize}"
                            th:selected="${pageSize} == ${selectedPageSize}"></option>
                    </select>
                </div>
                <div th:if="${clientlist.totalPages != 1}"
                    class="form-group col-md-11 pagination-centered">
                    <ul class="pagination">
                        <li th:class="${clientlist.number == 0} ? disabled"><a
                            class="pageLink"
                            th:href="@{/(pageSize=${selectedPageSize}, page=1)}">«</a>
                        </li>
                        <li th:class="${clientlist.number == 0} ? disabled"><a
                            class="pageLink"
                            th:href="@{/(pageSize=${selectedPageSize}, page=${clientlist.number})}">←</a>
                        </li>
                        <li
                            th:class="${clientlist.number == (page - 1)} ? 'active pointer-disabled'"
                            th:each="page : ${#numbers.sequence(pager.startPage, pager.endPage)}">
                            <a class="pageLink"
                            th:href="@{/(pageSize=${selectedPageSize}, page=${page})}"
                            th:text="${page}"></a>
                        </li>
                        <li
                            th:class="${clientlist.number + 1 == clientlist.totalPages} ? disabled">
                            <a class="pageLink"
                            th:href="@{/(pageSize=${selectedPageSize}, page=${clientlist.number + 2})}">→</a>
                        </li>
                        <li
                            th:class="${clientlist.number + 1 == clientlist.totalPages} ? disabled">
                            <a class="pageLink"
                            th:href="@{/(pageSize=${selectedPageSize}, page=${clientlist.totalPages})}">»</a>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
        <!-- END PAGE CONTENT -->
        <!-- END PAGE CONTAINER -->
    </div>
        <script
  integrity="sha256-VAvG3sHdS5LqTT+5A/aeq/bZGa/Uj04xKxY8KM/w9EE="
  crossorigin="anonymous"></script>
  
 
    <script
        integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
        crossorigin="anonymous"></script>
    <script th:inline="javascript">
        /*<![CDATA[*/
        $(document).ready(function() {
    changePageAndSize();
});
 
function changePageAndSize() {
    $('#pageSizeSelect').change(function(evt) {
        window.location.replace("/?pageSize=" + this.value + "&page=1");
    });
}
        /*]]>*/
    </script>
 
</body>
</html>

7 — Конфигурация

Указанные ниже свойства могут быть изменены в зависимости от ваших предпочтений, но это то, что я хотел для своей среды.

application.properties

1
2
3
4
5
6
7
8
9
#==================================
# = Thymeleaf configurations
#==================================
spring.thymeleaf.check-template-location=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
server.contextPath=/

8 — Демо

Это домашняя страница.

Это вторая страница.

Я могу изменить количество элементов на странице до 10.

Исходный код есть на Github

Опубликовано на Java Code Geeks с разрешения Майкла Гуда, партнера нашей программы JCG . Смотрите оригинальную статью здесь: PagingAndSortingRepository — Как использовать с Thymeleaf

Мнения, высказанные участниками Java Code Geeks, являются их собственными.