Статьи

Учебник по Spring Data Solr: разбиение на страницы

В предыдущих частях моего учебника Spring Data Solr мы реализовали простую функцию поиска, которая используется для поиска информации о записях todo. Текущая реализация нашей функции поиска показывает все результаты поиска на одной странице. Это не является жизнеспособным решением для большинства реальных приложений, потому что количество результатов поиска может быть настолько большим, что функция поиска больше не может использоваться.

Этот пост в блоге дает нам решение этой проблемы, описывая, как мы можем разбить на страницы результаты запроса или нашу функцию поиска с помощью Spring Data Solr.

Этот блог разделен на пять разделов:

  • В первом разделе описывается, как мы можем запросить правильную страницу вручную, и рассказывается о различных типах возвращаемых методов запросов.
  • Во втором разделе описывается, как мы можем получить количество результатов поиска, добавив пользовательский метод в наш репозиторий.
  • Третий раздел описывает, как мы можем разбить на страницы результаты поиска методов запроса.
  • Четвертый раздел учит нас разбивать на страницы результаты поиска динамических запросов.
  • Пятый и последний раздел описывает, как мы можем настроить и использовать метод, называемый веб-нумерацией страниц.

Примечание . Эти сообщения в блоге предоставляют дополнительную информацию, которая помогает нам понять концепции, описанные в этом сообщении:

Давайте начнем.

Несколько минут теории

Прежде чем мы начнем вносить изменения в наше примерное приложение, мы кратко рассмотрим теорию, стоящую за нумерацией страниц. Этот раздел состоит из двух подразделов, которые описаны ниже:

  • Первый раздел описывает, как мы можем указать параметры разбивки на страницы нашего запроса.
  • Второй раздел описывает различные типы возвращаемых данных метода запроса.

Давайте двигаться дальше.

Указание требуемой страницы

Используемые параметры разбиения на страницы определяются с помощью класса PageRequest, который реализует интерфейс Pageable .

Типичные требования к нумерации страниц приведены ниже:

  • Получите результаты запроса, относящиеся к одной странице.
  • Получите результаты запроса, относящиеся к одной странице, когда результаты запроса отсортированы, используя значение одного поля.
  • Получите результаты запроса, относящиеся к одной странице, когда результаты запроса отсортированы с использованием значений нескольких полей и порядок сортировки различных полей одинаков.
  • Получите результаты запроса, относящиеся к одной странице, когда результаты запроса сортируются с использованием значений нескольких полей и порядок сортировки разных полей не одинаков.

Давайте выясним, как мы можем создать объекты PageRequest, которые удовлетворяют заданным требованиям.

Сначала мы должны создать объект PageRequest, который указывает, что мы хотим получить результаты запроса, принадлежащие одной странице. Мы можем создать объект PageRequest , используя следующий код:

1
2
//Get the query results belonging to the first page when page size is 10.
new PageRequest(0, 10)

Во-вторых, мы должны создать объект PageRequest, который указывает, что мы хотим получить результаты, принадлежащие одной странице, когда результаты запроса сортируются с использованием значения одного поля. Мы можем создать объект PageRequest , используя следующий код:

1
2
3
/Gets the query results belonging to the first page when page size is 10.
//Query results are sorted in descending order by using id field.
new PageRequest(0, 10 Sort.Direction.DESC, "id")

В-третьих, мы должны создать объект PageRequest, который указывает, что мы хотим получить результаты, принадлежащие одной странице, когда результаты запроса сортируются с использованием нескольких полей и порядок сортировки различных полей одинаков. Мы можем создать объект PageRequest , используя следующий код:

1
2
3
//Gets the query results belonging to the first page when page size is 10.
//Query results are sorted in descending order by using id and description fields.
new PageRequest(0, 10 Sort.Direction.DESC, "id", "description")

В-четвертых, мы должны создать объект PageRequest, который указывает, что нужно получать результаты запроса, принадлежащие одной странице, когда результаты запроса сортируются с использованием нескольких полей, и порядок сортировки разных полей не одинаков. Мы можем создать этот объект, используя следующий код:

1
2
3
4
5
//Gets the query results belonging to the first page when page size is 10.
//Query results are sorted in descending order order by using the description field
//and in ascending order by using the id field.
Sort sort = new Sort(Sort.Direction.DESC, "description").and(new Sort(Sort.Direction.ASC, "id"))
new PageRequest(0, 10, sort);

Теперь мы знаем, как мы можем создавать новые объекты PageRequest . Давайте продолжим и поговорим о различных типах возвращаемых методов запросов.

Выбор типа возврата метода запроса

Когда метод запроса использует разбиение на страницы, он может иметь два типа возврата. Эти возвращаемые типы описаны ниже (мы будем предполагать, что имя нашего модельного класса — TodoDocument ):

  • Когда нас интересуют метаданные пагинации, тип возвращаемого значения нашего метода запроса должен быть Page <TodoDocument> (получите дополнительную информацию об интерфейсе страницы, который объявляет методы, используемые для получения метаданных пагинации).
  • Когда нас не интересуют метаданные разбиения на страницы, тип возвращаемого значения нашего метода запроса должен быть List <TodoDocument> .

Получение результатов поиска

Прежде чем мы сможем начать разбивать на страницы результаты поиска по нашим запросам, мы должны реализовать функцию, которая используется для получения количества записей todo, соответствующих заданным критериям поиска. Этот номер необходим для того, чтобы мы могли реализовать логику разбиения на страницы для внешнего интерфейса.

Мы можем реализовать эту функцию, выполнив следующие действия:

  1. Добавьте пользовательский метод в наш репозиторий. Этот метод используется для возврата количества результатов поиска.
  2. Создайте новый метод обслуживания, который использует наш собственный метод хранилища.

Эти шаги описаны более подробно в следующих подразделах.

Добавление пользовательского метода в наш репозиторий

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

  1. Создайте пользовательский интерфейс хранилища.
  2. Реализуйте пользовательский интерфейс хранилища.
  3. Изменить фактический интерфейс хранилища.

Давайте двигаться дальше и узнаем, как это делается.

Создание пользовательского интерфейса репозитория

Мы можем создать собственный интерфейс хранилища, выполнив следующие действия:

  1. Создайте интерфейс под названием CustomTodoDocumentRepository .
  2. Добавьте метод count () в созданный интерфейс. Этот метод принимает используемый термин поиска в качестве параметра метода.

Исходный код интерфейса CustomTodoDocumentRepository выглядит следующим образом:

1
2
3
4
5
6
public interface CustomTodoDocumentRepository {
 
    public long count(String searchTerm);
 
    //Other methods are omitted
}

Реализация пользовательского интерфейса репозитория

Мы можем реализовать пользовательский интерфейс хранилища, выполнив следующие действия:

  1. Создайте класс с именем TodoDocumentRepositoryImpl и реализуйте интерфейс CustomTodoDocumentRepository .
  2. Аннотируйте класс с помощью аннотации @Repository .
  3. Добавьте поле SolrTemplate в класс и аннотируйте поле аннотацией @Resource .
  4. Реализуйте метод count () .

Давайте подробнее рассмотрим реализацию метода count () . Мы можем реализовать этот метод, выполнив следующие действия:

  1. Получить слова данного поискового запроса.
  2. Создайте используемые критерии поиска, вызвав закрытый метод constructSearchConditions (), и передайте слова условия поиска в качестве параметра метода.
  3. Создайте выполненный запрос, создав новый объект SimpleQuery, и передайте созданный объект Criteria в качестве параметра конструктора.
  4. Получите счетчик результатов поиска, вызвав метод count () класса SolrTemplate, и передайте созданный объект SimpleQuery в качестве параметра метода.
  5. Вернуть количество результатов поиска.

Исходный код класса TodoDocumentRepositoryImpl выглядит следующим образом:

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
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.stereotype.Repository;
 
import javax.annotation.Resource;
 
@Repository
public class TodoDocumentRepositoryImpl implements CustomTodoDocumentRepository {
 
    @Resource
    private SolrTemplate solrTemplate;
 
    @Override
    public long count(String searchTerm) {
        String[] words = searchTerm.split(" ");
        Criteria conditions = createSearchConditions(words);
        SimpleQuery countQuery = new SimpleQuery(conditions);
 
        return solrTemplate.count(countQuery);
    }
 
    private Criteria createSearchConditions(String[] words) {
        Criteria conditions = null;
 
        for (String word: words) {
            if (conditions == null) {
                conditions = new Criteria("title").contains(word)
                        .or(new Criteria("description").contains(word));
            }
            else {
                conditions = conditions.or(new Criteria("title").contains(word))
                        .or(new Criteria("description").contains(word));
            }
        }
 
        return conditions;
    }
 
    //Other methods are omitted.
}

Изменение фактического интерфейса репозитория

Мы можем сделать пользовательский метод count () видимым для пользователей нашего репозитория, расширив CustomTodoRepositoryInterface . Исходный код TodoDocumentRepository выглядит следующим образом:

1
2
3
public interface TodoDocumentRepository extends CustomTodoRepository, SolrCrudRepository<TodoDocument, String> {
    //Repository methods are omitted.
}

Использование пользовательского метода репозитория

Мы можем использовать метод созданного репозитория, выполнив следующие действия:

  1. Изменить интерфейс TodoIndexService .
  2. Реализуйте измененный интерфейс.

Эти шаги описаны более подробно ниже.

Примечание . Мы также должны внести другие изменения, но я не буду описывать эти изменения здесь, поскольку они не связаны с Spring Data Solr.

Изменение интерфейса сервиса

Мы должны изменить интерфейс TodoIndexService , добавив в него новый метод countSearchResults () . Этот метод принимает используемый термин поиска в качестве параметра метода и возвращает количество результатов поиска. Исходный код интерфейса TodoIndexService выглядит следующим образом:

1
2
3
4
5
6
public interface TodoIndexService {
 
    public long countSearchResults(String searchTerm);
 
    //Other methods are omitted.
}

Реализация модифицированного интерфейса

Мы можем реализовать метод countSearchResults () , выполнив следующие действия:

  1. Добавьте метод countSearchResults () в класс RepositoryTodoIndexService .
  2. Получите счетчик результатов поиска, вызвав метод пользовательского хранилища и вернув счетчик результатов поиска.

Соответствующая часть класса RepositoryTodoIndexService выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
 
@Service
public class RepositoryTodoIndexService implements TodoIndexService {
 
    @Resource
    private TodoDocumentRepository repository;
 
    @Override
    public long countSearchResults(String searchTerm) {
        return repository.count(searchTerm);
    }
 
    //Other methods are omitted.
}

Разбейте результаты запроса на методы запроса

Когда мы создаем наши запросы, используя методы запросов, мы можем разбить на страницы результаты запроса, выполнив следующие действия:

  1. Добавьте новый параметр Pageable в метод запроса. Этот параметр указывает детали выбранной страницы.
  2. Измените уровень обслуживания, добавив новый параметр Pageable в метод search () интерфейса TodoIndexService .

Давайте начнем.

Модификация интерфейса репозитория

Мы можем добавить поддержку пагинации в наш репозиторий, добавив параметр Pageable в метод запроса, который используется для построения выполненного запроса. Давайте посмотрим на объявления наших методов запросов.

Генерация запроса из имени метода

Когда выполненный запрос создается с использованием генерации запроса из стратегии с именем метода, мы должны добавить параметр Pageable в метод findByTitleContainsOrDescriptionContains () интерфейса TodoDocumentRepository . Эти исходные коды нашего интерфейса репозитория выглядят следующим образом:

1
2
3
4
5
6
7
8
9
import org.springframework.data.domain.Pageable;
import org.springframework.data.solr.repository.SolrCrudRepository;
 
import java.util.List;
 
public interface TodoDocumentRepository extends CustomTodoDocumentRepository, SolrCrudRepository<TodoDocument, String> {
 
    public List<TodoDocument> findByTitleContainsOrDescriptionContains(String title, String description, Pageable page);
}

Именованные Запросы

Когда используются именованные запросы, мы должны добавить параметр Pageable в метод findByNamedQuery () интерфейса TodoDocumentRepository . Исходный код интерфейса TodoDocumentRepository выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
import org.springframework.data.domain.Pageable;
import org.springframework.data.solr.repository.Query;
import org.springframework.data.solr.repository.SolrCrudRepository;
 
import java.util.List;
 
public interface TodoDocumentRepository extends CustomTodoDocumentRepository, SolrCrudRepository<TodoDocument, String> {
 
    @Query(name = "TodoDocument.findByNamedQuery")
    public List<TodoDocument> findByNamedQuery(String searchTerm, Pageable page);
}

@Query Annotation

Когда выполненный запрос создается с использованием аннотации @Query , мы должны добавить параметр Pageable в метод findByQueryAnnotation () интерфейса TodoDocumentRepository . Исходный код нашего интерфейса репозитория выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
import org.springframework.data.domain.Pageable;
import org.springframework.data.solr.repository.Query;
import org.springframework.data.solr.repository.SolrCrudRepository;
 
import java.util.List;
 
public interface TodoDocumentRepository extends CustomTodoDocumentRepository, SolrCrudRepository<TodoDocument, String> {
 
    @Query("title:*?0* OR description:*?0*")
    public List<TodoDocument> findByQueryAnnotation(String searchTerm, Pageable page);
}

Изменение уровня сервиса

Мы должны внести следующие изменения в сервисный уровень нашего примера приложения:

  1. Добавьте параметр Pageable в метод search () интерфейса TodoIndexService .
  2. Реализуйте новый метод search ().

Примечание . Мы также должны внести другие изменения, но я не буду описывать эти изменения здесь, поскольку они не связаны с Spring Data Solr.

Исходный код интерфейса TodoIndexService выглядит следующим образом:

1
2
3
4
5
6
7
8
9
import org.springframework.data.domain.Pageable;
import java.util.List;
 
public interface TodoIndexService {
 
    public List<TodoDocument> search(String searchTerm, Pageable page);
 
    //Other methods are omitted.
}

Мы можем использовать измененные методы запроса, внеся следующие изменения в метод search () класса RepositoryIndexService :

  1. Получите разбитые на страницы результаты запроса, вызвав метод запроса нашего репозитория и передав в качестве параметров метода используемый поисковый термин и объект Pageable .
  2. Вернуть результаты запроса.

Давайте переместимся и посмотрим на различные реализации метода search () .

Генерация запроса из имени метода

Когда мы строим наши запросы, используя генерацию запросов из стратегии имени метода, мы можем получить результаты запроса, принадлежащие определенной странице, с помощью метода findByTitleContainsOrDescriptionContains () интерфейса TodoDocumentRepository .

Соответствующая часть класса RepositoryTodoIndexService выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
 
@Service
public class RepositoryTodoIndexService implements TodoIndexService {
 
    @Resource
    private TodoDocumentRepository repository;
 
    @Override
    public List<TodoDocument> search(String searchTerm, Pageable page) {
        return repository.findByTitleContainsOrDescriptionContains(searchTerm, searchTerm, page);
    }
    
    //Other methods are omitted
}

Именованные Запросы

Когда мы используем именованный запрос для построения выполненного запроса, мы можем получить результаты поиска, принадлежащие определенной странице, используя метод findByNamedQuery () интерфейса TodoDocumentRepository .

Соответствующая часть класса RepositoryTodoIndexService выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
 
@Service
public class RepositoryTodoIndexService implements TodoIndexService {
 
    @Resource
    private TodoDocumentRepository repository;
 
    @Override
    public List<TodoDocument> search(String searchTerm, Pageable page) {
        return repository.findByNamedQuery(searchTerm, page);
    }
    
    //Other methods are omitted
}

@Query Annotation

Когда мы создаем наш запрос с помощью аннотации @Query , мы можем получить результаты поиска, относящиеся к конкретной странице, вызвав метод findByQueryAnnotation () интерфейса TodoDocumentRepository .

Исходный код класса RepositoryTodoIndexService выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
 
@Service
public class RepositoryTodoIndexService implements TodoIndexService {
 
    @Resource
    private TodoDocumentRepository repository;
 
    @Override
    public List<TodoDocument> search(String searchTerm, Pageable page) {
        return repository.findByQueryAnnotation(searchTerm, page);
    }
    
    //Other methods are omitted
}

Разбиение результатов динамических запросов на страницы

Мы можем разбить на страницы результаты динамических запросов, выполнив следующие действия:

  1. Добавьте параметр Pageable в метод search () нашего пользовательского репозитория.
  2. Измените уровень обслуживания, добавив параметр Pageable в метод search () интерфейса TodoIndexService .

Эти шаги описаны более подробно в следующих подразделах.

Изменение пользовательского репозитория

Мы должны добавить поддержку нумерации страниц в наш пользовательский репозиторий. Мы можем сделать это, выполнив следующие действия:

  1. Измените пользовательский интерфейс репозитория, добавив параметр Pageable в его метод search () .
  2. Измените реализацию метода search () , добавив в него поддержку разбиения на страницы.

Давайте двигаться дальше и узнаем, как это делается.

Изменение пользовательского интерфейса репозитория

Мы должны добавить параметр Pageable в метод search (), объявленный в интерфейсе CustomTodoDocumentRepository . Исходный код нашего пользовательского интерфейса репозитория выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
import org.springframework.data.domain.Pageable;
 
import java.util.List;
 
public interface CustomTodoDocumentRepository {
 
    public List<TodoDocument> search(String searchTerm, Pageable page);
 
    //Other methods are omitted.
}

Реализация пользовательского метода репозитория

Наш следующий шаг — добавить поддержку разбиения на страницы для реализации метода search () . Мы можем реализовать метод search () класса TodoDocumentRepositoryImpl , выполнив следующие действия:

  1. Получить слова из условия поиска.
  2. Создайте используемые критерии поиска, вызвав закрытый метод createSearchConditions () и передав слова поиска в качестве параметра метода.
  3. Создайте выполненный запрос, создав новый объект SimpleQuery, и передайте созданный объект Criteria в качестве параметра конструктора.
  4. Задайте параметры разбивки на страницы запроса, вызвав метод setPageRequest () класса SimpleQuery . Передайте объект Pageable в качестве параметра метода.
  5. Получите результаты поиска, вызвав метод queryForPage () класса SolrTemplate . Передайте созданный запрос и тип ожидаемых возвращаемых объектов в качестве параметров метода.
  6. Верните результаты поиска, вызвав метод getContent () интерфейса Page .

Исходный код класса TodoDocumentRepositoryImpl выглядит следующим образом:

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
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.stereotype.Repository;
 
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
 
@Repository
public class TodoDocumentRepositoryImpl implements CustomTodoDocumentRepository {
 
    @Resource
    private SolrTemplate solrTemplate;
 
    @Override
    public List<TodoDocument> search(String searchTerm, Pageable page) {
        String[] words = searchTerm.split(" ");
 
        Criteria conditions = createSearchConditions(words);
        SimpleQuery search = new SimpleQuery(conditions);
        search.setPageRequest(page);
 
        Page results = solrTemplate.queryForPage(search, TodoDocument.class);
        return results.getContent();
    }
 
    private Criteria createSearchConditions(String[] words) {
        Criteria conditions = null;
 
        for (String word: words) {
            if (conditions == null) {
                conditions = new Criteria("title").contains(word)
                        .or(new Criteria("description").contains(word));
            }
            else {
                conditions = conditions.or(new Criteria("title").contains(word))
                        .or(new Criteria("description").contains(word));
            }
        }
 
        return conditions;
    }
 
    //Other methods are omitted.
}

Использование собственного репозитория

Прежде чем мы сможем использовать модифицированный метод репозитория, мы должны внести следующие изменения в сервисный уровень нашего примера приложения:

  1. Добавьте параметр Pageable в метод search () интерфейса TodoIndexService .
  2. Реализуйте метод search () .

Эти шаги описаны более подробно ниже.

Примечание . Мы также должны внести другие изменения, но я не буду описывать эти изменения здесь, поскольку они не связаны с Spring Data Solr.

Изменение интерфейса сервиса

Мы должны добавить параметр Pageable в метод search () интерфейса TodoIndexService . Исходный код TodoIndexService выглядит следующим образом:

1
2
3
4
5
6
7
8
9
import org.springframework.data.domain.Pageable;
import java.util.List;
 
public interface TodoIndexService {
 
    public List<TodoDocument> search(String searchTerm, Pageable page);
 
    //Other methods are omitted.
}

Реализация интерфейса сервиса

Когда мы создаем наш сайт с помощью API-критериев Spring Data Solr, мы можем получить результаты запроса, вызвав метод search () нашего пользовательского репозитория и передав пользовательский термин поиска и объект Pageable в качестве параметров метода.

Исходный код класса RepositoryTodoIndexService выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
 
@Service
public class RepositoryTodoIndexService implements TodoIndexService {
 
    @Resource
    private TodoDocumentRepository repository;
 
    @Override
    public List<TodoDocument> search(String searchTerm, Pageable page) {
        return repository.search(searchTerm, page);
    }
 
    //Other methods are omitted.
}

Использование веб-пагинации

Один вопрос до сих пор остается без ответа. Вот вопрос:

Где указаны параметры разбивки на страницы, используемые для разбивки на страницы результатов наших запросов?

Мы создадим параметры разбивки на страницы наших запросов, используя технику, называемую веб-нумерацией . Этот метод основан на пользовательском классе распознавателя аргументов, который называется PageableArgumentResolver . Этот класс анализирует информацию о разбиении на страницы из HTTP-запроса и позволяет добавить параметр метода Pageable в методы контроллера.

В этом разделе описывается, как мы можем настроить и использовать эту технику в нашем примере приложения. Он состоит из трех подразделов:

  • Первый подраздел описывает, как мы можем настроить класс PageableArgumentResolver .
  • Второй подраздел описывает, как мы можем его использовать.
  • В последнем подразделе рассказывается о плюсах и минусах веб-нумерации.

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

конфигурация

В этом подразделе описывается, как мы можем настроить класс PageableArgumentResolver, который будет использовать параметры извлечения страниц из HTTP-запросов. Давайте выясним, как мы это делаем, используя конфигурацию Java и конфигурацию XML.

Конфигурация Java

Мы можем добавить собственный преобразователь аргумента аргумента, внеся следующие изменения в класс ExampleApplicationContext :

  1. Переопределите метод addArgumentResolvers () класса WebMvcConfigurerAdapter .
  2. Реализуйте метод addArgumentResolvers () , создав новый объект PageableArgumentResolver и добавив созданный объект в список средств разрешения аргументов, который указан в качестве параметра метода.

Соответствующая часть класса ExampleApplicationContext выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
import org.springframework.data.web.PageableArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletWebArgumentResolverAdapter;
 
import java.util.List;
 
//Annotations are omitted.
public class ExampleApplicationContext extends WebMvcConfigurerAdapter {
 
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        PageableArgumentResolver pageableArgumentResolver = new PageableArgumentResolver();
        argumentResolvers.add(new ServletWebArgumentResolverAdapter(pageableArgumentResolver));
    }
 
    //Other methods are omitted.
}

Конфигурация XML

Мы можем настроить собственный преобразователь аргументов, внеся следующие изменения в файл exampleApplicationContext.xml :

  1. Используйте элемент arguments-resolvers пространства имен mvc для настройки пользовательских преобразователей аргументов.
  2. Сконфигурируйте бин PageableArgumentResolver внутри элемента arguments-resolvers .

Соответствующая часть файла exampleApplicationContext.xml выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean id="pageagleArgumentResolver" class="org.springframework.data.web.PageableArgumentResolver"/>
        </mvc:argument-resolvers>
    </mvc:annotation-driven>
 
    <!-- Configuration is omitted. -->
</beans>

использование

После того, как мы настроили класс PageableArgumentResolver с помощью одного из методов, которые были описаны ранее, мы можем добавить параметры метода Pageable в наши методы контроллера. Метод search () класса TodoController является хорошим примером этого. Соответствующая часть его исходного кода выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
 
import javax.annotation.Resource;
import java.util.List;
 
@Controller
public class TodoController {
 
    //Fields are omitted.
 
    @RequestMapping(value = "/api/todo/search/{searchTerm}", method = RequestMethod.GET)
    @ResponseBody
    public List<TodoDTO> search(@PathVariable("searchTerm") String searchTerm, Pageable page) {
        //Implementation is omitted.
    }
 
    //Other methods are omitted.
}

Однако добавления аргумента Pageable в метод контроллера недостаточно. Нам все еще нужно добавить параметры пагинации в HTTP-запрос. Это делается путем добавления специальных параметров запроса к запросу. Эти параметры запроса описаны ниже:

  • Параметр запроса page.page указывает запрашиваемую страницу.
  • Параметр запроса page.size указывает размер страницы.
  • Параметр запроса page.sort указывает свойство, которое используется для сортировки результатов запроса.
  • Параметр запроса page.sort.dir указывает порядок сортировки.

Давайте потратим немного времени и подумаем о плюсах и минусах веб-нумерации.

Плюсы и минусы

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

Pros

Использование веб-нумерации имеет одно важное преимущество:

Перенести параметры пагинации с веб-слоя на слой хранилища легко и просто. Все, что нам нужно сделать, — это настроить собственный преобразователь аргументов, добавить параметр Pageable в метод контроллера и отправить параметры разбиения на страницы, используя конкретные параметры запроса. Это намного проще, чем обработка параметров разбивки на страницы в нашем коде и создание объекта PageRequest вручную.

Cons

Недостатки использования веб-пагинации описаны ниже:

  • Веб-нумерация страниц создает зависимость между веб-слоем и Spring Data. Это означает, что детали реализации уровня хранилища просачиваются в верхние уровни нашего приложения. Хотя пуристы, вероятно, будут утверждать, что это огромная ошибка, я не разделяю их мнение. Я думаю, что абстракции должны сделать нашу жизнь проще, а не сложнее. Мы также должны помнить, что закон абстракций с утечками гласит, что все нетривиальные абстракции в некоторой степени являются утечками.
  • Одним из реальных недостатков веб-нумерации страниц является то, что мы можем использовать его, только если наши результаты поиска отсортированы по одному полю. Хотя это прекрасно для большинства случаев использования, бывают ситуации, когда это становится проблемой. Если это произойдет, мы должны обработать параметры нумерации страниц вручную.

Резюме

Теперь мы добавили нумерацию результатов поиска в наш пример приложения. Этот урок научил нас следующим вещам:

  • Мы научились создавать новые объекты PageRequest .
  • Мы узнали, что можем выбрать тип возврата нашего метода запроса из двух разных вариантов.
  • Мы научились разбивать на страницы результаты запросов методов запросов и динамических запросов.
  • Мы знаем, как мы можем использовать веб-нумерацию страниц, и мы знаем о ее плюсах и минусах.

Следующая часть моего учебника по Spring Data Solr описывает, как мы можем добавить пользовательские методы во все репозитории Spring Data Solr.

PS Примеры приложений этого блога доступны на Github ( методы запросов и динамические запросы ).