Перейти к основному содержимому

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

· 10 мин. чтения

1. Обзор

Эта статья будет кратким и практическим введением в Spring Data MongoDB.

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

2. MongoTemplate и MongoRepository

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

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

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

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.0.3.RELEASE</version>
</dependency>

Чтобы проверить, была ли выпущена какая-либо новая версия библиотеки, отслеживайте выпуски здесь .

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

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

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

<mongo:mongo-client id="mongoClient" host="localhost" />
<mongo:db-factory id="mongoDbFactory" dbname="test" mongo-client-ref="mongoClient" />

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

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

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> 
<constructor-arg ref="mongoDbFactory"/>
</bean>

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

<bean class=
"org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

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

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

@Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {

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

@Override
public MongoClient mongoClient() {
ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();

return MongoClients.create(mongoClientSettings);
}

@Override
public Collection getMappingBasePackages() {
return Collections.singleton("com.foreach");
}
}

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

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

@Configuration
public class SimpleMongoConfig {

@Bean
public MongoClient mongo() {
ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();

return MongoClients.create(mongoClientSettings);
}

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

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

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

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

<mongo:repositories 
base-package="com.foreach.repository" mongo-template-ref="mongoTemplate"/>

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

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

@EnableMongoRepositories(basePackages = "com.foreach.repository")

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

После настройки нам нужно создать репозиторий — расширение существующего интерфейса MongoRepository :

public interface UserRepository extends MongoRepository<User, String> {
//
}

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

5. Использование MongoTemplate

5.1. Вставлять

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

{
}

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

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

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

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

5.2. Сохранить – Вставить

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

Давайте посмотрим на первую семантику — вставку.

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

{
}

Когда мы теперь сохраняем нового пользователя:

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

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

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

Далее мы рассмотрим ту же операцию — сохранение — с семантикой обновления.

5.3. Сохранить — обновить

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

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

Когда мы сохраним существующего пользователя, мы обновим его:

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

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

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

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

5.4. ОбновитьFirst

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

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

[
{
"_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
"_class" : "com.foreach.model.User",
"name" : "Alex"
},
{
"_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
"_class" : "com.foreach.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" : "com.foreach.model.User",
"name" : "James"
},
{
"_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
"_class" : "com.foreach.model.User",
"name" : "Alex"
}
]

5.5. ОбновлениеМульти

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

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

[
{
"_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
"_class" : "com.foreach.model.User",
"name" : "ForEach"
},
{
"_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
"_class" : "com.foreach.model.User",
"name" : "ForEach"
}
]

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

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

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

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

5.6. найти и изменить

Эта операция работает как updateMulti , но возвращает объект до того, как он был изменен.

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

{
"_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
"_class" : "com.foreach.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);

Возвращенный пользовательский объект имеет те же значения, что и начальное состояние в базе данных.

Однако это новое состояние в базе данных:

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

5.7. Upsert

Upsert работает с семантикой поиска и изменения, иначе создайте : если документ соответствует, обновите его или создайте новый документ, объединив объект запроса и обновления.

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

{
"_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
"_class" : "com.foreach.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" : "com.foreach.model.User",
"name" : "Nick"
}

5.8. Удалять

Мы посмотрим на состояние базы данных перед вызовом remove :

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

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

mongoTemplate.remove(user, "user");

Результат будет ожидаемым:

{
}

6. Использование MongoRepository

6.1. Вставлять

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

{
}

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

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

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

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

Обратите внимание, что операция работает так же, как вставка в API MongoTemplate .

6.2. СохранитьВставить

Точно так же операция сохранения работает так же, как операция сохранения в API MongoTemplate .

Начнем с рассмотрения семантики операции вставки.

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

{
}

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

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

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

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

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

6.3. Сохранитьобновить

Давайте теперь посмотрим на ту же операцию, но с семантикой обновления.

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

{
"_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
"_class" : "com.foreach.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" : "com.foreach.model.User",
"name" : "Jim"
}

Обратите внимание еще раз, как сохранение работает с семантикой обновления , потому что мы используем существующий объект.

6.4. Удалить

Вот состояние базы данных перед вызовом delete :

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

Запустим удаление :

userRepository.delete(user);

И вот наш результат:

{
}

6.5. FindOne

Далее, это состояние базы данных при вызове findOne :

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

Давайте теперь выполним findOne :

userRepository.findOne(user.getId())

И результат вернет существующие данные:

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

6.6. Существуют

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

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

Теперь давайте запустим exists , который, конечно же, вернет true :

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

6.7. Найти все с сортировкой ``

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

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

Давайте теперь запустим findAll с Sort :

List<User> users = userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));

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

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

6.8. FindAll с Pageable _ ``

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

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

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

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

В полученном списке пользователей будет только один пользователь: ``

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

7. Аннотации

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

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

@Id
private String id;

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

Далее мы рассмотрим @Document :

@Document
public class User {
//
}

Эта аннотация просто помечает класс как объект предметной области , который необходимо сохранить в базе данных, а также позволяет нам выбрать имя используемой коллекции.

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

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

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