From 0d823df7f3c7b5853dde6f8b8fb67efaa1a17473 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 4 May 2018 12:21:15 +0200 Subject: [PATCH] DATAMONGO-1920 - Polishing. Slightly tweak method names. Document MongoDatabaseUtils usage in the context of MongoTransactionManager. Rename SessionSynchronization constants to align with AbstractPlatformTransactionManager. Slightly tweak Javadoc and reference docs for typos. Original pull request: #554. --- .../data/mongodb/MongoDatabaseUtils.java | 33 ++++---- .../data/mongodb/MongoTransactionManager.java | 77 +++++++++++++------ .../data/mongodb/SessionSynchronization.java | 11 +-- .../data/mongodb/core/MongoTemplate.java | 4 +- .../mongodb/MongoDatabaseUtilsUnitTests.java | 12 +-- .../core/MongoTemplateTransactionTests.java | 9 +-- .../PersonRepositoryTransactionalTests.java | 11 +-- .../test/util/AfterTransactionAssertion.java | 8 +- .../mongodb/test/util/MongoVersionRule.java | 12 +-- .../client-session-transactions.adoc | 43 ++++++----- 10 files changed, 125 insertions(+), 95 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java index 37155a314..713fc73dd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java @@ -15,9 +15,6 @@ */ package org.springframework.data.mongodb; -import com.mongodb.ReadPreference; -import com.mongodb.TransactionOptions; -import com.mongodb.WriteConcern; import org.springframework.lang.Nullable; import org.springframework.transaction.support.ResourceHolderSynchronization; import org.springframework.transaction.support.TransactionSynchronization; @@ -37,6 +34,7 @@ import com.mongodb.client.MongoDatabase; * Note: Intended for internal usage only. * * @author Christoph Strobl + * @author Mark Paluch * @currentRead Shadow's Edge - Brent Weeks * @since 2.1 */ @@ -44,27 +42,27 @@ public class MongoDatabaseUtils { /** * Obtain the default {@link MongoDatabase database} form the given {@link MongoDbFactory factory} using - * {@link SessionSynchronization#NATIVE native session synchronization}. + * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. *

* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current - * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() snychronization is active}. + * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * * @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from. - * @return must not be {@literal null}. + * @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}. */ public static MongoDatabase getDatabase(MongoDbFactory factory) { - return doGetMongoDatabase(null, factory, SessionSynchronization.NATIVE); + return doGetMongoDatabase(null, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION); } /** * Obtain the default {@link MongoDatabase database} form the given {@link MongoDbFactory factory}. *

* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current - * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() snychronization is active}. + * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * * @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from. * @param sessionSynchronization the synchronization to use. Must not be {@literal null}. - * @return must not be {@literal null}. + * @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}. */ public static MongoDatabase getDatabase(MongoDbFactory factory, SessionSynchronization sessionSynchronization) { return doGetMongoDatabase(null, factory, sessionSynchronization); @@ -72,29 +70,29 @@ public class MongoDatabaseUtils { /** * Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDbFactory factory} using - * {@link SessionSynchronization#NATIVE native session synchronization}. + * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. *

* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current - * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() snychronization is active}. + * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * * @param dbName the name of the {@link MongoDatabase} to get. * @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from. - * @return must not be {@literal null}. + * @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}. */ public static MongoDatabase getDatabase(String dbName, MongoDbFactory factory) { - return doGetMongoDatabase(dbName, factory, SessionSynchronization.NATIVE); + return doGetMongoDatabase(dbName, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION); } /** * Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDbFactory factory}. *

* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current - * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() snychronization is active}. + * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * * @param dbName the name of the {@link MongoDatabase} to get. * @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from. * @param sessionSynchronization the synchronization to use. Must not be {@literal null}. - * @return must not be {@literal null}. + * @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}. */ public static MongoDatabase getDatabase(String dbName, MongoDbFactory factory, SessionSynchronization sessionSynchronization) { @@ -128,21 +126,20 @@ public class MongoDatabaseUtils { // check for native MongoDB transaction if (resourceHolder != null && (resourceHolder.hasSession() || resourceHolder.isSynchronizedWithTransaction())) { - resourceHolder.requested(); if (!resourceHolder.hasSession()) { resourceHolder.setSession(createClientSession(dbFactory)); } + return resourceHolder.getSession(); } - if (SessionSynchronization.NATIVE.equals(sessionSynchronization)) { + if (SessionSynchronization.ON_ACTUAL_TRANSACTION.equals(sessionSynchronization)) { return null; } // init a non native MongoDB transaction by registering a MongoSessionSynchronization resourceHolder = new MongoResourceHolder(createClientSession(dbFactory), dbFactory); - resourceHolder.requested(); resourceHolder.getSession().startTransaction(); TransactionSynchronizationManager 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 dbe612aa9..fc403adae 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 @@ -43,11 +43,17 @@ import com.mongodb.client.ClientSession; * {@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. - * + *

+ * Application code is required to retrieve the {@link com.mongodb.client.MongoDatabase} via + * {@link MongoDatabaseUtils#getDatabase(MongoDbFactory)} instead of a standard {@link MongoDbFactory#getDb()} call. + * Spring classes such as {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly. + * * @author Christoph Strobl + * @author Mark Paluch * @currentRead Shadow's Edge - Brent Weeks * @since 2.1 * @see MongoDB Transaction Documentation + * @see MongoDatabaseUtils#getDatabase(MongoDbFactory, SessionSynchronization) */ public class MongoTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean { @@ -62,7 +68,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager * before using the instance. Use this constructor to prepare a {@link MongoTransactionManager} via a * {@link org.springframework.beans.factory.BeanFactory}. *

- * Optionally it is possible to set default {@link TransactionOptions transaction options} defining eg. + * Optionally it is possible to set default {@link TransactionOptions transaction options} defining * {@link com.mongodb.ReadConcern} and {@link com.mongodb.WriteConcern}. * * @see #setDbFactory(MongoDbFactory) @@ -145,7 +151,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager } resourceHolder.setSynchronizedWithTransaction(true); - TransactionSynchronizationManager.bindResource(dbFactory, resourceHolder); + TransactionSynchronizationManager.bindResource(getRequiredDbFactory(), resourceHolder); } /* @@ -296,7 +302,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @Override - public void afterPropertiesSet() throws Exception { + public void afterPropertiesSet() { getRequiredDbFactory(); } @@ -345,25 +351,25 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager return "null"; } - String debugString = "[" + ClassUtils.getShortName(session.getClass()) + "@" - + Integer.toHexString(session.hashCode()) + " "; + String debugString = String.format("[%s@%s ", ClassUtils.getShortName(session.getClass()), + Integer.toHexString(session.hashCode())); try { if (session.getServerSession() != null) { - debugString += "id = " + session.getServerSession().getIdentifier() + ", "; - debugString += "causallyConsistent = " + session.isCausallyConsistent() + ", "; - debugString += "txActive = " + session.hasActiveTransaction() + ", "; - debugString += "txNumber = " + session.getServerSession().getTransactionNumber() + ", "; - debugString += "statementId = " + session.getServerSession().getStatementId() + ", "; - debugString += "clusterTime = " + session.getClusterTime(); + debugString += String.format("id = %s, ", session.getServerSession().getIdentifier()); + debugString += String.format("causallyConsistent = %s, ", session.isCausallyConsistent()); + debugString += String.format("txActive = %s, ", session.hasActiveTransaction()); + debugString += String.format("txNumber = %d, ", session.getServerSession().getTransactionNumber()); + debugString += String.format("statementId = %d, ", session.getServerSession().getStatementId()); + debugString += String.format("clusterTime = %s", session.getClusterTime()); } else { debugString += "id = n/a"; - debugString += "causallyConsistent = " + session.isCausallyConsistent() + ", "; - debugString += "txActive = " + session.hasActiveTransaction() + ", "; - debugString += "clusterTime = " + session.getClusterTime(); + debugString += String.format("causallyConsistent = %s, ", session.isCausallyConsistent()); + debugString += String.format("txActive = %s, ", session.hasActiveTransaction()); + debugString += String.format("clusterTime = %s", session.getClusterTime()); } } catch (RuntimeException e) { - debugString += "error = " + e.getMessage(); + debugString += String.format("error = %s", e.getMessage()); } debugString += "]"; @@ -376,6 +382,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager * {@link MongoTransactionManager}. * * @author Christoph Strobl + * @author Mark Paluch * @since 2.1 * @see MongoResourceHolder */ @@ -387,22 +394,27 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager this.resourceHolder = resourceHolder; } + /** + * Set the {@link MongoResourceHolder}. + * + * @param resourceHolder can be {@literal null}. + */ void setResourceHolder(@Nullable MongoResourceHolder resourceHolder) { this.resourceHolder = resourceHolder; } + /** + * @return {@literal true} if a {@link MongoResourceHolder} is set. + */ boolean hasResourceHolder() { return resourceHolder != null; } - void commitTransaction() { - getRequiredSession().commitTransaction(); - } - - void abortTransaction() { - getRequiredSession().abortTransaction(); - } - + /** + * Start a MongoDB transaction optionally given {@link TransactionOptions}. + * + * @param options can be {@literal null} + */ void startTransaction(@Nullable TransactionOptions options) { ClientSession session = getRequiredSession(); @@ -413,6 +425,23 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager } } + /** + * Commit the transaction. + */ + void commitTransaction() { + getRequiredSession().commitTransaction(); + } + + /** + * Rollback (abort) the transaction. + */ + void abortTransaction() { + getRequiredSession().abortTransaction(); + } + + /** + * Close a {@link ClientSession} without regard to its transactional state. + */ void closeSession() { ClientSession session = getRequiredSession(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionSynchronization.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionSynchronization.java index 9b0cb7bfa..225b79508 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionSynchronization.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionSynchronization.java @@ -20,18 +20,19 @@ package org.springframework.data.mongodb; * define in which type of transactions to participate if any. * * @author Christoph Strobl + * @author Mark Paluch * @since 2.1 */ public enum SessionSynchronization { /** - * Synchronize with native MongoDB transactions as those initiated via {@link MongoTransactionManager}. + * Synchronize with any transaction even with empty transactions and initiate a MongoDB transaction when doing so by + * registering a MongoDB specific {@link org.springframework.transaction.support.ResourceHolderSynchronization}. */ - NATIVE, + ALWAYS, /** - * Synchronize with any ongoing transaction and initiate a MongoDB transaction when doing so by registering a MongoDB - * specific {@link org.springframework.transaction.support.ResourceHolderSynchronization}. + * Synchronize with native MongoDB transactions initiated via {@link MongoTransactionManager}. */ - ANY; + ON_ACTUAL_TRANSACTION; } 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 bf3f41af4..78c0722e5 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 @@ -207,7 +207,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, private @Nullable ResourceLoader resourceLoader; private @Nullable MongoPersistentEntityIndexCreator indexCreator; - private SessionSynchronization sessionSynchronization = SessionSynchronization.NATIVE; + private SessionSynchronization sessionSynchronization = SessionSynchronization.ON_ACTUAL_TRANSACTION; /** * Constructor used for a basic template configuration @@ -580,7 +580,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, /** * Define if {@link MongoTemplate} should participate in transactions. Default is set to - * {@link SessionSynchronization#NATIVE}.
+ * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.
* NOTE: MongoDB transactions require at least MongoDB 4.0. * * @since 2.1 diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java index 47aadedd7..96e9a61be 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java @@ -81,7 +81,7 @@ public class MongoDatabaseUtilsUnitTests { @Test // DATAMONGO-1920 public void shouldNotStartSessionWhenNoTransactionOngoing() { - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.NATIVE); + MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ON_ACTUAL_TRANSACTION); verify(dbFactory, never()).getSession(any()); verify(dbFactory, never()).withSession(any(ClientSession.class)); @@ -105,7 +105,7 @@ public class MongoDatabaseUtilsUnitTests { assertThat(transactionStatus.isNewTransaction()).isTrue(); assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse(); - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ANY); + MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ALWAYS); assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isTrue(); } @@ -136,7 +136,7 @@ public class MongoDatabaseUtilsUnitTests { assertThat(transactionStatus.isNewTransaction()).isTrue(); assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse(); - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ANY); + MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ALWAYS); assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isTrue(); @@ -170,7 +170,7 @@ public class MongoDatabaseUtilsUnitTests { assertThat(transactionStatus.isNewTransaction()).isTrue(); assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse(); - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.NATIVE); + MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ON_ACTUAL_TRANSACTION); assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse(); @@ -200,7 +200,7 @@ public class MongoDatabaseUtilsUnitTests { assertThat(transactionStatus.isNewTransaction()).isTrue(); assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isTrue(); - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.NATIVE); + MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ON_ACTUAL_TRANSACTION); transactionStatus.setRollbackOnly(); } @@ -226,7 +226,7 @@ public class MongoDatabaseUtilsUnitTests { assertThat(transactionStatus.isNewTransaction()).isTrue(); assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isTrue(); - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ANY); + MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ALWAYS); transactionStatus.setRollbackOnly(); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTransactionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTransactionTests.java index 2a455c4eb..4fa7f547f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTransactionTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTransactionTests.java @@ -94,7 +94,7 @@ public class MongoTemplateTransactionTests { @Autowired MongoTemplate template; @Autowired MongoClient client; - List>> assertionList; + List>> assertionList; @Before public void setUp() { @@ -104,13 +104,12 @@ public class MongoTemplateTransactionTests { } @BeforeTransaction - public void xxx() { - + public void beforeTransaction() { createOrReplaceCollection(DB_NAME, COLLECTION_NAME, client); } @AfterTransaction - public void verifyDbState() throws InterruptedException { + public void verifyDbState() { MongoCollection collection = client.getDatabase(DB_NAME).withReadPreference(ReadPreference.primary()) .getCollection(COLLECTION_NAME); @@ -162,7 +161,7 @@ public class MongoTemplateTransactionTests { private AfterTransactionAssertion assertAfterTransaction(Assassin assassin) { - AfterTransactionAssertion assertion = new AfterTransactionAssertion(assassin); + AfterTransactionAssertion assertion = new AfterTransactionAssertion<>(assassin); assertionList.add(assertion); return assertion; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java index 44b99ea18..1883d1613 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java @@ -103,15 +103,15 @@ public class PersonRepositoryTransactionalTests { List all; - List>> assertionList; + List>> assertionList; @Before - public void setUp() throws InterruptedException { + public void setUp() { assertionList = new CopyOnWriteArrayList<>(); } @BeforeTransaction - public void beforeTransaction() throws InterruptedException { + public void beforeTransaction() { createOrReplaceCollection(DB_NAME, template.getCollectionName(Person.class), client); @@ -123,7 +123,7 @@ public class PersonRepositoryTransactionalTests { } @AfterTransaction - public void verifyDbState() throws InterruptedException { + public void verifyDbState() { MongoCollection collection = client.getDatabase(DB_NAME) .getCollection(template.getCollectionName(Person.class)); @@ -144,6 +144,7 @@ public class PersonRepositoryTransactionalTests { public void shouldHonorCommitForDerivedQuery() { repository.removePersonByLastnameUsingAnnotatedQuery(durzo.getLastname()); + repository.removePersonByLastnameUsingAnnotatedQuery(durzo.getLastname()); assertAfterTransaction(durzo).isNotPresent(); } @@ -171,7 +172,7 @@ public class PersonRepositoryTransactionalTests { private AfterTransactionAssertion assertAfterTransaction(Person person) { - AfterTransactionAssertion assertion = new AfterTransactionAssertion(new Persistable() { + AfterTransactionAssertion assertion = new AfterTransactionAssertion<>(new Persistable() { @Nullable @Override diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AfterTransactionAssertion.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AfterTransactionAssertion.java index f043ddfd7..f5316c723 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AfterTransactionAssertion.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AfterTransactionAssertion.java @@ -27,14 +27,14 @@ import org.springframework.data.domain.Persistable; public class AfterTransactionAssertion { private final T persistable; - private boolean presentAfterTransaction; + private boolean expectToBePresent; public void isPresent() { - presentAfterTransaction = true; + expectToBePresent = true; } public void isNotPresent() { - presentAfterTransaction = false; + expectToBePresent = false; } public Object getId() { @@ -42,6 +42,6 @@ public class AfterTransactionAssertion { } public boolean shouldBePresent() { - return presentAfterTransaction; + return expectToBePresent; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersionRule.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersionRule.java index 697c82561..6aa4eb810 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersionRule.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersionRule.java @@ -133,14 +133,14 @@ public class MongoVersionRule implements TestRule { MongoVersion version = description.getAnnotation(MongoVersion.class); if (version != null) { - Version tmpMinVersion = Version.parse(version.asOf()); - if (!tmpMinVersion.equals(ANY) && !tmpMinVersion.equals(DEFAULT_LOW)) { - minVersion = tmpMinVersion; + Version expectedMinVersion = Version.parse(version.asOf()); + if (!expectedMinVersion.equals(ANY) && !expectedMinVersion.equals(DEFAULT_LOW)) { + minVersion = expectedMinVersion; } - Version tmpMaxVersion = Version.parse(version.until()); - if (!tmpMaxVersion.equals(ANY) && !tmpMaxVersion.equals(DEFAULT_HIGH)) { - maxVersion = tmpMaxVersion; + Version expectedMaxVersion = Version.parse(version.until()); + if (!expectedMaxVersion.equals(ANY) && !expectedMaxVersion.equals(DEFAULT_HIGH)) { + maxVersion = expectedMaxVersion; } } } diff --git a/src/main/asciidoc/reference/client-session-transactions.adoc b/src/main/asciidoc/reference/client-session-transactions.adoc index 0112712c0..bf9ad6cf1 100644 --- a/src/main/asciidoc/reference/client-session-transactions.adoc +++ b/src/main/asciidoc/reference/client-session-transactions.adoc @@ -1,15 +1,17 @@ [[mongo.sessions]] = MongoDB Sessions -As of version 3.6 MongoDB supports a concept of Sessions. The use of sessions enables MongoDBs https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#causal-consistency[Causal Consistency] model guaranteeing to execute operations in an order that respect their causal relationships. Those are split into ``ServerSession``s and ``ClientSession``s. In the following when we speak of session we refer to `ClientSession`. +As of version 3.6, MongoDB supports a concept of Sessions. The use of sessions enables MongoDB's https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#causal-consistency[Causal Consistency] model guaranteeing to execute operations in an order that respect their causal relationships. Those are split into ``ServerSession``s and ``ClientSession``s. In the following when we speak of session, we refer to `ClientSession`. WARNING: Operations within a client session are not isolated from operations outside the session. -Both `MongoOperations` and `ReactiveMongoOperations` provide gateway methods for tying a `ClientSession` to the operations themselves. `MongoCollection` and `MongoDatabase` use session proxy objects implementing MongoDB's collection and and database interfaces so there's no need to add a session on each call. This means that a potential call to `MongoCollection#find()` is delegated to `MongoCollection#find(ClientSession)`. +Both `MongoOperations` and `ReactiveMongoOperations` provide gateway methods for tying a `ClientSession` to the operations themselves. `MongoCollection` and `MongoDatabase` use session proxy objects implementing MongoDB's collection and database interfaces, so there's no need to add a session on each call. This means that a potential call to `MongoCollection#find()` is delegated to `MongoCollection#find(ClientSession)`. NOTE: Methods like `(Reactive)MongoOperations#getCollection` returning native MongoDB Java Driver gateway objects, such as `MongoCollection`, that themselves offer dedicated methods for `ClientSession` are *NOT* be session-proxied. So make sure to provide the `ClientSession` where needed when interacting directly with a `MongoCollection` or `MongoDatabase` and not via one of the `#execute` callbacks on `MongoOperations`. -.ClientSession with `MongoOperations` +Let's take a look at a simple session example: + +.`ClientSession` with `MongoOperations` ==== [source,java] ---- @@ -42,7 +44,7 @@ session.close() <4> WARNING: When dealing with ``DBRef``s, especially lazily loaded ones, it is essential to **not** close the `ClientSession` before all data is loaded. Otherwise, lazy fetch fails. -The reactive counterpart uses the very same building blocks as the imperative one. +The reactive counterpart uses the very same building blocks as the imperative one: .ClientSession with `ReactiveMongoOperations` ==== @@ -74,19 +76,20 @@ template.withSession(session) <3> Make sure to close the `ClientSession`. ==== -By using a `Publisher` providing the actual session you can defer session acquisition to the point of actual subscription. -Still you need to close the session when done in order to not pollute the server with stale sessions. Use the `doFinally` hook on `execute` to call `ClientSession#close()` when you don't need the session any more. +By using a `Publisher` providing the actual session, you can defer session acquisition to the point of actual subscription. +Still, you need to close the session when done to not pollute the server with stale sessions. Use the `doFinally` hook on `execute` to call `ClientSession#close()` when you don't need the session anymore. In case you prefer having more control over the session itself, you can always obtain the `ClientSession` via the driver and provide it via a `Supplier`. - [[mongo.transactions]] = MongoDB Transactions -As of version 4 MongoDB supports https://www.mongodb.com/transactions[Transactions]. Transactions are built on top of <> and therefore require an active `ClientSession`. +As of version 4, MongoDB supports https://www.mongodb.com/transactions[Transactions]. Transactions are built on top of <> and therefore require an active `ClientSession`. -NOTE: By default, unless you specify a `MongoTransactionManager` within your application context, transaction support is **DISABLED**. You may use `setSessionSynchronization(ANY)` to participate in ongoing non native MongoDB transactions. +NOTE: Unless you specify a `MongoTransactionManager` within your application context, transaction support is **DISABLED**. You may use `setSessionSynchronization(ALWAYS)` to participate in ongoing non-native MongoDB transactions. -To get full programmatic control over transactions you may want to use the session callback on `MongoOperations`. +To get full programmatic control over transactions, you may want to use the session callback on `MongoOperations`. + +An example of programmatic transaction control within a `SessionCallback` is shown below: .Programmatic transactions ==== @@ -123,16 +126,16 @@ template.withSession(session) <5> Do not forget to close the session when done. ==== -The above example allows you to have full control over transactional behavior while using the session scoped `MongoOperations` instance within the callback to ensure the session is passed on to each and every server call. +The above example allows you to have full control over transactional behavior while using the session scoped `MongoOperations` instance within the callback to ensure the session is passed on to every server call. To avoid some of the overhead that comes with this approach usage of a `TransactionTemplate` can take away some of the noise of manual transaction flow. -== Transactions with TransactionTemplate +== Transactions with `TransactionTemplate` -.Transactions with TransactionTemplate +.Transactions with `TransactionTemplate` ==== [source,java] ---- -template.setSessionSynchronization(ANY); <1> +template.setSessionSynchronization(ALWAYS); <1> // ... @@ -152,17 +155,17 @@ txTemplate.execute(new TransactionCallbackWithoutResult() { }; }); ---- -<1> Manually enable transaction synchronization. +<1> Enable transaction synchronization during Template API configuration. Changing state of `MongoTemplate` during runtime can cause threading/visibility issues. <2> Create the `TransactionTemplate` using the provided `PlatformTransactionManager`. <3> Within the callback the `ClientSession` and transaction are already registered. ==== -== Transactions with MongoTransactionManager +== Transactions with `MongoTransactionManager` `MongoTransactionManager` is the gateway to the well known Spring transaction support. It allows applications to use http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/transaction.html[managed transaction features of Spring]. -The `MongoTransactionManager` binds a `ClientSession` to the thread. `MongoTemplate` automatically detects those and operates on them accordingly. `MongoTemplate` can also participate in other, ongoing transactions. +The `MongoTransactionManager` binds a `ClientSession` to the thread. `MongoTemplate` detects those and operates on these resources which are associated with the transaction accordingly. `MongoTemplate` can also participate in other, ongoing transactions. -.Transactions with MongoTransactionManager +.Transactions with `MongoTransactionManager` ==== [source,java] ---- @@ -196,5 +199,5 @@ public class StateService { <2> Mark methods as transactional. ==== -NOTE: `@Transactional(readOnly = true)` advises the `MongoTransactionManager` to also start a transaction adding the - `ClientSession` to outgoing requests. \ No newline at end of file +NOTE: `@Transactional(readOnly = true)` advises `MongoTransactionManager` also to start a transaction adding the + `ClientSession` to outgoing requests.