#330 - Adapt to Criteria objects in Spring Data Relational.

This commit is contained in:
Mark Paluch
2020-03-27 11:08:46 +01:00
parent dbe935c45a
commit a9a3919cf1
26 changed files with 208 additions and 479 deletions

View File

@@ -34,9 +34,9 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.util.Assert;
@@ -529,11 +529,11 @@ public interface DatabaseClient {
S project(SqlIdentifier... selectedFields);
/**
* Configure a filter {@link Criteria}.
* Configure a filter {@link CriteriaDefinition}.
*
* @param criteria must not be {@literal null}.
*/
S matching(Criteria criteria);
S matching(CriteriaDefinition criteria);
/**
* Configure {@link Sort}.
@@ -705,8 +705,18 @@ public interface DatabaseClient {
* Specify an {@link Update} object containing assignments.
*
* @param update must not be {@literal null}.
* @deprecated since 1.1, use {@link #using(org.springframework.data.relational.core.query.Update)}.
*/
@Deprecated
UpdateMatchingSpec using(Update update);
/**
* Specify an {@link Update} object containing assignments.
*
* @param update must not be {@literal null}.
* @since 1.1
*/
UpdateMatchingSpec using(org.springframework.data.relational.core.query.Update update);
}
/**
@@ -750,11 +760,11 @@ public interface DatabaseClient {
interface UpdateMatchingSpec extends UpdateSpec {
/**
* Configure a filter {@link Criteria}.
* Configure a filter {@link CriteriaDefinition}.
*
* @param criteria must not be {@literal null}.
*/
UpdateSpec matching(Criteria criteria);
UpdateSpec matching(CriteriaDefinition criteria);
}
/**
@@ -801,11 +811,11 @@ public interface DatabaseClient {
TypedDeleteSpec<T> table(SqlIdentifier tableName);
/**
* Configure a filter {@link Criteria}.
* Configure a filter {@link CriteriaDefinition}.
*
* @param criteria must not be {@literal null}.
*/
DeleteSpec matching(Criteria criteria);
DeleteSpec matching(CriteriaDefinition criteria);
}
/**
@@ -814,11 +824,11 @@ public interface DatabaseClient {
interface DeleteMatchingSpec extends DeleteSpec {
/**
* Configure a filter {@link Criteria}.
* Configure a filter {@link CriteriaDefinition}.
*
* @param criteria must not be {@literal null}.
*/
DeleteSpec matching(Criteria criteria);
DeleteSpec matching(CriteriaDefinition criteria);
}
/**

View File

@@ -60,6 +60,7 @@ import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -683,7 +684,7 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
final SqlIdentifier table;
final List<SqlIdentifier> projectedFields;
final @Nullable Criteria criteria;
final @Nullable CriteriaDefinition criteria;
final Sort sort;
final Pageable page;
@@ -698,7 +699,8 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
this.page = Pageable.unpaged();
}
DefaultSelectSpecSupport(SqlIdentifier table, List<SqlIdentifier> projectedFields, @Nullable Criteria criteria,
DefaultSelectSpecSupport(SqlIdentifier table, List<SqlIdentifier> projectedFields,
@Nullable CriteriaDefinition criteria,
Sort sort, Pageable page) {
this.table = table;
this.projectedFields = projectedFields;
@@ -717,7 +719,7 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
return createInstance(this.table, projectedFields, this.criteria, this.sort, this.page);
}
public DefaultSelectSpecSupport where(Criteria whereCriteria) {
public DefaultSelectSpecSupport where(CriteriaDefinition whereCriteria) {
Assert.notNull(whereCriteria, "Criteria must not be null!");
@@ -753,12 +755,13 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
}
protected abstract DefaultSelectSpecSupport createInstance(SqlIdentifier table, List<SqlIdentifier> projectedFields,
@Nullable Criteria criteria, Sort sort, Pageable page);
@Nullable CriteriaDefinition criteria, Sort sort, Pageable page);
}
private class DefaultGenericSelectSpec extends DefaultSelectSpecSupport implements GenericSelectSpec {
DefaultGenericSelectSpec(SqlIdentifier table, List<SqlIdentifier> projectedFields, @Nullable Criteria criteria,
DefaultGenericSelectSpec(SqlIdentifier table, List<SqlIdentifier> projectedFields,
@Nullable CriteriaDefinition criteria,
Sort sort, Pageable page) {
super(table, projectedFields, criteria, sort, page);
}
@@ -806,7 +809,7 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
}
@Override
public DefaultGenericSelectSpec matching(Criteria criteria) {
public DefaultGenericSelectSpec matching(CriteriaDefinition criteria) {
return (DefaultGenericSelectSpec) super.where(criteria);
}
@@ -842,7 +845,7 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
@Override
protected DefaultGenericSelectSpec createInstance(SqlIdentifier table, List<SqlIdentifier> projectedFields,
@Nullable Criteria criteria, Sort sort, Pageable page) {
@Nullable CriteriaDefinition criteria, Sort sort, Pageable page) {
return new DefaultGenericSelectSpec(table, projectedFields, criteria, sort, page);
}
}
@@ -864,7 +867,8 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
this.mappingFunction = dataAccessStrategy.getRowMapper(typeToRead);
}
DefaultTypedSelectSpec(SqlIdentifier table, List<SqlIdentifier> projectedFields, @Nullable Criteria criteria,
DefaultTypedSelectSpec(SqlIdentifier table, List<SqlIdentifier> projectedFields,
@Nullable CriteriaDefinition criteria,
Sort sort, Pageable page, Class<T> typeToRead, BiFunction<Row, RowMetadata, T> mappingFunction) {
super(table, projectedFields, criteria, sort, page);
@@ -912,7 +916,7 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
}
@Override
public DefaultTypedSelectSpec<T> matching(Criteria criteria) {
public DefaultTypedSelectSpec<T> matching(CriteriaDefinition criteria) {
return (DefaultTypedSelectSpec<T>) super.where(criteria);
}
@@ -956,7 +960,7 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
@Override
protected DefaultTypedSelectSpec<T> createInstance(SqlIdentifier table, List<SqlIdentifier> projectedFields,
@Nullable Criteria criteria, Sort sort, Pageable page) {
@Nullable CriteriaDefinition criteria, Sort sort, Pageable page) {
return new DefaultTypedSelectSpec<>(table, projectedFields, criteria, sort, page, this.typeToRead,
this.mappingFunction);
}
@@ -1204,11 +1208,12 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
private final @Nullable Class<?> typeToUpdate;
private final @Nullable SqlIdentifier table;
private final @Nullable Update assignments;
private final @Nullable Criteria where;
private final @Nullable org.springframework.data.relational.core.query.Update assignments;
private final @Nullable CriteriaDefinition where;
DefaultGenericUpdateSpec(@Nullable Class<?> typeToUpdate, @Nullable SqlIdentifier table,
@Nullable Update assignments, @Nullable Criteria where) {
@Nullable org.springframework.data.relational.core.query.Update assignments,
@Nullable CriteriaDefinition where) {
this.typeToUpdate = typeToUpdate;
this.table = table;
this.assignments = assignments;
@@ -1220,11 +1225,20 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
Assert.notNull(update, "Update must not be null");
return new DefaultGenericUpdateSpec(this.typeToUpdate, this.table,
org.springframework.data.relational.core.query.Update.from(update.getAssignments()), this.where);
}
@Override
public UpdateMatchingSpec using(org.springframework.data.relational.core.query.Update update) {
Assert.notNull(update, "Update must not be null");
return new DefaultGenericUpdateSpec(this.typeToUpdate, this.table, update, this.where);
}
@Override
public UpdateSpec matching(Criteria criteria) {
public UpdateSpec matching(CriteriaDefinition criteria) {
Assert.notNull(criteria, "Criteria must not be null");
@@ -1333,11 +1347,12 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
}
Object id = columns.remove(ids.get(0)); // do not update the Id column.
Update update = null;
org.springframework.data.relational.core.query.Update update = null;
for (SqlIdentifier column : columns.keySet()) {
if (update == null) {
update = Update.update(dataAccessStrategy.toSql(column), columns.get(column));
update = org.springframework.data.relational.core.query.Update.update(dataAccessStrategy.toSql(column),
columns.get(column));
} else {
update = update.set(dataAccessStrategy.toSql(column), columns.get(column));
}
@@ -1376,16 +1391,17 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
private final @Nullable Class<T> typeToDelete;
private final @Nullable SqlIdentifier table;
private final @Nullable Criteria where;
private final @Nullable CriteriaDefinition where;
DefaultDeleteSpec(@Nullable Class<T> typeToDelete, @Nullable SqlIdentifier table, @Nullable Criteria where) {
DefaultDeleteSpec(@Nullable Class<T> typeToDelete, @Nullable SqlIdentifier table,
@Nullable CriteriaDefinition where) {
this.typeToDelete = typeToDelete;
this.table = table;
this.where = where;
}
@Override
public DeleteSpec matching(Criteria criteria) {
public DeleteSpec matching(CriteriaDefinition criteria) {
Assert.notNull(criteria, "Criteria must not be null!");

View File

@@ -39,6 +39,7 @@ import org.springframework.util.Assert;
* Default {@link StatementMapper} implementation.
*
* @author Mark Paluch
* @author Roman Chigvintsev
*/
class DefaultStatementMapper implements StatementMapper {

View File

@@ -20,8 +20,8 @@ import reactor.core.publisher.Mono;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.query.Update;
/**
* Interface specifying a basic set of reactive R2DBC operations using entities. Implemented by

View File

@@ -37,11 +37,12 @@ import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.ProjectionInformation;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
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.query.Update;
import org.springframework.data.relational.core.sql.Expression;
import org.springframework.data.relational.core.sql.Functions;
import org.springframework.data.relational.core.sql.SqlIdentifier;
@@ -181,7 +182,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
return spec.withProjection(Functions.count(table.column(entity.getRequiredIdProperty().getColumnName())));
});
Optional<Criteria> criteria = query.getCriteria();
Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {
selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
}
@@ -220,7 +221,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
.withProjection(columnName) //
.limit(1);
Optional<Criteria> criteria = query.getCriteria();
Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {
selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
}
@@ -266,7 +267,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
selectSpec = selectSpec.withSort(query.getSort());
}
Optional<Criteria> criteria = query.getCriteria();
Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {
selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
}
@@ -314,7 +315,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
StatementMapper.UpdateSpec selectSpec = statementMapper //
.createUpdate(tableName, update);
Optional<Criteria> criteria = query.getCriteria();
Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {
selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
}
@@ -343,7 +344,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
StatementMapper.DeleteSpec selectSpec = statementMapper //
.createDelete(tableName);
Optional<Criteria> criteria = query.getCriteria();
Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {
selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
}

View File

@@ -17,7 +17,7 @@ package org.springframework.data.r2dbc.core;
import reactor.core.publisher.Mono;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.SqlIdentifier;
/**

View File

@@ -17,7 +17,7 @@ package org.springframework.data.r2dbc.core;
import reactor.core.publisher.Mono;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

View File

@@ -18,7 +18,7 @@ package org.springframework.data.r2dbc.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.SqlIdentifier;
/**

View File

@@ -18,7 +18,7 @@ package org.springframework.data.r2dbc.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

View File

@@ -17,8 +17,8 @@ package org.springframework.data.r2dbc.core;
import reactor.core.publisher.Mono;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.query.Update;
import org.springframework.data.relational.core.sql.SqlIdentifier;
/**

View File

@@ -17,8 +17,8 @@ package org.springframework.data.r2dbc.core;
import reactor.core.publisher.Mono;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.query.Update;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

View File

@@ -30,7 +30,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.dialect.BindMarkers;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.sql.Expression;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.data.relational.core.sql.Table;
@@ -43,6 +43,7 @@ import org.springframework.lang.Nullable;
* and vendor-specific SQL differences.
*
* @author Mark Paluch
* @author Roman Chigvintsev
*/
public interface StatementMapper {
@@ -142,7 +143,7 @@ public interface StatementMapper {
* @param table
* @return the {@link UpdateSpec}.
*/
default UpdateSpec createUpdate(String table, Update update) {
default UpdateSpec createUpdate(String table, org.springframework.data.relational.core.query.Update update) {
return UpdateSpec.create(table, update);
}
@@ -153,7 +154,7 @@ public interface StatementMapper {
* @return the {@link UpdateSpec}.
* @since 1.1
*/
default UpdateSpec createUpdate(SqlIdentifier table, Update update) {
default UpdateSpec createUpdate(SqlIdentifier table, org.springframework.data.relational.core.query.Update update) {
return UpdateSpec.create(table, update);
}
@@ -196,13 +197,13 @@ public interface StatementMapper {
private final Table table;
private final List<String> projectedFields;
private final List<Expression> selectList;
private final Criteria criteria;
private final CriteriaDefinition criteria;
private final Sort sort;
private final long offset;
private final int limit;
protected SelectSpec(Table table, List<String> projectedFields, List<Expression> selectList,
@Nullable Criteria criteria, Sort sort, int limit, long offset) {
@Nullable CriteriaDefinition criteria, Sort sort, int limit, long offset) {
this.table = table;
this.projectedFields = projectedFields;
this.selectList = selectList;
@@ -300,7 +301,7 @@ public interface StatementMapper {
* @param criteria
* @return the {@link SelectSpec}.
*/
public SelectSpec withCriteria(Criteria criteria) {
public SelectSpec withCriteria(CriteriaDefinition criteria) {
return new SelectSpec(this.table, this.projectedFields, this.selectList, criteria, this.sort, this.limit,
this.offset);
}
@@ -381,7 +382,7 @@ public interface StatementMapper {
return Collections.unmodifiableList(selectList);
}
public Criteria getCriteria() {
public CriteriaDefinition getCriteria() {
return this.criteria;
}
@@ -473,11 +474,12 @@ public interface StatementMapper {
class UpdateSpec {
private final SqlIdentifier table;
@Nullable private final Update update;
@Nullable private final org.springframework.data.relational.core.query.Update update;
private final Criteria criteria;
private final CriteriaDefinition criteria;
protected UpdateSpec(SqlIdentifier table, @Nullable Update update, Criteria criteria) {
protected UpdateSpec(SqlIdentifier table, @Nullable org.springframework.data.relational.core.query.Update update,
CriteriaDefinition criteria) {
this.table = table;
this.update = update;
@@ -490,7 +492,7 @@ public interface StatementMapper {
* @param table
* @return the {@link InsertSpec}.
*/
public static UpdateSpec create(String table, Update update) {
public static UpdateSpec create(String table, org.springframework.data.relational.core.query.Update update) {
return create(SqlIdentifier.unquoted(table), update);
}
@@ -501,7 +503,7 @@ public interface StatementMapper {
* @return the {@link InsertSpec}.
* @since 1.1
*/
public static UpdateSpec create(SqlIdentifier table, Update update) {
public static UpdateSpec create(SqlIdentifier table, org.springframework.data.relational.core.query.Update update) {
return new UpdateSpec(table, update, Criteria.empty());
}
@@ -511,7 +513,7 @@ public interface StatementMapper {
* @param criteria
* @return the {@link UpdateSpec}.
*/
public UpdateSpec withCriteria(Criteria criteria) {
public UpdateSpec withCriteria(CriteriaDefinition criteria) {
return new UpdateSpec(this.table, this.update, criteria);
}
@@ -520,11 +522,11 @@ public interface StatementMapper {
}
@Nullable
public Update getUpdate() {
public org.springframework.data.relational.core.query.Update getUpdate() {
return this.update;
}
public Criteria getCriteria() {
public CriteriaDefinition getCriteria() {
return this.criteria;
}
}
@@ -536,9 +538,9 @@ public interface StatementMapper {
private final SqlIdentifier table;
private final Criteria criteria;
private final CriteriaDefinition criteria;
protected DeleteSpec(SqlIdentifier table, Criteria criteria) {
protected DeleteSpec(SqlIdentifier table, CriteriaDefinition criteria) {
this.table = table;
this.criteria = criteria;
}
@@ -570,7 +572,7 @@ public interface StatementMapper {
* @param criteria
* @return the {@link DeleteSpec}.
*/
public DeleteSpec withCriteria(Criteria criteria) {
public DeleteSpec withCriteria(CriteriaDefinition criteria) {
return new DeleteSpec(this.table, criteria);
}
@@ -578,7 +580,7 @@ public interface StatementMapper {
return this.table;
}
public Criteria getCriteria() {
public CriteriaDefinition getCriteria() {
return this.criteria;
}
}

View File

@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.List;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -45,8 +46,10 @@ import org.springframework.util.Assert;
*
* @author Mark Paluch
* @author Oliver Drotbohm
* @deprecated since 1.1, use {@link org.springframework.data.relational.core.query.Criteria} instead.
*/
public class Criteria {
@Deprecated
public class Criteria implements CriteriaDefinition {
private static final Criteria EMPTY = new Criteria(SqlIdentifier.EMPTY, Comparator.INITIAL, null);
@@ -69,8 +72,7 @@ public class Criteria {
}
private Criteria(@Nullable Criteria previous, Combinator combinator, List<Criteria> group,
@Nullable SqlIdentifier column, @Nullable Comparator comparator, @Nullable Object value,
boolean ignoreCase) {
@Nullable SqlIdentifier column, @Nullable Comparator comparator, @Nullable Object value, boolean ignoreCase) {
this.previous = previous;
this.combinator = previous != null && previous.isEmpty() ? Combinator.INITIAL : combinator;
@@ -263,14 +265,14 @@ public class Criteria {
* @see #hasPrevious()
*/
@Nullable
Criteria getPrevious() {
public Criteria getPrevious() {
return previous;
}
/**
* @return {@literal true} if this {@link Criteria} has a previous one.
*/
boolean hasPrevious() {
public boolean hasPrevious() {
return previous != null;
}
@@ -321,18 +323,21 @@ public class Criteria {
/**
* @return {@literal true} if this {@link Criteria} is empty.
*/
boolean isGroup() {
@Override
public boolean isGroup() {
return !this.group.isEmpty();
}
/**
* @return {@link Combinator} to combine this criteria with a previous one.
*/
Combinator getCombinator() {
@Override
public Combinator getCombinator() {
return combinator;
}
List<Criteria> getGroup() {
@Override
public List<Criteria> getGroup() {
return group;
}
@@ -340,7 +345,8 @@ public class Criteria {
* @return the column/property name.
*/
@Nullable
SqlIdentifier getColumn() {
@Override
public SqlIdentifier getColumn() {
return column;
}
@@ -348,7 +354,8 @@ public class Criteria {
* @return {@link Comparator}.
*/
@Nullable
Comparator getComparator() {
@Override
public Comparator getComparator() {
return comparator;
}
@@ -356,7 +363,8 @@ public class Criteria {
* @return the comparison value. Can be {@literal null}.
*/
@Nullable
Object getValue() {
@Override
public Object getValue() {
return value;
}
@@ -365,18 +373,11 @@ public class Criteria {
*
* @return {@literal true} if comparison should be done in case-insensitive way
*/
boolean isIgnoreCase() {
@Override
public boolean isIgnoreCase() {
return ignoreCase;
}
enum Comparator {
INITIAL, EQ, NEQ, LT, LTE, GT, GTE, IS_NULL, IS_NOT_NULL, LIKE, NOT_LIKE, NOT_IN, IN, IS_TRUE, IS_FALSE
}
enum Combinator {
INITIAL, AND, OR;
}
/**
* Interface declaring terminal builder methods to build a {@link Criteria}.
*/

View File

@@ -1,259 +0,0 @@
/*
* Copyright 2020 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.r2dbc.query;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Query object representing {@link Criteria}, columns, {@link Sort}, and limit/offset for a SQL query. {@link Query} is
* created with a fluent API creating immutable objects.
*
* @author Mark Paluch
* @since 1.1
* @see Criteria
* @see Sort
* @see Pageable
*/
public class Query {
private final @Nullable Criteria criteria;
private final List<SqlIdentifier> columns;
private final Sort sort;
private final int limit;
private final long offset;
/**
* Static factory method to create a {@link Query} using the provided {@link Criteria}.
*
* @param criteria must not be {@literal null}.
* @return a new {@link Query} for the given {@link Criteria}.
*/
public static Query query(Criteria criteria) {
return new Query(criteria);
}
/**
* Creates a new {@link Query} using the given {@link Criteria}.
*
* @param criteria must not be {@literal null}.
*/
private Query(@Nullable Criteria criteria) {
this(criteria, Collections.emptyList(), Sort.unsorted(), -1, -1);
}
private Query(@Nullable Criteria criteria, List<SqlIdentifier> columns, Sort sort, int limit, long offset) {
this.criteria = criteria;
this.columns = columns;
this.sort = sort;
this.limit = limit;
this.offset = offset;
}
/**
* Create a new empty {@link Query}.
*
* @return
*/
public static Query empty() {
return new Query(null);
}
/**
* Add columns to the query.
*
* @param columns
* @return a new {@link Query} object containing the former settings with {@code columns} applied.
*/
public Query columns(String... columns) {
Assert.notNull(columns, "Columns must not be null");
return withColumns(Arrays.stream(columns).map(SqlIdentifier::unquoted).collect(Collectors.toList()));
}
/**
* Add columns to the query.
*
* @param columns
* @return a new {@link Query} object containing the former settings with {@code columns} applied.
*/
public Query columns(Collection<String> columns) {
Assert.notNull(columns, "Columns must not be null");
return withColumns(columns.stream().map(SqlIdentifier::unquoted).collect(Collectors.toList()));
}
/**
* Add columns to the query.
*
* @param columns
* @return a new {@link Query} object containing the former settings with {@code columns} applied.
* @since 1.1
*/
public Query columns(SqlIdentifier... columns) {
Assert.notNull(columns, "Columns must not be null");
return withColumns(Arrays.asList(columns));
}
/**
* Add columns to the query.
*
* @param columns
* @return a new {@link Query} object containing the former settings with {@code columns} applied.
*/
private Query withColumns(Collection<SqlIdentifier> columns) {
Assert.notNull(columns, "Columns must not be null");
List<SqlIdentifier> newColumns = new ArrayList<>(this.columns);
newColumns.addAll(columns);
return new Query(this.criteria, newColumns, this.sort, this.limit, offset);
}
/**
* Set number of rows to skip before returning results.
*
* @param offset
* @return a new {@link Query} object containing the former settings with {@code offset} applied.
*/
public Query offset(long offset) {
return new Query(this.criteria, this.columns, this.sort, this.limit, offset);
}
/**
* Limit the number of returned documents to {@code limit}.
*
* @param limit
* @return a new {@link Query} object containing the former settings with {@code limit} applied.
*/
public Query limit(int limit) {
return new Query(this.criteria, this.columns, this.sort, limit, this.offset);
}
/**
* Set the given pagination information on the {@link Query} instance. Will transparently set {@code offset} and
* {@code limit} as well as applying the {@link Sort} instance defined with the {@link Pageable}.
*
* @param pageable
* @return a new {@link Query} object containing the former settings with {@link Pageable} applied.
*/
public Query with(Pageable pageable) {
if (pageable.isUnpaged()) {
return this;
}
assertNoCaseSort(pageable.getSort());
return new Query(this.criteria, this.columns, this.sort.and(sort), pageable.getPageSize(), pageable.getOffset());
}
/**
* Add a {@link Sort} to the {@link Query} instance.
*
* @param sort
* @return a new {@link Query} object containing the former settings with {@link Sort} applied.
*/
public Query sort(Sort sort) {
Assert.notNull(sort, "Sort must not be null!");
if (sort.isUnsorted()) {
return this;
}
assertNoCaseSort(sort);
return new Query(this.criteria, this.columns, this.sort.and(sort), this.limit, this.offset);
}
/**
* Return the {@link Criteria} to be applied.
*
* @return
*/
public Optional<Criteria> getCriteria() {
return Optional.ofNullable(this.criteria);
}
/**
* Return the columns that this query should project.
*
* @return
*/
public List<SqlIdentifier> getColumns() {
return columns;
}
/**
* Return {@literal true} if the {@link Query} has a sort parameter.
*
* @return {@literal true} if sorted.
* @see Sort#isSorted()
*/
public boolean isSorted() {
return sort.isSorted();
}
public Sort getSort() {
return sort;
}
/**
* Return the number of rows to skip.
*
* @return
*/
public long getOffset() {
return this.offset;
}
/**
* Return the maximum number of rows to be return.
*
* @return
*/
public int getLimit() {
return this.limit;
}
private static void assertNoCaseSort(Sort sort) {
for (Sort.Order order : sort) {
if (order.isIgnoreCase()) {
throw new IllegalArgumentException(String.format("Given sort contained an Order for %s with ignore case;"
+ " R2DBC does not support sorting ignoring case currently", order.getProperty()));
}
}
}
}

View File

@@ -20,7 +20,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PersistentPropertyPath;
@@ -35,10 +34,10 @@ import org.springframework.data.r2dbc.dialect.Bindings;
import org.springframework.data.r2dbc.dialect.MutableBindings;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.query.Criteria.Combinator;
import org.springframework.data.r2dbc.query.Criteria.Comparator;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
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.sql.*;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
@@ -50,6 +49,7 @@ import org.springframework.util.ClassUtils;
* Maps {@link Criteria} and {@link Sort} objects considering mapping metadata and dialect-specific conversion.
*
* @author Mark Paluch
* @author Roman Chigvintsev
*/
public class QueryMapper {
@@ -179,6 +179,34 @@ public class QueryMapper {
throw new IllegalArgumentException(String.format("Cannot map %s", expression));
}
/**
* Map a {@link CriteriaDefinition} object into {@link Condition} and consider value/{@code NULL} {@link Bindings}.
*
* @param markers bind markers object, must not be {@literal null}.
* @param criteria criteria definition to map, must not be {@literal null}.
* @param table must not be {@literal null}.
* @param entity related {@link RelationalPersistentEntity}, can be {@literal null}.
* @return the mapped {@link BoundCondition}.
* @since 1.1
*/
public BoundCondition getMappedObject(BindMarkers markers, CriteriaDefinition criteria, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
Assert.notNull(markers, "BindMarkers must not be null!");
Assert.notNull(criteria, "CriteriaDefinition must not be null!");
Assert.notNull(table, "Table must not be null!");
MutableBindings bindings = new MutableBindings(markers);
if (criteria.isEmpty()) {
throw new IllegalArgumentException("Cannot map empty Criteria");
}
Condition mapped = unroll(criteria, table, entity, bindings);
return new BoundCondition(bindings, mapped);
}
/**
* Map a {@link Criteria} object into {@link Condition} and consider value/{@code NULL} {@link Bindings}.
*
@@ -187,7 +215,9 @@ public class QueryMapper {
* @param table must not be {@literal null}.
* @param entity related {@link RelationalPersistentEntity}, can be {@literal null}.
* @return the mapped {@link BoundCondition}.
* @deprecated since 1.1.
*/
@Deprecated
public BoundCondition getMappedObject(BindMarkers markers, Criteria criteria, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
@@ -206,13 +236,13 @@ public class QueryMapper {
return new BoundCondition(bindings, mapped);
}
private Condition unroll(Criteria criteria, Table table, @Nullable RelationalPersistentEntity<?> entity,
private Condition unroll(CriteriaDefinition criteria, Table table, @Nullable RelationalPersistentEntity<?> entity,
MutableBindings bindings) {
Criteria current = criteria;
CriteriaDefinition current = criteria;
// reverse unroll criteria chain
Map<Criteria, Criteria> forwardChain = new HashMap<>();
Map<CriteriaDefinition, CriteriaDefinition> forwardChain = new HashMap<>();
while (current.hasPrevious()) {
forwardChain.put(current.getPrevious(), current);
@@ -223,7 +253,7 @@ public class QueryMapper {
Condition mapped = getCondition(current, bindings, table, entity);
while (forwardChain.containsKey(current)) {
Criteria criterion = forwardChain.get(current);
CriteriaDefinition criterion = forwardChain.get(current);
Condition result = null;
Condition condition = getCondition(criterion, bindings, table, entity);
@@ -245,11 +275,12 @@ public class QueryMapper {
}
@Nullable
private Condition unrollGroup(List<Criteria> criteria, Table table, Combinator combinator,
@Nullable RelationalPersistentEntity<?> entity, MutableBindings bindings) {
private Condition unrollGroup(List<? extends CriteriaDefinition> criteria, Table table,
CriteriaDefinition.Combinator combinator, @Nullable RelationalPersistentEntity<?> entity,
MutableBindings bindings) {
Condition mapped = null;
for (Criteria criterion : criteria) {
for (CriteriaDefinition criterion : criteria) {
if (criterion.isEmpty()) {
continue;
@@ -264,7 +295,7 @@ public class QueryMapper {
}
@Nullable
private Condition getCondition(Criteria criteria, MutableBindings bindings, Table table,
private Condition getCondition(CriteriaDefinition criteria, MutableBindings bindings, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
if (criteria.isEmpty()) {
@@ -281,14 +312,14 @@ public class QueryMapper {
return mapCondition(criteria, bindings, table, entity);
}
private Condition combine(Criteria criteria, @Nullable Condition currentCondition, Combinator combinator,
Condition nextCondition) {
private Condition combine(CriteriaDefinition criteria, @Nullable Condition currentCondition,
CriteriaDefinition.Combinator combinator, Condition nextCondition) {
if (currentCondition == null) {
currentCondition = nextCondition;
} else if (combinator == Combinator.AND) {
} else if (combinator == CriteriaDefinition.Combinator.AND) {
currentCondition = currentCondition.and(nextCondition);
} else if (combinator == Combinator.OR) {
} else if (combinator == CriteriaDefinition.Combinator.OR) {
currentCondition = currentCondition.or(nextCondition);
} else {
throw new IllegalStateException("Combinator " + criteria.getCombinator() + " not supported");
@@ -297,7 +328,7 @@ public class QueryMapper {
return currentCondition;
}
private Condition mapCondition(Criteria criteria, MutableBindings bindings, Table table,
private Condition mapCondition(CriteriaDefinition criteria, MutableBindings bindings, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
Field propertyField = createPropertyField(entity, criteria.getColumn(), this.mappingContext);
@@ -320,8 +351,7 @@ public class QueryMapper {
typeHint = actualType.getType();
}
return createCondition(column, mappedValue, typeHint, bindings, criteria.getComparator(),
criteria.isIgnoreCase());
return createCondition(column, mappedValue, typeHint, bindings, criteria.getComparator(), criteria.isIgnoreCase());
}
/**
@@ -382,16 +412,16 @@ public class QueryMapper {
}
if (comparator == Comparator.IS_TRUE) {
return column.isEqualTo(SQL.literalOf((Object) ("TRUE")));
return column.isEqualTo(SQL.literalOf(true));
}
if (comparator == Comparator.IS_FALSE) {
return column.isEqualTo(SQL.literalOf((Object) ("FALSE")));
return column.isEqualTo(SQL.literalOf(false));
}
Expression columnExpression = column;
if (ignoreCase && String.class == valueType) {
columnExpression = new Upper(column);
columnExpression = Functions.upper(column);
}
if (comparator == Comparator.NOT_IN || comparator == Comparator.IN) {
@@ -459,7 +489,7 @@ public class QueryMapper {
}
case NOT_LIKE: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return NotLike.create(columnExpression, expression);
return Conditions.notLike(columnExpression, expression);
}
default:
throw new UnsupportedOperationException("Comparator " + comparator + " not supported");
@@ -502,7 +532,7 @@ public class QueryMapper {
bindings.bindNull(bindMarker, valueType);
}
return ignoreCase ? new Upper(SQL.bindMarker(bindMarker.getPlaceholder()))
return ignoreCase ? Functions.upper(SQL.bindMarker(bindMarker.getPlaceholder()))
: SQL.bindMarker(bindMarker.getPlaceholder());
}
@@ -646,102 +676,6 @@ public class QueryMapper {
}
}
static class PassThruIdentifier implements SqlIdentifier {
final String name;
PassThruIdentifier(String name) {
this.name = name;
}
@Override
public String getReference(IdentifierProcessing processing) {
return name;
}
@Override
public String toSql(IdentifierProcessing processing) {
return name;
}
@Override
public SqlIdentifier transform(UnaryOperator<String> transformationFunction) {
return new PassThruIdentifier(transformationFunction.apply(name));
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o instanceof SqlIdentifier) {
return toString().equals(o.toString());
}
return false;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return toString().hashCode();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return toSql(IdentifierProcessing.ANSI);
}
}
// TODO: include support of NOT LIKE operator into spring-data-relational
/**
* Negated LIKE {@link Condition} comparing two {@link Expression}s.
* <p/>
* Results in a rendered condition: {@code <left> NOT LIKE <right>}.
*/
private static class NotLike implements Segment, Condition {
private final Comparison delegate;
private NotLike(Expression leftColumnOrExpression, Expression rightColumnOrExpression) {
this.delegate = Comparison.create(leftColumnOrExpression, "NOT LIKE", rightColumnOrExpression);
}
/**
* Creates new instance of this class with the given {@link Expression}s.
*
* @param leftColumnOrExpression the left {@link Expression}
* @param rightColumnOrExpression the right {@link Expression}
* @return {@link NotLike} condition
*/
public static NotLike create(Expression leftColumnOrExpression, Expression rightColumnOrExpression) {
Assert.notNull(leftColumnOrExpression, "Left expression must not be null!");
Assert.notNull(rightColumnOrExpression, "Right expression must not be null!");
return new NotLike(leftColumnOrExpression, rightColumnOrExpression);
}
@Override
public void visit(Visitor visitor) {
Assert.notNull(visitor, "Visitor must not be null!");
delegate.visit(visitor);
}
@Override
public String toString() {
return delegate.toString();
}
}
// TODO: include support of functions in WHERE conditions into spring-data-relational
/**
* Models the ANSI SQL {@code UPPER} function.
* <p>

View File

@@ -28,7 +28,9 @@ import org.springframework.util.Assert;
*
* @author Mark Paluch
* @author Oliver Drotbohm
* @deprecated since 1.1, use {@link org.springframework.data.relational.core.query.Update} instead.
*/
@Deprecated
public class Update {
private static final Update EMPTY = new Update(Collections.emptyMap());

View File

@@ -63,12 +63,33 @@ public class UpdateMapper extends QueryMapper {
* @param table must not be {@literal null}.
* @param entity related {@link RelationalPersistentEntity}, can be {@literal null}.
* @return the mapped {@link BoundAssignments}.
* @deprecated since 1.1, use
* {@link #getMappedObject(BindMarkers, org.springframework.data.relational.core.query.Update, Table, RelationalPersistentEntity)}
* instead.
*/
@Deprecated
public BoundAssignments getMappedObject(BindMarkers markers, Update update, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
return getMappedObject(markers, update.getAssignments(), table, entity);
}
/**
* Map a {@link org.springframework.data.relational.core.query.Update} object to {@link BoundAssignments} and consider
* value/{@code NULL} {@link Bindings}.
*
* @param markers bind markers object, must not be {@literal null}.
* @param update update definition to map, must not be {@literal null}.
* @param table must not be {@literal null}.
* @param entity related {@link RelationalPersistentEntity}, can be {@literal null}.
* @return the mapped {@link BoundAssignments}.
* @since 1.1
*/
public BoundAssignments getMappedObject(BindMarkers markers,
org.springframework.data.relational.core.query.Update update, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
return getMappedObject(markers, update.getAssignments(), table, entity);
}
/**
* Map a {@code assignments} object to {@link BoundAssignments} and consider value/{@code NULL} {@link Bindings}.
*

View File

@@ -26,8 +26,8 @@ import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.data.util.Lazy;

View File

@@ -16,7 +16,7 @@
package org.springframework.data.r2dbc.core
import kotlinx.coroutines.reactive.awaitSingle
import org.springframework.data.r2dbc.query.Update
import org.springframework.data.relational.core.query.Update
/**
* Extensions for [ReactiveUpdateOperation].

View File

@@ -32,11 +32,11 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.dialect.PostgresDialect;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.data.r2dbc.query.Query;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.r2dbc.testing.StatementRecorder;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.query.Update;
/**
* Unit tests for {@link R2dbcEntityTemplate}.

View File

@@ -29,8 +29,8 @@ import org.springframework.data.convert.WritingConverter;
import org.springframework.data.r2dbc.dialect.BindTarget;
import org.springframework.data.r2dbc.dialect.MySqlDialect;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Update;
/**
* Unit tests for {@link ReactiveDataAccessStrategy}.

View File

@@ -16,8 +16,8 @@
package org.springframework.data.r2dbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.r2dbc.query.Criteria.*;
import static org.springframework.data.r2dbc.query.Query.*;
import static org.springframework.data.relational.core.query.Criteria.*;
import static org.springframework.data.relational.core.query.Query.*;
import io.r2dbc.spi.test.MockResult;
import reactor.test.StepVerifier;

View File

@@ -16,8 +16,8 @@
package org.springframework.data.r2dbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.r2dbc.query.Criteria.*;
import static org.springframework.data.r2dbc.query.Query.*;
import static org.springframework.data.relational.core.query.Criteria.*;
import static org.springframework.data.relational.core.query.Query.*;
import io.r2dbc.spi.test.MockColumnMetadata;
import io.r2dbc.spi.test.MockResult;

View File

@@ -17,7 +17,7 @@ package org.springframework.data.r2dbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.r2dbc.query.Criteria.*;
import static org.springframework.data.r2dbc.query.Query.*;
import static org.springframework.data.relational.core.query.Query.*;
import io.r2dbc.spi.test.MockResult;
import reactor.test.StepVerifier;
@@ -28,9 +28,9 @@ import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.r2dbc.dialect.PostgresDialect;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.r2dbc.testing.StatementRecorder;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.query.Update;
/**
* Unit test for {@link ReactiveUpdateOperation}.

View File

@@ -25,8 +25,8 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.StatementMapper.UpdateSpec;
import org.springframework.data.r2dbc.dialect.BindTarget;
import org.springframework.data.r2dbc.dialect.PostgresDialect;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.data.r2dbc.query.Update;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Update;
/**
* Unit tests for {@link DefaultStatementMapper}.

View File

@@ -21,7 +21,7 @@ import io.mockk.verify
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.springframework.data.r2dbc.query.Update
import org.springframework.data.relational.core.query.Update
import reactor.core.publisher.Mono
/**