1. Обзор
Эта статья будет посвящена построению различных типов запросов в Spring Data MongoDB .
Мы будем рассматривать запросы документов с помощью классов Query и Criteries , автоматически генерируемых методов запросов, запросов JSON и QueryDSL.
Для настройки Maven ознакомьтесь с нашей вступительной статьей .
2. Запрос документов
Одним из наиболее распространенных способов запроса MongoDB с данными Spring является использование классов Query и Criteries , которые очень точно отражают собственные операторы.
2.1. Является
Это просто критерий равенства – давайте посмотрим, как он работает.
В следующем примере – мы ищем пользователей с именем Eric .
Давайте посмотрим на нашу базу данных:
[ { "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }, { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 55 } }
Теперь давайте посмотрим на код запроса:
Query query = new Query(); query.addCriteria(Criteria.where("name").is("Eric")); Listusers = mongoTemplate.find(query, User.class);
Эта логика возвращается, как и ожидалось:
{ "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }
2.2. Регулярное выражение
Более гибким и мощным типом запроса является регулярное выражение. Этот c создает критерий с использованием MongoDB $regex , который возвращает все записи, подходящие для этого регулярного выражения для этого поля.
Он работает аналогично startingWith, endingWith operations – давайте рассмотрим пример.
Теперь мы ищем всех пользователей, имена которых начинаются с A .
Вот состояние базы данных:
[ { "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }, { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 33 }, { "_id" : ObjectId("55c0e5e5511f0a164a581909"), "_class" : "org.baeldung.model.User", "name" : "Alice", "age" : 35 } ]
Теперь давайте создадим запрос:
Query query = new Query(); query.addCriteria(Criteria.where("name").regex("^A")); Listusers = mongoTemplate.find(query,User.class);
Это выполняется и возвращает 2 записи:
[ { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 33 }, { "_id" : ObjectId("55c0e5e5511f0a164a581909"), "_class" : "org.baeldung.model.User", "name" : "Alice", "age" : 35 } ]
Вот еще один быстрый пример, на этот раз ищем всех пользователей, имена которых заканчиваются на c :
Query query = new Query(); query.addCriteria(Criteria.where("name").regex("c$")); Listusers = mongoTemplate.find(query, User.class);
Таким образом, результат будет:
{ "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }
2.3. Lt и gt
Эти операторы создают критерий, используя оператор $lt (меньше) и $gt (больше).
Давайте быстро рассмотрим пример – мы ищем всех пользователей в возрасте от 20 до 50 лет.
База данных является:
[ { "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }, { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 55 } }
Этот код запроса:
Query query = new Query(); query.addCriteria(Criteria.where("age").lt(50).gt(20)); Listusers = mongoTemplate.find(query,User.class);
И результат – все пользователи, которые с возрастом более 20 и менее 50 лет:
{ "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }
2.4. Сортировка
Сортировка используется для указания порядка сортировки результатов.
Приведенный ниже пример возвращает всех пользователей, отсортированных по возрасту в порядке возрастания.
Во – первых, вот существующие данные:
[ { "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }, { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 33 }, { "_id" : ObjectId("55c0e5e5511f0a164a581909"), "_class" : "org.baeldung.model.User", "name" : "Alice", "age" : 35 } ]
После выполнения сортировка :
Query query = new Query(); query.with(Sort.by(Sort.Direction.ASC, "age")); Listusers = mongoTemplate.find(query,User.class);
И вот результат запроса – красиво отсортирован по возрасту :
[ { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 33 }, { "_id" : ObjectId("55c0e5e5511f0a164a581909"), "_class" : "org.baeldung.model.User", "name" : "Alice", "age" : 35 }, { "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 } ]
2.5. Возможность просмотра страниц
Давайте рассмотрим краткий пример использования разбиения на страницы.
Вот состояние базы данных:
[ { "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }, { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 33 }, { "_id" : ObjectId("55c0e5e5511f0a164a581909"), "_class" : "org.baeldung.model.User", "name" : "Alice", "age" : 35 } ]
Теперь логика запроса, просто запрашивающая страницу размером 2:
final Pageable pageableRequest = PageRequest.of(0, 2); Query query = new Query(); query.with(pageableRequest);
И результат – 2 документа, как и ожидалось:
[ { "_id" : ObjectId("55c0e5e5511f0a164a581907"), "_class" : "org.baeldung.model.User", "name" : "Eric", "age" : 45 }, { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 33 } ]
3. Сгенерированные методы запроса
Теперь давайте рассмотрим более распространенный тип запроса, который обычно предоставляет Spring Data, – автоматически генерируемые запросы из имен методов.
Единственное, что нам нужно сделать, чтобы использовать эти типы запросов, – это объявить метод в интерфейсе репозитория:
public interface UserRepository extends MongoRepository, QueryDslPredicateExecutor { ... }
3.1. FindByX
Мы начнем с простого, изучив тип запроса findBy – в данном случае поиск по имени:
ListfindByName(String name);
Как и в предыдущем разделе – 2.1 – запрос будет иметь те же результаты, найдя всех пользователей с заданным именем:
Listusers = userRepository.findByName("Eric");
3.2. Начиная с и заканчивая.
В 2.2 мы исследовали запрос на основе регулярных выражений|/. Начало и конец, конечно, менее мощные, но, тем не менее, весьма полезные – особенно если нам не нужно их фактически реализовывать.
Вот краткий пример того, как будут выглядеть операции:
ListfindByNameStartingWith(String regexp);
ListfindByNameEndingWith(String regexp);
Пример фактического использования этого, конечно, был бы очень простым:
Listusers = userRepository.findByNameStartingWith("A");
Listusers = userRepository.findByNameEndingWith("c");
И результаты точно такие же.
3.3. Между
Аналогично 2.3, это вернет всех пользователей с возрастом между agent и agent:
ListfindByAgeBetween(int ageGT, int ageLT);
Вызов метода приведет к тому, что будут найдены точно такие же документы:
Listusers = userRepository.findByAgeBetween(20, 50);
3.4. Лайк и Заказ По
На этот раз давайте рассмотрим более продвинутый пример – объединение двух типов модификаторов для сгенерированного запроса.
Мы будем искать всех пользователей, имена которых содержат букву A , а также будем упорядочивать результаты по возрасту в порядке возрастания:
Listusers = userRepository.findByNameLikeOrderByAgeAsc("A");
Для базы данных, которую мы использовали в 2.4 – результат будет:
[ { "_id" : ObjectId("55c0e5e5511f0a164a581908"), "_class" : "org.baeldung.model.User", "name" : "Antony", "age" : 33 }, { "_id" : ObjectId("55c0e5e5511f0a164a581909"), "_class" : "org.baeldung.model.User", "name" : "Alice", "age" : 35 } ]
4. Методы запроса JSON
Если мы не можем представить запрос с помощью имени метода или критериев, мы можем сделать что – то более низкого уровня – использовать @Query аннотацию .
С помощью этой аннотации мы можем указать необработанный запрос – в виде строки запроса Mongo JSON.
4.1. Найти
Давайте начнем с простого и посмотрим, как мы будем представлять a find по типу метода first:
@Query("{ 'name' : ?0 }") ListfindUsersByName(String name);
Этот метод должен возвращать пользователей по имени – заполнитель ?0 ссылается на первый параметр метода.
Listusers = userRepository.findUsersByName("Eric");
4.2 $регулярное выражение
Давайте также рассмотрим запрос, управляемый регулярным выражением , который, конечно, дает тот же результат, что и в 2.2 и 3.2:
@Query("{ 'name' : { $regex: ?0 } }") ListfindUsersByRegexpName(String regexp);
Использование также точно такое же:
Listusers = userRepository.findUsersByRegexpName("^A");
Listusers = userRepository.findUsersByRegexpName("c$");
4.3. $lt и $gt
Давайте теперь реализуем запрос lt и gt :
@Query("{ 'age' : { $gt: ?0, $lt: ?1 } }") ListfindUsersByAgeBetween(int ageGT, int ageLT);
Теперь, когда метод имеет 2 параметра, мы ссылаемся на каждый из них по индексу в необработанном запросе: ?0 и ?1 .
Listusers = userRepository.findUsersByAgeBetween(20, 50);
5. Запросы QueryDSL
MongoRepository имеет хорошую поддержку для проекта QueryDSL , поэтому мы также можем использовать этот хороший, типобезопасный API здесь.
5.1. Зависимости Maven
Во-первых, давайте убедимся, что у нас есть правильные зависимости Maven, определенные в pom:
com.mysema.querydsl querydsl-mongodb 4.3.1 com.mysema.querydsl querydsl-apt 4.3.1
5.2. Q-классы
QueryDSL использовал Q-классы для создания запросов. Но, поскольку мы на самом деле не хотим создавать их вручную, нам нужно как-то их сгенерировать .
Для этого мы собираемся использовать плагин apt-maven:
com.mysema.maven apt-maven-plugin 1.1.3 process target/generated-sources/java org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor
Давайте рассмотрим класс User , сосредоточившись конкретно на аннотации @QueryEntity :
@QueryEntity @Document public class User { @Id private String id; private String name; private Integer age; // standard getters and setters }
После запуска процесса цели жизненного цикла Maven (или какой-либо другой цели после этого) – плагин apt будет генерировать новые классы под целью/сгенерированными источниками/java/{структура вашего пакета} :
/** * QUser is a Querydsl query type for User */ @Generated("com.mysema.query.codegen.EntitySerializer") public class QUser extends EntityPathBase{ private static final long serialVersionUID = ...; public static final QUser user = new QUser("user"); public final NumberPath age = createNumber("age", Integer.class); public final StringPath id = createString("id"); public final StringPath name = createString("name"); public QUser(String variable) { super(User.class, forVariable(variable)); } public QUser(Path extends User> path) { super(path.getType(), path.getMetadata()); } public QUser(PathMetadata> metadata) { super(User.class, metadata); } }
Именно с помощью этого класса мы не будем создавать ваши запросы.
В качестве примечания – если вы используете Eclipse, введение этого плагина создаст следующее предупреждение в pom:
Вам нужно запустить сборку с помощью инструментов JDK или java.jar на пути к классу. Если это происходит во время сборки eclipse, убедитесь, что вы также запускаете eclipse под JDK (com.mysema.maven:apt-maven-plugin:1.1.3:process:default:generate-sources
Maven install работает нормально, и User класс генерируется, но плагин выделяется в pom.
Быстрое решение состоит в том, чтобы вручную указать на JDK в eclipse.ini :
... -vm {path_to_jdk}\jdk{your_version}\bin\javaw.exe
5.3. Новый Репозиторий
Теперь нам нужно фактически включить поддержку QueryDSL в наших репозиториях, что делается простым расширением интерфейса |/QueryDslPredicateExecutor :
public interface UserRepository extends MongoRepository, QuerydslPredicateExecutor
5.4. Эквалайзер
При включенной поддержке давайте теперь реализуем те же запросы , что и те, которые мы проиллюстрировали ранее.
Мы начнем с простого равенства:
QUser qUser = new QUser("user"); Predicate predicate = qUser.name.eq("Eric"); Listusers = (List ) userRepository.findAll(predicate);
5.5. Начиная с и заканчивая
Аналогично, давайте реализуем предыдущие запросы – и найдем пользователей с именами, начинающимися с A :
QUser qUser = new QUser("user"); Predicate predicate = qUser.name.startsWith("A"); Listusers = (List ) userRepository.findAll(predicate);
И заканчивая c :
QUser qUser = new QUser("user"); Predicate predicate = qUser.name.endsWith("c"); Listusers = (List ) userRepository.findAll(predicate);
Результат такой же, как в 2.2, 3.2 или 4.2.
5.6. Между
Следующий запрос вернет пользователей в возрасте от 20 до 50 лет – аналогично предыдущим разделам:
QUser qUser = new QUser("user"); Predicate predicate = qUser.age.between(20, 50); Listusers = (List ) userRepository.findAll(predicate);
6. Заключение
В этой статье мы рассмотрели множество способов запроса с помощью Spring Data MongoDB.
Интересно сделать шаг назад и посмотреть, сколько мощных способов у нас есть для запроса MongoDB – от ограниченного контроля до полного контроля с необработанными запросами.
Реализацию всех этих примеров и фрагментов кода можно найти в проекте GitHub .