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.
This commit is contained in:
@@ -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;
|
||||
* <strong>Note:</strong> 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}.
|
||||
* <p />
|
||||
* 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}.
|
||||
* <p />
|
||||
* 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}.
|
||||
* <p />
|
||||
* 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}.
|
||||
* <p />
|
||||
* 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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p />
|
||||
* 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 <a href="https://www.mongodb.com/transactions">MongoDB Transaction Documentation</a>
|
||||
* @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}.
|
||||
* <p />
|
||||
* 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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}.<br />
|
||||
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.<br />
|
||||
* <strong>NOTE:</strong> MongoDB transactions require at least MongoDB 4.0.
|
||||
*
|
||||
* @since 2.1
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class MongoTemplateTransactionTests {
|
||||
@Autowired MongoTemplate template;
|
||||
@Autowired MongoClient client;
|
||||
|
||||
List<AfterTransactionAssertion<Persistable<String>>> assertionList;
|
||||
List<AfterTransactionAssertion<? extends Persistable<?>>> 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<Document> 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<Assassin> assertion = new AfterTransactionAssertion<>(assassin);
|
||||
assertionList.add(assertion);
|
||||
return assertion;
|
||||
}
|
||||
|
||||
@@ -103,15 +103,15 @@ public class PersonRepositoryTransactionalTests {
|
||||
|
||||
List<Person> all;
|
||||
|
||||
List<org.springframework.data.mongodb.test.util.AfterTransactionAssertion<Persistable<String>>> assertionList;
|
||||
List<AfterTransactionAssertion<? extends Persistable<?>>> 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<Document> 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<Object>() {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
|
||||
@@ -27,14 +27,14 @@ import org.springframework.data.domain.Persistable;
|
||||
public class AfterTransactionAssertion<T extends Persistable> {
|
||||
|
||||
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<T extends Persistable> {
|
||||
}
|
||||
|
||||
public boolean shouldBePresent() {
|
||||
return presentAfterTransaction;
|
||||
return expectToBePresent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <<mongo.sessions,Sessions>> and therefore require an active `ClientSession`.
|
||||
As of version 4, MongoDB supports https://www.mongodb.com/transactions[Transactions]. Transactions are built on top of <<mongo.sessions,Sessions>> 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.
|
||||
NOTE: `@Transactional(readOnly = true)` advises `MongoTransactionManager` also to start a transaction adding the
|
||||
`ClientSession` to outgoing requests.
|
||||
|
||||
Reference in New Issue
Block a user