@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
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
|
||||
* @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
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
public PersistenceExceptionTranslator getExceptionTranslator() {
|
||||
return this.exceptionTranslator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoDatabaseFactory withSession(ClientSession session) {
|
||||
return new MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session, this);
|
||||
}
|
||||
|
||||
@@ -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<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* @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
|
||||
instance.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return exceptionTranslator.translateExceptionIfPossible(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
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
|
||||
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
|
||||
*
|
||||
* @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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<? extends Exception> clazz, int code) {
|
||||
|
||||
@@ -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>
|
||||
.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)
|
||||
----
|
||||
======
|
||||
|
||||
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)
|
||||
[[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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user