Введение в 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, поэтому его должно быть легко импортировать и запускать как есть.