From 0bb302cb8e516d9b4bdd2fa2946e607639c59095 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 17 Jul 2025 15:13:32 +0200 Subject: [PATCH] Document client disposal using MongoDatabaseFactory. Closes #5012 --- .../core/MongoDatabaseFactorySupport.java | 54 +++------------- .../SimpleMongoClientDatabaseFactory.java | 12 +++- .../SimpleReactiveMongoDatabaseFactory.java | 63 +++++-------------- 3 files changed, 30 insertions(+), 99 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java index 0a62b7aa4..34e2e4ba6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.core; import org.jspecify.annotations.Nullable; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; @@ -23,7 +24,6 @@ import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.SessionAwareMethodInterceptor; import org.springframework.lang.Contract; import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; import com.mongodb.ClientSessionOptions; import com.mongodb.WriteConcern; @@ -33,8 +33,7 @@ import com.mongodb.client.MongoDatabase; /** * Common base class for usage with both {@link com.mongodb.client.MongoClients} defining common properties such as - * database name and exception translator.
- * Not intended to be used directly. + * database name and exception translator. Not intended to be used directly. * * @author Christoph Strobl * @author Mark Paluch @@ -126,6 +125,7 @@ public abstract class MongoDatabaseFactorySupport implements MongoDatabaseFac */ protected abstract MongoDatabase doGetMongoDatabase(String dbName); + public void destroy() throws Exception { if (mongoInstanceCreated) { closeClient(); @@ -164,15 +164,8 @@ public abstract class MongoDatabaseFactorySupport implements MongoDatabaseFac * @author Christoph Strobl * @since 2.1 */ - static final class ClientSessionBoundMongoDbFactory implements MongoDatabaseFactory { - - private final ClientSession session; - private final MongoDatabaseFactory delegate; - - public ClientSessionBoundMongoDbFactory(ClientSession session, MongoDatabaseFactory delegate) { - this.session = session; - this.delegate = delegate; - } + record ClientSessionBoundMongoDbFactory(ClientSession session, + MongoDatabaseFactory delegate) implements MongoDatabaseFactory { @Override public MongoDatabase getMongoDatabase() throws DataAccessException { @@ -201,7 +194,7 @@ public abstract class MongoDatabaseFactorySupport implements MongoDatabaseFac @Override public boolean isTransactionActive() { - return session != null && session.hasActiveTransaction(); + return session.hasActiveTransaction(); } private MongoDatabase proxyMongoDatabase(MongoDatabase database) { @@ -230,39 +223,6 @@ public abstract class MongoDatabaseFactorySupport implements MongoDatabaseFac return targetType.cast(factory.getProxy(target.getClass().getClassLoader())); } - public ClientSession getSession() { - return this.session; - } - - public MongoDatabaseFactory getDelegate() { - return this.delegate; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - ClientSessionBoundMongoDbFactory that = (ClientSessionBoundMongoDbFactory) o; - - if (!ObjectUtils.nullSafeEquals(this.session, that.session)) { - return false; - } - return ObjectUtils.nullSafeEquals(this.delegate, that.delegate); - } - - @Override - public int hashCode() { - int result = ObjectUtils.nullSafeHashCode(this.session); - result = 31 * result + ObjectUtils.nullSafeHashCode(this.delegate); - return result; - } - - public String toString() { - return "MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session=" + this.getSession() + ", delegate=" - + this.getDelegate() + ")"; - } } + } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactory.java index 2b51b5e07..afacbbad9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactory.java @@ -34,7 +34,8 @@ public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySuppor implements DisposableBean { /** - * Creates a new {@link SimpleMongoClientDatabaseFactory} instance for the given {@code connectionString}. + * Creates a new {@link SimpleMongoClientDatabaseFactory} instance for the given {@code connectionString}. Using this + * constructor will create a new {@link MongoClient} instance that will be closed when calling {@link #destroy()}. * * @param connectionString connection coordinates for a database connection. Must contain a database name and must not * be {@literal null} or empty. @@ -45,7 +46,8 @@ public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySuppor } /** - * Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}. + * Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}. Using this + * constructor will create a new {@link MongoClient} instance that will be closed when calling {@link #destroy()}. * * @param connectionString connection coordinates for a database connection. Must contain also a database name and not * be {@literal null}. @@ -55,7 +57,10 @@ public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySuppor } /** - * Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}. + * Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}. Note that the + * client will not be closed when calling {@link #destroy()} as we assume a managed client instance that we do not + * want to close on {@link #destroy()} meaning that you (or the application container) must dispose the client + * instance once it is no longer required for use. * * @param mongoClient must not be {@literal null}. * @param databaseName must not be {@literal null} or empty. @@ -89,4 +94,5 @@ public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySuppor protected MongoDatabase doGetMongoDatabase(String dbName) { return getMongoClient().getDatabase(dbName); } + } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java index 529f912e6..6787cd9d0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java @@ -19,6 +19,7 @@ import reactor.core.publisher.Mono; import org.bson.codecs.configuration.CodecRegistry; import org.jspecify.annotations.Nullable; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.dao.DataAccessException; @@ -26,7 +27,6 @@ import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.SessionAwareMethodInterceptor; import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; import com.mongodb.ClientSessionOptions; import com.mongodb.ConnectionString; @@ -55,7 +55,9 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React private @Nullable WriteConcern writeConcern; /** - * Creates a new {@link SimpleReactiveMongoDatabaseFactory} instance from the given {@link ConnectionString}. + * Creates a new {@link SimpleReactiveMongoDatabaseFactory} instance from the given {@link ConnectionString}. Using + * this constructor will create a new {@link MongoClient} instance that will be closed when calling + * {@link #destroy()}. * * @param connectionString must not be {@literal null}. */ @@ -64,7 +66,10 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React } /** - * Creates a new {@link SimpleReactiveMongoDatabaseFactory} instance from the given {@link MongoClient}. + * Creates a new {@link SimpleReactiveMongoDatabaseFactory} instance from the given {@link MongoClient}. Note that the + * client will not be closed when calling {@link #destroy()} as we assume a managed client instance that we do not + * want to close on {@link #destroy()} meaning that you (or the application container) must dispose the client + * instance once it is no longer required for use. * * @param mongoClient must not be {@literal null}. * @param databaseName must not be {@literal null}. @@ -163,16 +168,8 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React * @author Christoph Strobl * @since 2.1 */ - static final class ClientSessionBoundMongoDbFactory implements ReactiveMongoDatabaseFactory { - - private final ClientSession session; - private final ReactiveMongoDatabaseFactory delegate; - - ClientSessionBoundMongoDbFactory(ClientSession session, ReactiveMongoDatabaseFactory delegate) { - - this.session = session; - this.delegate = delegate; - } + record ClientSessionBoundMongoDbFactory(ClientSession session, + ReactiveMongoDatabaseFactory delegate) implements ReactiveMongoDatabaseFactory { @Override public Mono getMongoDatabase() throws DataAccessException { @@ -206,7 +203,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React @Override public boolean isTransactionActive() { - return session != null && session.hasActiveTransaction(); + return session.hasActiveTransaction(); } private MongoDatabase decorateDatabase(MongoDatabase database) { @@ -217,7 +214,8 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React return createProxyInstance(session, database, MongoDatabase.class); } - private MongoCollection proxyCollection(com.mongodb.session.ClientSession session, MongoCollection collection) { + private MongoCollection proxyCollection(com.mongodb.session.ClientSession session, + MongoCollection collection) { return createProxyInstance(session, collection, MongoCollection.class); } @@ -234,39 +232,6 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React return targetType.cast(factory.getProxy(target.getClass().getClassLoader())); } - public ClientSession getSession() { - return this.session; - } - - public ReactiveMongoDatabaseFactory getDelegate() { - return this.delegate; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - ClientSessionBoundMongoDbFactory that = (ClientSessionBoundMongoDbFactory) o; - - if (!ObjectUtils.nullSafeEquals(this.session, that.session)) { - return false; - } - return ObjectUtils.nullSafeEquals(this.delegate, that.delegate); - } - - @Override - public int hashCode() { - int result = ObjectUtils.nullSafeHashCode(this.session); - result = 31 * result + ObjectUtils.nullSafeHashCode(this.delegate); - return result; - } - - public String toString() { - return "SimpleReactiveMongoDatabaseFactory.ClientSessionBoundMongoDbFactory(session=" + this.getSession() - + ", delegate=" + this.getDelegate() + ")"; - } } + }