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:
committed by
Mark Paluch
parent
0fdeaebbee
commit
f3bc0af8c4
@@ -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
|
||||
|
||||
@@ -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 //
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,9 +176,8 @@ public class QueryMapper {
|
||||
return expression;
|
||||
}
|
||||
|
||||
if (expression instanceof Column) {
|
||||
if (expression instanceof Column column) {
|
||||
|
||||
Column column = (Column) expression;
|
||||
Field field = createPropertyField(entity, column.getName());
|
||||
TableLike table = column.getTable();
|
||||
|
||||
@@ -186,9 +185,7 @@ public class QueryMapper {
|
||||
return column instanceof Aliased ? columnFromTable.as(((Aliased) column).getAlias()) : columnFromTable;
|
||||
}
|
||||
|
||||
if (expression instanceof SimpleFunction) {
|
||||
|
||||
SimpleFunction function = (SimpleFunction) expression;
|
||||
if (expression instanceof SimpleFunction function) {
|
||||
|
||||
List<Expression> arguments = function.getExpressions();
|
||||
List<Expression> mappedArguments = new ArrayList<>(arguments.size());
|
||||
@@ -367,15 +364,14 @@ public class QueryMapper {
|
||||
Class<?> typeHint;
|
||||
|
||||
Comparator comparator = criteria.getComparator();
|
||||
if (criteria.getValue() instanceof Parameter) {
|
||||
|
||||
Parameter parameter = (Parameter) criteria.getValue();
|
||||
if (criteria.getValue()instanceof Parameter parameter) {
|
||||
|
||||
mappedValue = convertValue(comparator, parameter.getValue(), propertyField.getTypeHint());
|
||||
typeHint = getTypeHint(mappedValue, actualType.getType(), parameter);
|
||||
} else if (criteria.getValue() instanceof ValueFunction<?> valueFunction) {
|
||||
|
||||
mappedValue = valueFunction.transform(v -> convertValue(comparator, v, propertyField.getTypeHint())).apply(getEscaper(comparator));
|
||||
mappedValue = valueFunction.map(v -> convertValue(comparator, v, propertyField.getTypeHint()))
|
||||
.apply(getEscaper(comparator));
|
||||
|
||||
typeHint = actualType.getType();
|
||||
} else {
|
||||
|
||||
@@ -118,7 +118,7 @@ public class UpdateMapper extends QueryMapper {
|
||||
|
||||
} else if (value instanceof ValueFunction<?> valueFunction) {
|
||||
|
||||
mappedValue = valueFunction.transform(v -> convertValue(v, propertyField.getTypeHint())).apply(Escaper.DEFAULT);
|
||||
mappedValue = valueFunction.map(v -> convertValue(v, propertyField.getTypeHint())).apply(Escaper.DEFAULT);
|
||||
|
||||
if (mappedValue == null) {
|
||||
return Assignments.value(column, SQL.nullLiteral());
|
||||
|
||||
@@ -58,14 +58,18 @@ public interface ValueFunction<T> extends Function<Escaper, T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the inner value of the ValueFunction using the profided transformation.
|
||||
* Return a new ValueFunction applying the given mapping {@link Function}. The mapping function is applied after
|
||||
* applying {@link Escaper}.
|
||||
*
|
||||
* The default implementation just return the current {@literal ValueFunction}.
|
||||
* This is not a valid implementation and serves just to maintain backward compatibility.
|
||||
*
|
||||
* @param transformation to be applied to the underlying value.
|
||||
* @param mapper the mapping function to apply to the value.
|
||||
* @param <R> the type of the value returned from the mapping function.
|
||||
* @return a new {@literal ValueFunction}.
|
||||
* @since 3.2
|
||||
*/
|
||||
default ValueFunction<T> transform(Function<Object, Object> transformation) {return this;};
|
||||
default <R> ValueFunction<R> map(Function<T, R> mapper) {
|
||||
|
||||
Assert.notNull(mapper, "Mapping function must not be null");
|
||||
|
||||
return escaper -> mapper.apply(this.apply(escaper));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PersistentPropertyPaths;
|
||||
@@ -46,7 +46,6 @@ public class SingleQuerySqlGenerator implements SqlGenerator {
|
||||
private final Dialect dialect;
|
||||
private final AliasFactory aliases;
|
||||
private final RelationalPersistentEntity<?> aggregate;
|
||||
private final Table table;
|
||||
|
||||
public SingleQuerySqlGenerator(RelationalMappingContext context, AliasFactory aliasFactory, Dialect dialect,
|
||||
RelationalPersistentEntity<?> aggregate) {
|
||||
@@ -55,47 +54,14 @@ public class SingleQuerySqlGenerator implements SqlGenerator {
|
||||
this.aliases = aliasFactory;
|
||||
this.dialect = dialect;
|
||||
this.aggregate = aggregate;
|
||||
|
||||
this.table = Table.create(aggregate.getQualifiedTableName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String findAll() {
|
||||
return createSelect(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String findById() {
|
||||
|
||||
AggregatePath path = getRootIdPath();
|
||||
Condition condition = Conditions.isEqual(table.column(path.getColumnInfo().name()), Expressions.just(":id"));
|
||||
|
||||
public String findAll(@Nullable Condition condition) {
|
||||
return createSelect(condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String findAllById() {
|
||||
|
||||
AggregatePath path = getRootIdPath();
|
||||
Condition condition = Conditions.in(table.column(path.getColumnInfo().name()), Expressions.just(":ids"));
|
||||
|
||||
return createSelect(condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String findAllByCondition(BiFunction<Table, RelationalPersistentEntity, Condition> conditionSource) {
|
||||
Condition condition = conditionSource.apply(table, aggregate);
|
||||
return createSelect(condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link AggregatePath} to the id property of the aggregate root.
|
||||
*/
|
||||
private AggregatePath getRootIdPath() {
|
||||
return context.getAggregatePath(aggregate).append(aggregate.getRequiredIdProperty());
|
||||
}
|
||||
|
||||
String createSelect(Condition condition) {
|
||||
String createSelect(@Nullable Condition condition) {
|
||||
|
||||
AggregatePath rootPath = context.getAggregatePath(aggregate);
|
||||
QueryMeta queryMeta = createInlineQuery(rootPath, condition);
|
||||
@@ -168,7 +134,7 @@ public class SingleQuerySqlGenerator implements SqlGenerator {
|
||||
|
||||
List<QueryMeta> inlineQueries = new ArrayList<>();
|
||||
|
||||
for (PersistentPropertyPath ppp : paths) {
|
||||
for (PersistentPropertyPath<? extends RelationalPersistentProperty> ppp : paths) {
|
||||
|
||||
QueryMeta queryMeta = createInlineQuery(context.getAggregatePath(ppp), null);
|
||||
inlineQueries.add(queryMeta);
|
||||
@@ -188,7 +154,7 @@ public class SingleQuerySqlGenerator implements SqlGenerator {
|
||||
* @param condition a condition that is to be applied to the query. May be {@literal null}.
|
||||
* @return an inline query for the given path.
|
||||
*/
|
||||
private QueryMeta createInlineQuery(AggregatePath basePath, Condition condition) {
|
||||
private QueryMeta createInlineQuery(AggregatePath basePath, @Nullable Condition condition) {
|
||||
|
||||
RelationalPersistentEntity<?> entity = basePath.getRequiredLeafEntity();
|
||||
Table table = Table.create(entity.getQualifiedTableName());
|
||||
|
||||
@@ -15,11 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.sqlgeneration;
|
||||
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.core.sql.Condition;
|
||||
import org.springframework.data.relational.core.sql.Table;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Generates SQL statements for loading aggregates.
|
||||
@@ -28,13 +25,12 @@ import java.util.function.BiFunction;
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface SqlGenerator {
|
||||
String findAll();
|
||||
|
||||
String findById();
|
||||
default String findAll() {
|
||||
return findAll(null);
|
||||
}
|
||||
|
||||
String findAllById();
|
||||
|
||||
String findAllByCondition(BiFunction<Table, RelationalPersistentEntity, Condition> conditionSource);
|
||||
String findAll(@Nullable Condition condition);
|
||||
|
||||
AliasFactory getAliasFactory();
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.relational.repository.query;
|
||||
|
||||
import org.springframework.data.relational.core.dialect.Escaper;
|
||||
import org.springframework.data.relational.core.query.ValueFunction;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Value function that has an underlying value and a modifier that gets applied after the escaper.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 3.2
|
||||
*/
|
||||
record ModifyingValueFunction(Object value, Function<String, String> modifier) implements ValueFunction<String> {
|
||||
|
||||
static ModifyingValueFunction of(Object value, Function<String, String> modifier) {
|
||||
return new ModifyingValueFunction(value, modifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(Escaper escaper) {
|
||||
return modifier.apply(escaper.escape(value.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueFunction<String> transform(Function<Object, Object> transformation) {
|
||||
return new ModifyingValueFunction(transformation.apply(value), modifier);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.relational.core.dialect.Escaper;
|
||||
import org.springframework.data.relational.core.query.ValueFunction;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
@@ -136,16 +137,12 @@ class ParameterMetadataProvider implements Iterable<ParameterMetadata> {
|
||||
return value;
|
||||
}
|
||||
|
||||
switch (partType) {
|
||||
case STARTING_WITH:
|
||||
return ModifyingValueFunction.of(value, s -> s + "%");
|
||||
case ENDING_WITH:
|
||||
return ModifyingValueFunction.of(value, s -> "%" + s);
|
||||
case CONTAINING:
|
||||
case NOT_CONTAINING:
|
||||
return ModifyingValueFunction.of(value, s -> "%" + s + "%");
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
return switch (partType) {
|
||||
case STARTING_WITH -> (ValueFunction<String>) escaper -> escaper.escape(value.toString()) + "%";
|
||||
case ENDING_WITH -> (ValueFunction<String>) escaper -> "%" + escaper.escape(value.toString());
|
||||
case CONTAINING, NOT_CONTAINING -> (ValueFunction<String>) escaper -> "%" + escaper.escape(value.toString())
|
||||
+ "%";
|
||||
default -> value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.data.relational.core.mapping;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.SoftAssertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
@@ -44,7 +43,6 @@ public class DerivedSqlIdentifierUnitTests {
|
||||
assertThat(identifier.toSql(BRACKETS_LOWER_CASE)).isEqualTo("[somename]");
|
||||
assertThat(identifier.getReference(BRACKETS_LOWER_CASE)).isEqualTo("someName");
|
||||
assertThat(identifier.getReference()).isEqualTo("someName");
|
||||
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-386
|
||||
@@ -77,12 +75,12 @@ public class DerivedSqlIdentifierUnitTests {
|
||||
SqlIdentifier notSimple = SqlIdentifier.from(new DerivedSqlIdentifier("simple", false),
|
||||
new DerivedSqlIdentifier("not", false));
|
||||
|
||||
assertSoftly(softly -> {
|
||||
assertThat(basis).isEqualTo(equal).isEqualTo(SqlIdentifier.unquoted("simple"))
|
||||
.hasSameHashCodeAs(SqlIdentifier.unquoted("simple"));
|
||||
assertThat(equal).isEqualTo(basis);
|
||||
assertThat(basis).isNotEqualTo(quoted);
|
||||
assertThat(basis).isNotEqualTo(notSimple);
|
||||
|
||||
softly.assertThat(basis).isEqualTo(equal);
|
||||
softly.assertThat(equal).isEqualTo(basis);
|
||||
softly.assertThat(basis).isNotEqualTo(quoted);
|
||||
softly.assertThat(basis).isNotEqualTo(notSimple);
|
||||
});
|
||||
assertThat(quoted).isEqualTo(SqlIdentifier.quoted("SIMPLE")).hasSameHashCodeAs(SqlIdentifier.quoted("SIMPLE"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.relational.core.sqlgeneration;
|
||||
|
||||
import static org.springframework.data.relational.core.sqlgeneration.SqlAssert.*;
|
||||
@@ -28,7 +27,10 @@ import org.springframework.data.relational.core.dialect.Dialect;
|
||||
import org.springframework.data.relational.core.dialect.PostgresDialect;
|
||||
import org.springframework.data.relational.core.mapping.AggregatePath;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
|
||||
import org.springframework.data.relational.core.sql.Conditions;
|
||||
import org.springframework.data.relational.core.sql.Table;
|
||||
|
||||
/**
|
||||
* Tests for {@link SingleQuerySqlGenerator}.
|
||||
@@ -76,7 +78,8 @@ class SingleQuerySqlGeneratorUnitTests {
|
||||
@Test // GH-1446
|
||||
void createSelectForFindById() {
|
||||
|
||||
String sql = sqlGenerator.findById();
|
||||
Table table = Table.create(persistentEntity.getQualifiedTableName());
|
||||
String sql = sqlGenerator.findAll(table.column("id").isEqualTo(Conditions.just(":id")));
|
||||
|
||||
SqlAssert baseSelect = assertThatParsed(sql).hasInlineView();
|
||||
|
||||
@@ -94,13 +97,14 @@ class SingleQuerySqlGeneratorUnitTests {
|
||||
col("\"id\"").as(alias("id")), //
|
||||
col("\"name\"").as(alias("name")) //
|
||||
) //
|
||||
.extractWhereClause().isEqualTo("\"trivial_aggregate\".\"id\" = :id");
|
||||
.extractWhereClause().isEqualTo("\"trivial_aggregate\".id = :id");
|
||||
}
|
||||
|
||||
@Test // GH-1446
|
||||
void createSelectForFindAllById() {
|
||||
|
||||
String sql = sqlGenerator.findAllById();
|
||||
Table table = Table.create(persistentEntity.getQualifiedTableName());
|
||||
String sql = sqlGenerator.findAll(table.column("id").in(Conditions.just(":ids")));
|
||||
|
||||
SqlAssert baseSelect = assertThatParsed(sql).hasInlineView();
|
||||
|
||||
@@ -118,7 +122,7 @@ class SingleQuerySqlGeneratorUnitTests {
|
||||
col("\"id\"").as(alias("id")), //
|
||||
col("\"name\"").as(alias("name")) //
|
||||
) //
|
||||
.extractWhereClause().isEqualTo("\"trivial_aggregate\".\"id\" IN (:ids)");
|
||||
.extractWhereClause().isEqualTo("\"trivial_aggregate\".id IN (:ids)");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -133,7 +137,8 @@ class SingleQuerySqlGeneratorUnitTests {
|
||||
@Test // GH-1446
|
||||
void createSelectForFindById() {
|
||||
|
||||
String sql = sqlGenerator.findById();
|
||||
Table table = Table.create(persistentEntity.getQualifiedTableName());
|
||||
String sql = sqlGenerator.findAll(table.column("id").isEqualTo(Conditions.just(":id")));
|
||||
|
||||
String rootRowNumber = rnAlias();
|
||||
String rootCount = rcAlias();
|
||||
@@ -167,7 +172,7 @@ class SingleQuerySqlGeneratorUnitTests {
|
||||
col("\"id\"").as(alias("id")), //
|
||||
col("\"name\"").as(alias("name")) //
|
||||
) //
|
||||
.extractWhereClause().isEqualTo("\"single_reference_aggregate\".\"id\" = :id");
|
||||
.extractWhereClause().isEqualTo("\"single_reference_aggregate\".id = :id");
|
||||
baseSelect.hasInlineViewSelectingFrom("\"trivial_aggregate\"") //
|
||||
.hasExactlyColumns( //
|
||||
rn(col("\"single_reference_aggregate\"")).as(trivialsRowNumber), //
|
||||
@@ -206,13 +211,14 @@ class SingleQuerySqlGeneratorUnitTests {
|
||||
private class AbstractTestFixture {
|
||||
final Class<?> aggregateRootType;
|
||||
final SingleQuerySqlGenerator sqlGenerator;
|
||||
final RelationalPersistentEntity<?> persistentEntity;
|
||||
final AliasFactory aliases;
|
||||
|
||||
private AbstractTestFixture(Class<?> aggregateRootType) {
|
||||
|
||||
this.aggregateRootType = aggregateRootType;
|
||||
this.sqlGenerator = new SingleQuerySqlGenerator(context, new AliasFactory(), dialect,
|
||||
context.getRequiredPersistentEntity(aggregateRootType));
|
||||
this.persistentEntity = context.getRequiredPersistentEntity(aggregateRootType);
|
||||
this.sqlGenerator = new SingleQuerySqlGenerator(context, new AliasFactory(), dialect, persistentEntity);
|
||||
this.aliases = sqlGenerator.getAliasFactory();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user