diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java index 1f97bb69e..3f826d9ff 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java @@ -36,16 +36,18 @@ import com.mongodb.client.ClientSession; /** * A {@link org.springframework.transaction.PlatformTransactionManager} implementation that manages - * {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}.
- * Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread.
+ * {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}. + *

+ * Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread. * {@link TransactionDefinition#isReadOnly() Readonly} transactions operate on a {@link ClientSession} and enable causal * consistency, and also {@link ClientSession#startTransaction() start}, {@link ClientSession#commitTransaction() - * commit} or {@link ClientSession#abortTransaction() abort} a transaction.
+ * commit} or {@link ClientSession#abortTransaction() abort} a transaction. + *

* Application code is required to retrieve the {@link com.mongodb.client.MongoDatabase} via * {@link MongoDatabaseUtils#getDatabase(MongoDatabaseFactory)} instead of a standard * {@link MongoDatabaseFactory#getMongoDatabase()} call. Spring classes such as - * {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly.
- * By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. One may override + * {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly. By default, failure of a + * {@literal commit} operation raises a {@link TransactionSystemException}. One may override * {@link #doCommit(MongoTransactionObject)} to implement the * Retry Commit Operation * behavior as outlined in the MongoDB reference manual. @@ -205,7 +207,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager * By default those labels are ignored, nevertheless one might check for * {@link MongoException#UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL transient commit errors labels} and retry the the * commit.
- * + * *

 	 * 
 	 * int retries = 3;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java
index 4f293c8ed..d1be22a77 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java
@@ -37,20 +37,21 @@ import com.mongodb.reactivestreams.client.ClientSession;
 /**
  * A {@link org.springframework.transaction.ReactiveTransactionManager} implementation that manages
  * {@link com.mongodb.reactivestreams.client.ClientSession} based transactions for a single
- * {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory}. 
+ * {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory}. + *

* Binds a {@link ClientSession} from the specified * {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory} to the subscriber - * {@link reactor.util.context.Context}.
- * {@link org.springframework.transaction.TransactionDefinition#isReadOnly() Readonly} transactions operate on a - * {@link ClientSession} and enable causal consistency, and also {@link ClientSession#startTransaction() start}, + * {@link reactor.util.context.Context}. {@link org.springframework.transaction.TransactionDefinition#isReadOnly() + * Readonly} transactions operate on a {@link ClientSession} and enable causal consistency, and also + * {@link ClientSession#startTransaction() start}, * {@link com.mongodb.reactivestreams.client.ClientSession#commitTransaction() commit} or - * {@link ClientSession#abortTransaction() abort} a transaction.
+ * {@link ClientSession#abortTransaction() abort} a transaction. + *

* Application code is required to retrieve the {@link com.mongodb.reactivestreams.client.MongoDatabase} via * {@link org.springframework.data.mongodb.ReactiveMongoDatabaseUtils#getDatabase(ReactiveMongoDatabaseFactory)} instead * of a standard {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getMongoDatabase()} call. Spring - * classes such as {@link org.springframework.data.mongodb.core.ReactiveMongoTemplate} use this strategy implicitly. - *
- * By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. You can override + * classes such as {@link org.springframework.data.mongodb.core.ReactiveMongoTemplate} use this strategy implicitly. By + * default, failure of a {@literal commit} operation raises a {@link TransactionSystemException}. You can override * {@link #doCommit(TransactionSynchronizationManager, ReactiveMongoTransactionObject)} to implement the * Retry Commit Operation * behavior as outlined in the MongoDB reference manual. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 03c0bb768..a49cd4ae9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -48,7 +48,6 @@ import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.convert.EntityReader; import org.springframework.data.domain.OffsetScrollPosition; -import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Window; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResult; @@ -160,6 +159,9 @@ import com.mongodb.client.result.UpdateResult; *

* You can also set the default {@link #setReadPreference(ReadPreference) ReadPreference} on the template level to * generally apply a {@link ReadPreference}. + *

+ * When using transactions make sure to create this template with the same {@link MongoDatabaseFactory} that is also + * used for {@code MongoTransactionManager} creation. * * @author Thomas Risberg * @author Graeme Rocher @@ -223,6 +225,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, /** * Constructor used for a basic template configuration. + *

+ * If you intend to use transactions, make sure to use {@link #MongoTemplate(MongoDatabaseFactory)} or + * {@link #MongoTemplate(MongoDatabaseFactory, MongoConverter)} constructors, otherwise, this template will not + * participate in transactions using the default {@code SessionSynchronization.ON_ACTUAL_TRANSACTION} setting as + * {@code MongoTransactionManager} uses strictly its configured {@link MongoDatabaseFactory} for transaction + * participation. * * @param mongoClient must not be {@literal null}. * @param databaseName must not be {@literal null} or empty. @@ -641,7 +649,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, /** * Define if {@link MongoTemplate} should participate in transactions. Default is set to - * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.
+ * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}. + *

* NOTE: MongoDB transactions require at least MongoDB 4.0. * * @since 2.1 diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index 1e24b360f..e1ad78893 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core; -import static org.springframework.data.mongodb.core.query.SerializationUtils.serializeToJsonSafely; +import static org.springframework.data.mongodb.core.query.SerializationUtils.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -110,18 +110,7 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; -import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent; -import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent; -import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent; -import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeDeleteEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; -import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent; -import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterConvertCallback; -import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterSaveCallback; -import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeConvertCallback; -import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeSaveCallback; +import org.springframework.data.mongodb.core.mapping.event.*; import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Collation; @@ -189,6 +178,9 @@ import com.mongodb.reactivestreams.client.MongoDatabase; *

* You can also set the default {@link #setReadPreference(ReadPreference) ReadPreference} on the template level to * generally apply a {@link ReadPreference}. + *

+ * When using transactions make sure to create this template with the same {@link ReactiveMongoDatabaseFactory} that is + * also used for {@code ReactiveMongoTransactionManager} creation. * * @author Mark Paluch * @author Christoph Strobl @@ -231,6 +223,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati /** * Constructor used for a basic template configuration. + *

+ * If you intend to use transactions, make sure to use {@link #ReactiveMongoTemplate(ReactiveMongoDatabaseFactory)} or + * {@link #ReactiveMongoTemplate(ReactiveMongoDatabaseFactory, MongoConverter)} constructors, otherwise, this template + * will not participate in transactions using the default {@code SessionSynchronization.ON_ACTUAL_TRANSACTION} setting + * as {@code ReactiveMongoTransactionManager} uses strictly its configured {@link ReactiveMongoDatabaseFactory} for + * transaction participation. * * @param mongoClient must not be {@literal null}. * @param databaseName must not be {@literal null} or empty. @@ -573,7 +571,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati /** * Define if {@link ReactiveMongoTemplate} should participate in transactions. Default is set to - * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.
+ * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}. + *

* NOTE: MongoDB transactions require at least MongoDB 4.0. * * @since 2.2 diff --git a/src/main/antora/modules/ROOT/pages/mongodb/client-session-transactions.adoc b/src/main/antora/modules/ROOT/pages/mongodb/client-session-transactions.adoc index f825690d7..910f2cace 100644 --- a/src/main/antora/modules/ROOT/pages/mongodb/client-session-transactions.adoc +++ b/src/main/antora/modules/ROOT/pages/mongodb/client-session-transactions.adoc @@ -294,6 +294,11 @@ static class Config extends AbstractMongoClientConfiguration { return new MongoTransactionManager(dbFactory); } + @Bean + MongoTemplate mongoTemplate(MongoDatabaseFactory dbFactory) { <1> + return new MongoTemplate(dbFactory); + } + // ... } @@ -314,6 +319,7 @@ public class StateService { ---- <1> Register `MongoTransactionManager` in the application context. +Also, make sure to use the same `MongoDatabaseFactory` when creating `MongoTemplate` to participate in transactions in the scope of the same `MongoDatabaseFactory`. <2> Mark methods as transactional. NOTE: `@Transactional(readOnly = true)` advises `MongoTransactionManager` to also start a transaction that adds the @@ -333,6 +339,11 @@ public class Config extends AbstractReactiveMongoConfiguration { return new ReactiveMongoTransactionManager(factory); } + @Bean + ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoDatabaseFactory dbFactory) { <1> + return new ReactiveMongoTemplate(dbFactory); + } + // ... } @@ -351,6 +362,7 @@ public class StateService { ---- <1> Register `ReactiveMongoTransactionManager` in the application context. +Also, make sure to use the same `ReactiveMongoDatabaseFactory` when creating `ReactiveMongoTemplate` to participate in transactions in the scope of the same `ReactiveMongoDatabaseFactory`. <2> Mark methods as transactional. NOTE: `@Transactional(readOnly = true)` advises `ReactiveMongoTransactionManager` to also start a transaction that adds the `ClientSession` to outgoing requests. @@ -418,20 +430,20 @@ Please refer to https://docs.mongodb.com/manual/reference/connection-string/#con MongoDB does *not* support collection operations, such as collection creation, within a transaction. This also affects the on the fly collection creation that happens on first usage. -Therefore make sure to have all required structures in place. +Therefore, make sure to have all required structures in place. *Transient Errors* MongoDB can add special labels to errors raised during transactional operations. Those may indicate transient failures that might vanish by merely retrying the operation. We highly recommend https://github.com/spring-projects/spring-retry[Spring Retry] for those purposes. -Nevertheless one may override `MongoTransactionManager#doCommit(MongoTransactionObject)` to implement a https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation[Retry Commit Operation] +Nevertheless, one may override `MongoTransactionManager#doCommit(MongoTransactionObject)` to implement a https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation[Retry Commit Operation] behavior as outlined in the MongoDB reference manual. *Count* MongoDB `count` operates upon collection statistics which may not reflect the actual situation within a transaction. -The server responds with _error 50851_ when issuing a `count` command inside of a multi-document transaction. +The server responds with _error 50851_ when issuing a `count` command inside a multi-document transaction. Once `MongoTemplate` detects an active transaction, all exposed `count()` methods are converted and delegated to the aggregation framework using `$match` and `$count` operators, preserving `Query` settings, such as `collation`. Restrictions apply when using geo commands inside of the aggregation count helper. @@ -453,9 +465,9 @@ The following snippet shows `count` usage inside the session-bound closure: session.startTransaction(); template.withSession(session) - .execute(action -> { - action.count(query(where("state").is("active")), Step.class) - ... + .execute(ops -> { + return ops.count(query(where("state").is("active")), Step.class) + }); ---- ====