From 7fbd4966d7789dd9d0413d3aba5eff07a46cce0f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 30 Aug 2024 10:38:12 +0200 Subject: [PATCH] Polishing. Original pull request: #605 See #3148 See #2939 --- .../TransientClientSessionException.java | 9 ++- .../mongodb/TransientMongoDbException.java | 9 ++- .../mongodb/core/MongoClientFactoryBean.java | 33 +++++----- .../core/MongoDatabaseFactorySupport.java | 28 ++++++--- .../core/MongoExceptionTranslator.java | 36 +++++------ .../core/ReactiveMongoClientFactoryBean.java | 16 ++--- .../SimpleMongoClientDatabaseFactory.java | 2 +- .../SimpleReactiveMongoDatabaseFactory.java | 26 +++++--- .../data/mongodb/util/MongoDbErrorCodes.java | 61 +++++++++---------- .../MongoExceptionTranslatorUnitTests.java | 27 +++----- .../ROOT/pages/mongodb/template-api.adoc | 31 +++++++--- 11 files changed, 148 insertions(+), 130 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java index fd16ae97e..153240a79 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java @@ -16,24 +16,23 @@ package org.springframework.data.mongodb; import org.springframework.dao.TransientDataAccessException; -import org.springframework.lang.Nullable; /** * {@link TransientDataAccessException} specific to MongoDB {@link com.mongodb.session.ClientSession} related data * access failures such as reading data using an already closed session. * * @author Christoph Strobl - * @since 3.3 + * @since 4.4 */ public class TransientClientSessionException extends TransientMongoDbException { /** * Constructor for {@link TransientClientSessionException}. * - * @param msg the detail message. Can be {@literal null}. - * @param cause the root cause. Can be {@literal null}. + * @param msg the detail message. + * @param cause the root cause. */ - public TransientClientSessionException(@Nullable String msg, @Nullable Throwable cause) { + public TransientClientSessionException(String msg, Throwable cause) { super(msg, cause); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java index 2a253c2b3..af3bd0d32 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java @@ -16,7 +16,6 @@ package org.springframework.data.mongodb; import org.springframework.dao.TransientDataAccessException; -import org.springframework.lang.Nullable; /** * Root of the hierarchy of MongoDB specific data access exceptions that are considered transient such as @@ -24,17 +23,17 @@ import org.springframework.lang.Nullable; * specific labels}. * * @author Christoph Strobl - * @since 3.3 + * @since 4.4 */ public class TransientMongoDbException extends TransientDataAccessException { /** * Constructor for {@link TransientMongoDbException}. * - * @param msg the detail message. Can be {@literal null}. - * @param cause the root cause. Can be {@literal null}. + * @param msg the detail message. + * @param cause the root cause. */ - public TransientMongoDbException(String msg, @Nullable Throwable cause) { + public TransientMongoDbException(String msg, Throwable cause) { super(msg, cause); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java index 64a12e9c0..231a4df4a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java @@ -55,8 +55,6 @@ import com.mongodb.event.ClusterListener; */ public class MongoClientFactoryBean extends AbstractFactoryBean implements PersistenceExceptionTranslator { - private static final PersistenceExceptionTranslator DEFAULT_EXCEPTION_TRANSLATOR = new MongoExceptionTranslator(); - private @Nullable MongoClientSettings mongoClientSettings; private @Nullable String host; private @Nullable Integer port; @@ -64,7 +62,7 @@ public class MongoClientFactoryBean extends AbstractFactoryBean imp private @Nullable ConnectionString connectionString; private @Nullable String replicaSet = null; - private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_EXCEPTION_TRANSLATOR; + private PersistenceExceptionTranslator exceptionTranslator = MongoExceptionTranslator.DEFAULT_EXCEPTION_TRANSLATOR; /** * Set the {@link MongoClientSettings} to be used when creating {@link MongoClient}. @@ -116,23 +114,34 @@ public class MongoClientFactoryBean extends AbstractFactoryBean imp * @param exceptionTranslator */ public void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exceptionTranslator) { - this.exceptionTranslator = exceptionTranslator == null ? DEFAULT_EXCEPTION_TRANSLATOR : exceptionTranslator; - } - - public Class getObjectType() { - return MongoClient.class; + this.exceptionTranslator = exceptionTranslator == null ? MongoExceptionTranslator.DEFAULT_EXCEPTION_TRANSLATOR + : exceptionTranslator; } + @Override @Nullable public DataAccessException translateExceptionIfPossible(RuntimeException ex) { return exceptionTranslator.translateExceptionIfPossible(ex); } + @Override + public Class getObjectType() { + return MongoClient.class; + } + @Override protected MongoClient createInstance() throws Exception { return createMongoClient(computeClientSetting()); } + @Override + protected void destroyInstance(@Nullable MongoClient instance) throws Exception { + + if (instance != null) { + instance.close(); + } + } + /** * Create {@link MongoClientSettings} based on configuration and priority (lower is better). *
    @@ -324,14 +333,6 @@ public class MongoClientFactoryBean extends AbstractFactoryBean imp return !fromConnectionStringIsDefault ? fromConnectionString : defaultValue; } - @Override - protected void destroyInstance(@Nullable MongoClient instance) throws Exception { - - if (instance != null) { - instance.close(); - } - } - private MongoClient createMongoClient(MongoClientSettings settings) throws UnknownHostException { return MongoClients.create(settings, SpringDataMongoDB.driverInformation()); } 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 7e363632d..a73b426dc 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 @@ -32,8 +32,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. - *
    + * database name and exception translator.
    * Not intended to be used directly. * * @author Christoph Strobl @@ -47,8 +46,8 @@ public abstract class MongoDatabaseFactorySupport implements MongoDatabaseFac private final C mongoClient; private final String databaseName; private final boolean mongoInstanceCreated; - private final PersistenceExceptionTranslator exceptionTranslator; + private PersistenceExceptionTranslator exceptionTranslator; private @Nullable WriteConcern writeConcern; /** @@ -75,15 +74,31 @@ public abstract class MongoDatabaseFactorySupport implements MongoDatabaseFac this.exceptionTranslator = exceptionTranslator; } + /** + * Configures the {@link PersistenceExceptionTranslator} to be used. + * + * @param exceptionTranslator the exception translator to set. + * @since 4.4 + */ + public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + + @Override + public PersistenceExceptionTranslator getExceptionTranslator() { + return this.exceptionTranslator; + } + /** * Configures the {@link WriteConcern} to be used on the {@link MongoDatabase} instance being created. * - * @param writeConcern the writeConcern to set + * @param writeConcern the writeConcern to set. */ public void setWriteConcern(WriteConcern writeConcern) { this.writeConcern = writeConcern; } + @Override public MongoDatabase getMongoDatabase() throws DataAccessException { return getMongoDatabase(getDefaultDatabaseName()); } @@ -116,10 +131,7 @@ public abstract class MongoDatabaseFactorySupport implements MongoDatabaseFac } } - public PersistenceExceptionTranslator getExceptionTranslator() { - return this.exceptionTranslator; - } - + @Override public MongoDatabaseFactory withSession(ClientSession session) { return new MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session, this); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java index 2d570c06f..6c695b2a0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core; import java.util.Set; import org.bson.BsonInvalidOperationException; + import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DataIntegrityViolationException; @@ -25,11 +26,9 @@ import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.dao.PermissionDeniedDataAccessException; -import org.springframework.dao.TransientDataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.ClientSessionException; import org.springframework.data.mongodb.TransientClientSessionException; -import org.springframework.data.mongodb.TransientMongoDbException; import org.springframework.data.mongodb.UncategorizedMongoDbException; import org.springframework.data.mongodb.util.MongoDbErrorCodes; import org.springframework.lang.Nullable; @@ -53,6 +52,8 @@ import com.mongodb.bulk.BulkWriteError; */ public class MongoExceptionTranslator implements PersistenceExceptionTranslator { + public static final MongoExceptionTranslator DEFAULT_EXCEPTION_TRANSLATOR = new MongoExceptionTranslator(); + private static final Set DUPLICATE_KEY_EXCEPTIONS = Set.of("MongoException.DuplicateKey", "DuplicateKeyException"); @@ -70,18 +71,7 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator @Override @Nullable public DataAccessException translateExceptionIfPossible(RuntimeException ex) { - - DataAccessException translatedException = doTranslateException(ex); - if (translatedException == null) { - return null; - } - - // Translated exceptions that per se are not be recoverable (eg. WriteConflicts), might still be transient inside a - // transaction. Let's wrap those. - return (isTransientFailure(ex) && !(translatedException instanceof TransientDataAccessException)) - ? new TransientMongoDbException(ex.getMessage(), translatedException) - : translatedException; - + return doTranslateException(ex); } @Nullable @@ -180,21 +170,23 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator /** * Check if a given exception holds an error label indicating a transient failure. * - * @param e + * @param e the exception to inspect. * @return {@literal true} if the given {@link Exception} is a {@link MongoException} holding one of the transient * exception error labels. * @see MongoException#hasErrorLabel(String) - * @since 3.3 + * @since 4.4 */ - public static boolean isTransientFailure(Exception e) { + public boolean isTransientFailure(Exception e) { - if (!(e instanceof MongoException)) { - return false; + if (e instanceof MongoException mongoException) { + return mongoException.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL) + || mongoException.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL); } - MongoException mongoException = (MongoException) e; + if (e.getCause() != e && e.getCause() instanceof Exception ex) { + return isTransientFailure(ex); + } - return mongoException.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL) - || mongoException.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL); + return false; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java index f7755773d..615599de3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java @@ -36,13 +36,11 @@ import com.mongodb.reactivestreams.client.MongoClients; public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean implements PersistenceExceptionTranslator { - private static final PersistenceExceptionTranslator DEFAULT_EXCEPTION_TRANSLATOR = new MongoExceptionTranslator(); - private @Nullable String connectionString; private @Nullable String host; private @Nullable Integer port; private @Nullable MongoClientSettings mongoClientSettings; - private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_EXCEPTION_TRANSLATOR; + private PersistenceExceptionTranslator exceptionTranslator = MongoExceptionTranslator.DEFAULT_EXCEPTION_TRANSLATOR; /** * Configures the host to connect to. @@ -86,7 +84,13 @@ public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean getMongoDatabase() throws DataAccessException { return getMongoDatabase(databaseName); } + @Override public Mono getMongoDatabase(String dbName) throws DataAccessException { Assert.hasText(dbName, "Database name must not be empty"); @@ -118,6 +133,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React * * @see DisposableBean#destroy() */ + @Override public void destroy() throws Exception { if (mongoInstanceCreated) { @@ -125,10 +141,6 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React } } - public PersistenceExceptionTranslator getExceptionTranslator() { - return this.exceptionTranslator; - } - @Override public CodecRegistry getCodecRegistry() { return this.mongo.getDatabase(databaseName).getCodecRegistry(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java index 2b7ccde4d..30cee7b95 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java @@ -128,7 +128,10 @@ public final class MongoDbErrorCodes { clientSessionCodes.put(263, "OperationNotSupportedInTransaction"); clientSessionCodes.put(264, "TooManyLogicalSessions"); - errorCodes = new HashMap<>(); + errorCodes = new HashMap<>( + dataAccessResourceFailureCodes.size() + dataIntegrityViolationCodes.size() + duplicateKeyCodes.size() + + invalidDataAccessApiUsageException.size() + permissionDeniedCodes.size() + clientSessionCodes.size(), + 1f); errorCodes.putAll(dataAccessResourceFailureCodes); errorCodes.putAll(dataIntegrityViolationCodes); errorCodes.putAll(duplicateKeyCodes); @@ -149,12 +152,12 @@ public final class MongoDbErrorCodes { /** * @param exception can be {@literal null}. * @return - * @since 3.3 + * @since 4.4 */ - public static boolean isDataIntegrityViolationError(@Nullable Exception exception) { + public static boolean isDataIntegrityViolationError(Exception exception) { - if(exception instanceof MongoException) { - return isDataIntegrityViolationCode(((MongoException) exception).getCode()); + if (exception instanceof MongoException me) { + return isDataIntegrityViolationCode(me.getCode()); } return false; } @@ -166,12 +169,12 @@ public final class MongoDbErrorCodes { /** * @param exception can be {@literal null}. * @return - * @since 3.3 + * @since 4.4 */ - public static boolean isDataAccessResourceError(@Nullable Exception exception) { + public static boolean isDataAccessResourceError(Exception exception) { - if(exception instanceof MongoException) { - return isDataAccessResourceFailureCode(((MongoException) exception).getCode()); + if (exception instanceof MongoException me) { + return isDataAccessResourceFailureCode(me.getCode()); } return false; } @@ -183,12 +186,12 @@ public final class MongoDbErrorCodes { /** * @param exception can be {@literal null}. * @return - * @since 3.3 + * @since 4.4 */ - public static boolean isDuplicateKeyError(@Nullable Exception exception) { + public static boolean isDuplicateKeyError(Exception exception) { - if(exception instanceof MongoException) { - return isDuplicateKeyCode(((MongoException) exception).getCode()); + if (exception instanceof MongoException me) { + return isDuplicateKeyCode(me.getCode()); } return false; } @@ -196,14 +199,10 @@ public final class MongoDbErrorCodes { /** * @param exception can be {@literal null}. * @return - * @since 3.3 + * @since 4.4 */ - public static boolean isDataDuplicateKeyError(@Nullable Exception exception) { - - if(exception instanceof MongoException) { - return isDuplicateKeyCode(((MongoException) exception).getCode()); - } - return false; + public static boolean isDataDuplicateKeyError(Exception exception) { + return isDuplicateKeyError(exception); } public static boolean isPermissionDeniedCode(@Nullable Integer errorCode) { @@ -213,11 +212,11 @@ public final class MongoDbErrorCodes { /** * @param exception can be {@literal null}. * @return - * @since 3.3 + * @since 4.4 */ - public static boolean isPermissionDeniedError(@Nullable Exception exception) { + public static boolean isPermissionDeniedError(Exception exception) { - if(exception instanceof MongoException) { + if (exception instanceof MongoException) { return isPermissionDeniedCode(((MongoException) exception).getCode()); } return false; @@ -230,12 +229,12 @@ public final class MongoDbErrorCodes { /** * @param exception can be {@literal null}. * @return - * @since 3.3 + * @since 4.4 */ - public static boolean isInvalidDataAccessApiUsageError(@Nullable Exception exception) { + public static boolean isInvalidDataAccessApiUsageError(Exception exception) { - if(exception instanceof MongoException) { - return isInvalidDataAccessApiUsageCode(((MongoException) exception).getCode()); + if (exception instanceof MongoException me) { + return isInvalidDataAccessApiUsageCode(me.getCode()); } return false; } @@ -265,12 +264,12 @@ public final class MongoDbErrorCodes { /** * @param exception can be {@literal null}. * @return - * @since 3.3 + * @since 4.4 */ - public static boolean isClientSessionFailure(@Nullable Exception exception) { + public static boolean isClientSessionFailure(Exception exception) { - if(exception instanceof MongoException) { - return isClientSessionFailureCode(((MongoException) exception).getCode()); + if (exception instanceof MongoException me) { + return isClientSessionFailureCode(me.getCode()); } return false; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java index bfffa4afa..b0907d415 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java @@ -20,18 +20,16 @@ import static org.assertj.core.api.Assertions.*; import org.bson.BsonDocument; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - import org.mockito.Mockito; + import org.springframework.core.NestedRuntimeException; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.mongodb.ClientSessionException; import org.springframework.data.mongodb.MongoTransactionException; -import org.springframework.data.mongodb.TransientMongoDbException; import org.springframework.data.mongodb.UncategorizedMongoDbException; import org.springframework.lang.Nullable; @@ -41,9 +39,7 @@ import com.mongodb.MongoInternalException; import com.mongodb.MongoSocketException; import com.mongodb.MongoSocketReadTimeoutException; import com.mongodb.MongoSocketWriteException; -import com.mongodb.MongoWriteException; import com.mongodb.ServerAddress; -import com.mongodb.WriteError; /** * Unit tests for {@link MongoExceptionTranslator}. @@ -180,30 +176,21 @@ class MongoExceptionTranslatorUnitTests { MongoException source = new MongoException(267, "PreparedTransactionInProgress"); source.addLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL); - expectExceptionWithCauseMessage(translator.translateExceptionIfPossible(source), TransientMongoDbException.class, + expectExceptionWithCauseMessage(translator.translateExceptionIfPossible(source), + UncategorizedMongoDbException.class, "PreparedTransactionInProgress"); + assertThat(translator.isTransientFailure(source)).isTrue(); + assertThat(translator.isTransientFailure(translator.translateExceptionIfPossible(source))).isTrue(); } @Test // DATAMONGO-2073 - public void translateMongoExceptionWithTransientLabelToTransientMongoDbException() { + public void translateMongoExceptionWithTransientLabel() { MongoException exception = new MongoException(0, ""); exception.addLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL); DataAccessException translatedException = translator.translateExceptionIfPossible(exception); - expectExceptionWithCauseMessage(translatedException, TransientMongoDbException.class); - } - - @Test // DATAMONGO-2073 - public void wrapsTranslatedExceptionsWhenTransientLabelPresent() { - - MongoException exception = new MongoWriteException(new WriteError(112, "WriteConflict", new BsonDocument()), null); - exception.addLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL); - - DataAccessException translatedException = translator.translateExceptionIfPossible(exception); - - assertThat(translatedException).isInstanceOf(TransientMongoDbException.class); - assertThat(translatedException.getCause()).isInstanceOf(DataIntegrityViolationException.class); + expectExceptionWithCauseMessage(translatedException, UncategorizedMongoDbException.class); } private void checkTranslatedMongoException(Class clazz, int code) { diff --git a/src/main/antora/modules/ROOT/pages/mongodb/template-api.adoc b/src/main/antora/modules/ROOT/pages/mongodb/template-api.adoc index ab3470618..f2a7a19bd 100644 --- a/src/main/antora/modules/ROOT/pages/mongodb/template-api.adoc +++ b/src/main/antora/modules/ROOT/pages/mongodb/template-api.adoc @@ -31,7 +31,8 @@ The `execute` callbacks gives you a reference to either a `MongoCollection` or a * ` T` *execute* `(String collectionName, CollectionCallback action)`: Runs the given `CollectionCallback` on the collection of the given name. -* ` T` *execute* `(DbCallback action)`: Runs a DbCallback, translating any exceptions as necessary. Spring Data MongoDB provides support for the Aggregation Framework introduced to MongoDB in version 2.2. +* ` T` *execute* `(DbCallback action)`: Runs a DbCallback, translating any exceptions as necessary. +Spring Data MongoDB provides support for the Aggregation Framework introduced to MongoDB in version 2.2. * ` T` *execute* `(String collectionName, DbCallback action)`: Runs a `DbCallback` on the collection of the given name translating any exceptions as necessary. @@ -90,6 +91,7 @@ List all = template.query(SWCharacter.class) <1> .matching(query(where("jedi").is(true))) <4> .all(); ---- + <1> The type used to map fields used in the query to. <2> The collection name to use if not defined on the domain type. <3> Result type if not using the original domain type. @@ -107,9 +109,8 @@ Flux all = template.query(SWCharacter.class) ---- ====== -NOTE: Using projections allows `MongoTemplate` to optimize result mapping by limiting the actual response to fields required -by the projection target type. This applies as long as the javadoc:org.springframework.data.mongodb.core.query.Query[] itself does not contain any field restriction and the -target type is a closed interface or DTO projection. +NOTE: Using projections allows `MongoTemplate` to optimize result mapping by limiting the actual response to fields required by the projection target type. +This applies as long as the javadoc:org.springframework.data.mongodb.core.query.Query[] itself does not contain any field restriction and the target type is a closed interface or DTO projection. WARNING: Projections must not be applied to xref:mongodb/mapping/document-references.adoc[DBRefs]. @@ -143,8 +144,8 @@ Flux> results = template.query(SWCharacter.class) [[mongo-template.exception-translation]] == Exception Translation -The Spring framework provides exception translation for a wide variety of database and mapping technologies. T -his has traditionally been for JDBC and JPA. +The Spring framework provides exception translation for a wide variety of database and mapping technologies. +This has traditionally been for JDBC and JPA. The Spring support for MongoDB extends this feature to the MongoDB Database by providing an implementation of the `org.springframework.dao.support.PersistenceExceptionTranslator` interface. The motivation behind mapping to Spring's link:{springDocsUrl}/data-access.html#dao-exceptions[consistent data access exception hierarchy] is that you are then able to write portable and descriptive exception handling code without resorting to coding against MongoDB error codes. @@ -152,9 +153,25 @@ All of Spring's data access exceptions are inherited from the root `DataAccessEx Note that not all exceptions thrown by the MongoDB driver inherit from the `MongoException` class. The inner exception and message are preserved so that no information is lost. -Some of the mappings performed by the `MongoExceptionTranslator` are `com.mongodb.Network to DataAccessResourceFailureException` and `MongoException` error codes 1003, 12001, 12010, 12011, and 12012 to `InvalidDataAccessApiUsageException`. +Some of the mappings performed by the javadoc:org.springframework.data.mongodb.core.MongoExceptionTranslator[] are `com.mongodb.Network` to `DataAccessResourceFailureException` and `MongoException` error codes 1003, 12001, 12010, 12011, and 12012 to `InvalidDataAccessApiUsageException`. Look into the implementation for more details on the mapping. +Exception Translation can be configured by setting a customized javadoc:org.springframework.data.mongodb.core.MongoExceptionTranslator[] on your `MongoDatabaseFactory` or its reactive variant. +You might also want to set the exception translator on the corresponding `MongoClientFactoryBean`. + +.Configuring `MongoExceptionTranslator` +==== +[source,java] +---- +ConnectionString uri = new ConnectionString("mongodb://username:password@localhost/database"); +SimpleMongoClientDatabaseFactory mongoDbFactory = new SimpleMongoClientDatabaseFactory(uri); +mongoDbFactory.setExceptionTranslator(myCustomExceptionTranslator); +---- +==== + +A motivation to customize exception can be MongoDB's behavior during transactions where some failures (such as write conflicts) can become transient and where a retry could lead to a successful operation. +In such a case, you could wrap exceptions with a specific MongoDB label and apply a different exception translation stragegy. + [[mongo-template.type-mapping]] == Domain Type Mapping