Рубрики
Без рубрики

Руководство по запросам в Spring Data MongoDB

Как запросить MongoDB с данными Spring: Запрос и критерии, автоматически сгенерированные методы репозитория, необработанные запросы с аннотацией @Query, а также QueryDSL.

Автор оригинала: baeldung.

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"));
List users = 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"));
List users = 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$"));
List users = 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));
List users = 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"));
List users = 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 – в данном случае поиск по имени:

List findByName(String name);

Как и в предыдущем разделе – 2.1 – запрос будет иметь те же результаты, найдя всех пользователей с заданным именем:

List users = userRepository.findByName("Eric");

3.2. Начиная с и заканчивая.

В 2.2 мы исследовали запрос на основе регулярных выражений|/. Начало и конец, конечно, менее мощные, но, тем не менее, весьма полезные – особенно если нам не нужно их фактически реализовывать.

Вот краткий пример того, как будут выглядеть операции:

List findByNameStartingWith(String regexp);
List findByNameEndingWith(String regexp);

Пример фактического использования этого, конечно, был бы очень простым:

List users = userRepository.findByNameStartingWith("A");
List users = userRepository.findByNameEndingWith("c");

И результаты точно такие же.

3.3. Между

Аналогично 2.3, это вернет всех пользователей с возрастом между agent и agent:

List findByAgeBetween(int ageGT, int ageLT);

Вызов метода приведет к тому, что будут найдены точно такие же документы:

List users = userRepository.findByAgeBetween(20, 50);

3.4. Лайк и Заказ По

На этот раз давайте рассмотрим более продвинутый пример – объединение двух типов модификаторов для сгенерированного запроса.

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

List users = 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 }")
List findUsersByName(String name);

Этот метод должен возвращать пользователей по имени – заполнитель ?0 ссылается на первый параметр метода.

List users = userRepository.findUsersByName("Eric");

4.2 $регулярное выражение

Давайте также рассмотрим запрос, управляемый регулярным выражением , который, конечно, дает тот же результат, что и в 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
    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 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");
List users = (List) userRepository.findAll(predicate);

5.5. Начиная с и заканчивая

Аналогично, давайте реализуем предыдущие запросы – и найдем пользователей с именами, начинающимися с 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. Между

Следующий запрос вернет пользователей в возрасте от 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 – от ограниченного контроля до полного контроля с необработанными запросами.

Реализацию всех этих примеров и фрагментов кода можно найти в проекте GitHub .