Browse Source

Polishing.

Original pull request: #605
See #3148
See #2939
pull/4778/merge
Mark Paluch 1 year ago
parent
commit
7fbd4966d7
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java
  2. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java
  3. 33
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java
  4. 28
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java
  5. 36
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java
  6. 16
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java
  7. 2
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactory.java
  8. 26
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java
  9. 61
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java
  10. 27
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java
  11. 31
      src/main/antora/modules/ROOT/pages/mongodb/template-api.adoc

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java

@ -16,24 +16,23 @@ @@ -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);
}
}

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java

@ -16,7 +16,6 @@ @@ -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; @@ -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);
}
}

33
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java

@ -55,8 +55,6 @@ import com.mongodb.event.ClusterListener; @@ -55,8 +55,6 @@ import com.mongodb.event.ClusterListener;
*/
public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> 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<MongoClient> imp @@ -64,7 +62,7 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> 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<MongoClient> imp @@ -116,23 +114,34 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> imp
* @param exceptionTranslator
*/
public void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator == null ? DEFAULT_EXCEPTION_TRANSLATOR : exceptionTranslator;
}
public Class<? extends MongoClient> 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<? extends MongoClient> 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).
* <ol>
@ -324,14 +333,6 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> imp @@ -324,14 +333,6 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> 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());
}

28
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java

@ -32,8 +32,7 @@ import com.mongodb.client.MongoDatabase; @@ -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.
* <br />
* database name and exception translator. <br />
* Not intended to be used directly.
*
* @author Christoph Strobl
@ -47,8 +46,8 @@ public abstract class MongoDatabaseFactorySupport<C> implements MongoDatabaseFac @@ -47,8 +46,8 @@ public abstract class MongoDatabaseFactorySupport<C> 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<C> implements MongoDatabaseFac @@ -75,15 +74,31 @@ public abstract class MongoDatabaseFactorySupport<C> 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<C> implements MongoDatabaseFac @@ -116,10 +131,7 @@ public abstract class MongoDatabaseFactorySupport<C> implements MongoDatabaseFac
}
}
public PersistenceExceptionTranslator getExceptionTranslator() {
return this.exceptionTranslator;
}
@Override
public MongoDatabaseFactory withSession(ClientSession session) {
return new MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session, this);
}

36
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java

@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core; @@ -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; @@ -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; @@ -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<String> DUPLICATE_KEY_EXCEPTIONS = Set.of("MongoException.DuplicateKey",
"DuplicateKeyException");
@ -70,18 +71,7 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator @@ -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 @@ -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;
}
}

16
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java

@ -36,13 +36,11 @@ import com.mongodb.reactivestreams.client.MongoClients; @@ -36,13 +36,11 @@ import com.mongodb.reactivestreams.client.MongoClients;
public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean<MongoClient>
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<MongoCli @@ -86,7 +84,13 @@ public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean<MongoCli
* @param exceptionTranslator
*/
public void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator == null ? DEFAULT_EXCEPTION_TRANSLATOR : exceptionTranslator;
this.exceptionTranslator = exceptionTranslator == null ? MongoExceptionTranslator.DEFAULT_EXCEPTION_TRANSLATOR
: exceptionTranslator;
}
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
@Override
@ -123,8 +127,4 @@ public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean<MongoCli @@ -123,8 +127,4 @@ public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean<MongoCli
instance.close();
}
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
}

2
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactory.java

@ -72,7 +72,7 @@ public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySuppor @@ -72,7 +72,7 @@ public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySuppor
* @param mongoInstanceCreated
*/
SimpleMongoClientDatabaseFactory(MongoClient mongoClient, String databaseName, boolean mongoInstanceCreated) {
super(mongoClient, databaseName, mongoInstanceCreated, new MongoExceptionTranslator());
super(mongoClient, databaseName, mongoInstanceCreated, MongoExceptionTranslator.DEFAULT_EXCEPTION_TRANSLATOR);
}
@Override

26
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java

@ -51,8 +51,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React @@ -51,8 +51,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
private final String databaseName;
private final boolean mongoInstanceCreated;
private final PersistenceExceptionTranslator exceptionTranslator;
private PersistenceExceptionTranslator exceptionTranslator = MongoExceptionTranslator.DEFAULT_EXCEPTION_TRANSLATOR;
private @Nullable WriteConcern writeConcern;
/**
@ -85,7 +84,21 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React @@ -85,7 +84,21 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
this.mongo = client;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.exceptionTranslator = new MongoExceptionTranslator();
}
/**
* 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;
}
/**
@ -97,10 +110,12 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React @@ -97,10 +110,12 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
this.writeConcern = writeConcern;
}
@Override
public Mono<MongoDatabase> getMongoDatabase() throws DataAccessException {
return getMongoDatabase(databaseName);
}
@Override
public Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException {
Assert.hasText(dbName, "Database name must not be empty");
@ -118,6 +133,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React @@ -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 @@ -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();

61
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java

@ -128,7 +128,10 @@ public final class MongoDbErrorCodes { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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;
}

27
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java

@ -20,18 +20,16 @@ import static org.assertj.core.api.Assertions.*; @@ -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; @@ -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 { @@ -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<? extends Exception> clazz, int code) {

31
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 @@ -31,7 +31,8 @@ The `execute` callbacks gives you a reference to either a `MongoCollection` or a
* `<T> T` *execute* `(String collectionName, CollectionCallback<T> action)`: Runs the given `CollectionCallback` on the collection of the given name.
* `<T> T` *execute* `(DbCallback<T> 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> T` *execute* `(DbCallback<T> 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> T` *execute* `(String collectionName, DbCallback<T> action)`: Runs a `DbCallback` on the collection of the given name translating any exceptions as necessary.
@ -90,6 +91,7 @@ List<Jedi> all = template.query(SWCharacter.class) <1> @@ -90,6 +91,7 @@ List<Jedi> 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<Jedi> all = template.query(SWCharacter.class) @@ -107,9 +109,8 @@ Flux<Jedi> 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<GeoResult<Jedi>> results = template.query(SWCharacter.class) @@ -143,8 +144,8 @@ Flux<GeoResult<Jedi>> 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 @@ -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

Loading…
Cancel
Save