2727import org .apache .commons .logging .Log ;
2828import org .apache .commons .logging .LogFactory ;
2929import org .springframework .context .ApplicationContextAware ;
30- import org .springframework .core .convert .ConverterNotFoundException ;
3130import org .springframework .core .convert .converter .Converter ;
32- import org .springframework .core . convert . converter . ConverterRegistry ;
31+ import org .springframework .dao . NonTransientDataAccessException ;
3332import org .springframework .data .convert .CustomConversions ;
3433import org .springframework .data .jdbc .core .mapping .AggregateReference ;
3534import org .springframework .data .jdbc .core .mapping .JdbcValue ;
@@ -91,8 +90,6 @@ public MappingJdbcConverter(RelationalMappingContext context, RelationResolver r
9190
9291 this .typeFactory = JdbcTypeFactory .unsupported ();
9392 this .relationResolver = relationResolver ;
94-
95- registerAggregateReferenceConverters ();
9693 }
9794
9895 /**
@@ -112,14 +109,6 @@ public MappingJdbcConverter(RelationalMappingContext context, RelationResolver r
112109
113110 this .typeFactory = typeFactory ;
114111 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 );
123112 }
124113
125114 @ Nullable
@@ -184,34 +173,78 @@ private Class<?> doGetColumnType(RelationalPersistentProperty property) {
184173 return componentColumnType ;
185174 }
186175
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+ */
187184 @ Override
188185 @ Nullable
189- public Object readValue (@ Nullable Object value , TypeInformation <?> type ) {
186+ public Object readValue (@ Nullable Object value , TypeInformation <?> targetType ) {
190187
191- if (value == null ) {
192- return value ;
188+ if (null == value ) {
189+ return null ;
193190 }
194191
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+
195204 if (value instanceof Array array ) {
196205 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 );
200209 }
201210 }
202211
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 ;
204237 }
205238
206- @ Override
207239 @ Nullable
208- public Object writeValue (@ Nullable Object value , TypeInformation <?> type ) {
240+ @ Override
241+ protected Object getPotentiallyConvertedSimpleWrite (Object value , TypeInformation <?> type ) {
209242
210- if (value == null ) {
211- return null ;
243+ if (value instanceof AggregateReference <?, ?> aggregateReference ) {
244+ return writeValue ( aggregateReference . getId (), type ) ;
212245 }
213246
214- return super .writeValue (value , type );
247+ return super .getPotentiallyConvertedSimpleWrite (value , type );
215248 }
216249
217250 private boolean canWriteAsJdbcValue (@ Nullable Object value ) {
@@ -244,28 +277,37 @@ private boolean canWriteAsJdbcValue(@Nullable Object value) {
244277 public JdbcValue writeJdbcValue (@ Nullable Object value , TypeInformation <?> columnType , SQLType sqlType ) {
245278
246279 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+
247285 Object convertedValue = writeValue (value , targetType );
248286
249287 if (convertedValue instanceof JdbcValue result ) {
250288 return result ;
251289 }
252290
253- if (convertedValue == null || ! convertedValue . getClass (). isArray () ) {
254- return JdbcValue .of (convertedValue , sqlType );
291+ if (convertedValue == null ) {
292+ return JdbcValue .of (null , sqlType );
255293 }
256294
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 ) {
259298
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+ }
263302
264- if (componentType == Byte .class ) {
265- convertedValue = ArrayUtils .toPrimitive ((Byte []) convertedValue );
266- }
303+ if (componentType == Byte .class ) {
304+ convertedValue = ArrayUtils .toPrimitive ((Byte []) convertedValue );
305+ }
267306
268- return JdbcValue .of (convertedValue , JDBCType .BINARY );
307+ return JdbcValue .of (convertedValue , JDBCType .BINARY );
308+ }
309+
310+ return JdbcValue .of (convertedValue , sqlType );
269311 }
270312
271313 @ SuppressWarnings ("unchecked" )
@@ -298,6 +340,12 @@ protected RelationalPropertyValueProvider newValueProvider(RowDocumentAccessor d
298340 return super .newValueProvider (documentAccessor , evaluator , context );
299341 }
300342
343+ private static class FailedToAccessJdbcArrayException extends NonTransientDataAccessException {
344+ public FailedToAccessJdbcArrayException (SQLException e ) {
345+ super ("Failed to read array" , e );
346+ }
347+ }
348+
301349 /**
302350 * {@link RelationalPropertyValueProvider} using a resolving context to lookup relations. This is highly
303351 * context-sensitive. Note that the identifier is held here because of a chicken and egg problem, while
0 commit comments