From c0803ddafef7a4bc4ec070df6581d46c4d59ff4a Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 31 Mar 2020 11:45:53 +0200 Subject: [PATCH] DATAJDBC-341 - Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove SpecialColumnValue in favor of JdbcPropertyValueProvider.hasProperty(…). Rename ResultSetWrapper to ResultSetAccessor. Replace ResultSetAccessor usage in JdbcConverter with ResultSet to avoid visibility issues. Original pull request: #201. --- .../jdbc/core/convert/BasicJdbcConverter.java | 97 ++++++++----------- .../jdbc/core/convert/EntityRowMapper.java | 6 +- ...dbcBackReferencePropertyValueProvider.java | 23 +++-- .../data/jdbc/core/convert/JdbcConverter.java | 4 +- .../convert/JdbcPropertyValueProvider.java | 31 ++++-- .../jdbc/core/convert/MapEntityRowMapper.java | 2 +- ...SetWrapper.java => ResultSetAccessor.java} | 48 ++++++--- .../convert/EntityRowMapperUnitTests.java | 45 +++------ src/main/asciidoc/jdbc.adoc | 10 +- src/main/asciidoc/new-features.adoc | 2 +- 10 files changed, 137 insertions(+), 131 deletions(-) rename spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/{ResultSetWrapper.java => ResultSetAccessor.java} (61%) diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java index a7d1bf84..afd78e6e 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java @@ -24,18 +24,16 @@ import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.CustomConversions; -import org.springframework.data.jdbc.core.convert.ResultSetWrapper.SpecialColumnValue; import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.data.jdbc.support.JdbcUtil; -import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mapping.model.PropertyValueProvider; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.relational.core.conversion.BasicRelationalConverter; import org.springframework.data.relational.core.conversion.RelationalConverter; @@ -77,7 +75,8 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc /** * Creates a new {@link BasicRelationalConverter} given {@link MappingContext} and a * {@link JdbcTypeFactory#unsupported() no-op type factory} throwing {@link UnsupportedOperationException} on type - * creation. Use {@link #BasicJdbcConverter(MappingContext, RelationResolver, CustomConversions, JdbcTypeFactory, IdentifierProcessing)} + * creation. Use + * {@link #BasicJdbcConverter(MappingContext, RelationResolver, CustomConversions, JdbcTypeFactory, IdentifierProcessing)} * (MappingContext, RelationResolver, JdbcTypeFactory)} to convert arrays and large objects into JDBC-specific types. * * @param context must not be {@literal null}. @@ -106,9 +105,9 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc * @since 2.0 */ public BasicJdbcConverter( - MappingContext, ? extends RelationalPersistentProperty> context, - RelationResolver relationResolver, CustomConversions conversions, JdbcTypeFactory typeFactory, - IdentifierProcessing identifierProcessing) { + MappingContext, ? extends RelationalPersistentProperty> context, + RelationResolver relationResolver, CustomConversions conversions, JdbcTypeFactory typeFactory, + IdentifierProcessing identifierProcessing) { super(context, conversions); @@ -199,7 +198,7 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc @Nullable public Object readValue(@Nullable Object value, TypeInformation type) { - if (value == null || value == SpecialColumnValue.NO_SUCH_COLUMN) { + if (value == null) { return value; } @@ -320,36 +319,31 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc return null; } - @SuppressWarnings("unchecked") @Override - public T mapRow(RelationalPersistentEntity entity, ResultSetWrapper resultSet, Object key) { - return new ReadingContext( - new PersistentPropertyPathExtension( - getMappingContext(), entity), - resultSet, Identifier.empty(), key).mapRow(); + public T mapRow(RelationalPersistentEntity entity, ResultSet resultSet, Object key) { + return new ReadingContext(new PersistentPropertyPathExtension(getMappingContext(), entity), + new ResultSetAccessor(resultSet), Identifier.empty(), key).mapRow(); } @Override - public T mapRow(PersistentPropertyPathExtension path, ResultSetWrapper resultSet, Identifier identifier, - Object key) { - return new ReadingContext(path, resultSet, identifier, key).mapRow(); + public T mapRow(PersistentPropertyPathExtension path, ResultSet resultSet, Identifier identifier, Object key) { + return new ReadingContext(path, new ResultSetAccessor(resultSet), identifier, key).mapRow(); } private class ReadingContext { private final RelationalPersistentEntity entity; - private final ResultSetWrapper resultSet; private final PersistentPropertyPathExtension rootPath; private final PersistentPropertyPathExtension path; private final Identifier identifier; private final Object key; - private final PropertyValueProvider propertyValueProvider; - private final PropertyValueProvider backReferencePropertyValueProvider; + private final JdbcPropertyValueProvider propertyValueProvider; + private final JdbcBackReferencePropertyValueProvider backReferencePropertyValueProvider; @SuppressWarnings("unchecked") - private ReadingContext(PersistentPropertyPathExtension rootPath, ResultSetWrapper resultSet, Identifier identifier, + private ReadingContext(PersistentPropertyPathExtension rootPath, ResultSetAccessor accessor, Identifier identifier, Object key) { RelationalPersistentEntity entity = (RelationalPersistentEntity) rootPath.getLeafEntity(); @@ -357,38 +351,33 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc Assert.notNull(entity, "The rootPath must point to an entity."); this.entity = entity; - this.resultSet = resultSet; this.rootPath = rootPath; - this.path = new PersistentPropertyPathExtension( - getMappingContext(), - this.entity); + this.path = new PersistentPropertyPathExtension(getMappingContext(), this.entity); this.identifier = identifier; this.key = key; - propertyValueProvider = new JdbcPropertyValueProvider(identifierProcessing, path, resultSet); - backReferencePropertyValueProvider = new JdbcBackReferencePropertyValueProvider(identifierProcessing, path, - resultSet); + this.propertyValueProvider = new JdbcPropertyValueProvider(identifierProcessing, path, accessor); + this.backReferencePropertyValueProvider = new JdbcBackReferencePropertyValueProvider(identifierProcessing, path, + accessor); } - private ReadingContext(RelationalPersistentEntity entity, ResultSetWrapper resultSet, - PersistentPropertyPathExtension rootPath, PersistentPropertyPathExtension path, Identifier identifier, - Object key) { - + private ReadingContext(RelationalPersistentEntity entity, PersistentPropertyPathExtension rootPath, + PersistentPropertyPathExtension path, Identifier identifier, Object key, + JdbcPropertyValueProvider propertyValueProvider, + JdbcBackReferencePropertyValueProvider backReferencePropertyValueProvider) { this.entity = entity; - this.resultSet = resultSet; this.rootPath = rootPath; this.path = path; this.identifier = identifier; this.key = key; - - propertyValueProvider = new JdbcPropertyValueProvider(identifierProcessing, path, resultSet); - backReferencePropertyValueProvider = new JdbcBackReferencePropertyValueProvider(identifierProcessing, path, - resultSet); + this.propertyValueProvider = propertyValueProvider; + this.backReferencePropertyValueProvider = backReferencePropertyValueProvider; } private ReadingContext extendBy(RelationalPersistentProperty property) { return new ReadingContext<>( (RelationalPersistentEntity) getMappingContext().getRequiredPersistentEntity(property.getActualType()), - resultSet, rootPath.extendBy(property), path.extendBy(property), identifier, key); + rootPath.extendBy(property), path.extendBy(property), identifier, key, + propertyValueProvider.extendBy(property), backReferencePropertyValueProvider.extendBy(property)); } T mapRow() { @@ -412,11 +401,16 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc continue; } - Object value = readOrLoadProperty(idValue, property); - if (value != SpecialColumnValue.NO_SUCH_COLUMN) { - propertyAccessor.setProperty(property, value); + // skip absent simple properties + if (isSimpleProperty(property)) { + + if (!propertyValueProvider.hasProperty(property)) { + continue; + } } + Object value = readOrLoadProperty(idValue, property); + propertyAccessor.setProperty(property, value); } return propertyAccessor.getBean(); @@ -467,8 +461,7 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc } Object value = propertyValueProvider.getPropertyValue(property); - return value == SpecialColumnValue.NO_SUCH_COLUMN ? SpecialColumnValue.NO_SUCH_COLUMN - : readValue(value, property.getTypeInformation()); + return value != null ? readValue(value, property.getTypeInformation()) : null; } @Nullable @@ -501,7 +494,7 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc } Object value = readOrLoadProperty(idValue, embeddedProperty); - if (value != null && value != SpecialColumnValue.NO_SUCH_COLUMN) { + if (value != null) { return true; } } @@ -541,21 +534,15 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc Assert.notNull(parameterName, "A constructor parameter name must not be null to be used with Spring Data JDBC"); RelationalPersistentProperty property = entity.getRequiredPersistentProperty(parameterName); - - Object value = readOrLoadProperty(idValue, property); - - if (value == SpecialColumnValue.NO_SUCH_COLUMN) { - - throw new MappingException( - String.format("Couldn't create instance of type %s with id. No column found for argument named '%s'.", - entity.getType(), parameterName)); - } - - return value; + return readOrLoadProperty(idValue, property); }); return populateProperties(instance, idValue); } } + private boolean isSimpleProperty(RelationalPersistentProperty property) { + return !property.isCollectionLike() && !property.isEntity() && !property.isMap() && !property.isEmbedded(); + } + } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/EntityRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/EntityRowMapper.java index 80a37309..a45a57ac 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/EntityRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/EntityRowMapper.java @@ -64,11 +64,9 @@ public class EntityRowMapper implements RowMapper { @Override public T mapRow(ResultSet resultSet, int rowNumber) { - ResultSetWrapper wrappedResultSet = new ResultSetWrapper(resultSet); - return path == null // - ? converter.mapRow(entity, wrappedResultSet, rowNumber) // - : converter.mapRow(path, wrappedResultSet, identifier, rowNumber); + ? converter.mapRow(entity, resultSet, rowNumber) // + : converter.mapRow(path, resultSet, identifier, rowNumber); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcBackReferencePropertyValueProvider.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcBackReferencePropertyValueProvider.java index 54f5627e..9d0723a8 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcBackReferencePropertyValueProvider.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcBackReferencePropertyValueProvider.java @@ -21,9 +21,8 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp import org.springframework.data.relational.core.sql.IdentifierProcessing; /** - * {@link PropertyValueProvider} obtaining values from a {@link ResultSetWrapper}. - * For a given id property it provides the value in the resultset under which other entities refer back to it. - * + * {@link PropertyValueProvider} obtaining values from a {@link ResultSetAccessor}. For a given id property it provides + * the value in the resultset under which other entities refer back to it. * * @author Jens Schauder * @since 2.0 @@ -32,15 +31,16 @@ class JdbcBackReferencePropertyValueProvider implements PropertyValueProvider T getPropertyValue(RelationalPersistentProperty property) { - return (T)resultSet.getObject(basePath.extendBy(property).getReverseColumnNameAlias().getReference(identifierProcessing)); + return (T) resultSet + .getObject(basePath.extendBy(property).getReverseColumnNameAlias().getReference(identifierProcessing)); + } + + public JdbcBackReferencePropertyValueProvider extendBy(RelationalPersistentProperty property) { + return new JdbcBackReferencePropertyValueProvider(identifierProcessing, basePath.extendBy(property), resultSet); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java index cc773dec..55a3e35d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java @@ -54,7 +54,7 @@ public interface JdbcConverter extends RelationalConverter { * @param * @return */ - T mapRow(RelationalPersistentEntity entity, ResultSetWrapper resultSet, Object key); + T mapRow(RelationalPersistentEntity entity, ResultSet resultSet, Object key); /** * Read the current row from {@link ResultSet} to an {@link PersistentPropertyPathExtension#getActualType() entity}. @@ -66,7 +66,7 @@ public interface JdbcConverter extends RelationalConverter { * @param * @return */ - T mapRow(PersistentPropertyPathExtension path, ResultSetWrapper resultSet, Identifier identifier, Object key); + T mapRow(PersistentPropertyPathExtension path, ResultSet resultSet, Identifier identifier, Object key); /** * The type to be used to store this property in the database. Multidimensional arrays are unwrapped to reflect a diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcPropertyValueProvider.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcPropertyValueProvider.java index a979becf..3521ffd1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcPropertyValueProvider.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcPropertyValueProvider.java @@ -21,7 +21,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp import org.springframework.data.relational.core.sql.IdentifierProcessing; /** - * {@link PropertyValueProvider} obtaining values from a {@link ResultSetWrapper}. + * {@link PropertyValueProvider} obtaining values from a {@link ResultSetAccessor}. * * @author Jens Schauder * @since 2.0 @@ -30,15 +30,16 @@ class JdbcPropertyValueProvider implements PropertyValueProvider T getPropertyValue(RelationalPersistentProperty property) { + return (T) resultSet.getObject(getColumnName(property)); + } - String columnName = basePath.extendBy(property).getColumnAlias().getReference(identifierProcessing); - return (T) resultSet.getObject(columnName); + /** + * Returns whether the underlying source contains a data source for the given {@link RelationalPersistentProperty}. + * + * @param property + * @return + */ + public boolean hasProperty(RelationalPersistentProperty property) { + return resultSet.hasValue(getColumnName(property)); + } + + private String getColumnName(RelationalPersistentProperty property) { + return basePath.extendBy(property).getColumnAlias().getReference(identifierProcessing); + } + + public JdbcPropertyValueProvider extendBy(RelationalPersistentProperty property) { + return new JdbcPropertyValueProvider(identifierProcessing, basePath.extendBy(property), resultSet); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java index 4309482c..9b29bf33 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java @@ -54,6 +54,6 @@ class MapEntityRowMapper implements RowMapper> { } private T mapEntity(ResultSet resultSet, Object key) { - return converter.mapRow(path, new ResultSetWrapper(resultSet), identifier, key); + return converter.mapRow(path, resultSet, identifier, key); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetWrapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessor.java similarity index 61% rename from spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetWrapper.java rename to spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessor.java index 7529e724..63eec6da 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetWrapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessor.java @@ -27,25 +27,29 @@ import org.springframework.lang.Nullable; import org.springframework.util.LinkedCaseInsensitiveMap; /** - * Wraps a {@link java.sql.ResultSet} in order to provide fast lookup of columns by name, including for missing columns. + * Wrapper value object for a {@link java.sql.ResultSet} to be able to access raw values by + * {@link org.springframework.data.relational.core.mapping.RelationalPersistentProperty} references. Provides fast + * lookup of columns by name, including for absent columns. * * @author Jens Schauder + * @author Mark Paluch + * @since 2.0 */ -class ResultSetWrapper { +class ResultSetAccessor { - private static final Logger LOG = LoggerFactory.getLogger(ResultSetWrapper.class); + private static final Logger LOG = LoggerFactory.getLogger(ResultSetAccessor.class); private final ResultSet resultSet; private final Map indexLookUp; - ResultSetWrapper(ResultSet resultSet) { + ResultSetAccessor(ResultSet resultSet) { this.resultSet = resultSet; - indexLookUp = indexColumns(); + this.indexLookUp = indexColumns(resultSet); } - private Map indexColumns() { + private static Map indexColumns(ResultSet resultSet) { try { @@ -57,29 +61,35 @@ class ResultSetWrapper { for (int i = 1; i <= columnCount; i++) { String label = metaData.getColumnLabel(i); + if (index.containsKey(label)) { - LOG.warn("We encountered a ResutSet with multiple columns associated with the same label {}.", label); + LOG.warn("ResultSet contains {} multiple times", label); continue; } - index.put(label, i); + index.put(label, i); } return index; } catch (SQLException se) { - throw new MappingException("Failure while accessing metadata."); + throw new MappingException("Cannot obtain result metadata", se); } - } + /** + * Returns the value if the result set contains the {@code columnName}. + * + * @param columnName the column name (label). + * @return + * @see ResultSet#getObject(int) + */ @Nullable - Object getObject(String columnName) { + public Object getObject(String columnName) { try { - int column = findColumnIndex(columnName); - - return column > 0 ? resultSet.getObject(column) : SpecialColumnValue.NO_SUCH_COLUMN; + int index = findColumnIndex(columnName); + return index > 0 ? resultSet.getObject(index) : null; } catch (SQLException o_O) { throw new MappingException(String.format("Could not read value %s from result set!", columnName), o_O); } @@ -89,7 +99,13 @@ class ResultSetWrapper { return indexLookUp.getOrDefault(columnName, -1); } - enum SpecialColumnValue { - NO_SUCH_COLUMN + /** + * Returns {@literal true} if the result set contains the {@code columnName}. + * + * @param columnName the column name (label). + * @return + */ + public boolean hasValue(String columnName) { + return indexLookUp.containsKey(columnName); } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java index f335fdf3..b9d1d7c1 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java @@ -43,17 +43,16 @@ import java.util.stream.Stream; import javax.naming.OperationNotSupportedException; -import org.assertj.core.api.Assertions; import org.assertj.core.api.SoftAssertions; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; + import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; -import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; @@ -476,10 +475,10 @@ public class EntityRowMapperUnitTests { ID_FOR_ENTITY_NOT_REFERENCING_MAP); rs.next(); - assertThatThrownBy(() -> createRowMapper(TrivialImmutable.class).mapRow(rs, 1)) // - .hasMessageContaining("TrivialImmutable") // - .hasMessageContaining("name"); + TrivialImmutable trivialImmutable = createRowMapper(TrivialImmutable.class).mapRow(rs, 1); + assertThat(trivialImmutable.id).isEqualTo(23L); + assertThat(trivialImmutable.name).isNull(); } @Test // DATAJDBC-341 @@ -530,16 +529,15 @@ public class EntityRowMapperUnitTests { } @Test // DATAJDBC-341 - public void immutableEmbeddedWithSomeColumnsMissingShouldThrowAnException() throws SQLException { + public void immutableEmbeddedWithSomeColumnsMissingShouldNotBeEmpty() throws SQLException { ResultSet rs = mockResultSet(asList("ID", "VALUE"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "some value"); rs.next(); - Assertions.assertThatThrownBy( // - () -> createRowMapper(WithNullableEmbeddedImmutableValue.class).mapRow(rs, 1) // - ).isInstanceOf(MappingException.class) // - .hasMessageContaining("'name'"); + WithNullableEmbeddedImmutableValue result = createRowMapper(WithNullableEmbeddedImmutableValue.class).mapRow(rs, 1); + + assertThat(result.embeddedImmutableValue).isNotNull(); } @Test // DATAJDBC-341 @@ -619,19 +617,6 @@ public class EntityRowMapperUnitTests { .containsExactly(ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24L, null); } - @Test // DATAJDBC-341 - public void immutableOneToOneWithMissingColumnResultsInException() throws SQLException { - - ResultSet rs = mockResultSet(asList("ID", "NAME", "CHILD_ID"), // - ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24L); - rs.next(); - - Assertions.assertThatThrownBy( // - () -> createRowMapper(OneToOneImmutable.class).mapRow(rs, 1) // - ).isInstanceOf(MappingException.class) // - .hasMessageContaining("'name'"); - } - @Test // DATAJDBC-341 public void oneToOneWithMissingIdColumnResultsInNullProperty() throws SQLException { @@ -641,10 +626,7 @@ public class EntityRowMapperUnitTests { OneToOne extracted = createRowMapper(OneToOne.class).mapRow(rs, 1); - assertThat(extracted) // - .isNotNull() // - .extracting(e -> e.id, e -> e.name, e -> e.child.id, e -> e.child.name) // - .containsExactly(ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", null, "Alfred"); + assertThat(extracted.child).isNull(); } @Test // DATAJDBC-341 @@ -654,10 +636,11 @@ public class EntityRowMapperUnitTests { ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", "Alfred"); rs.next(); - Assertions.assertThatThrownBy( // - () -> createRowMapper(OneToOneImmutable.class).mapRow(rs, 1) // - ).isInstanceOf(MappingException.class) // - .hasMessageContaining("'id'"); + OneToOneImmutable result = createRowMapper(OneToOneImmutable.class).mapRow(rs, 1); + + assertThat(result.id).isEqualTo(23); + assertThat(result.name).isEqualTo("alpha"); + assertThat(result.child).isNull(); } // Model classes to be used in tests diff --git a/src/main/asciidoc/jdbc.adoc b/src/main/asciidoc/jdbc.adoc index a411cecc..37ee42ef 100644 --- a/src/main/asciidoc/jdbc.adoc +++ b/src/main/asciidoc/jdbc.adoc @@ -444,15 +444,15 @@ For converting the query result into entities the same `RowMapper` is used by de The query you provide must match the format the `RowMapper` expects. Columns for all properties that are used in the constructor of an entity must be provided. Columns for properties that get set via setter, wither or field access are optional. -Properties that don't have a matching column will not get set. -The query is used for populating the aggregate root, embedded entities and one to one relationships including arrays of primitive types which get stored and loaded as SQL-array-types. -For maps, lists, sets and arrays of entities separate queries get generated. +Properties that don't have a matching column in the result will not be set. +The query is used for populating the aggregate root, embedded entities and one-to-one relationships including arrays of primitive types which get stored and loaded as SQL-array-types. +Separate queries are generated for maps, lists, sets and arrays of entities. -NOTE: Spring fully supports Java 8’s parameter name discovery based on the `-parameters` compiler flag. By using this flag in your build as an alternative to debug information, you can omit the `@Param` annotation for named parameters. +NOTE: Spring fully supports Java 8’s parameter name discovery based on the `-parameters` compiler flag. +By using this flag in your build as an alternative to debug information, you can omit the `@Param` annotation for named parameters. NOTE: Spring Data JDBC supports only named parameters. - [[jdbc.query-methods.named-query]] === Named Queries diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index d54f35ba..d50f9237 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -10,7 +10,7 @@ This section covers the significant changes for each version. * Support for `PagingAndSortingRepository`. * Full Support for H2. * All SQL identifiers know get quoted by default. -* Missing column no longer cause exceptions as long as they are not required by the persitence constructor. +* Missing columns no longer cause exceptions. [[new-features.1-1-0]] == What's New in Spring Data JDBC 1.1