Polishing.

Simplify ValueFunction mapping. Remove invariants of findBy SQL generation in favor of the Condition-based variant. Reduce visibility. Change return value of AggregateReader to List

See #1601
Original pull request: #1617
This commit is contained in:
Jens Schauder
2023-09-25 10:09:37 +02:00
committed by Mark Paluch
parent 0fdeaebbee
commit f3bc0af8c4
15 changed files with 120 additions and 227 deletions

View File

@@ -18,16 +18,16 @@ package org.springframework.data.jdbc.core.convert;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.AggregatePath;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.Condition;
@@ -36,6 +36,7 @@ import org.springframework.data.relational.core.sqlgeneration.AliasFactory;
import org.springframework.data.relational.core.sqlgeneration.SingleQuerySqlGenerator;
import org.springframework.data.relational.core.sqlgeneration.SqlGenerator;
import org.springframework.data.relational.domain.RowDocument;
import org.springframework.data.util.Streamable;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.lang.Nullable;
@@ -43,7 +44,7 @@ import org.springframework.util.Assert;
/**
* Reads complete Aggregates from the database, by generating appropriate SQL using a {@link SingleQuerySqlGenerator}
* through {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}. Results are converterd into an
* through {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}. Results are converted into an
* intermediate {@link RowDocumentResultSetExtractor RowDocument} and mapped via
* {@link org.springframework.data.relational.core.conversion.RelationalConverter#read(Class, RowDocument)}.
*
@@ -55,7 +56,8 @@ import org.springframework.util.Assert;
class AggregateReader<T> {
private final RelationalPersistentEntity<T> aggregate;
private final org.springframework.data.relational.core.sqlgeneration.SqlGenerator sqlGenerator;
private final Table table;
private final SqlGenerator sqlGenerator;
private final JdbcConverter converter;
private final NamedParameterJdbcOperations jdbcTemplate;
private final RowDocumentResultSetExtractor extractor;
@@ -66,6 +68,7 @@ class AggregateReader<T> {
this.converter = converter;
this.aggregate = aggregate;
this.jdbcTemplate = jdbcTemplate;
this.table = Table.create(aggregate.getQualifiedTableName());
this.sqlGenerator = new CachingSqlGenerator(
new SingleQuerySqlGenerator(converter.getMappingContext(), aliasFactory, dialect, aggregate));
@@ -74,62 +77,58 @@ class AggregateReader<T> {
createPathToColumnMapping(aliasFactory));
}
@Nullable
public T findById(Object id) {
Query query = Query.query(Criteria.where(aggregate.getRequiredIdProperty().getName()).is(id)).limit(1);
return findOne(query);
}
@Nullable
public T findOne(Query query) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
Condition condition = createCondition(query, parameterSource);
return jdbcTemplate.query(sqlGenerator.findAll(condition), parameterSource, this::extractZeroOrOne);
}
public List<T> findAll() {
return jdbcTemplate.query(sqlGenerator.findAll(), this::extractAll);
}
public List<T> findAllById(Iterable<?> ids) {
Collection<?> identifiers = ids instanceof Collection<?> idl ? idl : Streamable.of(ids).toList();
Query query = Query.query(Criteria.where(aggregate.getRequiredIdProperty().getName()).in(identifiers)).limit(1);
return findAll(query);
}
public List<T> findAll(Query query) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
Condition condition = createCondition(query, parameterSource);
return jdbcTemplate.query(sqlGenerator.findAll(condition), parameterSource, this::extractAll);
}
@Nullable
public T findById(Object id) {
id = converter.writeValue(id, aggregate.getRequiredIdProperty().getTypeInformation());
return jdbcTemplate.query(sqlGenerator.findById(), Map.of("id", id), this::extractZeroOrOne);
}
public Iterable<T> findAllById(Iterable<?> ids) {
List<Object> convertedIds = new ArrayList<>();
for (Object id : ids) {
convertedIds.add(converter.writeValue(id, aggregate.getRequiredIdProperty().getTypeInformation()));
}
return jdbcTemplate.query(sqlGenerator.findAllById(), Map.of("ids", convertedIds), this::extractAll);
}
public Iterable<T> findAllBy(Query query) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
BiFunction<Table, RelationalPersistentEntity, Condition> condition = createConditionSource(query, parameterSource);
return jdbcTemplate.query(sqlGenerator.findAllByCondition(condition), parameterSource, this::extractAll);
}
public Optional<T> findOneByQuery(Query query) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
BiFunction<Table, RelationalPersistentEntity, Condition> condition = createConditionSource(query, parameterSource);
return Optional.ofNullable(
jdbcTemplate.query(sqlGenerator.findAllByCondition(condition), parameterSource, this::extractZeroOrOne));
}
private BiFunction<Table, RelationalPersistentEntity, Condition> createConditionSource(Query query, MapSqlParameterSource parameterSource) {
private Condition createCondition(Query query, MapSqlParameterSource parameterSource) {
QueryMapper queryMapper = new QueryMapper(converter);
BiFunction<Table, RelationalPersistentEntity, Condition> condition = (table, aggregate) -> {
Optional<CriteriaDefinition> criteria = query.getCriteria();
return criteria
.map(criteriaDefinition -> queryMapper.getMappedObject(parameterSource, criteriaDefinition, table, aggregate))
.orElse(null);
};
return condition;
Optional<CriteriaDefinition> criteria = query.getCriteria();
return criteria
.map(criteriaDefinition -> queryMapper.getMappedObject(parameterSource, criteriaDefinition, table, aggregate))
.orElse(null);
}
/**
* Extracts a list of aggregates from the given {@link ResultSet} by utilizing the
* {@link RowDocumentResultSetExtractor} and the {@link JdbcConverter}. When used as a method reference this conforms
* to the {@link org.springframework.jdbc.core.ResultSetExtractor} contract.
*
*
* @param rs the {@link ResultSet} from which to extract the data. Must not be {(}@literal null}.
* @return a {@code List} of aggregates, fully converted.
* @throws SQLException
@@ -195,21 +194,15 @@ class AggregateReader<T> {
* @author Jens Schauder
* @since 3.2
*/
static class CachingSqlGenerator implements org.springframework.data.relational.core.sqlgeneration.SqlGenerator {
private final org.springframework.data.relational.core.sqlgeneration.SqlGenerator delegate;
static class CachingSqlGenerator implements SqlGenerator {
private final SqlGenerator delegate;
private final String findAll;
private final String findById;
private final String findAllById;
public CachingSqlGenerator(SqlGenerator delegate) {
this.delegate = delegate;
findAll = delegate.findAll();
findById = delegate.findById();
findAllById = delegate.findAllById();
this.findAll = delegate.findAll();
}
@Override
@@ -218,18 +211,8 @@ class AggregateReader<T> {
}
@Override
public String findById() {
return findById;
}
@Override
public String findAllById() {
return findAllById;
}
@Override
public String findAllByCondition(BiFunction<Table, RelationalPersistentEntity, Condition> conditionSource) {
return delegate.findAllByCondition(conditionSource);
public String findAll(@Nullable Condition condition) {
return delegate.findAll(condition);
}
@Override

View File

@@ -22,8 +22,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import org.springframework.data.domain.Sort;
import org.springframework.data.jdbc.core.mapping.JdbcValue;
@@ -35,7 +33,6 @@ import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.InvalidPersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.Escaper;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.CriteriaDefinition;
@@ -77,7 +74,7 @@ public class QueryMapper {
Assert.notNull(converter, "JdbcConverter must not be null");
this.converter = converter;
this.mappingContext = (MappingContext) converter.getMappingContext();
this.mappingContext = converter.getMappingContext();
}
/**
@@ -310,7 +307,7 @@ public class QueryMapper {
sqlType = getTypeHint(mappedValue, actualType.getType(), settableValue);
} else if (criteria.getValue() instanceof ValueFunction valueFunction) {
mappedValue = valueFunction.transform(v -> convertValue(comparator, v, propertyField.getTypeHint()));
mappedValue = valueFunction.map(v -> convertValue(comparator, v, propertyField.getTypeHint()));
sqlType = propertyField.getSqlType();
} else if (propertyField instanceof MetadataBackedField metadataBackedField //

View File

@@ -77,13 +77,12 @@ class SingleQueryDataAccessStrategy implements ReadingDataAccessStrategy {
@Override
public <T> Optional<T> findOne(Query query, Class<T> domainType) {
return getReader(domainType).findOneByQuery(query);
return Optional.ofNullable(getReader(domainType).findOne(query));
}
@Override
public <T> Iterable<T> findAll(Query query, Class<T> domainType) {
return getReader(domainType).findAllBy(query);
return getReader(domainType).findAll(query);
}
@Override

View File

@@ -87,6 +87,7 @@ class SingleQueryFallbackDataAccessStrategy extends DelegatingDataAccessStrategy
return super.findAllById(ids, domainType);
}
@Override
public <T> Optional<T> findOne(Query query, Class<T> domainType) {
if (isSingleSelectQuerySupported(domainType) && isSingleSelectQuerySupported(query)) {
@@ -137,11 +138,6 @@ class SingleQueryFallbackDataAccessStrategy extends DelegatingDataAccessStrategy
referenceFound = true;
}
// AggregateReferences aren't supported yet
// if (property.isAssociation()) {
// return false;
// }
}
return true;

View File

@@ -21,12 +21,13 @@ import org.springframework.data.relational.core.query.ValueFunction;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
/**
* This {@link SqlParameterSource} will apply escaping to it's values.
*
* This {@link SqlParameterSource} will apply escaping to its values.
*
* @author Jens Schauder
* @since 3.2
*/
public class EscapingParameterSource implements SqlParameterSource {
class EscapingParameterSource implements SqlParameterSource {
private final SqlParameterSource parameterSource;
private final Escaper escaper;

View File

@@ -15,12 +15,12 @@
*/
package org.springframework.data.jdbc.repository.query;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.Escaper;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
/**
* Value object encapsulating a query containing named parameters and a{@link SqlParameterSource} to bind the parameters.
* Value object encapsulating a query containing named parameters and a{@link SqlParameterSource} to bind the
* parameters.
*
* @author Mark Paluch
* @author Jens Schauder
@@ -41,13 +41,12 @@ class ParametrizedQuery {
return query;
}
SqlParameterSource getParameterSource(Escaper escaper) {
return new EscapingParameterSource(parameterSource, escaper);
}
@Override
public String toString() {
return this.query;
}
public SqlParameterSource getParameterSource(Escaper escaper) {
return new EscapingParameterSource(parameterSource, escaper);
}
}