2626
2727import org .apache .commons .logging .Log ;
2828import org .apache .commons .logging .LogFactory ;
29-
3029import org .springframework .context .ApplicationContextAware ;
3130import org .springframework .core .convert .ConverterNotFoundException ;
3231import org .springframework .core .convert .converter .Converter ;
@@ -81,7 +80,7 @@ public class MappingJdbcConverter extends MappingRelationalConverter implements
8180 * {@link #MappingJdbcConverter(RelationalMappingContext, RelationResolver, CustomConversions, JdbcTypeFactory)}
8281 * (MappingContext, RelationResolver, JdbcTypeFactory)} to convert arrays and large objects into JDBC-specific types.
8382 *
84- * @param context must not be {@literal null}.
83+ * @param context must not be {@literal null}.
8584 * @param relationResolver used to fetch additional relations from the database. Must not be {@literal null}.
8685 */
8786 public MappingJdbcConverter (RelationalMappingContext context , RelationResolver relationResolver ) {
@@ -99,12 +98,12 @@ public MappingJdbcConverter(RelationalMappingContext context, RelationResolver r
9998 /**
10099 * Creates a new {@link MappingJdbcConverter} given {@link MappingContext}.
101100 *
102- * @param context must not be {@literal null}.
101+ * @param context must not be {@literal null}.
103102 * @param relationResolver used to fetch additional relations from the database. Must not be {@literal null}.
104- * @param typeFactory must not be {@literal null}
103+ * @param typeFactory must not be {@literal null}
105104 */
106105 public MappingJdbcConverter (RelationalMappingContext context , RelationResolver relationResolver ,
107- CustomConversions conversions , JdbcTypeFactory typeFactory ) {
106+ CustomConversions conversions , JdbcTypeFactory typeFactory ) {
108107
109108 super (context , conversions );
110109
@@ -301,7 +300,7 @@ public <R> R readAndResolve(TypeInformation<R> type, RowDocument source, Identif
301300
302301 @ Override
303302 protected RelationalPropertyValueProvider newValueProvider (RowDocumentAccessor documentAccessor ,
304- ValueExpressionEvaluator evaluator , ConversionContext context ) {
303+ ValueExpressionEvaluator evaluator , ConversionContext context ) {
305304
306305 if (context instanceof ResolvingConversionContext rcc ) {
307306
@@ -330,7 +329,7 @@ class ResolvingRelationalPropertyValueProvider implements RelationalPropertyValu
330329 private final Identifier identifier ;
331330
332331 private ResolvingRelationalPropertyValueProvider (AggregatePathValueProvider delegate , RowDocumentAccessor accessor ,
333- ResolvingConversionContext context , Identifier identifier ) {
332+ ResolvingConversionContext context , Identifier identifier ) {
334333
335334 AggregatePath path = context .aggregatePath ();
336335
@@ -339,15 +338,15 @@ private ResolvingRelationalPropertyValueProvider(AggregatePathValueProvider dele
339338 this .context = context ;
340339 this .identifier = path .isEntity ()
341340 ? potentiallyAppendIdentifier (identifier , path .getRequiredLeafEntity (),
342- property -> delegate .getValue (path .append (property )))
341+ property -> delegate .getValue (path .append (property )))
343342 : identifier ;
344343 }
345344
346345 /**
347346 * Conditionally append the identifier if the entity has an identifier property.
348347 */
349348 static Identifier potentiallyAppendIdentifier (Identifier base , RelationalPersistentEntity <?> entity ,
350- Function <RelationalPersistentProperty , Object > getter ) {
349+ Function <RelationalPersistentProperty , Object > getter ) {
351350
352351 if (entity .hasIdProperty ()) {
353352
@@ -422,7 +421,7 @@ public <T> T getPropertyValue(RelationalPersistentProperty property) {
422421 @ Override
423422 public boolean hasValue (RelationalPersistentProperty property ) {
424423
425- if ((property .isCollectionLike () && property .isEntity ())|| property .isMap ()) {
424+ if ((property .isCollectionLike () && property .isEntity ()) || property .isMap ()) {
426425 // attempt relation fetch
427426 return true ;
428427 }
@@ -445,12 +444,38 @@ public boolean hasValue(RelationalPersistentProperty property) {
445444 return delegate .hasValue (aggregatePath );
446445 }
447446
447+ @ Override
448+ public boolean hasNonEmptyValue (RelationalPersistentProperty property ) {
449+
450+ if ((property .isCollectionLike () && property .isEntity ()) || property .isMap ()) {
451+ // attempt relation fetch
452+ return true ;
453+ }
454+
455+ AggregatePath aggregatePath = context .aggregatePath ();
456+
457+ if (property .isEntity ()) {
458+
459+ RelationalPersistentEntity <?> entity = getMappingContext ().getRequiredPersistentEntity (property );
460+ if (entity .hasIdProperty ()) {
461+
462+ RelationalPersistentProperty referenceId = entity .getRequiredIdProperty ();
463+ AggregatePath toUse = aggregatePath .append (referenceId );
464+ return delegate .hasValue (toUse );
465+ }
466+
467+ return delegate .hasValue (aggregatePath .getTableInfo ().reverseColumnInfo ().alias ());
468+ }
469+
470+ return delegate .hasNonEmptyValue (aggregatePath );
471+ }
472+
448473 @ Override
449474 public RelationalPropertyValueProvider withContext (ConversionContext context ) {
450475
451476 return context == this .context ? this
452477 : new ResolvingRelationalPropertyValueProvider (delegate .withContext (context ), accessor ,
453- (ResolvingConversionContext ) context , identifier );
478+ (ResolvingConversionContext ) context , identifier );
454479 }
455480 }
456481
@@ -462,7 +487,7 @@ public RelationalPropertyValueProvider withContext(ConversionContext context) {
462487 * @param identifier
463488 */
464489 private record ResolvingConversionContext (ConversionContext delegate , AggregatePath aggregatePath ,
465- Identifier identifier ) implements ConversionContext {
490+ Identifier identifier ) implements ConversionContext {
466491
467492 @ Override
468493 public <S > S convert (Object source , TypeInformation <? extends S > typeHint ) {
0 commit comments