Skip to content

Commit ebb7b58

Browse files
committed
Document how to create MongoTemplate and MongoTransactionManager for default transaction participation.
Refine documentation and indicate relationship to MongoDatabaseFactory. Closes #5019
1 parent eafe497 commit ebb7b58

File tree

5 files changed

+59
-36
lines changed

5 files changed

+59
-36
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,18 @@
3636

3737
/**
3838
* A {@link org.springframework.transaction.PlatformTransactionManager} implementation that manages
39-
* {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}. <br />
40-
* Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread. <br />
39+
* {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}.
40+
* <p>
41+
* Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread.
4142
* {@link TransactionDefinition#isReadOnly() Readonly} transactions operate on a {@link ClientSession} and enable causal
4243
* consistency, and also {@link ClientSession#startTransaction() start}, {@link ClientSession#commitTransaction()
43-
* commit} or {@link ClientSession#abortTransaction() abort} a transaction. <br />
44+
* commit} or {@link ClientSession#abortTransaction() abort} a transaction.
45+
* <p>
4446
* Application code is required to retrieve the {@link com.mongodb.client.MongoDatabase} via
4547
* {@link MongoDatabaseUtils#getDatabase(MongoDatabaseFactory)} instead of a standard
4648
* {@link MongoDatabaseFactory#getMongoDatabase()} call. Spring classes such as
47-
* {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly. <br />
48-
* By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. One may override
49+
* {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly. By default, failure of a
50+
* {@literal commit} operation raises a {@link TransactionSystemException}. One may override
4951
* {@link #doCommit(MongoTransactionObject)} to implement the
5052
* <a href="https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation">Retry Commit Operation</a>
5153
* behavior as outlined in the MongoDB reference manual.
@@ -205,7 +207,7 @@ protected final void doCommit(DefaultTransactionStatus status) throws Transactio
205207
* By default those labels are ignored, nevertheless one might check for
206208
* {@link MongoException#UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL transient commit errors labels} and retry the the
207209
* commit. <br />
208-
*
210+
*
209211
* <pre>
210212
* <code>
211213
* int retries = 3;

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,21 @@
3737
/**
3838
* A {@link org.springframework.transaction.ReactiveTransactionManager} implementation that manages
3939
* {@link com.mongodb.reactivestreams.client.ClientSession} based transactions for a single
40-
* {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory}. <br />
40+
* {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory}.
41+
* <p>
4142
* Binds a {@link ClientSession} from the specified
4243
* {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory} to the subscriber
43-
* {@link reactor.util.context.Context}. <br />
44-
* {@link org.springframework.transaction.TransactionDefinition#isReadOnly() Readonly} transactions operate on a
45-
* {@link ClientSession} and enable causal consistency, and also {@link ClientSession#startTransaction() start},
44+
* {@link reactor.util.context.Context}. {@link org.springframework.transaction.TransactionDefinition#isReadOnly()
45+
* Readonly} transactions operate on a {@link ClientSession} and enable causal consistency, and also
46+
* {@link ClientSession#startTransaction() start},
4647
* {@link com.mongodb.reactivestreams.client.ClientSession#commitTransaction() commit} or
47-
* {@link ClientSession#abortTransaction() abort} a transaction. <br />
48+
* {@link ClientSession#abortTransaction() abort} a transaction.
49+
* <p>
4850
* Application code is required to retrieve the {@link com.mongodb.reactivestreams.client.MongoDatabase} via
4951
* {@link org.springframework.data.mongodb.ReactiveMongoDatabaseUtils#getDatabase(ReactiveMongoDatabaseFactory)} instead
5052
* of a standard {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getMongoDatabase()} call. Spring
51-
* classes such as {@link org.springframework.data.mongodb.core.ReactiveMongoTemplate} use this strategy implicitly.
52-
* <br />
53-
* By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. You can override
53+
* classes such as {@link org.springframework.data.mongodb.core.ReactiveMongoTemplate} use this strategy implicitly. By
54+
* default, failure of a {@literal commit} operation raises a {@link TransactionSystemException}. You can override
5455
* {@link #doCommit(TransactionSynchronizationManager, ReactiveMongoTransactionObject)} to implement the
5556
* <a href="https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation">Retry Commit Operation</a>
5657
* behavior as outlined in the MongoDB reference manual.

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
import org.springframework.dao.support.PersistenceExceptionTranslator;
4949
import org.springframework.data.convert.EntityReader;
5050
import org.springframework.data.domain.OffsetScrollPosition;
51-
import org.springframework.data.domain.Pageable;
5251
import org.springframework.data.domain.Window;
5352
import org.springframework.data.geo.Distance;
5453
import org.springframework.data.geo.GeoResult;
@@ -160,6 +159,9 @@
160159
* <p>
161160
* You can also set the default {@link #setReadPreference(ReadPreference) ReadPreference} on the template level to
162161
* generally apply a {@link ReadPreference}.
162+
* <p>
163+
* When using transactions make sure to create this template with the same {@link MongoDatabaseFactory} that is also
164+
* used for {@code MongoTransactionManager} creation.
163165
*
164166
* @author Thomas Risberg
165167
* @author Graeme Rocher
@@ -223,6 +225,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
223225

224226
/**
225227
* Constructor used for a basic template configuration.
228+
* <p>
229+
* If you intend to use transactions, make sure to use {@link #MongoTemplate(MongoDatabaseFactory)} or
230+
* {@link #MongoTemplate(MongoDatabaseFactory, MongoConverter)} constructors, otherwise, this template will not
231+
* participate in transactions using the default {@code SessionSynchronization.ON_ACTUAL_TRANSACTION} setting as
232+
* {@code MongoTransactionManager} uses strictly its configured {@link MongoDatabaseFactory} for transaction
233+
* participation.
226234
*
227235
* @param mongoClient must not be {@literal null}.
228236
* @param databaseName must not be {@literal null} or empty.
@@ -641,7 +649,8 @@ public MongoTemplate withSession(ClientSession session) {
641649

642650
/**
643651
* Define if {@link MongoTemplate} should participate in transactions. Default is set to
644-
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.<br />
652+
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.
653+
* <p>
645654
* <strong>NOTE:</strong> MongoDB transactions require at least MongoDB 4.0.
646655
*
647656
* @since 2.1

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
package org.springframework.data.mongodb.core;
1717

18-
import static org.springframework.data.mongodb.core.query.SerializationUtils.serializeToJsonSafely;
18+
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
1919

2020
import reactor.core.publisher.Flux;
2121
import reactor.core.publisher.Mono;
@@ -110,18 +110,7 @@
110110
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
111111
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
112112
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
113-
import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent;
114-
import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent;
115-
import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent;
116-
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
117-
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
118-
import org.springframework.data.mongodb.core.mapping.event.BeforeDeleteEvent;
119-
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
120-
import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent;
121-
import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterConvertCallback;
122-
import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterSaveCallback;
123-
import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeConvertCallback;
124-
import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeSaveCallback;
113+
import org.springframework.data.mongodb.core.mapping.event.*;
125114
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
126115
import org.springframework.data.mongodb.core.query.BasicQuery;
127116
import org.springframework.data.mongodb.core.query.Collation;
@@ -189,6 +178,9 @@
189178
* <p>
190179
* You can also set the default {@link #setReadPreference(ReadPreference) ReadPreference} on the template level to
191180
* generally apply a {@link ReadPreference}.
181+
* <p>
182+
* When using transactions make sure to create this template with the same {@link ReactiveMongoDatabaseFactory} that is
183+
* also used for {@code ReactiveMongoTransactionManager} creation.
192184
*
193185
* @author Mark Paluch
194186
* @author Christoph Strobl
@@ -231,6 +223,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
231223

232224
/**
233225
* Constructor used for a basic template configuration.
226+
* <p>
227+
* If you intend to use transactions, make sure to use {@link #ReactiveMongoTemplate(ReactiveMongoDatabaseFactory)} or
228+
* {@link #ReactiveMongoTemplate(ReactiveMongoDatabaseFactory, MongoConverter)} constructors, otherwise, this template
229+
* will not participate in transactions using the default {@code SessionSynchronization.ON_ACTUAL_TRANSACTION} setting
230+
* as {@code ReactiveMongoTransactionManager} uses strictly its configured {@link ReactiveMongoDatabaseFactory} for
231+
* transaction participation.
234232
*
235233
* @param mongoClient must not be {@literal null}.
236234
* @param databaseName must not be {@literal null} or empty.
@@ -573,7 +571,8 @@ public <T> Flux<T> execute(ReactiveSessionCallback<T> action, Consumer<ClientSes
573571

574572
/**
575573
* Define if {@link ReactiveMongoTemplate} should participate in transactions. Default is set to
576-
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.<br />
574+
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.
575+
* <p>
577576
* <strong>NOTE:</strong> MongoDB transactions require at least MongoDB 4.0.
578577
*
579578
* @since 2.2

src/main/antora/modules/ROOT/pages/mongodb/client-session-transactions.adoc

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ static class Config extends AbstractMongoClientConfiguration {
294294
return new MongoTransactionManager(dbFactory);
295295
}
296296
297+
@Bean
298+
MongoTemplate mongoTemplate(MongoDatabaseFactory dbFactory) { <1>
299+
return new MongoTemplate(dbFactory);
300+
}
301+
297302
// ...
298303
}
299304
@@ -314,6 +319,7 @@ public class StateService {
314319
----
315320

316321
<1> Register `MongoTransactionManager` in the application context.
322+
Also, make sure to use the same `MongoDatabaseFactory` when creating `MongoTemplate` to participate in transactions in the scope of the same `MongoDatabaseFactory`.
317323
<2> Mark methods as transactional.
318324

319325
NOTE: `@Transactional(readOnly = true)` advises `MongoTransactionManager` to also start a transaction that adds the
@@ -333,6 +339,11 @@ public class Config extends AbstractReactiveMongoConfiguration {
333339
return new ReactiveMongoTransactionManager(factory);
334340
}
335341
342+
@Bean
343+
ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoDatabaseFactory dbFactory) { <1>
344+
return new ReactiveMongoTemplate(dbFactory);
345+
}
346+
336347
// ...
337348
}
338349
@@ -351,6 +362,7 @@ public class StateService {
351362
----
352363

353364
<1> Register `ReactiveMongoTransactionManager` in the application context.
365+
Also, make sure to use the same `ReactiveMongoDatabaseFactory` when creating `ReactiveMongoTemplate` to participate in transactions in the scope of the same `ReactiveMongoDatabaseFactory`.
354366
<2> Mark methods as transactional.
355367

356368
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
418430

419431
MongoDB does *not* support collection operations, such as collection creation, within a transaction.
420432
This also affects the on the fly collection creation that happens on first usage.
421-
Therefore make sure to have all required structures in place.
433+
Therefore, make sure to have all required structures in place.
422434

423435
*Transient Errors*
424436

425437
MongoDB can add special labels to errors raised during transactional operations.
426438
Those may indicate transient failures that might vanish by merely retrying the operation.
427439
We highly recommend https://github.com/spring-projects/spring-retry[Spring Retry] for those purposes.
428-
Nevertheless one may override `MongoTransactionManager#doCommit(MongoTransactionObject)` to implement a https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation[Retry Commit Operation]
440+
Nevertheless, one may override `MongoTransactionManager#doCommit(MongoTransactionObject)` to implement a https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation[Retry Commit Operation]
429441
behavior as outlined in the MongoDB reference manual.
430442

431443
*Count*
432444

433445
MongoDB `count` operates upon collection statistics which may not reflect the actual situation within a transaction.
434-
The server responds with _error 50851_ when issuing a `count` command inside of a multi-document transaction.
446+
The server responds with _error 50851_ when issuing a `count` command inside a multi-document transaction.
435447
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`.
436448

437449
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:
453465
session.startTransaction();
454466
455467
template.withSession(session)
456-
.execute(action -> {
457-
action.count(query(where("state").is("active")), Step.class)
458-
...
468+
.execute(ops -> {
469+
return ops.count(query(where("state").is("active")), Step.class)
470+
});
459471
----
460472
====
461473

0 commit comments

Comments
 (0)