Введение в Spring Data MongoDB

Введение в Spring Data MongoDB

1. обзор

Эта статья будет быстрой и практичнойintroduction to Spring Data MongoDB.

Мы рассмотрим основы, используя какMongoTemplate, так иMongoRepository, используя практические примеры для иллюстрации каждой операции.

Дальнейшее чтение:

Геопространственная поддержка в MongoDB

Узнайте, как хранить, индексировать и искать геопространственные данные с MongoDB

Read more

Тестирование интеграции с Spring Boot с помощью встроенного MongoDB

Узнайте, как использовать встроенное решение MongoDB от Flapdoodle вместе со Spring Boot для бесперебойного выполнения интеграционных тестов MongoDB.

Read more

2. MongoTemplateand MongoRepository

The MongoTemplate следует стандартному шаблону в Spring и предоставляет готовый базовый API для базового механизма сохранения.

The repository следует подходу Spring, ориентированному на данные, и поставляется с более гибкими и сложными операциями API, основанными на хорошо известных шаблонах доступа во всех проектах Spring Data.

Для обоих нам нужно начать с определения зависимости - например, вpom.xml с Maven:


    org.springframework.data
    spring-data-mongodb
    2.1.9.RELEASE



    org.springframework.data
    spring-data-releasetrain
    Lovelace-SR9
    pom
    import

Чтобы проверить, выпущена ли какая-либо новая версия библиотеки -track the releases here.

3. Конфигурация дляMongoTemplate

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

Давайте начнем с простой конфигурации XML для шаблона Mongo:


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

Далее - нам нужно определить (и настроить) шаблонный компонент:


    

И, наконец, нам нужно определить постпроцессор для перевода любыхMongoExceptions, добавленных в аннотированные классы@Repository:

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

Теперь давайте создадим аналогичную конфигурацию с использованием конфигурации Java, расширив базовый класс для конфигурации MongoDBAbstractMongoConfiguration:

@Configuration
public class MongoConfig extends AbstractMongoConfiguration {

    @Override
    protected String getDatabaseName() {
        return "test";
    }

    @Override
    public MongoClient mongoClient() {
        return new MongoClient("127.0.0.1", 27017);
    }

    @Override
    protected String getMappingBasePackage() {
        return "org.example";
    }
}

Примечание. Нам не нужно было определять bean-компонентMongoTemplate в предыдущей конфигурации, поскольку он уже определен вAbstractMongoConfiguration.

Мы также можем использовать нашу конфигурацию с нуля без расширенияAbstractMongoConfiguration - следующим образом:

@Configuration
public class SimpleMongoConfig {

    @Bean
    public MongoClient mongo() {
        return new MongoClient("localhost");
    }

    @Bean
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), "test");
    }
}

4. Конфигурация дляMongoRepository

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

Чтобы использовать пользовательские репозитории (расширениеMongoRepository) - нам нужно продолжить настройку из раздела 3.1 и настроить репозитории:

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

Точно так же мы воспользуемся конфигурацией, которую мы уже создали в разделе 3.2, и добавим в смесь новую аннотацию:

@EnableMongoRepositories(basePackages = "org.example.repository")

4.3. Создать репозиторий

Теперь, после настройки, нам нужно создать репозиторий - расширяя существующий интерфейсMongoRepository:

public interface UserRepository extends MongoRepository {
    //
}

Теперь мы можем автоматически подключить этотUserRepository и использовать операции изMongoRepository или добавить пользовательские операции.

5. ИспользуяMongoTemplate

5.1. Insertс

Начнем с операции вставки; начнем также с пустой базы данных:

{
}

Теперь, если мы добавим нового пользователя:

User user = new User();
user.setName("Jon");
mongoTemplate.insert(user, "user");

База данных будет выглядеть так:

{
    "_id" : ObjectId("55b4fda5830b550a8c2ca25a"),
    "_class" : "org.example.model.User",
    "name" : "Jon"
}

5.2. Save – Insertс

Операцияsave имеет семантику сохранения или обновления: если идентификатор присутствует, она выполняет обновление, если нет - выполняет вставку.

Давайте посмотрим на первую семантику - вставку; вот начальное состояние базы данных:

{
}

Когда мы теперьsave новый пользователь:

User user = new User();
user.setName("Albert");
mongoTemplate.save(user, "user");

Объект будет вставлен в базу данных:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "org.example.model.User",
    "name" : "Albert"
}

Затем мы рассмотрим ту же операцию -save - с семантикой обновления.

5.3. Save – Updateс

Теперь посмотрим наsave с семантикой обновления, работающий с существующей сущностью:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "org.example.model.User",
    "name" : "Jack"
}

Теперь, когда мыsave существующий пользователь - мы обновим его:

user = mongoTemplate.findOne(
  Query.query(Criteria.where("name").is("Jack")), User.class);
user.setName("Jim");
mongoTemplate.save(user, "user");

База данных будет выглядеть так:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "org.example.model.User",
    "name" : "Jim"
}

Как видите, в этом конкретном примереsave использует семантикуupdate, потому что мы используем объект с заданным_id.

5.4. UpdateFirstс

updateFirst обновляет самый первый документ, соответствующий запросу.

Начнем с начального состояния базы данных:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "Alex"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "org.example.model.User",
        "name" : "Alex"
    }
]

Теперь, когда мы запускаемupdateFirst:

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Alex"));
Update update = new Update();
update.set("name", "James");
mongoTemplate.updateFirst(query, update, User.class);

Только первая запись будет обновлена:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "James"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "org.example.model.User",
        "name" : "Alex"
    }
]

5.5. UpdateMultiс

UpdateMultiupdates all document that matches the given query.

Во-первых, вот состояние базы данных до выполненияupdateMulti:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "Eugen"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "org.example.model.User",
        "name" : "Eugen"
    }
]

Теперь давайте запустим операциюupdateMulti:

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Eugen"));
Update update = new Update();
update.set("name", "Victor");
mongoTemplate.updateMulti(query, update, User.class);

Оба существующих объекта будут обновлены в базе данных:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "Victor"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "org.example.model.User",
        "name" : "Victor"
    }
]

5.6. FindAndModifyс

Эта операция работает какupdateMulti, но этоreturns the object before it was modified.

Сначала - состояние базы данных перед вызовомfindAndModify:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Markus"
}

Давайте посмотрим на реальный код операции:

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Markus"));
Update update = new Update();
update.set("name", "Nick");
User user = mongoTemplate.findAndModify(query, update, User.class);

Возвращенныйuser object имеет те же значения, что и исходное состояние в базе данных.

Тем не менее, новое состояние в базе данных:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Nick"
}

5.7. Upsertс

upsert работает сfind and modify else create semantics: если документ соответствует, обновите его, иначе создайте новый документ, комбинируя запрос и объект обновления.

Начнем с начального состояния базы данных:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Markus"
}

Теперь - давайте запустимupsert:

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Markus"));
Update update = new Update();
update.set("name", "Nick");
mongoTemplate.upsert(query, update, User.class);

Вот состояние базы данных после операции:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Nick"
}

5.8. Removeс

Состояние базы данных до вызоваremove:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Benn"
}

Теперь запустимremove:

mongoTemplate.remove(user, "user");

Результат будет таким, как ожидалось:

{
}

6. ИспользуяMongoRepository

6.1. Insertс

Во-первых - состояние базы данных перед запускомinsert:

{
}

Теперь, когда мы вставляем нового пользователя:

User user = new User();
user.setName("Jon");
userRepository.insert(user);

Вот конечное состояние базы данных:

{
    "_id" : ObjectId("55b4fda5830b550a8c2ca25a"),
    "_class" : "org.example.model.User",
    "name" : "Jon"
}

Обратите внимание, как эта операция работает так же, какinsert в APIMongoTemplate.

6.2. Save -Insert

Аналогично -save работает так же, как операцияsave в APIMongoTemplate.

Начнем с рассмотренияthe insert semantics операции; вот начальное состояние базы данных:

{
}

Теперь - выполняем операциюsave:

User user = new User();
user.setName("Aaron");
userRepository.save(user);

Это приводит к добавлению пользователя в базу данных:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "org.example.model.User",
    "name" : "Aaron"
}

Еще раз обратите внимание, как в этом примереsave работает с семантикойinsert, потому что мы вставляем новый объект.

6.3. Save -Update

Давайте теперь посмотрим на ту же операцию, но сupdate semantics.

Во-первых, вот состояние базы данных до запуска новогоsave:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "org.example.model.User",
    "name" : "Jack"81*6
}

Сейчас - мы выполняем операцию:

user = mongoTemplate.findOne(
  Query.query(Criteria.where("name").is("Jack")), User.class);
user.setName("Jim");
userRepository.save(user);

Наконец, вот состояние базы данных:

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "org.example.model.User",
    "name" : "Jim"
}

Еще раз обратите внимание, как в этом примереsave работает с семантикойupdate, потому что мы используем существующий объект.

6.4. Deleteс

Состояние базы данных до вызоваdelete:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Benn"
}

Запустимdelete:

userRepository.delete(user);

Результатом будет просто:

{
}

6.5. FindOneс

Состояние базы данных при вызовеfindOne:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Chris"
}

Теперь выполнимfindOne:

userRepository.findOne(user.getId())

Результат, который вернет существующие данные:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Chris"
}

6.6. Existsс

Состояние базы данных до вызоваexists:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Harris"
}

Теперь запустимexists:

boolean isExists = userRepository.exists(user.getId());

Что, конечно, вернетtrue.

6.7. FindAll #with[.pl-en] Sort#

Состояние базы данных до вызоваfindAll:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "Brendan"
    },
    {
       "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
       "_class" : "org.example.model.User",
       "name" : "Adam"
    }
]

Теперь запустимfindAll сSort:

List users = userRepository.findAll(new Sort(Sort.Direction.ASC, "name"));

Результатом будетsorted by name in ascending order:

[
    {
        "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "Adam"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "Brendan"
    }
]

6.8. FindAll #with[.pl-en] Pageable #

Состояние базы данных до вызоваfindAll:

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "Brendan"
    },
    {
        "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
        "_class" : "org.example.model.User",
        "name" : "Adam"
    }
]

Теперь выполнимfindAll с запросом разбивки на страницы:

Pageable pageableRequest = PageRequest.of(0, 1);
Page page = userRepository.findAll(pageableRequest);
List users = pages.getContent();

В результате в спискеusers будет только один пользователь: __

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "org.example.model.User",
    "name" : "Brendan"
}

7. Аннотации

Наконец, давайте также рассмотрим простые аннотации, которые Spring Data использует для управления этими операциями API.

@Id
private String id;

Аннотация уровня поля@Id может украшать любой тип, включаяlong иstring.

Если значение поля@Id не равно нулю, оно сохраняется в базе данных как есть; в противном случае конвертер предположит, что вы хотите сохранитьObjectId в базе данных (работает либоObjectId, String, либоBigInteger).

Далее -@Document:

@Document
public class User {
    //
}

Эта аннотация простоmarks a class as being a domain object, которая должна быть сохранена в базе данных, а также позволяет нам выбрать имя коллекции, которая будет использоваться.

8. Заключение

Эта статья была быстрым, но исчерпывающим введением в использование MongoDB с данными Spring, как через APIMongoTemplate, так и с использованиемMongoRepository.

Реализация всех этих примеров и фрагментов кодаcan be foundover on Github - это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.

Related