27
27
import org .apache .commons .logging .Log ;
28
28
import org .apache .commons .logging .LogFactory ;
29
29
import org .springframework .context .ApplicationContextAware ;
30
- import org .springframework .core .convert .ConverterNotFoundException ;
31
30
import org .springframework .core .convert .converter .Converter ;
32
- import org .springframework .core . convert . converter . ConverterRegistry ;
31
+ import org .springframework .dao . NonTransientDataAccessException ;
33
32
import org .springframework .data .convert .CustomConversions ;
34
33
import org .springframework .data .jdbc .core .mapping .AggregateReference ;
35
34
import org .springframework .data .jdbc .core .mapping .JdbcValue ;
@@ -91,8 +90,6 @@ public MappingJdbcConverter(RelationalMappingContext context, RelationResolver r
91
90
92
91
this .typeFactory = JdbcTypeFactory .unsupported ();
93
92
this .relationResolver = relationResolver ;
94
-
95
- registerAggregateReferenceConverters ();
96
93
}
97
94
98
95
/**
@@ -112,14 +109,6 @@ public MappingJdbcConverter(RelationalMappingContext context, RelationResolver r
112
109
113
110
this .typeFactory = typeFactory ;
114
111
this .relationResolver = relationResolver ;
115
-
116
- registerAggregateReferenceConverters ();
117
- }
118
-
119
- private void registerAggregateReferenceConverters () {
120
-
121
- ConverterRegistry registry = (ConverterRegistry ) getConversionService ();
122
- AggregateReferenceConverters .getConvertersToRegister (getConversionService ()).forEach (registry ::addConverter );
123
112
}
124
113
125
114
@ Nullable
@@ -184,34 +173,78 @@ private Class<?> doGetColumnType(RelationalPersistentProperty property) {
184
173
return componentColumnType ;
185
174
}
186
175
176
+ /**
177
+ * Read and convert a single value that is coming from a database to the {@literal targetType} expected by the domain
178
+ * model.
179
+ *
180
+ * @param value a value as it is returned by the driver accessing the persistence store. May be {@code null}.
181
+ * @param targetType {@link TypeInformation} into which the value is to be converted. Must not be {@code null}.
182
+ * @return
183
+ */
187
184
@ Override
188
185
@ Nullable
189
- public Object readValue (@ Nullable Object value , TypeInformation <?> type ) {
186
+ public Object readValue (@ Nullable Object value , TypeInformation <?> targetType ) {
190
187
191
- if (value == null ) {
192
- return value ;
188
+ if (null == value ) {
189
+ return null ;
193
190
}
194
191
192
+ TypeInformation <?> originalTargetType = targetType ;
193
+ value = readJdbcArray (value );
194
+ targetType = determineNestedTargetType (targetType );
195
+
196
+ return possiblyReadToAggregateReference (getPotentiallyConvertedSimpleRead (value , targetType ), originalTargetType );
197
+ }
198
+
199
+ /**
200
+ * Unwrap a Jdbc array, if such a value is provided
201
+ */
202
+ private Object readJdbcArray (Object value ) {
203
+
195
204
if (value instanceof Array array ) {
196
205
try {
197
- return super . readValue ( array .getArray (), type );
198
- } catch (SQLException | ConverterNotFoundException e ) {
199
- LOG . info ( "Failed to extract a value of type %s from an Array; Attempting to use standard conversions" , e );
206
+ return array .getArray ();
207
+ } catch (SQLException e ) {
208
+ throw new FailedToAccessJdbcArrayException ( e );
200
209
}
201
210
}
202
211
203
- return super .readValue (value , type );
212
+ return value ;
213
+ }
214
+
215
+ /**
216
+ * Determine the id type of an {@link AggregateReference} that the rest of the conversion infrastructure needs to use
217
+ * as a conversion target.
218
+ */
219
+ private TypeInformation <?> determineNestedTargetType (TypeInformation <?> ultimateTargetType ) {
220
+
221
+ if (AggregateReference .class .isAssignableFrom (ultimateTargetType .getType ())) {
222
+ // the id type of a AggregateReference
223
+ return ultimateTargetType .getTypeArguments ().get (1 );
224
+ }
225
+ return ultimateTargetType ;
226
+ }
227
+
228
+ /**
229
+ * Convert value to an {@link AggregateReference} if that is specified by the parameter targetType.
230
+ */
231
+ private Object possiblyReadToAggregateReference (Object value , TypeInformation <?> targetType ) {
232
+
233
+ if (AggregateReference .class .isAssignableFrom (targetType .getType ())) {
234
+ return AggregateReference .to (value );
235
+ }
236
+ return value ;
204
237
}
205
238
206
- @ Override
207
239
@ Nullable
208
- public Object writeValue (@ Nullable Object value , TypeInformation <?> type ) {
240
+ @ Override
241
+ protected Object getPotentiallyConvertedSimpleWrite (Object value , TypeInformation <?> type ) {
209
242
210
- if (value == null ) {
211
- return null ;
243
+ if (value instanceof AggregateReference <?, ?> aggregateReference ) {
244
+ return writeValue ( aggregateReference . getId (), type ) ;
212
245
}
213
246
214
- return super .writeValue (value , type );
247
+ return super .getPotentiallyConvertedSimpleWrite (value , type );
215
248
}
216
249
217
250
private boolean canWriteAsJdbcValue (@ Nullable Object value ) {
@@ -244,28 +277,37 @@ private boolean canWriteAsJdbcValue(@Nullable Object value) {
244
277
public JdbcValue writeJdbcValue (@ Nullable Object value , TypeInformation <?> columnType , SQLType sqlType ) {
245
278
246
279
TypeInformation <?> targetType = canWriteAsJdbcValue (value ) ? TypeInformation .of (JdbcValue .class ) : columnType ;
280
+
281
+ if (value instanceof AggregateReference <?, ?> aggregateReference ) {
282
+ return writeJdbcValue (aggregateReference .getId (), columnType , sqlType );
283
+ }
284
+
247
285
Object convertedValue = writeValue (value , targetType );
248
286
249
287
if (convertedValue instanceof JdbcValue result ) {
250
288
return result ;
251
289
}
252
290
253
- if (convertedValue == null || ! convertedValue . getClass (). isArray () ) {
254
- return JdbcValue .of (convertedValue , sqlType );
291
+ if (convertedValue == null ) {
292
+ return JdbcValue .of (null , sqlType );
255
293
}
256
294
257
- Class <?> componentType = convertedValue .getClass ().getComponentType ();
258
- if (componentType != byte .class && componentType != Byte .class ) {
295
+ if (convertedValue .getClass ().isArray ()) {// array conversion
296
+ Class <?> componentType = convertedValue .getClass ().getComponentType ();
297
+ if (componentType != byte .class && componentType != Byte .class ) {
259
298
260
- Object [] objectArray = requireObjectArray (convertedValue );
261
- return JdbcValue .of (typeFactory .createArray (objectArray ), JDBCType .ARRAY );
262
- }
299
+ Object [] objectArray = requireObjectArray (convertedValue );
300
+ return JdbcValue .of (typeFactory .createArray (objectArray ), JDBCType .ARRAY );
301
+ }
263
302
264
- if (componentType == Byte .class ) {
265
- convertedValue = ArrayUtils .toPrimitive ((Byte []) convertedValue );
266
- }
303
+ if (componentType == Byte .class ) {
304
+ convertedValue = ArrayUtils .toPrimitive ((Byte []) convertedValue );
305
+ }
267
306
268
- return JdbcValue .of (convertedValue , JDBCType .BINARY );
307
+ return JdbcValue .of (convertedValue , JDBCType .BINARY );
308
+ }
309
+
310
+ return JdbcValue .of (convertedValue , sqlType );
269
311
}
270
312
271
313
@ SuppressWarnings ("unchecked" )
@@ -298,6 +340,12 @@ protected RelationalPropertyValueProvider newValueProvider(RowDocumentAccessor d
298
340
return super .newValueProvider (documentAccessor , evaluator , context );
299
341
}
300
342
343
+ private static class FailedToAccessJdbcArrayException extends NonTransientDataAccessException {
344
+ public FailedToAccessJdbcArrayException (SQLException e ) {
345
+ super ("Failed to read array" , e );
346
+ }
347
+ }
348
+
301
349
/**
302
350
* {@link RelationalPropertyValueProvider} using a resolving context to lookup relations. This is highly
303
351
* context-sensitive. Note that the identifier is held here because of a chicken and egg problem, while
0 commit comments