DATAMONGO-2490 - Fix dbref fetching during ongoing transaction.

Original pull request: #875.
This commit is contained in:
Christoph Strobl
2020-07-03 10:10:00 +02:00
committed by Mark Paluch
parent 773f20f861
commit be46540959
4 changed files with 63 additions and 5 deletions

View File

@@ -79,7 +79,7 @@ public class MongoDatabaseUtils {
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(String dbName, MongoDbFactory factory) {
public static MongoDatabase getDatabase(@Nullable String dbName, MongoDbFactory factory) {
return doGetMongoDatabase(dbName, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION);
}
@@ -88,13 +88,13 @@ public class MongoDatabaseUtils {
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@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 the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(String dbName, MongoDbFactory factory,
public static MongoDatabase getDatabase(@Nullable String dbName, MongoDbFactory factory,
SessionSynchronization sessionSynchronization) {
return doGetMongoDatabase(dbName, factory, sessionSynchronization);
}

View File

@@ -45,6 +45,7 @@ import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.ClientSessionException;
import org.springframework.data.mongodb.LazyLoadingException;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.MongoDatabaseUtils;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.lang.Nullable;
import org.springframework.objenesis.ObjenesisStd;
@@ -497,7 +498,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
*/
protected MongoCollection<Document> getCollection(DBRef dbref) {
return (StringUtils.hasText(dbref.getDatabaseName()) ? mongoDbFactory.getDb(dbref.getDatabaseName())
: mongoDbFactory.getDb()).getCollection(dbref.getCollectionName(), Document.class);
return MongoDatabaseUtils.getDatabase(dbref.getDatabaseName(), mongoDbFactory)
.getCollection(dbref.getCollectionName(), Document.class);
}
}

View File

@@ -31,6 +31,7 @@ import org.junit.Test;
import org.junit.rules.TestRule;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.test.util.MongoTestUtils;
import org.springframework.data.mongodb.test.util.MongoVersion;
@@ -54,6 +55,7 @@ public class ClientSessionTests {
private static final String DB_NAME = "client-session-tests";
private static final String COLLECTION_NAME = "test";
private static final String REF_COLLECTION_NAME = "test-with-ref";
MongoTemplate template;
MongoClient client;
@@ -154,6 +156,31 @@ public class ClientSessionTests {
assertThat(template.exists(query(where("id").is(saved.getId())), SomeDoc.class)).isFalse();
}
@Test // DATAMONGO-2490
public void shouldBeAbleToReadDbRefDuringTransaction() {
SomeDoc ref = new SomeDoc("ref-1", "da value");
WithDbRef source = new WithDbRef("source-1", "da source", ref);
ClientSession session = mongoClient.startSession(ClientSessionOptions.builder().causallyConsistent(true).build());
assertThat(session.getOperationTime()).isNull();
session.startTransaction();
WithDbRef saved = template.withSession(() -> session).execute(action -> {
template.save(ref);
template.save(source);
return template.findOne(query(where("id").is(source.id)), WithDbRef.class);
});
assertThat(saved.getSomeDocRef()).isEqualTo(ref);
session.abortTransaction();
}
@Data
@AllArgsConstructor
@org.springframework.data.mongodb.core.mapping.Document(COLLECTION_NAME)
@@ -163,4 +190,14 @@ public class ClientSessionTests {
String value;
}
@Data
@AllArgsConstructor
@org.springframework.data.mongodb.core.mapping.Document(REF_COLLECTION_NAME)
static class WithDbRef {
@Id String id;
String value;
@DBRef SomeDoc someDocRef;
}
}

View File

@@ -20,6 +20,7 @@ import static org.springframework.data.mongodb.test.util.MongoTestUtils.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import org.bson.Document;
@@ -175,6 +176,25 @@ public class PersonRepositoryTransactionalTests {
assertAfterTransaction(hu).isNotPresent();
}
@Test // DATAMONGO-2490
public void shouldBeAbleToReadDbRefDuringTransaction() {
User rat = new User();
rat.setUsername("rat");
template.save(rat);
Person elene = new Person("Elene", "Cromwyll", 18);
elene.setCoworker(rat);
repository.save(elene);
Optional<Person> loaded = repository.findById(elene.getId());
assertThat(loaded).isPresent();
assertThat(loaded.get().getCoworker()).isNotNull();
assertThat(loaded.get().getCoworker().getUsername()).isEqualTo(rat.getUsername());
}
private AfterTransactionAssertion assertAfterTransaction(Person person) {
AfterTransactionAssertion assertion = new AfterTransactionAssertion<>(new Persistable<Object>() {