Skip to content

Explore infrastructure for Dialect-specific Criteria #2076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-1953-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data Relational Parent</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-jdbc-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-1953-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-jdbc</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-1953-SNAPSHOT</version>

<name>Spring Data JDBC</name>
<description>Spring Data module for JDBC repositories.</description>
Expand All @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-1953-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
import org.springframework.data.relational.core.binding.BindMarkersFactory;
import org.springframework.data.relational.core.conversion.MappingRelationalConverter;
import org.springframework.data.relational.core.conversion.ObjectPath;
import org.springframework.data.relational.core.conversion.RelationalConverter;
Expand Down Expand Up @@ -283,6 +284,31 @@ public <R> R readAndResolve(TypeInformation<R> type, RowDocument source, Identif
return readAggregate(context, source, entity.getTypeInformation());
}

public BindMarkersFactory getBindMarkersFactory() {
return BindMarkersFactory.named(":p", "", 32, MappingJdbcConverter::filterBindMarker);
}

private static String filterBindMarker(CharSequence input) {

StringBuilder builder = new StringBuilder();

for (int i = 0; i < input.length(); i++) {

char ch = input.charAt(i);

// ascii letter or digit
if (Character.isLetterOrDigit(ch) && ch < 127) {
builder.append(ch);
}
}

if (builder.isEmpty()) {
return "";
}

return "_" + builder;
}

@Override
protected RelationalPropertyValueProvider newValueProvider(RowDocumentAccessor documentAccessor,
ValueExpressionEvaluator evaluator, ConversionContext context) {
Expand All @@ -298,6 +324,7 @@ protected RelationalPropertyValueProvider newValueProvider(RowDocumentAccessor d
return super.newValueProvider(documentAccessor, evaluator, context);
}


/**
* {@link RelationalPropertyValueProvider} using a resolving context to lookup relations. This is highly
* context-sensitive. Note that the identifier is held here because of a chicken and egg problem, while
Expand Down
4 changes: 2 additions & 2 deletions spring-data-r2dbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-r2dbc</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-1953-SNAPSHOT</version>

<name>Spring Data R2DBC</name>
<description>Spring Data module for R2DBC</description>
Expand All @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-1953-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.query.CriteriaDefinition.Comparator;
import org.springframework.data.relational.core.query.QueryExpression;
import org.springframework.data.relational.core.query.ValueFunction;
import org.springframework.data.relational.core.sql.*;
import org.springframework.data.relational.domain.SqlSort;
Expand All @@ -48,6 +49,7 @@
import org.springframework.r2dbc.core.binding.MutableBindings;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
* Maps {@link CriteriaDefinition} and {@link Sort} objects considering mapping metadata and dialect-specific
Expand Down Expand Up @@ -296,31 +298,58 @@ private Condition combine(CriteriaDefinition criteria, @Nullable Condition curre
private Condition mapCondition(CriteriaDefinition criteria, MutableBindings bindings, Table table,
@Nullable RelationalPersistentEntity<?> entity) {

Field propertyField = createPropertyField(entity, criteria.getColumn(), this.mappingContext);
Column column = table.column(propertyField.getMappedColumnName());
TypeInformation<?> actualType = propertyField.getTypeHint().getRequiredActualType();

Object mappedValue;
Class<?> typeHint;
TypeInformation<?> typeHint;
Class<?> targetType;
Expression expression;
String nameHint;

if (criteria.hasExpression()) {

R2dbcEvaluationContext context = new R2dbcEvaluationContext(table, converter, entity, bindings);
QueryExpression queryExpression = criteria.getExpression();
typeHint = queryExpression.getType(context).getTargetType();

expression = queryExpression.evaluate(context);
nameHint = queryExpression.getNameHint();

if (criteria.getComparator() == null) {
return Conditions.from(expression);
}

} else if (criteria.hasColumn()) {

Field propertyField = createPropertyField(entity, criteria.getColumn(), this.mappingContext);
Column column = table.column(propertyField.getMappedColumnName());
expression = column;
nameHint = column.getName().getReference();
typeHint = propertyField.getTypeHint();

} else {
throw new IllegalStateException("Cannot map empty Criteria");
}

TypeInformation<?> actualType = typeHint.getRequiredActualType();

Comparator comparator = criteria.getComparator();

if (criteria.getValue() instanceof Parameter parameter) {

mappedValue = convertValue(comparator, parameter.getValue(), propertyField.getTypeHint());
typeHint = getTypeHint(mappedValue, actualType.getType(), parameter);
mappedValue = convertValue(comparator, parameter.getValue(), typeHint);
targetType = getTypeHint(mappedValue, actualType.getType(), parameter);
} else if (criteria.getValue() instanceof ValueFunction<?> valueFunction) {

mappedValue = valueFunction.map(v -> convertValue(comparator, v, propertyField.getTypeHint()))
.apply(getEscaper(comparator));
mappedValue = valueFunction.map(v -> convertValue(comparator, v, typeHint)).apply(getEscaper(comparator));

typeHint = actualType.getType();
targetType = actualType.getType();
} else {

mappedValue = convertValue(comparator, criteria.getValue(), propertyField.getTypeHint());
typeHint = actualType.getType();
mappedValue = convertValue(comparator, criteria.getValue(), typeHint);
targetType = actualType.getType();
}

return createCondition(column, mappedValue, typeHint, bindings, comparator, criteria.isIgnoreCase());
return createCondition(expression, nameHint, mappedValue, targetType, bindings, comparator,
criteria.isIgnoreCase());
}

private Escaper getEscaper(Comparator comparator) {
Expand Down Expand Up @@ -393,32 +422,32 @@ protected MappingContext<? extends RelationalPersistentEntity<?>, RelationalPers
return this.mappingContext;
}

private Condition createCondition(Column column, @Nullable Object mappedValue, Class<?> valueType,
MutableBindings bindings, Comparator comparator, boolean ignoreCase) {
private Condition createCondition(Expression source, String nameHint, @Nullable Object mappedValue,
Class<?> valueType, MutableBindings bindings, Comparator comparator, boolean ignoreCase) {

if (comparator.equals(Comparator.IS_NULL)) {
return column.isNull();
return Conditions.isNull(source);
}

if (comparator.equals(Comparator.IS_NOT_NULL)) {
return column.isNotNull();
return Conditions.isNull(source).not();
}

if (comparator == Comparator.IS_TRUE) {
Expression bind = booleanBind(column, mappedValue, valueType, bindings, ignoreCase);

return column.isEqualTo(bind);
Expression bind = booleanBind(nameHint, mappedValue, valueType, bindings, ignoreCase);
return Conditions.isEqual(source, bind);
}

if (comparator == Comparator.IS_FALSE) {
Expression bind = booleanBind(column, mappedValue, valueType, bindings, ignoreCase);

return column.isEqualTo(bind);
Expression bind = booleanBind(nameHint, mappedValue, valueType, bindings, ignoreCase);
return Conditions.isEqual(source, bind);
}

Expression columnExpression = column;
Expression columnExpression = source;
if (ignoreCase) {
columnExpression = Functions.upper(column);
columnExpression = Functions.upper(source);
}

if (comparator == Comparator.NOT_IN || comparator == Comparator.IN) {
Expand All @@ -432,15 +461,16 @@ private Condition createCondition(Column column, @Nullable Object mappedValue, C

for (Object o : (Iterable<?>) mappedValue) {

BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
BindMarker bindMarker = bindMarker(nameHint, bindings);
expressions.add(bind(o, valueType, bindings, bindMarker));
}

condition = Conditions.in(columnExpression, expressions.toArray(new Expression[0]));

} else {

BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
BindMarker bindMarker = bindMarker(nameHint, bindings);
;
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);

condition = Conditions.in(columnExpression, expression);
Expand All @@ -457,53 +487,51 @@ private Condition createCondition(Column column, @Nullable Object mappedValue, C

Pair<Object, Object> pair = (Pair<Object, Object>) mappedValue;

Expression begin = bind(pair.getFirst(), valueType, bindings,
bindings.nextMarker(column.getName().getReference()), ignoreCase);
Expression end = bind(pair.getSecond(), valueType, bindings, bindings.nextMarker(column.getName().getReference()),
ignoreCase);
Expression begin = bind(pair.getFirst(), valueType, bindings, bindMarker(nameHint, bindings), ignoreCase);
Expression end = bind(pair.getSecond(), valueType, bindings, bindMarker(nameHint, bindings), ignoreCase);

return comparator == Comparator.BETWEEN ? Conditions.between(columnExpression, begin, end)
: Conditions.notBetween(columnExpression, begin, end);
}

BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
BindMarker bindMarker = bindMarker(nameHint, bindings);
;

switch (comparator) {
case EQ: {
return switch (comparator) {
case EQ -> {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return Conditions.isEqual(columnExpression, expression);
yield Conditions.isEqual(columnExpression, expression);
}
case NEQ: {
case NEQ -> {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return Conditions.isEqual(columnExpression, expression).not();
yield Conditions.isEqual(columnExpression, expression).not();
}
case LT: {
case LT -> {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return column.isLess(expression);
yield Conditions.isLess(source, expression);
}
case LTE: {
case LTE -> {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return column.isLessOrEqualTo(expression);
yield Conditions.isLessOrEqualTo(source, expression);
}
case GT: {
case GT -> {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return column.isGreater(expression);
yield Conditions.isGreater(source, expression);
}
case GTE: {
case GTE -> {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return column.isGreaterOrEqualTo(expression);
yield Conditions.isGreaterOrEqualTo(source, expression);
}
case LIKE: {
case LIKE -> {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return Conditions.like(columnExpression, expression);
yield Conditions.like(columnExpression, expression);
}
case NOT_LIKE: {
case NOT_LIKE -> {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return Conditions.notLike(columnExpression, expression);
yield Conditions.notLike(columnExpression, expression);
}
default:
throw new UnsupportedOperationException("Comparator " + comparator + " not supported");
}
default -> throw new UnsupportedOperationException("Comparator " + comparator + " not supported");
};
}

Field createPropertyField(@Nullable RelationalPersistentEntity<?> entity, SqlIdentifier key) {
Expand All @@ -515,10 +543,6 @@ Field createPropertyField(@Nullable RelationalPersistentEntity<?> entity, SqlIde
return entity == null ? new Field(key) : new MetadataBackedField(key, entity, mappingContext);
}

Class<?> getTypeHint(@Nullable Object mappedValue, Class<?> propertyType) {
return propertyType;
}

Class<?> getTypeHint(@Nullable Object mappedValue, Class<?> propertyType, Parameter parameter) {

if (mappedValue == null || propertyType.equals(Object.class)) {
Expand Down Expand Up @@ -550,13 +574,18 @@ private Expression bind(@Nullable Object mappedValue, Class<?> valueType, Mutabl
: SQL.bindMarker(bindMarker.getPlaceholder());
}

private Expression booleanBind(Column column, Object mappedValue, Class<?> valueType, MutableBindings bindings,
boolean ignoreCase) {
BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
private Expression booleanBind(@Nullable String nameHint, Object mappedValue, Class<?> valueType,
MutableBindings bindings, boolean ignoreCase) {

BindMarker bindMarker = bindMarker(nameHint, bindings);

return bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
}

private static BindMarker bindMarker(@Nullable String nameHint, MutableBindings bindings) {
return StringUtils.hasText(nameHint) ? bindings.nextMarker(nameHint) : bindings.nextMarker();
}

/**
* Value object to represent a field and its meta-information.
*/
Expand Down
Loading