Обработка транзакций MongoDB 4.0 с данными Spring
Оригинальная ссылка:весна.IO/блог/2018/0…
автор:christophstrobl
Переводчик:hh23485
существуетMongoDB 4.0, транзакции ACID использовались дляDocument
хранилище, обеспечивает поддержание состояния согласованности данных по принципу «все или ничего». Итак, давайте проверим эту функцию непосредственно в синхронной модели и модели реактивного выполнения.
На момент написания этой статьи многодокументные транзакции MongoDB поддерживаются в наборе с одной репликой и выглядят так, как будто они используют транзакцию реляционной базы данных. Увидев API, предоставляемый водителем, сразу чувствуешь себя как дома.
try (ClientSession session = client.startSession()) {
session.startTransaction();
try {
collection.insertOne(session, documentOne);
collection.insertOne(session, documentTwo);
session.commitTransaction();
} catch (Exception e) {
session.abortTransaction();
}
}
Логические сеансы строятся поверх MongoDB, а транзакции, конечно же, и транзакции создают основу.
Логические сеансы предоставляют MongoDBпричинно-следственная связьи основа бизнеса. клиент изclient.startSession()
Сеанс не должен быть слишком длинным, и его следует немедленно закрыть, когда он больше не используется. Поэтому обязательно используйтеclose()
чтобы закрыть сеанс клиента.
На низкоуровневом уровне протокола приведенный выше фрагмент кода будет преобразован в следующую серию команд, вы можете ясно увидеть, что каждая команда содержит сеанс (lsid
).startTransaction
Флаги будут отправлены с первой командой, чтобы указать на начало транзакции. После завершения транзакции отправьтеcommitTransaction
Представляет фиксацию транзакции.
{ insert: "col", ordered: true, $db: "db",
$clusterTime: { … },
lsid: { id: { $binary: { base64 : "I3M7Nj…", … } } },
txnNumber: 1,
startTransaction: true,
documents: [ { … } ] }
{ insert: "col", ordered: true, $db: "db",
$clusterTime: { … },
lsid: { id: { $binary: { base64 : "I3M7Nj…", … } } },
txnNumber: 1,
autocommit: false,
documents: [ { …} ] }
{ commitTransaction: 1,
$db: "admin",
$clusterTime: { … },
lsid: { id: { $binary: { base64 : "I3M7Nj…", … } } },
txnNumber: 1 }
С предстоящим выпуском Spring DataLovelaceверсии модуль MongoDB будет обеспечивать поддержку синхронных и реактивных транзакций.
Мы начнем с синхронного режима, и вы, вероятно, уже хорошо знакомы с поддержкой транзакций в Spring Framework (Поддержка транзакций Spring Framework). Следовательно,MongoTransactionManager
существование не удивительно. Менеджер транзакций — это точка входа для поддержки транзакций на основе аннотаций в императивном мире.
Теперь, поскольку MongoDB не поддерживает транзакции в более ранних версиях, вы должны явноApplicationContext
зарегистрирован вMongoTransactionManager
. Если вы сделаете это,MongoTemplate
начнет участвовать в делах управления. Это момент, который вы должны иметь в виду. В следующем примере показано, как следует настроить диспетчер транзакций.
@Configuration
class Config extends AbstractMongoConfiguration {
@Bean
MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
}
@Service
class DocumentService {
private final MongoOperations operations;
DocumentService(MongoOperations operations) {
this.operations = operations;
}
@Transactional
void insertDocuments() {
operations.insert(documentOne);
operations.insert(documentTwo);
}
}
Очень живая операция, правда? Тем не менее, здесь есть некоторые неявные недостатки.кластерПоддержка транзакций в контексте не будет поддерживаться до следующего основного выпуска MongDB, поэтому при ее использовании возникнут ошибки. Кроме того, как пользователь MongoDB, вы можете привыкнуть ко всем удобствам, которые она предоставляет, но некоторые функции недоступны в транзакциях, включая почти все метакоманды, создание коллекций, индексов и неявно при использовании коллекций. Чтобы избежать ошибок и возни, обязательно настройте нужную структуру. Кроме того, некоторые команды могут немного отличаться. Например, используя статистику сбора коллекцииcount
Команды могут быть неточными в транзакции. Команда завершится ошибкой и потребует использования агрегированных документов подсчета, текущий драйвер уже предоставляет альтернативуcountDocuments
для решения этой проблемы с помощью стратегии агрегации.
Имея это в виду, давайте перейдем к реактивной части использования.
существуетДрайвер ReactiveStreams для MongoDBОбеспечивает реактивный pointcut для транзакций с несколькими документами. Трубка родного драйвераPublisher
превратиться вReactorТипы позволяют вам выразить транзакционное использование следующим образом:
Mono.from(client.startSession()).flatMap(session -> {
session.startTransaction();
return Mono.from(collection.insertOne(session, documentOne))
.then(Mono.from(collection.insertOne(session, documentTwo)))
.onErrorResume(e -> Mono.from(session.abortTransaction())
.then(Mono.error(e)))
.flatMap(val -> Mono.from(session.commitTransaction())
.then(Mono.just(val)))
.doFinally(signal -> session.close());
});
Вне зависимости от того, успешен ли результат транзакции или выполнен откат, нам необходимо гарантировать завершение транзакции. следовательно,onErrorResume(...)
Гарантирует, что транзакцию можно будет откатить в случае сбоя, а затемflatMap(...)
В коммитах оба этапа сохраняют результаты или ошибки основного потока.
В отличие от раздела синхронизации, на момент написания для реактивных моделей нет менеджера транзакций, позволяющего передавать аннотации.@Transactional
Это так просто, чтобы завершить деловую работу.
Вместо этого вам нужно пройтиReactiveMongoTemplate.inTransaction(...)
Получить закрытие сделки. Он отвечает за все необходимые операции сеанса, фиксации и завершения, сохраняя при этом основной поток результатов. Операции в методе обратного вызова выполняются внутри транзакции MongoDB, и шаги внешней обработки не повлияют на транзакцию. Это означает, что ошибка обработки вне замыкания не приведет к завершению транзакции, как описано в следующем примере.
template.inTransaction().execute(action ->
// All code in here runs inside the transaction
action.insert(documentOne).then(action.insert(documentTwo)
).flatMap(val -> {
// An exception here does not affect the transaction
});
В этом примере вы можете получить доступ кClientSession
, который хранится в ReactorContext
, и вы можете пройтиReactiveMongoContext.getSession()
чтобы получить это.
И последнее: мы очень рады, что вы попробуете его и оставите нам отзыв, так что зацените его.Spring Data Examples, где можно найти соответствующиепроект.
Если вы хотите узнать больше о Spring Data или об экосистеме Spring в целом, предстоящая конференция в ВашингтонеSpringOne PlatformВстреча - очень хорошая возможность для вас. Проверятьбеседаи зарегистрируйтесь.