diff --git a/src/main/java/org/springframework/data/r2dbc/convert/EntityRowMapper.java b/src/main/java/org/springframework/data/r2dbc/convert/EntityRowMapper.java index a551d9c..c5458ca 100644 --- a/src/main/java/org/springframework/data/r2dbc/convert/EntityRowMapper.java +++ b/src/main/java/org/springframework/data/r2dbc/convert/EntityRowMapper.java @@ -43,6 +43,6 @@ public class EntityRowMapper implements BiFunction { */ @Override public T apply(Row row, RowMetadata metadata) { - return converter.read(typeRoRead, row); + return converter.read(typeRoRead, row, metadata); } } diff --git a/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java b/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java index 4224c55..72e63f0 100644 --- a/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java +++ b/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java @@ -84,8 +84,21 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R // Entity reading // ---------------------------------- + /* + * (non-Javadoc) + * @see org.springframework.data.convert.EntityReader#read(java.lang.Class, S) + */ @Override public R read(Class type, Row row) { + return read(type, row, null); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.convert.R2dbcConverter#read(java.lang.Class, io.r2dbc.spi.Row, io.r2dbc.spi.RowMetadata) + */ + @Override + public R read(Class type, Row row, @Nullable RowMetadata metadata) { TypeInformation typeInfo = ClassTypeInformation.from(type); Class rawType = typeInfo.getType(); @@ -99,10 +112,10 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R return getConversionService().convert(row, rawType); } - return read(getRequiredPersistentEntity(type), row); + return read(getRequiredPersistentEntity(type), row, metadata); } - private R read(RelationalPersistentEntity entity, Row row) { + private R read(RelationalPersistentEntity entity, Row row, @Nullable RowMetadata metadata) { R result = createInstance(row, "", entity); @@ -115,7 +128,7 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R continue; } - Object value = readFrom(row, property, ""); + Object value = readFrom(row, metadata, property, ""); if (value != null) { propertyAccessor.setProperty(property, value); @@ -129,20 +142,27 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R * Read a single value or a complete Entity from the {@link Row} passed as an argument. * * @param row the {@link Row} to extract the value from. Must not be {@literal null}. + * @param metadata the {@link RowMetadata}. Can be {@literal null}. * @param property the {@link RelationalPersistentProperty} for which the value is intended. Must not be * {@literal null}. * @param prefix to be used for all column names accessed by this method. Must not be {@literal null}. * @return the value read from the {@link Row}. May be {@literal null}. */ - private Object readFrom(Row row, RelationalPersistentProperty property, String prefix) { + private Object readFrom(Row row, @Nullable RowMetadata metadata, RelationalPersistentProperty property, + String prefix) { try { if (property.isEntity()) { - return readEntityFrom(row, property); + return readEntityFrom(row, metadata, property); } - Object value = row.get(prefix + property.getColumnName()); + String identifier = prefix + property.getColumnName(); + if (metadata != null && !metadata.getColumnNames().contains(identifier)) { + return null; + } + + Object value = row.get(identifier); return getPotentiallyConvertedSimpleRead(value, property.getTypeInformation().getType()); } catch (Exception o_O) { @@ -178,13 +198,13 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R } @SuppressWarnings("unchecked") - private S readEntityFrom(Row row, PersistentProperty property) { + private S readEntityFrom(Row row, RowMetadata metadata, PersistentProperty property) { String prefix = property.getName() + "_"; RelationalPersistentEntity entity = getMappingContext().getRequiredPersistentEntity(property.getActualType()); - if (readFrom(row, entity.getRequiredIdProperty(), prefix) == null) { + if (readFrom(row, metadata, entity.getRequiredIdProperty(), prefix) == null) { return null; } @@ -195,7 +215,7 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R for (RelationalPersistentProperty p : entity) { if (!entity.isConstructorArgument(property)) { - propertyAccessor.setProperty(p, readFrom(row, p, prefix)); + propertyAccessor.setProperty(p, readFrom(row, metadata, p, prefix)); } } @@ -213,6 +233,10 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R // Entity writing // ---------------------------------- + /* + * (non-Javadoc) + * @see org.springframework.data.convert.EntityWriter#write(java.lang.Object, java.lang.Object) + */ @Override public void write(Object source, OutboundRow sink) { @@ -313,6 +337,11 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R return Enum.class.isAssignableFrom(value.getClass()) ? ((Enum) value).name() : value; } + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.convert.R2dbcConverter#getArrayValue(org.springframework.data.r2dbc.dialect.ArrayColumns, org.springframework.data.relational.core.mapping.RelationalPersistentProperty, java.lang.Object) + */ + @Override public Object getArrayValue(ArrayColumns arrayColumns, RelationalPersistentProperty property, Object value) { Class targetType = arrayColumns.getArrayType(property.getActualType()); @@ -337,6 +366,7 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R * @param object must not be {@literal null}. * @return */ + @Override @SuppressWarnings("unchecked") public BiFunction populateIdIfNecessary(T object) { diff --git a/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverter.java b/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverter.java index 4852235..ed9d345 100644 --- a/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverter.java +++ b/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverter.java @@ -71,4 +71,14 @@ public interface R2dbcConverter * @return */ BiFunction populateIdIfNecessary(T object); + + /** + * Reads the given source into the given type. + * + * @param type they type to convert the given source to. + * @param source the source to create an object of the given type from. + * @param metadata the {@link RowMetadata}. + * @return + */ + R read(Class type, Row source, RowMetadata metadata); } diff --git a/src/test/java/org/springframework/data/r2dbc/convert/EntityRowMapperUnitTests.java b/src/test/java/org/springframework/data/r2dbc/convert/EntityRowMapperUnitTests.java index 215591e..360b528 100644 --- a/src/test/java/org/springframework/data/r2dbc/convert/EntityRowMapperUnitTests.java +++ b/src/test/java/org/springframework/data/r2dbc/convert/EntityRowMapperUnitTests.java @@ -7,13 +7,15 @@ import io.r2dbc.spi.Row; import io.r2dbc.spi.RowMetadata; import lombok.RequiredArgsConstructor; +import java.util.Collection; import java.util.List; import java.util.Set; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.data.r2dbc.convert.EntityRowMapper; + import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy; import org.springframework.data.r2dbc.dialect.PostgresDialect; @@ -30,6 +32,14 @@ public class EntityRowMapperUnitTests { Row rowMock = mock(Row.class); RowMetadata metadata = mock(RowMetadata.class); + Collection columns = mock(Collection.class); + + @Before + public void before() { + + when(columns.contains(anyString())).thenReturn(true); + when(metadata.getColumnNames()).thenReturn(columns); + } @Test // gh-22 public void shouldMapSimpleEntity() { diff --git a/src/test/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategyTestSupport.java b/src/test/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategyTestSupport.java index 1c43aff..8be51db 100644 --- a/src/test/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategyTestSupport.java +++ b/src/test/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategyTestSupport.java @@ -29,13 +29,13 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; +import java.util.Collection; import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.Function; import org.junit.Test; -import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy; -import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy; + import org.springframework.data.r2dbc.dialect.Dialect; import org.springframework.data.r2dbc.mapping.SettableValue; @@ -177,6 +177,9 @@ public abstract class ReactiveDataAccessStrategyTestSupport { ReactiveDataAccessStrategy strategy = getStrategy(); Row rowMock = mock(Row.class); RowMetadata metadataMock = mock(RowMetadata.class); + Collection columnNames = mock(Collection.class); + when(metadataMock.getColumnNames()).thenReturn(columnNames); + when(columnNames.contains(fieldname)).thenReturn(true); PrimitiveTypes toSave = new PrimitiveTypes(); setter.accept(toSave, testValue); diff --git a/src/test/java/org/springframework/data/r2dbc/repository/H2R2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/H2R2dbcRepositoryIntegrationTests.java index 9625735..0c7b802 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/H2R2dbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/H2R2dbcRepositoryIntegrationTests.java @@ -81,7 +81,7 @@ public class H2R2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIn Flux findByNameContains(String name); @Override - @Query("SELECT * FROM legoset") + @Query("SELECT name FROM legoset") Flux findAsProjection(); @Override diff --git a/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java index 5faf2cd..aeb61e8 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java @@ -85,7 +85,7 @@ public class MySqlR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositor Flux findByNameContains(String name); @Override - @Query("SELECT * FROM legoset") + @Query("SELECT name FROM legoset") Flux findAsProjection(); @Override diff --git a/src/test/java/org/springframework/data/r2dbc/repository/PostgresR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/PostgresR2dbcRepositoryIntegrationTests.java index 4107fce..7fc6977 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/PostgresR2dbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/PostgresR2dbcRepositoryIntegrationTests.java @@ -85,7 +85,7 @@ public class PostgresR2dbcRepositoryIntegrationTests extends AbstractR2dbcReposi Flux findByNameContains(String name); @Override - @Query("SELECT * FROM legoset") + @Query("SELECT name FROM legoset") Flux findAsProjection(); @Override diff --git a/src/test/java/org/springframework/data/r2dbc/repository/SqlServerR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/SqlServerR2dbcRepositoryIntegrationTests.java index 719d188..ca2f790 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/SqlServerR2dbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/SqlServerR2dbcRepositoryIntegrationTests.java @@ -90,7 +90,7 @@ public class SqlServerR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepos Flux findByNameContains(String name); @Override - @Query("SELECT * FROM legoset") + @Query("SELECT name FROM legoset") Flux findAsProjection(); @Override