Avoid nested entity creation if column value is null.

We now no longer attempt to create instances of nested entities if the column value is null. Previously the null check happened after checking registered custom conversions which has lead to potential object creation for columns containing null values.

Closes #670
This commit is contained in:
Mark Paluch
2021-10-25 15:43:26 +02:00
parent 7f3bc97a4e
commit 4d65223355
2 changed files with 54 additions and 10 deletions

View File

@@ -167,7 +167,11 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R
value = row.get(identifier);
}
if (value != null && getConversions().hasCustomReadTarget(value.getClass(), property.getType())) {
if (value == null) {
return null;
}
if (getConversions().hasCustomReadTarget(value.getClass(), property.getType())) {
return readValue(value, property.getTypeInformation());
}
@@ -175,10 +179,6 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R
return readEntityFrom(row, metadata, property);
}
if (value == null) {
return null;
}
return readValue(value, property.getTypeInformation());
} catch (Exception o_O) {
@@ -613,8 +613,8 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R
if (idPropertyUpdateNeeded) {
return potentiallySetId(row, metadata, propertyAccessor, idProperty) //
? (T) propertyAccessor.getBean() //
: object;
? (T) propertyAccessor.getBean() //
: object;
}
return object;

View File

@@ -65,7 +65,8 @@ public class MappingR2dbcConverterUnitTests {
R2dbcCustomConversions conversions = R2dbcCustomConversions.of(PostgresDialect.INSTANCE,
Arrays.asList(StringToMapConverter.INSTANCE, MapToStringConverter.INSTANCE,
CustomConversionPersonToOutboundRowConverter.INSTANCE, RowToCustomConversionPerson.INSTANCE));
CustomConversionPersonToOutboundRowConverter.INSTANCE, RowToCustomConversionPerson.INSTANCE,
StringToSimplePersonConverter.INSTANCE));
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
@@ -82,8 +83,7 @@ public class MappingR2dbcConverterUnitTests {
converter.write(new Person("id", "Walter", "White", instant, localDateTime), row);
assertThat(row).containsEntry(SqlIdentifier.unquoted("id"), Parameter.fromOrEmpty("id", String.class));
assertThat(row).containsEntry(SqlIdentifier.unquoted("firstname"),
Parameter.fromOrEmpty("Walter", String.class));
assertThat(row).containsEntry(SqlIdentifier.unquoted("firstname"), Parameter.fromOrEmpty("Walter", String.class));
assertThat(row).containsEntry(SqlIdentifier.unquoted("lastname"), Parameter.fromOrEmpty("White", String.class));
assertThat(row).containsEntry(SqlIdentifier.unquoted("instant"), Parameter.from(instant));
assertThat(row).containsEntry(SqlIdentifier.unquoted("local_date_time"), Parameter.from(localDateTime));
@@ -237,6 +237,19 @@ public class MappingR2dbcConverterUnitTests {
assertThat(result.world).isEqualTo("No, universe");
}
@Test // GH-670
void considersConverterBeforeEntityConstruction() {
MockRow row = MockRow.builder().identified("id", Object.class, 42).identified("person", Object.class, null).build();
MockRowMetadata metadata = MockRowMetadata.builder().columnMetadata(MockColumnMetadata.builder().name("id").build())
.columnMetadata(MockColumnMetadata.builder().name("person").build()).build();
WithSimplePersonConstructor result = converter.read(WithSimplePersonConstructor.class, row, metadata);
assertThat(result.id).isEqualTo(42);
assertThat(result.person).isNull();
}
@AllArgsConstructor
static class Person {
@Id String id;
@@ -350,6 +363,17 @@ public class MappingR2dbcConverterUnitTests {
}
}
@ReadingConverter
enum StringToSimplePersonConverter implements Converter<String, SimplePerson> {
INSTANCE;
@Override
public SimplePerson convert(String source) {
return new SimplePerson(source);
}
}
static class WithSpelExpression {
private final long id;
@@ -362,4 +386,24 @@ public class MappingR2dbcConverterUnitTests {
this.world = world;
}
}
static class WithSimplePersonConstructor {
private final long id;
private final SimplePerson person;
public WithSimplePersonConstructor(long id, SimplePerson person) {
this.id = id;
this.person = person;
}
}
static class SimplePerson {
private final String name;
SimplePerson(String name) {
this.name = name;
}
}
}