Skip to content

Commit 0ebb4a7

Browse files
schaudermp911de
authored andcommitted
DATAJDBC-102 - Determine the EntityInstantiator to be used dynamically.
Replaced the direct use of EntityInstantiator with EntityInstantiators. Moved it into the MappingContext because instantiation is part of the mapping process. Original pull request: spring-projects#68.
1 parent 1747639 commit 0ebb4a7

File tree

11 files changed

+106
-42
lines changed

11 files changed

+106
-42
lines changed

src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.dao.EmptyResultDataAccessException;
2828
import org.springframework.dao.InvalidDataAccessApiUsageException;
2929
import org.springframework.dao.NonTransientDataAccessException;
30+
import org.springframework.data.convert.EntityInstantiators;
3031
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
3132
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
3233
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
@@ -58,18 +59,20 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
5859
private final @NonNull SqlGeneratorSource sqlGeneratorSource;
5960
private final @NonNull JdbcMappingContext context;
6061
private final @NonNull NamedParameterJdbcOperations operations;
62+
private final @NonNull EntityInstantiators instantiators;
6163
private final @NonNull DataAccessStrategy accessStrategy;
6264

6365
/**
6466
* Creates a {@link DefaultDataAccessStrategy} which references it self for resolution of recursive data accesses.
6567
* Only suitable if this is the only access strategy in use.
6668
*/
6769
public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, JdbcMappingContext context,
68-
NamedParameterJdbcOperations operations) {
70+
NamedParameterJdbcOperations operations, EntityInstantiators instantiators) {
6971

7072
this.sqlGeneratorSource = sqlGeneratorSource;
7173
this.operations = operations;
7274
this.context = context;
75+
this.instantiators = instantiators;
7376
this.accessStrategy = this;
7477
}
7578

@@ -321,7 +324,7 @@ private <S> Optional<Object> getIdFromHolder(KeyHolder holder, JdbcPersistentEnt
321324
}
322325

323326
public <T> EntityRowMapper<T> getEntityRowMapper(Class<T> domainType) {
324-
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), context, accessStrategy);
327+
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), context, instantiators, accessStrategy);
325328
}
326329

327330
private RowMapper getMapEntityRowMapper(JdbcPersistentProperty property) {

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424

2525
import org.springframework.core.convert.ConversionService;
2626
import org.springframework.core.convert.converter.Converter;
27-
import org.springframework.data.convert.ClassGeneratingEntityInstantiator;
28-
import org.springframework.data.convert.EntityInstantiator;
27+
import org.springframework.data.convert.EntityInstantiators;
2928
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
3029
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
3130
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
@@ -49,18 +48,20 @@ public class EntityRowMapper<T> implements RowMapper<T> {
4948
private static final Converter<Iterable<?>, Map<?, ?>> ITERABLE_OF_ENTRY_TO_MAP_CONVERTER = new IterableOfEntryToMapConverter();
5049

5150
private final JdbcPersistentEntity<T> entity;
52-
private final EntityInstantiator instantiator = new ClassGeneratingEntityInstantiator();
51+
5352
private final ConversionService conversions;
5453
private final JdbcMappingContext context;
5554
private final DataAccessStrategy accessStrategy;
5655
private final JdbcPersistentProperty idProperty;
56+
private final EntityInstantiators instantiators;
5757

58-
public EntityRowMapper(JdbcPersistentEntity<T> entity, JdbcMappingContext context,
58+
public EntityRowMapper(JdbcPersistentEntity<T> entity, JdbcMappingContext context, EntityInstantiators instantiators,
5959
DataAccessStrategy accessStrategy) {
6060

6161
this.entity = entity;
6262
this.conversions = context.getConversions();
6363
this.context = context;
64+
this.instantiators = instantiators;
6465
this.accessStrategy = accessStrategy;
6566

6667
idProperty = entity.getIdProperty();
@@ -97,7 +98,9 @@ public T mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
9798
}
9899

99100
private T createInstance(ResultSet rs) {
100-
return instantiator.createInstance(entity, new ResultSetParameterValueProvider(rs, entity, conversions, ""));
101+
102+
return instantiators.getInstantiatorFor(entity) //
103+
.createInstance(entity, new ResultSetParameterValueProvider(rs, entity, conversions, ""));
101104
}
102105

103106
/**
@@ -135,8 +138,8 @@ private <S> S readEntityFrom(ResultSet rs, PersistentProperty<?> property) {
135138
return null;
136139
}
137140

138-
S instance = instantiator.createInstance(entity,
139-
new ResultSetParameterValueProvider(rs, entity, conversions, prefix));
141+
S instance = instantiators.getInstantiatorFor(entity) //
142+
.createInstance(entity, new ResultSetParameterValueProvider(rs, entity, conversions, prefix));
140143

141144
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(instance);
142145
ConvertingPropertyAccessor propertyAccessor = new ConvertingPropertyAccessor(accessor, conversions);

src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.apache.ibatis.session.SqlSession;
2424
import org.mybatis.spring.SqlSessionTemplate;
25+
import org.springframework.data.convert.EntityInstantiators;
2526
import org.springframework.data.jdbc.core.CascadingDataAccessStrategy;
2627
import org.springframework.data.jdbc.core.DataAccessStrategy;
2728
import org.springframework.data.jdbc.core.DefaultDataAccessStrategy;
@@ -58,15 +59,17 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
5859
*/
5960
public static DataAccessStrategy createCombinedAccessStrategy(JdbcMappingContext context,
6061
NamedParameterJdbcOperations operations, SqlSession sqlSession) {
61-
return createCombinedAccessStrategy(context, operations, sqlSession, NamespaceStrategy.DEFAULT_INSTANCE);
62+
return createCombinedAccessStrategy(context, new EntityInstantiators(), operations, sqlSession,
63+
NamespaceStrategy.DEFAULT_INSTANCE);
6264
}
6365

6466
/**
6567
* Create a {@link DataAccessStrategy} that first checks for queries defined by MyBatis and if it doesn't find one
6668
* uses a {@link DefaultDataAccessStrategy}
6769
*/
6870
public static DataAccessStrategy createCombinedAccessStrategy(JdbcMappingContext context,
69-
NamedParameterJdbcOperations operations, SqlSession sqlSession, NamespaceStrategy namespaceStrategy) {
71+
EntityInstantiators instantiators, NamedParameterJdbcOperations operations, SqlSession sqlSession,
72+
NamespaceStrategy namespaceStrategy) {
7073

7174
// the DefaultDataAccessStrategy needs a reference to the returned DataAccessStrategy. This creates a dependency
7275
// cycle. In order to create it, we need something that allows to defer closing the cycle until all the elements are
@@ -79,8 +82,13 @@ public static DataAccessStrategy createCombinedAccessStrategy(JdbcMappingContext
7982
asList(myBatisDataAccessStrategy, delegatingDataAccessStrategy));
8083

8184
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context);
82-
DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy(sqlGeneratorSource, context,
83-
operations, cascadingDataAccessStrategy);
85+
DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy( //
86+
sqlGeneratorSource, //
87+
context, //
88+
operations, //
89+
instantiators, //
90+
cascadingDataAccessStrategy //
91+
);
8492

8593
delegatingDataAccessStrategy.setDelegate(defaultDataAccessStrategy);
8694

@@ -93,8 +101,9 @@ public static DataAccessStrategy createCombinedAccessStrategy(JdbcMappingContext
93101
* Use a {@link SqlSessionTemplate} for {@link SqlSession} or a similar implementation tying the session to the proper
94102
* transaction. Note that the resulting {@link DataAccessStrategy} only handles MyBatis. It does not include the
95103
* functionality of the {@link org.springframework.data.jdbc.core.DefaultDataAccessStrategy} which one normally still
96-
* wants. Use {@link #createCombinedAccessStrategy(JdbcMappingContext, SqlSession)} to create such a
97-
* {@link DataAccessStrategy}.
104+
* wants. Use
105+
* {@link #createCombinedAccessStrategy(JdbcMappingContext, EntityInstantiators, NamedParameterJdbcOperations, SqlSession, NamespaceStrategy)}
106+
* to create such a {@link DataAccessStrategy}.
98107
*
99108
* @param sqlSession Must be non {@literal null}.
100109
*/

src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.lang.reflect.Method;
1919

2020
import org.springframework.core.convert.ConversionService;
21+
import org.springframework.data.convert.EntityInstantiators;
2122
import org.springframework.data.jdbc.core.DataAccessStrategy;
2223
import org.springframework.data.jdbc.core.EntityRowMapper;
2324
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
@@ -43,27 +44,30 @@
4344
class JdbcQueryLookupStrategy implements QueryLookupStrategy {
4445

4546
private final JdbcMappingContext context;
47+
private final EntityInstantiators instantiators;
4648
private final DataAccessStrategy accessStrategy;
4749
private final RowMapperMap rowMapperMap;
48-
private final ConversionService conversionService;
4950
private final NamedParameterJdbcOperations operations;
5051

52+
private final ConversionService conversionService;
53+
5154
/**
5255
* Creates a new {@link JdbcQueryLookupStrategy} for the given {@link JdbcMappingContext}, {@link DataAccessStrategy}
5356
* and {@link RowMapperMap}.
54-
*
57+
*
5558
* @param context must not be {@literal null}.
5659
* @param accessStrategy must not be {@literal null}.
5760
* @param rowMapperMap must not be {@literal null}.
5861
*/
59-
JdbcQueryLookupStrategy(JdbcMappingContext context, DataAccessStrategy accessStrategy, RowMapperMap rowMapperMap,
60-
NamedParameterJdbcOperations operations) {
62+
JdbcQueryLookupStrategy(JdbcMappingContext context, EntityInstantiators instantiators,
63+
DataAccessStrategy accessStrategy, RowMapperMap rowMapperMap, NamedParameterJdbcOperations operations) {
6164

6265
Assert.notNull(context, "JdbcMappingContext must not be null!");
6366
Assert.notNull(accessStrategy, "DataAccessStrategy must not be null!");
6467
Assert.notNull(rowMapperMap, "RowMapperMap must not be null!");
6568

6669
this.context = context;
70+
this.instantiators = instantiators;
6771
this.accessStrategy = accessStrategy;
6872
this.rowMapperMap = rowMapperMap;
6973
this.conversionService = context.getConversions();
@@ -104,6 +108,7 @@ private RowMapper<?> determineDefaultRowMapper(JdbcQueryMethod queryMethod) {
104108
? new EntityRowMapper<>( //
105109
context.getRequiredPersistentEntity(domainType), //
106110
context, //
111+
instantiators, //
107112
accessStrategy) //
108113
: typeMappedRowMapper;
109114
}

src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Optional;
1919

2020
import org.springframework.context.ApplicationEventPublisher;
21+
import org.springframework.data.convert.EntityInstantiators;
2122
import org.springframework.data.jdbc.core.DataAccessStrategy;
2223
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
2324
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
@@ -49,6 +50,7 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
4950
private final NamedParameterJdbcOperations operations;
5051

5152
private RowMapperMap rowMapperMap = RowMapperMap.EMPTY;
53+
private EntityInstantiators instantiators = new EntityInstantiators();
5254

5355
/**
5456
* Creates a new {@link JdbcRepositoryFactory} for the given {@link DataAccessStrategy}, {@link JdbcMappingContext}
@@ -82,6 +84,18 @@ public void setRowMapperMap(RowMapperMap rowMapperMap) {
8284
this.rowMapperMap = rowMapperMap;
8385
}
8486

87+
/**
88+
* Set the {@link EntityInstantiators} used for instantiating entity instances.
89+
*
90+
* @param instantiators Must not be {@code null}.
91+
*/
92+
public void setEntityInstantiators(EntityInstantiators instantiators) {
93+
94+
Assert.notNull(instantiators, "EntityInstantiators must not be null.");
95+
96+
this.instantiators = instantiators;
97+
}
98+
8599
@SuppressWarnings("unchecked")
86100
@Override
87101
public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> aClass) {
@@ -127,6 +141,6 @@ protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrate
127141
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
128142
}
129143

130-
return Optional.of(new JdbcQueryLookupStrategy(context, accessStrategy, rowMapperMap, operations));
144+
return Optional.of(new JdbcQueryLookupStrategy(context, instantiators, accessStrategy, rowMapperMap, operations));
131145
}
132146
}

src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.springframework.beans.factory.annotation.Autowired;
2121
import org.springframework.context.ApplicationEventPublisher;
2222
import org.springframework.context.ApplicationEventPublisherAware;
23+
import org.springframework.data.convert.EntityInstantiators;
2324
import org.springframework.data.jdbc.core.DataAccessStrategy;
2425
import org.springframework.data.jdbc.core.DefaultDataAccessStrategy;
2526
import org.springframework.data.jdbc.core.SqlGeneratorSource;
@@ -49,6 +50,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
4950
private DataAccessStrategy dataAccessStrategy;
5051
private RowMapperMap rowMapperMap = RowMapperMap.EMPTY;
5152
private NamedParameterJdbcOperations operations;
53+
private EntityInstantiators instantiators = new EntityInstantiators();
5254

5355
/**
5456
* Creates a new {@link JdbcRepositoryFactoryBean} for the given repository interface.
@@ -115,6 +117,11 @@ public void setJdbcOperations(NamedParameterJdbcOperations operations) {
115117
this.operations = operations;
116118
}
117119

120+
@Autowired(required = false)
121+
public void setInstantiators(EntityInstantiators instantiators) {
122+
this.instantiators = instantiators;
123+
}
124+
118125
/*
119126
* (non-Javadoc)
120127
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#afterPropertiesSet()
@@ -127,13 +134,18 @@ public void afterPropertiesSet() {
127134
if (dataAccessStrategy == null) {
128135

129136
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(mappingContext);
130-
this.dataAccessStrategy = new DefaultDataAccessStrategy(sqlGeneratorSource, mappingContext, operations);
137+
this.dataAccessStrategy = new DefaultDataAccessStrategy(sqlGeneratorSource, mappingContext, operations,
138+
instantiators);
131139
}
132140

133141
if (rowMapperMap == null) {
134142
this.rowMapperMap = RowMapperMap.EMPTY;
135143
}
136144

145+
if (instantiators == null) {
146+
this.instantiators = new EntityInstantiators();
147+
}
148+
137149
super.afterPropertiesSet();
138150
}
139151
}

src/test/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategyUnitTests.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
package org.springframework.data.jdbc.core;
1717

1818
import static org.assertj.core.api.Assertions.*;
19-
import static org.mockito.ArgumentMatchers.*;
19+
import static org.mockito.ArgumentMatchers.any;
20+
import static org.mockito.ArgumentMatchers.eq;
2021
import static org.mockito.Mockito.*;
2122

2223
import lombok.RequiredArgsConstructor;
@@ -26,8 +27,8 @@
2627
import org.junit.Test;
2728
import org.mockito.ArgumentCaptor;
2829
import org.springframework.data.annotation.Id;
30+
import org.springframework.data.convert.EntityInstantiators;
2931
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
30-
import org.springframework.data.jdbc.core.mapping.NamingStrategy;
3132
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
3233
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
3334
import org.springframework.jdbc.support.KeyHolder;
@@ -47,8 +48,12 @@ public class DefaultDataAccessStrategyUnitTests {
4748
HashMap<String, Object> additionalParameters = new HashMap<>();
4849
ArgumentCaptor<SqlParameterSource> paramSourceCaptor = ArgumentCaptor.forClass(SqlParameterSource.class);
4950

50-
DefaultDataAccessStrategy accessStrategy = new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context,
51-
jdbcOperations);
51+
DefaultDataAccessStrategy accessStrategy = new DefaultDataAccessStrategy( //
52+
new SqlGeneratorSource(context), //
53+
context, //
54+
jdbcOperations, //
55+
new EntityInstantiators() //
56+
);
5257

5358
@Test // DATAJDBC-146
5459
public void additionalParameterForIdDoesNotLeadToDuplicateParameters() {

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818
import static java.util.Arrays.*;
1919
import static org.assertj.core.api.Assertions.*;
20-
import static org.mockito.ArgumentMatchers.*;
20+
import static org.mockito.ArgumentMatchers.any;
21+
import static org.mockito.ArgumentMatchers.eq;
2122
import static org.mockito.Mockito.*;
2223

2324
import lombok.RequiredArgsConstructor;
@@ -40,12 +41,12 @@
4041
import org.springframework.core.convert.support.DefaultConversionService;
4142
import org.springframework.core.convert.support.GenericConversionService;
4243
import org.springframework.data.annotation.Id;
44+
import org.springframework.data.convert.EntityInstantiators;
4345
import org.springframework.data.convert.Jsr310Converters;
4446
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
4547
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
4648
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
4749
import org.springframework.data.jdbc.core.mapping.NamingStrategy;
48-
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
4950
import org.springframework.util.Assert;
5051

5152
/**
@@ -199,8 +200,12 @@ private <T> EntityRowMapper<T> createRowMapper(Class<T> type, NamingStrategy nam
199200
DefaultConversionService.addDefaultConverters(conversionService);
200201
Jsr310Converters.getConvertersToRegister().forEach(conversionService::addConverter);
201202

202-
return new EntityRowMapper<>((JdbcPersistentEntity<T>) context.getRequiredPersistentEntity(type), context,
203-
accessStrategy);
203+
return new EntityRowMapper<>( //
204+
(JdbcPersistentEntity<T>) context.getRequiredPersistentEntity(type), //
205+
context, //
206+
new EntityInstantiators(), //
207+
accessStrategy //
208+
);
204209
}
205210

206211
private static ResultSet mockResultSet(List<String> columns, Object... values) {
@@ -212,7 +217,7 @@ private static ResultSet mockResultSet(List<String> columns, Object... values) {
212217
"Number of values [%d] must be a multiple of the number of columns [%d]", //
213218
values.length, //
214219
columns.size() //
215-
) //
220+
) //
216221
);
217222

218223
List<Map<String, Object>> result = convertValues(columns, values);

0 commit comments

Comments
 (0)