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