Browse Source

DATAJDBC-181 - Use NamingStrategy for instantiating entities.

The ResultSetParameterValueProvider uses the relevant property for a requested parameter in order to obtain the proper column name to use.

Improved mocking of ResultSets in tests to actually fail when a non existing column gets requested instead of just returning null.
pull/47/merge
Jens Schauder 8 years ago committed by Greg Turnquist
parent
commit
18bf6ca77c
No known key found for this signature in database
GPG Key ID: CB2FA4D512B5C413
  1. 26
      src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java
  2. 65
      src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java

26
src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java

@ -16,6 +16,7 @@
package org.springframework.data.jdbc.core; package org.springframework.data.jdbc.core;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ClassGeneratingEntityInstantiator; import org.springframework.data.convert.ClassGeneratingEntityInstantiator;
@ -94,7 +95,7 @@ public class EntityRowMapper<T> implements RowMapper<T> {
} }
private T createInstance(ResultSet rs) { private T createInstance(ResultSet rs) {
return instantiator.createInstance(entity, ResultSetParameterValueProvider.of(rs, conversions, "")); return instantiator.createInstance(entity, new ResultSetParameterValueProvider(rs, entity, conversions, ""));
} }
private Object readFrom(ResultSet resultSet, JdbcPersistentProperty property, String prefix) { private Object readFrom(ResultSet resultSet, JdbcPersistentProperty property, String prefix) {
@ -123,7 +124,7 @@ public class EntityRowMapper<T> implements RowMapper<T> {
return null; return null;
} }
S instance = instantiator.createInstance(entity, ResultSetParameterValueProvider.of(rs, conversions, prefix)); S instance = instantiator.createInstance(entity, new ResultSetParameterValueProvider(rs, entity, conversions, prefix));
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(instance); PersistentPropertyAccessor accessor = entity.getPropertyAccessor(instance);
ConvertingPropertyAccessor propertyAccessor = new ConvertingPropertyAccessor(accessor, conversions); ConvertingPropertyAccessor propertyAccessor = new ConvertingPropertyAccessor(accessor, conversions);
@ -135,27 +136,18 @@ public class EntityRowMapper<T> implements RowMapper<T> {
return instance; return instance;
} }
@RequiredArgsConstructor
private static class ResultSetParameterValueProvider implements ParameterValueProvider<JdbcPersistentProperty> { private static class ResultSetParameterValueProvider implements ParameterValueProvider<JdbcPersistentProperty> {
@NonNull @NonNull
private final ResultSet resultSet; private final ResultSet resultSet;
@NonNull @NonNull
private final JdbcPersistentEntity<?> entity;
@NonNull
private final ConversionService conversionService; private final ConversionService conversionService;
@NonNull @NonNull
private final String prefix; private final String prefix;
private ResultSetParameterValueProvider(ResultSet resultSet, ConversionService conversionService, String prefix) {
this.resultSet = resultSet;
this.conversionService = conversionService;
this.prefix = prefix;
}
public static ResultSetParameterValueProvider of(ResultSet resultSet, ConversionService conversionService,
String prefix) {
return new ResultSetParameterValueProvider(resultSet, conversionService, prefix);
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter) * @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
@ -163,12 +155,8 @@ public class EntityRowMapper<T> implements RowMapper<T> {
@Override @Override
public <T> T getParameterValue(Parameter<T, JdbcPersistentProperty> parameter) { public <T> T getParameterValue(Parameter<T, JdbcPersistentProperty> parameter) {
String name = parameter.getName(); String column = prefix + entity.getRequiredPersistentProperty(parameter.getName()).getColumnName();
if (name == null) {
return null;
}
String column = prefix + name;
try { try {
return conversionService.convert(resultSet.getObject(column), parameter.getType().getType()); return conversionService.convert(resultSet.getObject(column), parameter.getType().getType());
} catch (SQLException o_O) { } catch (SQLException o_O) {

65
src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java

@ -23,9 +23,12 @@ import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService; import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.convert.Jsr310Converters; import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext; import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty; import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.repository.query.Param;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -54,6 +57,12 @@ public class EntityRowMapperUnitTests {
public static final long ID_FOR_ENTITY_REFERENCING_MAP = 42L; public static final long ID_FOR_ENTITY_REFERENCING_MAP = 42L;
public static final long ID_FOR_ENTITY_REFERENCING_LIST = 4711L; public static final long ID_FOR_ENTITY_REFERENCING_LIST = 4711L;
public static final long ID_FOR_ENTITY_NOT_REFERENCING_MAP = 23L; public static final long ID_FOR_ENTITY_NOT_REFERENCING_MAP = 23L;
public static final DefaultNamingStrategy X_APPENDING_NAMINGSTRATEGY = new DefaultNamingStrategy() {
@Override
public String getColumnName(JdbcPersistentProperty property) {
return super.getColumnName(property) + "x";
}
};
@Test // DATAJDBC-113 @Test // DATAJDBC-113
public void simpleEntitiesGetProperlyExtracted() throws SQLException { public void simpleEntitiesGetProperlyExtracted() throws SQLException {
@ -70,6 +79,36 @@ public class EntityRowMapperUnitTests {
.containsExactly(ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha"); .containsExactly(ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
} }
@Test // DATAJDBC-181
public void namingStrategyGetsHonored() throws SQLException {
ResultSet rs = mockResultSet(asList("idx", "namex"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
rs.next();
Trivial extracted = createRowMapper(Trivial.class, X_APPENDING_NAMINGSTRATEGY).mapRow(rs, 1);
assertThat(extracted) //
.isNotNull() //
.extracting(e -> e.id, e -> e.name) //
.containsExactly(ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
}
@Test // DATAJDBC-181
public void namingStrategyGetsHonoredForConstructor() throws SQLException {
ResultSet rs = mockResultSet(asList("idx", "namex"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
rs.next();
TrivialImmutable extracted = createRowMapper(TrivialImmutable.class, X_APPENDING_NAMINGSTRATEGY).mapRow(rs, 1);
assertThat(extracted) //
.isNotNull() //
.extracting(e -> e.id, e -> e.name) //
.containsExactly(ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
}
@Test // DATAJDBC-113 @Test // DATAJDBC-113
public void simpleOneToOneGetsProperlyExtracted() throws SQLException { public void simpleOneToOneGetsProperlyExtracted() throws SQLException {
@ -131,8 +170,18 @@ public class EntityRowMapperUnitTests {
} }
private <T> EntityRowMapper<T> createRowMapper(Class<T> type) { private <T> EntityRowMapper<T> createRowMapper(Class<T> type) {
return createRowMapper(type, new DefaultNamingStrategy());
}
private <T> EntityRowMapper<T> createRowMapper(Class<T> type, NamingStrategy namingStrategy) {
JdbcMappingContext context = new JdbcMappingContext( //
namingStrategy, //
mock(NamedParameterJdbcOperations.class), //
__ -> {
} //
);
JdbcMappingContext context = new JdbcMappingContext(mock(NamedParameterJdbcOperations.class));
DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class); DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class);
// the ID of the entity is used to determine what kind of ResultSet is needed for subsequent selects. // the ID of the entity is used to determine what kind of ResultSet is needed for subsequent selects.
@ -240,7 +289,12 @@ public class EntityRowMapperUnitTests {
} }
private Object getObject(String column) { private Object getObject(String column) {
return values.get(index).get(column);
Map<String, Object> rowMap = values.get(index);
Assert.isTrue(rowMap.containsKey(column), String.format("Trying to access a column (%s) that does not exist", column));
return rowMap.get(column);
} }
private boolean next() { private boolean next() {
@ -251,6 +305,13 @@ public class EntityRowMapperUnitTests {
} }
@RequiredArgsConstructor @RequiredArgsConstructor
static class TrivialImmutable {
@Id
private final Long id;
private final String name;
}
static class Trivial { static class Trivial {
@Id @Id

Loading…
Cancel
Save