Руководство по запросам в Spring Data MongoDB
1. обзор
В этой статье мы сосредоточимся на построении разныхtypes of queries in Spring Data MongoDB.
Мы рассмотрим запросы к документам с помощью классовQuery иCriteria, автоматически сгенерированные методы запросов, запросы JSON и QueryDSL.
Для настройки Maven взгляните на нашintroductory article.
2. Документы Запрос
Один из наиболее распространенных способов запроса MongoDB с помощью Spring Data - использование классовQuery иCriteria, которые очень точно отражают собственные операторы.
2.1. Isс
Это просто критерий равенства - давайте посмотрим, как он работает.
В следующем примере мы ищем пользователей с именемEric.
Давайте посмотрим на нашу базу данных:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 55
}
}
Теперь посмотрим на код запроса:
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Eric"));
List users = mongoTemplate.find(query, User.class);
Эта логика возвращает, как и ожидалось:
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
}
2.2. Regexс
Более гибкий и мощный тип запроса - это регулярное выражение. Этот c` ++ `повторяет критерий с использованием MongoDB$regex, который возвращает все записи, подходящие для этого регулярного выражения для этого поля.
Он работает аналогично операциямstartingWith, endingWith - давайте рассмотрим пример.
Теперь мы ищем всех пользователей, чьи имена начинаются сA.
Вот состояние базы данных:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.example.model.User",
"name" : "Alice",
"age" : 35
}
]
Теперь создадим запрос:
Query query = new Query();
query.addCriteria(Criteria.where("name").regex("^A"));
List users = mongoTemplate.find(query,User.class);
Это запускает и возвращает 2 записи:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.example.model.User",
"name" : "Alice",
"age" : 35
}
]
Вот еще один быстрый пример, на этот раз ищем всех пользователей, чьи имена заканчиваются наc:
Query query = new Query();
query.addCriteria(Criteria.where("name").regex("c$"));
List users = mongoTemplate.find(query, User.class);
Таким образом, результат будет:
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
}
2.3. Lt иgt
Эти операторы создают критерий, используя оператор$lt (меньше чем) и$gt (больше чем).
Давайте быстро рассмотрим пример - мы ищем всех пользователей в возрасте от 20 до 50 лет.
База данных:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 55
}
}
Этот код запроса:
Query query = new Query();
query.addCriteria(Criteria.where("age").lt(50).gt(20));
List users = mongoTemplate.find(query,User.class);
И в результате - все пользователи, которым от 20 и до 50 лет:
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
}
2.4. Sortс
Sort используется для указания порядка сортировки результатов.
В приведенном ниже примере возвращаются все пользователи, отсортированные по возрасту в порядке возрастания.
Во-первых, вот существующие данные:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.example.model.User",
"name" : "Alice",
"age" : 35
}
]
После выполненияsort:
Query query = new Query();
query.with(new Sort(Sort.Direction.ASC, "age"));
List users = mongoTemplate.find(query,User.class);
И вот результат запроса - красиво отсортированный поage:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.example.model.User",
"name" : "Alice",
"age" : 35
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
}
]
2.5. Pageableс
Давайте посмотрим на быстрый пример использования нумерации страниц.
Вот состояние базы данных:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.example.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.example.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.example.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 33
}
]
3. Сгенерированные методы запроса
Теперь давайте рассмотрим наиболее распространенный тип запросов, который обычно предоставляет Spring Data - автоматически сгенерированные запросы на основе имен методов.
Единственное, что нам нужно сделать, чтобы использовать такие запросы, - это объявить метод в интерфейсе репозитория:
public interface UserRepository
extends MongoRepository, QueryDslPredicateExecutor {
...
}
3.1. FindByXс
Мы начнем с простого, с изучения типа запроса findBy - в данном случае поиск по имени: __
List findByName(String name);
Как и в предыдущем разделе - 2.1 - запрос будет иметь те же результаты, найдя всех пользователей с заданным именем:
List users = userRepository.findByName("Eric");
3.2. StartingWith и endWith.
В 2.2 мы исследовали запрос на основеregex. Начало и конец, конечно, менее эффективны, но тем не менее весьма полезны - особенно если нам не нужно их реализовывать.
Вот краткий пример того, как будут выглядеть операции: __
List findByNameStartingWith(String regexp);
List findByNameEndingWith(String regexp);
Пример реального использования этого, конечно, будет очень простым:
List users = userRepository.findByNameStartingWith("A");
List users = userRepository.findByNameEndingWith("c");
И результаты точно такие же.
3.3. Betweenс
Как и в 2.3, это вернет всех пользователей с возрастом отageGT доageLT:.
List findByAgeBetween(int ageGT, int ageLT);
Вызов метода приведет к тому, что будут найдены точно такие же документы:
List users = userRepository.findByAgeBetween(20, 50);
3.4. Like иOrderBy
На этот раз давайте рассмотрим более сложный пример, сочетающий два типа модификаторов для сгенерированного запроса.
Мы будем искать всех пользователей, имена которых содержат буквуA, а также отсортировать результаты по возрасту в порядке возрастания:
List users = userRepository.findByNameLikeOrderByAgeAsc("A");
Для базы данных мы использовали в 2.4 - результат будет:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.example.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.example.model.User",
"name" : "Alice",
"age" : 35
}
]
4. Методы запросов JSON
Если мы не можем представить запрос с помощью имени метода или критериев, мы можем сделать что-то более низкое -use the @Query annotation.
С помощью этой аннотации мы можем указать необработанный запрос - как строку запроса Mongo JSON.
4.1. FindByс
Давайте начнем с простого и сначала посмотрим, как мы представимa find by type of method:
@Query("{ 'name' : ?0 }")
List findUsersByName(String name);
Этот метод должен возвращать пользователей по имени - заполнитель?0 ссылается на первый параметр метода.
List users = userRepository.findUsersByName("Eric");
4.2 $regex
Давайте также посмотрим наa regex driven query - который, конечно, дает тот же результат, что и в 2.2 и 3.2:
@Query("{ 'name' : { $regex: ?0 } }")
List findUsersByRegexpName(String regexp);
Использование также точно так же:
List users = userRepository.findUsersByRegexpName("^A");
List users = userRepository.findUsersByRegexpName("c$");
4.3. $lt и$gt
Теперь давайте реализуем запросы lt иgt:
@Query("{ 'age' : { $gt: ?0, $lt: ?1 } }")
List findUsersByAgeBetween(int ageGT, int ageLT);
Теперь, как теперь, когда метод имеет 2 параметра, мы ссылаемся на каждый из них по индексу в необработанном запросе:?0 и?1.
List users = userRepository.findUsersByAgeBetween(20, 50);
5. Запросы QueryDSL
MongoRepository имеет хорошую поддержку для проектаQueryDSL, поэтому здесь мы также можем использовать этот красивый, безопасный для типов API.
5.1. Зависимости Maven
Во-первых, давайте убедитесь, что у нас есть правильные зависимости Maven, определенные в pom:
com.mysema.querydsl
querydsl-mongodb
3.6.6
com.mysema.querydsl
querydsl-apt
3.6.6
5.2. Q-классов
QueryDSL использовал Q-классы для создания запросов. Но, поскольку мы не хотим создавать их вручную,we need to generate them каким-то образом.
Для этого мы воспользуемся плагином apt-maven-plugin:
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
}
После выполнения целиprocess жизненного цикла Maven (или любой другой цели после этой) - плагин aptwill generate the new classes подtarget/generated-sources/java/\{your package structure}:
/**
* 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 или иметь tools.jar в пути к классам. Если это происходит во время сборки eclipse, убедитесь, что вы также запускаете eclipse под JDK (com.mysema.maven: apt-maven-plugin: 1.1.3: process: default: generate-sources
Maveninstall отлично работает и создается классQUser, но плагин выделен в файле pom.
Быстрое исправление - вручную указать JDK вeclipse.ini:
...
-vm
{path_to_jdk}\jdk{your_version}\bin\javaw.exe
5.3. The New Repositoryс
Теперь нам нужно включить поддержку QueryDSL в наших репозиториях - это делается простоextending the QueryDslPredicateExecutor interface:
public interface UserRepository extends
MongoRepository, QuerydslPredicateExecutor
5.4. Eqс
При включенной поддержкеlet’s now implement the same queries, как показано ранее.
Начнем с простого равенства:
QUser qUser = new QUser("user");
Predicate predicate = qUser.name.eq("Eric");
List users = (List) userRepository.findAll(predicate);
5.5. StartingWith иEndingWith
Точно так же давайте реализуем предыдущие запросы и найдем пользователей с именами, начинающимися сA:
QUser qUser = new QUser("user");
Predicate predicate = qUser.name.startsWith("A");
List users = (List) userRepository.findAll(predicate);
И заканчиваяc:
QUser qUser = new QUser("user");
Predicate predicate = qUser.name.endsWith("c");
List users = (List) userRepository.findAll(predicate);
Результат такой же, как в 2.2, 3.2 или 4.2.
5.6. Betweenс
Следующий запрос вернет пользователей в возрасте от 20 до 50 лет - аналогично предыдущим разделам:
QUser qUser = new QUser("user");
Predicate predicate = qUser.age.between(20, 50);
List users = (List) userRepository.findAll(predicate);
6. Заключение
В этой статье мы рассмотрели множество способов запроса с помощью Spring Data MongoDB.
Интересно сделать шаг назад и увидеть, сколько у нас есть эффективных способов запроса MongoDB - от ограниченного контроля до полного контроля с помощью необработанных запросов.
Реализация всех этих примеров и фрагментов кодаcan be found inthe GitHub project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.