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 .