25
25
import org .springframework .dao .InvalidDataAccessApiUsageException ;
26
26
import org .springframework .dao .InvalidDataAccessResourceUsageException ;
27
27
import org .springframework .dao .PermissionDeniedDataAccessException ;
28
+ import org .springframework .dao .TransientDataAccessException ;
28
29
import org .springframework .dao .support .PersistenceExceptionTranslator ;
29
30
import org .springframework .data .mongodb .ClientSessionException ;
30
- import org .springframework .data .mongodb .MongoTransactionException ;
31
+ import org .springframework .data .mongodb .TransientClientSessionException ;
32
+ import org .springframework .data .mongodb .TransientMongoDbException ;
31
33
import org .springframework .data .mongodb .UncategorizedMongoDbException ;
32
34
import org .springframework .data .mongodb .util .MongoDbErrorCodes ;
33
35
import org .springframework .lang .Nullable ;
@@ -65,9 +67,26 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
65
67
66
68
private static final Set <String > SECURITY_EXCEPTIONS = Set .of ("MongoCryptException" );
67
69
70
+ @ Override
68
71
@ Nullable
69
72
public DataAccessException translateExceptionIfPossible (RuntimeException ex ) {
70
73
74
+ DataAccessException translatedException = doTranslateException (ex );
75
+ if (translatedException == null ) {
76
+ return null ;
77
+ }
78
+
79
+ // Translated exceptions that per se are not be recoverable (eg. WriteConflicts), might still be transient inside a
80
+ // transaction. Let's wrap those.
81
+ return (isTransientFailure (ex ) && !(translatedException instanceof TransientDataAccessException ))
82
+ ? new TransientMongoDbException (ex .getMessage (), translatedException )
83
+ : translatedException ;
84
+
85
+ }
86
+
87
+ @ Nullable
88
+ DataAccessException doTranslateException (RuntimeException ex ) {
89
+
71
90
// Check for well-known MongoException subclasses.
72
91
73
92
if (ex instanceof BsonInvalidOperationException ) {
@@ -94,13 +113,13 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
94
113
95
114
if (DATA_INTEGRITY_EXCEPTIONS .contains (exception )) {
96
115
97
- if (ex instanceof MongoServerException mse ) {
98
- if (mse . getCode () == 11000 ) {
116
+ if (ex instanceof MongoServerException ) {
117
+ if (MongoDbErrorCodes . isDataDuplicateKeyError ( ex ) ) {
99
118
return new DuplicateKeyException (ex .getMessage (), ex );
100
119
}
101
120
if (ex instanceof MongoBulkWriteException bulkException ) {
102
- for (BulkWriteError x : bulkException .getWriteErrors ()) {
103
- if (x . getCode () == 11000 ) {
121
+ for (BulkWriteError writeError : bulkException .getWriteErrors ()) {
122
+ if (MongoDbErrorCodes . isDuplicateKeyCode ( writeError . getCode ()) ) {
104
123
return new DuplicateKeyException (ex .getMessage (), ex );
105
124
}
106
125
}
@@ -115,20 +134,27 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
115
134
116
135
int code = mongoException .getCode ();
117
136
118
- if (MongoDbErrorCodes .isDuplicateKeyCode ( code )) {
137
+ if (MongoDbErrorCodes .isDuplicateKeyError ( mongoException )) {
119
138
return new DuplicateKeyException (ex .getMessage (), ex );
120
- } else if (MongoDbErrorCodes .isDataAccessResourceFailureCode (code )) {
139
+ }
140
+ if (MongoDbErrorCodes .isDataAccessResourceError (mongoException )) {
121
141
return new DataAccessResourceFailureException (ex .getMessage (), ex );
122
- } else if (MongoDbErrorCodes .isInvalidDataAccessApiUsageCode (code ) || code == 10003 || code == 12001
123
- || code == 12010 || code == 12011 || code == 12012 ) {
142
+ }
143
+ if (MongoDbErrorCodes .isInvalidDataAccessApiUsageError (mongoException ) || code == 12001 || code == 12010
144
+ || code == 12011 || code == 12012 ) {
124
145
return new InvalidDataAccessApiUsageException (ex .getMessage (), ex );
125
- } else if (MongoDbErrorCodes .isPermissionDeniedCode (code )) {
146
+ }
147
+ if (MongoDbErrorCodes .isPermissionDeniedError (mongoException )) {
126
148
return new PermissionDeniedDataAccessException (ex .getMessage (), ex );
127
- } else if (MongoDbErrorCodes .isClientSessionFailureCode (code )) {
128
- return new ClientSessionException (ex .getMessage (), ex );
129
- } else if (MongoDbErrorCodes .isTransactionFailureCode (code )) {
130
- return new MongoTransactionException (ex .getMessage (), ex );
131
- } else if (ex .getCause () != null && SECURITY_EXCEPTIONS .contains (ClassUtils .getShortName (ex .getCause ().getClass ()))) {
149
+ }
150
+ if (MongoDbErrorCodes .isDataIntegrityViolationError (mongoException )) {
151
+ return new DataIntegrityViolationException (mongoException .getMessage (), mongoException );
152
+ }
153
+ if (MongoDbErrorCodes .isClientSessionFailure (mongoException )) {
154
+ return isTransientFailure (mongoException ) ? new TransientClientSessionException (ex .getMessage (), ex )
155
+ : new ClientSessionException (ex .getMessage (), ex );
156
+ }
157
+ if (ex .getCause () != null && SECURITY_EXCEPTIONS .contains (ClassUtils .getShortName (ex .getCause ().getClass ()))) {
132
158
return new PermissionDeniedDataAccessException (ex .getMessage (), ex );
133
159
}
134
160
@@ -150,4 +176,25 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
150
176
// that translation should not occur.
151
177
return null ;
152
178
}
179
+
180
+ /**
181
+ * Check if a given exception holds an error label indicating a transient failure.
182
+ *
183
+ * @param e
184
+ * @return {@literal true} if the given {@link Exception} is a {@link MongoException} holding one of the transient
185
+ * exception error labels.
186
+ * @see MongoException#hasErrorLabel(String)
187
+ * @since 3.3
188
+ */
189
+ public static boolean isTransientFailure (Exception e ) {
190
+
191
+ if (!(e instanceof MongoException )) {
192
+ return false ;
193
+ }
194
+
195
+ MongoException mongoException = (MongoException ) e ;
196
+
197
+ return mongoException .hasErrorLabel (MongoException .TRANSIENT_TRANSACTION_ERROR_LABEL )
198
+ || mongoException .hasErrorLabel (MongoException .UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL );
199
+ }
153
200
}
0 commit comments