Skip to content

Commit ae1983d

Browse files
committed
Move logic for creating SqlParamterSource for insert and update into its own class.
+ To focus DefaultDataAccessStrategy tests on the influence of insert parameters on how it collaborates with JdbcOperations
1 parent 3434fa8 commit ae1983d

File tree

10 files changed

+397
-274
lines changed

10 files changed

+397
-274
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java

Lines changed: 17 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
7676
private final JdbcConverter converter;
7777
private final NamedParameterJdbcOperations operations;
7878
private final BatchJdbcOperations batchOperations;
79+
private final SqlParametersFactory sqlParametersFactory;
7980

8081
/**
8182
* Creates a {@link DefaultDataAccessStrategy}
@@ -87,24 +88,26 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
8788
* @since 1.1
8889
*/
8990
public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, RelationalMappingContext context,
90-
JdbcConverter converter, NamedParameterJdbcOperations operations) {
91-
this(sqlGeneratorSource, context, converter, operations, new BatchJdbcOperations(operations.getJdbcOperations()));
91+
JdbcConverter converter, NamedParameterJdbcOperations operations, SqlParametersFactory sqlParametersFactory) {
92+
this(sqlGeneratorSource, context, converter, operations, new BatchJdbcOperations(operations.getJdbcOperations()), sqlParametersFactory);
9293
}
9394

9495
DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, RelationalMappingContext context,
9596
JdbcConverter converter, NamedParameterJdbcOperations operations,
96-
BatchJdbcOperations batchOperations) {
97+
BatchJdbcOperations batchOperations, SqlParametersFactory sqlParametersFactory) {
9798

9899
Assert.notNull(sqlGeneratorSource, "SqlGeneratorSource must not be null");
99100
Assert.notNull(context, "RelationalMappingContext must not be null");
100101
Assert.notNull(converter, "JdbcConverter must not be null");
101102
Assert.notNull(operations, "NamedParameterJdbcOperations must not be null");
103+
Assert.notNull(sqlParametersFactory, "SqlParametersFactory must not be null");
102104

103105
this.sqlGeneratorSource = sqlGeneratorSource;
104106
this.context = context;
105107
this.converter = converter;
106108
this.operations = operations;
107109
this.batchOperations = batchOperations;
110+
this.sqlParametersFactory = sqlParametersFactory;
108111
}
109112

110113
/*
@@ -114,24 +117,12 @@ public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, Relation
114117
@Override
115118
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier, boolean includeId) {
116119

117-
SqlGenerator sqlGenerator = sql(domainType);
118-
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
119-
120-
SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
121-
PersistentProperty::isIdProperty, getIdentifierProcessing());
122-
123-
identifier.forEach((name, value, type) -> addConvertedPropertyValue(parameterSource, name, value, type));
124-
125-
Object idValue = getIdValueOrNull(instance, persistentEntity);
126-
if (idValue != null) {
120+
SqlIdentifierParameterSource parameterSource = sqlParametersFactory.getInsert(instance, domainType, identifier);
127121

128-
RelationalPersistentProperty idProperty = persistentEntity.getRequiredIdProperty();
129-
addConvertedPropertyValue(parameterSource, idProperty, idValue, idProperty.getColumnName());
130-
}
131-
132-
String insertSql = sqlGenerator.getInsert(parameterSource.getIdentifiers());
122+
String insertSql = sql(domainType).getInsert(parameterSource.getIdentifiers());
133123

134124
if (!includeId) {
125+
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
135126
return executeInsertAndReturnGeneratedId(persistentEntity, parameterSource, insertSql);
136127
} else {
137128

@@ -143,9 +134,9 @@ public <T> Object insert(T instance, Class<T> domainType, Identifier identifier,
143134
@Override
144135
public <T> Object[] insert(List<RecordDescriptor<T>> recordDescriptors, Class<T> domainType, boolean includeId) {
145136

146-
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
137+
Assert.notEmpty(recordDescriptors, "recordDescriptors must contain at least one recordDescriptor");
147138
SqlIdentifierParameterSource[] sqlParameterSources = recordDescriptors.stream()
148-
.map(recordDescriptor -> getParameterSource(recordDescriptor, persistentEntity))
139+
.map(recordDescriptor -> sqlParametersFactory.getInsert(recordDescriptor.getInstance(), domainType, recordDescriptor.getIdentifier()))
149140
.toArray(SqlIdentifierParameterSource[]::new);
150141

151142
String insertSql = sql(domainType).getInsert(sqlParameterSources[0].getIdentifiers());
@@ -158,6 +149,7 @@ public <T> Object[] insert(List<RecordDescriptor<T>> recordDescriptors, Class<T>
158149

159150
IdGeneration idGeneration = sqlGeneratorSource.getDialect().getIdGeneration();
160151

152+
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
161153
if (idGeneration.driverRequiresKeyColumnNames()) {
162154

163155
String[] keyColumnNames = getKeyColumnNames(persistentEntity.getType());
@@ -169,6 +161,7 @@ public <T> Object[] insert(List<RecordDescriptor<T>> recordDescriptors, Class<T>
169161
} else {
170162
batchOperations.insert(insertSql, sqlParameterSources, holder);
171163
}
164+
// TODO: Is this needed?
172165
if (!persistentEntity.hasIdProperty()) {
173166
return new Object[sqlParameterSources.length];
174167
}
@@ -183,29 +176,13 @@ public <T> Object[] insert(List<RecordDescriptor<T>> recordDescriptors, Class<T>
183176
} else {
184177
id = keys.entrySet().stream().findFirst() //
185178
.map(Map.Entry::getValue) //
186-
// TODO: Missing a test for this
187-
.orElse(null);
179+
.orElseThrow(() -> new IllegalStateException("KeyHolder contains an empty key list."));
188180
}
189181
ids[i] = id;
190182
}
191183
return ids;
192184
}
193185

194-
private <T> SqlIdentifierParameterSource getParameterSource(RecordDescriptor<T> recordDescriptor, RelationalPersistentEntity<T> persistentEntity) {
195-
SqlIdentifierParameterSource parameterSource = getParameterSource(recordDescriptor.getInstance(), persistentEntity, "",
196-
PersistentProperty::isIdProperty, getIdentifierProcessing());
197-
198-
recordDescriptor.getIdentifier().forEach((name, value, type) -> addConvertedPropertyValue(parameterSource, name, value, type));
199-
200-
Object idValue = getIdValueOrNull(recordDescriptor.getInstance(), persistentEntity);
201-
if (idValue != null) {
202-
203-
RelationalPersistentProperty idProperty = persistentEntity.getRequiredIdProperty();
204-
addConvertedPropertyValue(parameterSource, idProperty, idValue, idProperty.getColumnName());
205-
}
206-
return parameterSource;
207-
}
208-
209186
@Nullable
210187
private <T> Object executeInsertAndReturnGeneratedId(RelationalPersistentEntity<T> persistentEntity, SqlIdentifierParameterSource parameterSource, String insertSql) {
211188

@@ -234,10 +211,7 @@ private <T> Object executeInsertAndReturnGeneratedId(RelationalPersistentEntity<
234211
*/
235212
@Override
236213
public <S> boolean update(S instance, Class<S> domainType) {
237-
238-
RelationalPersistentEntity<S> persistentEntity = getRequiredPersistentEntity(domainType);
239-
return operations.update(sql(domainType).getUpdate(),
240-
getParameterSource(instance, persistentEntity, "", Predicates.includeAll(), getIdentifierProcessing())) != 0;
214+
return operations.update(sql(domainType).getUpdate(), sqlParametersFactory.getUpdate(instance, domainType)) != 0;
241215
}
242216

243217
/*
@@ -250,8 +224,7 @@ public <S> boolean updateWithVersion(S instance, Class<S> domainType, Number pre
250224
RelationalPersistentEntity<S> persistentEntity = getRequiredPersistentEntity(domainType);
251225

252226
// Adjust update statement to set the new version and use the old version in where clause.
253-
SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
254-
Predicates.includeAll(), getIdentifierProcessing());
227+
SqlIdentifierParameterSource parameterSource = sqlParametersFactory.getUpdate(instance, domainType);
255228
parameterSource.addValue(VERSION_SQL_PARAMETER, previousVersion);
256229

257230
int affectedRows = operations.update(sql(domainType).getUpdateWithVersion(), parameterSource);
@@ -502,67 +475,6 @@ public <T> Iterable<T> findAll(Class<T> domainType, Pageable pageable) {
502475
return operations.query(sql(domainType).getFindAll(pageable), (RowMapper<T>) getEntityRowMapper(domainType));
503476
}
504477

505-
private <S, T> SqlIdentifierParameterSource getParameterSource(@Nullable S instance,
506-
RelationalPersistentEntity<S> persistentEntity, String prefix,
507-
Predicate<RelationalPersistentProperty> skipProperty, IdentifierProcessing identifierProcessing) {
508-
509-
SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing);
510-
511-
PersistentPropertyAccessor<S> propertyAccessor = instance != null ? persistentEntity.getPropertyAccessor(instance)
512-
: NoValuePropertyAccessor.instance();
513-
514-
persistentEntity.doWithAll(property -> {
515-
516-
if (skipProperty.test(property) || !property.isWritable()) {
517-
return;
518-
}
519-
if (property.isEntity() && !property.isEmbedded()) {
520-
return;
521-
}
522-
523-
if (property.isEmbedded()) {
524-
525-
Object value = propertyAccessor.getProperty(property);
526-
RelationalPersistentEntity<?> embeddedEntity = context.getPersistentEntity(property.getType());
527-
SqlIdentifierParameterSource additionalParameters = getParameterSource((T) value,
528-
(RelationalPersistentEntity<T>) embeddedEntity, prefix + property.getEmbeddedPrefix(), skipProperty,
529-
identifierProcessing);
530-
parameters.addAll(additionalParameters);
531-
} else {
532-
533-
Object value = propertyAccessor.getProperty(property);
534-
SqlIdentifier paramName = property.getColumnName().transform(prefix::concat);
535-
536-
addConvertedPropertyValue(parameters, property, value, paramName);
537-
}
538-
});
539-
540-
return parameters;
541-
}
542-
543-
/**
544-
* Returns the id value if its not a primitive zero. Returns {@literal null} if the id value is null or a primitive
545-
* zero.
546-
*/
547-
@Nullable
548-
@SuppressWarnings("unchecked")
549-
private <S, ID> ID getIdValueOrNull(S instance, RelationalPersistentEntity<S> persistentEntity) {
550-
551-
ID idValue = (ID) persistentEntity.getIdentifierAccessor(instance).getIdentifier();
552-
553-
return isIdPropertyNullOrScalarZero(idValue, persistentEntity) ? null : idValue;
554-
}
555-
556-
private static <S, ID> boolean isIdPropertyNullOrScalarZero(@Nullable ID idValue,
557-
RelationalPersistentEntity<S> persistentEntity) {
558-
559-
RelationalPersistentProperty idProperty = persistentEntity.getIdProperty();
560-
return idValue == null //
561-
|| idProperty == null //
562-
|| (idProperty.getType() == int.class && idValue.equals(0)) //
563-
|| (idProperty.getType() == long.class && idValue.equals(0L));
564-
}
565-
566478
@Nullable
567479
private <S> Object getIdFromHolder(KeyHolder holder, RelationalPersistentEntity<S> persistentEntity) {
568480

@@ -599,6 +511,7 @@ private RowMapper<?> getMapEntityRowMapper(PersistentPropertyPathExtension path,
599511
return new MapEntityRowMapper<>(path, converter, identifier, keyColumn, getIdentifierProcessing());
600512
}
601513

514+
// TODO: Move to SqlParametersFactory
602515
private <T> SqlIdentifierParameterSource createIdParameterSource(Object id, Class<T> domainType) {
603516

604517
SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(getIdentifierProcessing());
@@ -687,47 +600,4 @@ private <T> String[] getKeyColumnNames(Class<T> domainType) {
687600
return new String[] { idColumn.getReference(getIdentifierProcessing()) };
688601
}
689602

690-
/**
691-
* Utility to create {@link Predicate}s.
692-
*/
693-
static class Predicates {
694-
695-
/**
696-
* Include all {@link Predicate} returning {@literal false} to never skip a property.
697-
*
698-
* @return the include all {@link Predicate}.
699-
*/
700-
static Predicate<RelationalPersistentProperty> includeAll() {
701-
return it -> false;
702-
}
703-
}
704-
705-
/**
706-
* A {@link PersistentPropertyAccessor} implementation always returning null
707-
*
708-
* @param <T>
709-
*/
710-
static class NoValuePropertyAccessor<T> implements PersistentPropertyAccessor<T> {
711-
712-
private static final NoValuePropertyAccessor INSTANCE = new NoValuePropertyAccessor();
713-
714-
static <T> NoValuePropertyAccessor<T> instance() {
715-
return INSTANCE;
716-
}
717-
718-
@Override
719-
public void setProperty(PersistentProperty<?> property, @Nullable Object value) {
720-
throw new UnsupportedOperationException("Cannot set value on 'null' target object.");
721-
}
722-
723-
@Override
724-
public Object getProperty(PersistentProperty<?> property) {
725-
return null;
726-
}
727-
728-
@Override
729-
public T getBean() {
730-
return null;
731-
}
732-
}
733603
}

0 commit comments

Comments
 (0)