Refactor query method execution to use R2dbcEntityTemplate.
Repository query methods are now executed through R2dbcEntityTemplate to participate in entity callbacks. Previously, query methods were executed directly using DatabaseClient which didn't allow for entity callbacks. Closes #591
This commit is contained in:
@@ -15,15 +15,23 @@
|
||||
*/
|
||||
package org.springframework.data.r2dbc.core;
|
||||
|
||||
import io.r2dbc.spi.Row;
|
||||
import io.r2dbc.spi.RowMetadata;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.TransientDataAccessResourceException;
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.relational.core.query.Query;
|
||||
import org.springframework.data.relational.core.query.Update;
|
||||
import org.springframework.r2dbc.core.DatabaseClient;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.RowsFetchSpec;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Interface specifying a basic set of reactive R2DBC operations using entities. Implemented by
|
||||
@@ -95,7 +103,7 @@ public interface R2dbcEntityOperations extends FluentR2dbcOperations {
|
||||
* Execute a {@code SELECT} query and convert the resulting items to a stream of entities.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @param entityClass the entity type must not be {@literal null}.
|
||||
* @return the result objects returned by the action.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
@@ -105,7 +113,7 @@ public interface R2dbcEntityOperations extends FluentR2dbcOperations {
|
||||
* Execute a {@code SELECT} query and convert the resulting item to an entity ensuring exactly one result.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @param entityClass the entity type must not be {@literal null}.
|
||||
* @return exactly one result or {@link Mono#empty()} if no match found.
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
@@ -117,7 +125,7 @@ public interface R2dbcEntityOperations extends FluentR2dbcOperations {
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param update must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @param entityClass the entity type must not be {@literal null}.
|
||||
* @return the number of affected rows.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
@@ -127,12 +135,105 @@ public interface R2dbcEntityOperations extends FluentR2dbcOperations {
|
||||
* Remove entities (rows)/columns from the table by {@link Query}.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @param entityClass the entity type must not be {@literal null}.
|
||||
* @return the number of affected rows.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
Mono<Integer> delete(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with org.springframework.r2dbc.core.PreparedOperation
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute a query for a {@link RowsFetchSpec}, given {@link PreparedOperation}. Any provided bindings within
|
||||
* {@link PreparedOperation} are applied to the underlying {@link DatabaseClient}. The query is issued as-is without
|
||||
* additional pre-processing such as named parameter expansion. Results of the query are mapped onto
|
||||
* {@code entityClass}.
|
||||
*
|
||||
* @param operation the prepared operation wrapping a SQL query and bind parameters.
|
||||
* @param entityClass the entity type must not be {@literal null}.
|
||||
* @return a {@link RowsFetchSpec} ready to materialize.
|
||||
* @since 1.4
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
<T> RowsFetchSpec<T> query(PreparedOperation<?> operation, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a {@link RowsFetchSpec}, given {@link PreparedOperation}. Any provided bindings within
|
||||
* {@link PreparedOperation} are applied to the underlying {@link DatabaseClient}. The query is issued as-is without
|
||||
* additional pre-processing such as named parameter expansion. Results of the query are mapped using {@link Function
|
||||
* rowMapper}.
|
||||
*
|
||||
* @param operation the prepared operation wrapping a SQL query and bind parameters.
|
||||
* @param rowMapper the row mapper must not be {@literal null}.
|
||||
* @return a {@link RowsFetchSpec} with {@link Function rowMapper} applied ready to materialize.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
* @since 1.4
|
||||
* @see #query(PreparedOperation, BiFunction)
|
||||
*/
|
||||
default <T> RowsFetchSpec<T> query(PreparedOperation<?> operation, Function<Row, T> rowMapper)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.notNull(rowMapper, "Row mapper must not be null");
|
||||
|
||||
return query(operation, ((row, rowMetadata) -> rowMapper.apply(row)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a query for a {@link RowsFetchSpec}, given {@link PreparedOperation}. Any provided bindings within
|
||||
* {@link PreparedOperation} are applied to the underlying {@link DatabaseClient}. The query is issued as-is without
|
||||
* additional pre-processing such as named parameter expansion. Results of the query are mapped using
|
||||
* {@link BiFunction rowMapper}.
|
||||
*
|
||||
* @param operation the prepared operation wrapping a SQL query and bind parameters.
|
||||
* @param rowMapper the row mapper must not be {@literal null}.
|
||||
* @return a {@link RowsFetchSpec} with {@link Function rowMapper} applied ready to materialize.
|
||||
* @since 1.4
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
<T> RowsFetchSpec<T> query(PreparedOperation<?> operation, BiFunction<Row, RowMetadata, T> rowMapper)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a {@link RowsFetchSpec} in the context of {@code entityClass}, given {@link PreparedOperation}.
|
||||
* Any provided bindings within {@link PreparedOperation} are applied to the underlying {@link DatabaseClient}. The
|
||||
* query is issued as-is without additional pre-processing such as named parameter expansion. Results of the query are
|
||||
* mapped using {@link Function rowMapper}.
|
||||
*
|
||||
* @param operation the prepared operation wrapping a SQL query and bind parameters.
|
||||
* @param entityClass the entity type must not be {@literal null}.
|
||||
* @param rowMapper the row mapper must not be {@literal null}.
|
||||
* @return a {@link RowsFetchSpec} with {@link Function rowMapper} applied ready to materialize.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
* @since 1.4
|
||||
* @see #query(PreparedOperation, Class, BiFunction)
|
||||
*/
|
||||
default <T> RowsFetchSpec<T> query(PreparedOperation<?> operation, Class<?> entityClass, Function<Row, T> rowMapper)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.notNull(rowMapper, "Row mapper must not be null");
|
||||
|
||||
return query(operation, entityClass, ((row, rowMetadata) -> rowMapper.apply(row)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a query for a {@link RowsFetchSpec} in the context of {@code entityClass}, given {@link PreparedOperation}.
|
||||
* Any provided bindings within {@link PreparedOperation} are applied to the underlying {@link DatabaseClient}. The
|
||||
* query is issued as-is without additional pre-processing such as named parameter expansion. Results of the query are
|
||||
* mapped using {@link BiFunction rowMapper}.
|
||||
*
|
||||
* @param operation the prepared operation wrapping a SQL query and bind parameters.
|
||||
* @param entityClass the entity type must not be {@literal null}.
|
||||
* @param rowMapper the row mapper must not be {@literal null}.
|
||||
* @return a {@link RowsFetchSpec} with {@link Function rowMapper} applied ready to materialize.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
* @since 1.4
|
||||
* @see #query(PreparedOperation, Class, BiFunction)
|
||||
*/
|
||||
<T> RowsFetchSpec<T> query(PreparedOperation<?> operation, Class<?> entityClass,
|
||||
BiFunction<Row, RowMetadata, T> rowMapper) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with entities
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -140,7 +241,7 @@ public interface R2dbcEntityOperations extends FluentR2dbcOperations {
|
||||
/**
|
||||
* Insert the given entity and emit the entity if the insert was applied.
|
||||
*
|
||||
* @param entity The entity to insert, must not be {@literal null}.
|
||||
* @param entity the entity to insert, must not be {@literal null}.
|
||||
* @return the inserted entity.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
@@ -149,7 +250,7 @@ public interface R2dbcEntityOperations extends FluentR2dbcOperations {
|
||||
/**
|
||||
* Update the given entity and emit the entity if the update was applied.
|
||||
*
|
||||
* @param entity The entity to update, must not be {@literal null}.
|
||||
* @param entity the entity to update, must not be {@literal null}.
|
||||
* @return the updated entity.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
* @throws TransientDataAccessResourceException if the update did not affect any rows.
|
||||
|
||||
@@ -307,7 +307,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
public Mono<Long> count(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "entity class must not be null");
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
|
||||
return doCount(query, entityClass, getTableName(entityClass));
|
||||
}
|
||||
@@ -344,7 +344,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
public Mono<Boolean> exists(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "entity class must not be null");
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
|
||||
return doExists(query, entityClass, getTableName(entityClass));
|
||||
}
|
||||
@@ -383,7 +383,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
public <T> Flux<T> select(Query query, Class<T> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "entity class must not be null");
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
|
||||
SqlIdentifier tableName = getTableName(entityClass);
|
||||
return doSelect(query, entityClass, tableName, entityClass, RowsFetchSpec::all);
|
||||
@@ -432,24 +432,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
|
||||
PreparedOperation<?> operation = statementMapper.getMappedObject(selectSpec);
|
||||
|
||||
boolean simpleType;
|
||||
BiFunction<Row, RowMetadata, T> rowMapper;
|
||||
if (returnType.isInterface()) {
|
||||
simpleType = getConverter().isSimpleType(entityClass);
|
||||
rowMapper = dataAccessStrategy.getRowMapper(entityClass)
|
||||
.andThen(o -> projectionFactory.createProjection(returnType, o));
|
||||
} else {
|
||||
simpleType = getConverter().isSimpleType(returnType);
|
||||
rowMapper = dataAccessStrategy.getRowMapper(returnType);
|
||||
}
|
||||
|
||||
// avoid top-level null values if the read type is a simple one (e.g. SELECT MAX(age) via Integer.class)
|
||||
if (simpleType) {
|
||||
return new UnwrapOptionalFetchSpecAdapter<>(this.databaseClient.sql(operation)
|
||||
.map((row, metadata) -> Optional.ofNullable(rowMapper.apply(row, metadata))));
|
||||
}
|
||||
|
||||
return this.databaseClient.sql(operation).map(rowMapper);
|
||||
return getRowsFetchSpec(databaseClient.sql(operation), entityClass, returnType);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -470,7 +453,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(update, "Update must not be null");
|
||||
Assert.notNull(entityClass, "entity class must not be null");
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
|
||||
return doUpdate(query, update, entityClass, getTableName(entityClass));
|
||||
}
|
||||
@@ -499,7 +482,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
public Mono<Integer> delete(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "entity class must not be null");
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
|
||||
return doDelete(query, entityClass, getTableName(entityClass));
|
||||
}
|
||||
@@ -508,18 +491,64 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
|
||||
StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
|
||||
|
||||
StatementMapper.DeleteSpec selectSpec = statementMapper //
|
||||
StatementMapper.DeleteSpec deleteSpec = statementMapper //
|
||||
.createDelete(tableName);
|
||||
|
||||
Optional<CriteriaDefinition> criteria = query.getCriteria();
|
||||
if (criteria.isPresent()) {
|
||||
selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
|
||||
deleteSpec = criteria.map(deleteSpec::withCriteria).orElse(deleteSpec);
|
||||
}
|
||||
|
||||
PreparedOperation<?> operation = statementMapper.getMappedObject(selectSpec);
|
||||
PreparedOperation<?> operation = statementMapper.getMappedObject(deleteSpec);
|
||||
return this.databaseClient.sql(operation).fetch().rowsUpdated().defaultIfEmpty(0);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with org.springframework.r2dbc.core.PreparedOperation
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> RowsFetchSpec<T> query(PreparedOperation<?> operation, Class<T> entityClass) {
|
||||
|
||||
Assert.notNull(operation, "PreparedOperation must not be null");
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
|
||||
return new EntityCallbackAdapter<>(getRowsFetchSpec(databaseClient.sql(operation), entityClass, entityClass),
|
||||
getTableNameOrEmpty(entityClass));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.util.function.BiFunction)
|
||||
*/
|
||||
@Override
|
||||
public <T> RowsFetchSpec<T> query(PreparedOperation<?> operation, BiFunction<Row, RowMetadata, T> rowMapper) {
|
||||
|
||||
Assert.notNull(operation, "PreparedOperation must not be null");
|
||||
Assert.notNull(rowMapper, "Row mapper must not be null");
|
||||
|
||||
return new EntityCallbackAdapter<>(databaseClient.sql(operation).map(rowMapper), SqlIdentifier.EMPTY);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.lang.Class, java.util.function.BiFunction)
|
||||
*/
|
||||
@Override
|
||||
public <T> RowsFetchSpec<T> query(PreparedOperation<?> operation, Class<?> entityClass,
|
||||
BiFunction<Row, RowMetadata, T> rowMapper) {
|
||||
|
||||
Assert.notNull(operation, "PreparedOperation must not be null");
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
Assert.notNull(rowMapper, "Row mapper must not be null");
|
||||
|
||||
return new EntityCallbackAdapter<>(databaseClient.sql(operation).map(rowMapper), getTableNameOrEmpty(entityClass));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with entities
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -817,6 +846,13 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
return getRequiredEntity(entityClass).getTableName();
|
||||
}
|
||||
|
||||
SqlIdentifier getTableNameOrEmpty(Class<?> entityClass) {
|
||||
|
||||
RelationalPersistentEntity<?> entity = this.mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
return entity != null ? entity.getTableName() : SqlIdentifier.EMPTY;
|
||||
}
|
||||
|
||||
private RelationalPersistentEntity<?> getRequiredEntity(Class<?> entityClass) {
|
||||
return this.mappingContext.getRequiredPersistentEntity(entityClass);
|
||||
}
|
||||
@@ -846,6 +882,30 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
return query.getColumns().stream().map(table::column).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private <T> RowsFetchSpec<T> getRowsFetchSpec(DatabaseClient.GenericExecuteSpec executeSpec, Class<?> entityClass,
|
||||
Class<T> returnType) {
|
||||
|
||||
boolean simpleType;
|
||||
|
||||
BiFunction<Row, RowMetadata, T> rowMapper;
|
||||
if (returnType.isInterface()) {
|
||||
simpleType = getConverter().isSimpleType(entityClass);
|
||||
rowMapper = dataAccessStrategy.getRowMapper(entityClass)
|
||||
.andThen(o -> projectionFactory.createProjection(returnType, o));
|
||||
} else {
|
||||
simpleType = getConverter().isSimpleType(returnType);
|
||||
rowMapper = dataAccessStrategy.getRowMapper(returnType);
|
||||
}
|
||||
|
||||
// avoid top-level null values if the read type is a simple one (e.g. SELECT MAX(age) via Integer.class)
|
||||
if (simpleType) {
|
||||
return new UnwrapOptionalFetchSpecAdapter<>(
|
||||
executeSpec.map((row, metadata) -> Optional.ofNullable(rowMapper.apply(row, metadata))));
|
||||
}
|
||||
|
||||
return executeSpec.map(rowMapper);
|
||||
}
|
||||
|
||||
private static ReactiveDataAccessStrategy getDataAccessStrategy(
|
||||
org.springframework.data.r2dbc.core.DatabaseClient databaseClient) {
|
||||
|
||||
@@ -989,6 +1049,11 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RowsFetchSpec} adapter emitting values from {@link Optional} if they exist.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static class UnwrapOptionalFetchSpecAdapter<T> implements RowsFetchSpec<T> {
|
||||
|
||||
private final RowsFetchSpec<Optional<T>> delegate;
|
||||
@@ -1012,4 +1077,37 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
|
||||
return delegate.all().handle((optional, sink) -> optional.ifPresent(sink::next));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RowsFetchSpec} adapter applying {@link #maybeCallAfterConvert(Object, SqlIdentifier)} to each emitted
|
||||
* object.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private class EntityCallbackAdapter<T> implements RowsFetchSpec<T> {
|
||||
|
||||
private final RowsFetchSpec<T> delegate;
|
||||
private final SqlIdentifier tableName;
|
||||
|
||||
private EntityCallbackAdapter(RowsFetchSpec<T> delegate, SqlIdentifier tableName) {
|
||||
this.delegate = delegate;
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> one() {
|
||||
return delegate.one().flatMap(it -> maybeCallAfterConvert(it, tableName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> first() {
|
||||
return delegate.first().flatMap(it -> maybeCallAfterConvert(it, tableName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return delegate.all().flatMap(it -> maybeCallAfterConvert(it, tableName));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,6 +71,21 @@ public class SettableValue {
|
||||
return new SettableValue(Parameter.empty(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a {@link SettableValue} from {@link Parameter}. Retains empty/type information.
|
||||
*
|
||||
* @param parameter the parameter to create a {@link SettableValue} from.
|
||||
* @return a new {@link SettableValue} from {@link Parameter}.
|
||||
* @since 1.4
|
||||
*/
|
||||
public static SettableValue fromParameter(Parameter parameter) {
|
||||
|
||||
Assert.notNull(parameter, "Parameter must not be null");
|
||||
|
||||
return parameter.isEmpty() ? SettableValue.empty(parameter.getType())
|
||||
: SettableValue.fromOrEmpty(parameter.getValue(), parameter.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column value. Can be {@literal null}.
|
||||
*
|
||||
|
||||
@@ -15,19 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.r2dbc.repository.query;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.data.mapping.model.EntityInstantiators;
|
||||
import org.springframework.data.r2dbc.convert.EntityRowMapper;
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
|
||||
import org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution.ResultProcessingConverter;
|
||||
import org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution.ResultProcessingExecution;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
|
||||
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
@@ -35,8 +31,8 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
import org.springframework.data.util.ReflectionUtils;
|
||||
import org.springframework.r2dbc.core.DatabaseClient;
|
||||
import org.springframework.r2dbc.core.FetchSpec;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.RowsFetchSpec;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -49,25 +45,26 @@ import org.springframework.util.Assert;
|
||||
public abstract class AbstractR2dbcQuery implements RepositoryQuery {
|
||||
|
||||
private final R2dbcQueryMethod method;
|
||||
private final DatabaseClient databaseClient;
|
||||
private final R2dbcEntityOperations entityOperations;
|
||||
private final R2dbcConverter converter;
|
||||
private final EntityInstantiators instantiators;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractR2dbcQuery} from the given {@link R2dbcQueryMethod} and {@link DatabaseClient}.
|
||||
* Creates a new {@link AbstractR2dbcQuery} from the given {@link R2dbcQueryMethod} and {@link R2dbcEntityOperations}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @param entityOperations must not be {@literal null}.
|
||||
* @param converter must not be {@literal null}.
|
||||
* @since 1.4
|
||||
*/
|
||||
public AbstractR2dbcQuery(R2dbcQueryMethod method, DatabaseClient databaseClient, R2dbcConverter converter) {
|
||||
public AbstractR2dbcQuery(R2dbcQueryMethod method, R2dbcEntityOperations entityOperations, R2dbcConverter converter) {
|
||||
|
||||
Assert.notNull(method, "R2dbcQueryMethod must not be null!");
|
||||
Assert.notNull(databaseClient, "DatabaseClient must not be null!");
|
||||
Assert.notNull(entityOperations, "R2dbcEntityOperations must not be null!");
|
||||
Assert.notNull(converter, "R2dbcConverter must not be null!");
|
||||
|
||||
this.method = method;
|
||||
this.databaseClient = databaseClient;
|
||||
this.entityOperations = entityOperations;
|
||||
this.converter = converter;
|
||||
this.instantiators = new EntityInstantiators();
|
||||
}
|
||||
@@ -91,38 +88,25 @@ public abstract class AbstractR2dbcQuery implements RepositoryQuery {
|
||||
return createQuery(parameterAccessor).flatMapMany(it -> executeQuery(parameterAccessor, it));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private Publisher<?> executeQuery(RelationalParameterAccessor parameterAccessor, BindableQuery it) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private Publisher<?> executeQuery(RelationalParameterAccessor parameterAccessor, PreparedOperation<?> operation) {
|
||||
|
||||
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(parameterAccessor);
|
||||
DatabaseClient.GenericExecuteSpec boundQuery = it.bind(databaseClient.sql(it));
|
||||
|
||||
FetchSpec<Object> fetchSpec;
|
||||
RowsFetchSpec<?> fetchSpec;
|
||||
|
||||
if (isExistsQuery()) {
|
||||
fetchSpec = (FetchSpec) boundQuery.map(row -> true);
|
||||
} else if (requiresMapping()) {
|
||||
|
||||
Class<?> typeToRead = resolveResultType(processor);
|
||||
EntityRowMapper rowMapper = new EntityRowMapper<>(typeToRead, converter);
|
||||
|
||||
if (converter.isSimpleType(typeToRead)) {
|
||||
fetchSpec = new UnwrapOptionalFetchSpecAdapter<>(
|
||||
boundQuery.map((row, rowMetadata) -> Optional.ofNullable(rowMapper.apply(row, rowMetadata))));
|
||||
|
||||
} else {
|
||||
fetchSpec = new FetchSpecAdapter<>(boundQuery.map(rowMapper));
|
||||
}
|
||||
if (isModifyingQuery()) {
|
||||
fetchSpec = entityOperations.getDatabaseClient().sql(operation).fetch();
|
||||
} else if (isExistsQuery()) {
|
||||
fetchSpec = entityOperations.getDatabaseClient().sql(operation).map(row -> true);
|
||||
} else {
|
||||
fetchSpec = (FetchSpec) boundQuery.fetch();
|
||||
fetchSpec = entityOperations.query(operation, resolveResultType(processor));
|
||||
}
|
||||
|
||||
SqlIdentifier tableName = method.getEntityInformation().getTableName();
|
||||
|
||||
R2dbcQueryExecution execution = new ResultProcessingExecution(getExecutionToWrap(processor.getReturnedType()),
|
||||
new ResultProcessingConverter(processor, converter.getMappingContext(), instantiators));
|
||||
|
||||
return execution.execute(fetchSpec, processor.getReturnedType().getDomainType(), tableName);
|
||||
return execution.execute(RowsFetchSpec.class.cast(fetchSpec));
|
||||
}
|
||||
|
||||
Class<?> resolveResultType(ResultProcessor resultProcessor) {
|
||||
@@ -136,45 +120,47 @@ public abstract class AbstractR2dbcQuery implements RepositoryQuery {
|
||||
return returnedType.isProjecting() ? returnedType.getDomainType() : returnedType.getReturnedType();
|
||||
}
|
||||
|
||||
private boolean requiresMapping() {
|
||||
return !isModifyingQuery();
|
||||
}
|
||||
|
||||
private R2dbcQueryExecution getExecutionToWrap(ReturnedType returnedType) {
|
||||
|
||||
if (isModifyingQuery()) {
|
||||
|
||||
if (Boolean.class.isAssignableFrom(returnedType.getReturnedType())) {
|
||||
return (q, t, c) -> q.rowsUpdated().map(integer -> integer > 0);
|
||||
return fetchSpec -> {
|
||||
|
||||
Assert.isInstanceOf(FetchSpec.class, fetchSpec);
|
||||
|
||||
FetchSpec<?> fs = (FetchSpec<?>) fetchSpec;
|
||||
|
||||
if (Boolean.class.isAssignableFrom(returnedType.getReturnedType())) {
|
||||
return fs.rowsUpdated().map(integer -> integer > 0);
|
||||
}
|
||||
|
||||
if (Number.class.isAssignableFrom(returnedType.getReturnedType())) {
|
||||
|
||||
return (q, t, c) -> q.rowsUpdated().map(integer -> {
|
||||
return converter.getConversionService().convert(integer, returnedType.getReturnedType());
|
||||
});
|
||||
return fs.rowsUpdated()
|
||||
.map(integer -> converter.getConversionService().convert(integer, returnedType.getReturnedType()));
|
||||
}
|
||||
|
||||
if (ReflectionUtils.isVoid(returnedType.getReturnedType())) {
|
||||
return (q, t, c) -> q.rowsUpdated().then();
|
||||
return fs.rowsUpdated().then();
|
||||
}
|
||||
|
||||
return (q, t, c) -> q.rowsUpdated();
|
||||
return fs.rowsUpdated();
|
||||
};
|
||||
}
|
||||
|
||||
if (isCountQuery()) {
|
||||
return (q, t, c) -> q.first().defaultIfEmpty(0L);
|
||||
return (fetchSpec) -> fetchSpec.first().defaultIfEmpty(0L);
|
||||
}
|
||||
|
||||
if (isExistsQuery()) {
|
||||
return (q, t, c) -> q.first().defaultIfEmpty(false);
|
||||
return (fetchSpec) -> fetchSpec.first().defaultIfEmpty(false);
|
||||
}
|
||||
|
||||
if (method.isCollectionQuery()) {
|
||||
return (q, t, c) -> q.all();
|
||||
return RowsFetchSpec::all;
|
||||
}
|
||||
|
||||
return (q, t, c) -> q.one();
|
||||
return RowsFetchSpec::one;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,63 +193,6 @@ public abstract class AbstractR2dbcQuery implements RepositoryQuery {
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @return a mono emitting a {@link BindableQuery}.
|
||||
*/
|
||||
protected abstract Mono<BindableQuery> createQuery(RelationalParameterAccessor accessor);
|
||||
protected abstract Mono<PreparedOperation<?>> createQuery(RelationalParameterAccessor accessor);
|
||||
|
||||
private static class FetchSpecAdapter<T> implements FetchSpec<T> {
|
||||
|
||||
private final RowsFetchSpec<T> delegate;
|
||||
|
||||
private FetchSpecAdapter(RowsFetchSpec<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> one() {
|
||||
return delegate.one();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> first() {
|
||||
return delegate.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return delegate.all();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Integer> rowsUpdated() {
|
||||
throw new UnsupportedOperationException("Not supported after applying a row mapper");
|
||||
}
|
||||
}
|
||||
|
||||
private static class UnwrapOptionalFetchSpecAdapter<T> implements FetchSpec<T> {
|
||||
|
||||
private final RowsFetchSpec<Optional<T>> delegate;
|
||||
|
||||
private UnwrapOptionalFetchSpecAdapter(RowsFetchSpec<Optional<T>> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> one() {
|
||||
return delegate.one().handle((optional, sink) -> optional.ifPresent(sink::next));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> first() {
|
||||
return delegate.first().handle((optional, sink) -> optional.ifPresent(sink::next));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return delegate.all().handle((optional, sink) -> optional.ifPresent(sink::next));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Integer> rowsUpdated() {
|
||||
throw new UnsupportedOperationException("Not supported after applying a row mapper");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
|
||||
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.r2dbc.core.DatabaseClient;
|
||||
import org.springframework.r2dbc.core.binding.BindTarget;
|
||||
|
||||
/**
|
||||
* {@link ExpressionEvaluatingParameterBinder} allows to evaluate, convert and bind parameters to placeholders within a
|
||||
@@ -58,42 +58,35 @@ class ExpressionEvaluatingParameterBinder {
|
||||
* Bind values provided by {@link RelationalParameterAccessor} to placeholders in {@link ExpressionQuery} while
|
||||
* considering potential conversions and parameter types.
|
||||
*
|
||||
* @param bindSpec must not be {@literal null}.
|
||||
* @param bindTarget must not be {@literal null}.
|
||||
* @param parameterAccessor must not be {@literal null}.
|
||||
* @param evaluator must not be {@literal null}.
|
||||
*/
|
||||
DatabaseClient.GenericExecuteSpec bind(DatabaseClient.GenericExecuteSpec bindSpec,
|
||||
void bind(BindTarget bindTarget,
|
||||
RelationalParameterAccessor parameterAccessor, R2dbcSpELExpressionEvaluator evaluator) {
|
||||
|
||||
Object[] values = parameterAccessor.getValues();
|
||||
Parameters<?, ?> bindableParameters = parameterAccessor.getBindableParameters();
|
||||
|
||||
DatabaseClient.GenericExecuteSpec bindSpecToUse = bindExpressions(bindSpec, evaluator);
|
||||
bindSpecToUse = bindParameters(bindSpecToUse, parameterAccessor.hasBindableNullValue(), values, bindableParameters);
|
||||
|
||||
return bindSpecToUse;
|
||||
bindExpressions(bindTarget, evaluator);
|
||||
bindParameters(bindTarget, parameterAccessor.hasBindableNullValue(), values, bindableParameters);
|
||||
}
|
||||
|
||||
private DatabaseClient.GenericExecuteSpec bindExpressions(DatabaseClient.GenericExecuteSpec bindSpec,
|
||||
private void bindExpressions(BindTarget bindSpec,
|
||||
R2dbcSpELExpressionEvaluator evaluator) {
|
||||
|
||||
DatabaseClient.GenericExecuteSpec bindSpecToUse = bindSpec;
|
||||
|
||||
for (ParameterBinding binding : expressionQuery.getBindings()) {
|
||||
|
||||
org.springframework.r2dbc.core.Parameter valueForBinding = getBindValue(
|
||||
evaluator.evaluate(binding.getExpression()));
|
||||
|
||||
bindSpecToUse = bind(bindSpecToUse, binding.getParameterName(), valueForBinding);
|
||||
bind(bindSpec, binding.getParameterName(), valueForBinding);
|
||||
}
|
||||
|
||||
return bindSpecToUse;
|
||||
}
|
||||
|
||||
private DatabaseClient.GenericExecuteSpec bindParameters(DatabaseClient.GenericExecuteSpec bindSpec,
|
||||
private void bindParameters(BindTarget bindSpec,
|
||||
boolean hasBindableNullValue, Object[] values, Parameters<?, ?> bindableParameters) {
|
||||
|
||||
DatabaseClient.GenericExecuteSpec bindSpecToUse = bindSpec;
|
||||
int bindingIndex = 0;
|
||||
|
||||
for (Parameter bindableParameter : bindableParameters) {
|
||||
@@ -109,7 +102,7 @@ class ExpressionEvaluatingParameterBinder {
|
||||
org.springframework.r2dbc.core.Parameter parameter = getBindValue(values, bindableParameter);
|
||||
|
||||
if (!parameter.isEmpty() || hasBindableNullValue) {
|
||||
bindSpecToUse = bind(bindSpecToUse, name.get(), parameter);
|
||||
bind(bindSpec, name.get(), parameter);
|
||||
}
|
||||
|
||||
// skip unused named parameters if there is SpEL
|
||||
@@ -118,12 +111,10 @@ class ExpressionEvaluatingParameterBinder {
|
||||
org.springframework.r2dbc.core.Parameter parameter = getBindValue(values, bindableParameter);
|
||||
|
||||
if (!parameter.isEmpty() || hasBindableNullValue) {
|
||||
bindSpecToUse = bind(bindSpecToUse, bindingIndex++, parameter);
|
||||
bind(bindSpec, bindingIndex++, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bindSpecToUse;
|
||||
}
|
||||
|
||||
private org.springframework.r2dbc.core.Parameter getBindValue(Object[] values, Parameter bindableParameter) {
|
||||
@@ -134,26 +125,25 @@ class ExpressionEvaluatingParameterBinder {
|
||||
return dataAccessStrategy.getBindValue(parameter);
|
||||
}
|
||||
|
||||
private static DatabaseClient.GenericExecuteSpec bind(DatabaseClient.GenericExecuteSpec spec, String name,
|
||||
private static void bind(BindTarget spec, String name,
|
||||
org.springframework.r2dbc.core.Parameter parameter) {
|
||||
|
||||
Object value = parameter.getValue();
|
||||
if (value == null) {
|
||||
return spec.bindNull(name, parameter.getType());
|
||||
spec.bindNull(name, parameter.getType());
|
||||
} else {
|
||||
return spec.bind(name, value);
|
||||
spec.bind(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static DatabaseClient.GenericExecuteSpec bind(DatabaseClient.GenericExecuteSpec spec, int index,
|
||||
private static void bind(BindTarget spec, int index,
|
||||
org.springframework.r2dbc.core.Parameter parameter) {
|
||||
|
||||
Object value = parameter.getValue();
|
||||
if (value == null) {
|
||||
return spec.bindNull(index, parameter.getType());
|
||||
spec.bindNull(index, parameter.getType());
|
||||
} else {
|
||||
|
||||
return spec.bind(index, value);
|
||||
spec.bind(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
|
||||
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
|
||||
import org.springframework.data.relational.repository.query.RelationalEntityMetadata;
|
||||
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
|
||||
@@ -52,13 +53,13 @@ public class PartTreeR2dbcQuery extends AbstractR2dbcQuery {
|
||||
* {@link R2dbcConverter} and {@link ReactiveDataAccessStrategy}.
|
||||
*
|
||||
* @param method query method, must not be {@literal null}.
|
||||
* @param databaseClient database client, must not be {@literal null}.
|
||||
* @param entityOperations entity operations, must not be {@literal null}.
|
||||
* @param converter converter, must not be {@literal null}.
|
||||
* @param dataAccessStrategy data access strategy, must not be {@literal null}.
|
||||
*/
|
||||
public PartTreeR2dbcQuery(R2dbcQueryMethod method, DatabaseClient databaseClient, R2dbcConverter converter,
|
||||
public PartTreeR2dbcQuery(R2dbcQueryMethod method, R2dbcEntityOperations entityOperations, R2dbcConverter converter,
|
||||
ReactiveDataAccessStrategy dataAccessStrategy) {
|
||||
super(method, databaseClient, converter);
|
||||
super(method, entityOperations, converter);
|
||||
|
||||
this.processor = method.getResultProcessor();
|
||||
this.dataAccessStrategy = dataAccessStrategy;
|
||||
@@ -105,7 +106,7 @@ public class PartTreeR2dbcQuery extends AbstractR2dbcQuery {
|
||||
* @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#createQuery(org.springframework.data.relational.repository.query.RelationalParameterAccessor)
|
||||
*/
|
||||
@Override
|
||||
protected Mono<BindableQuery> createQuery(RelationalParameterAccessor accessor) {
|
||||
protected Mono<PreparedOperation<?>> createQuery(RelationalParameterAccessor accessor) {
|
||||
|
||||
return Mono.fromSupplier(() -> {
|
||||
|
||||
@@ -119,13 +120,20 @@ public class PartTreeR2dbcQuery extends AbstractR2dbcQuery {
|
||||
RelationalEntityMetadata<?> entityMetadata = getQueryMethod().getEntityInformation();
|
||||
R2dbcQueryCreator queryCreator = new R2dbcQueryCreator(tree, dataAccessStrategy, entityMetadata, accessor,
|
||||
projectedProperties);
|
||||
PreparedOperation<?> preparedQuery = queryCreator.createQuery(getDynamicSort(accessor));
|
||||
|
||||
return new PreparedOperationBindableQuery(preparedQuery);
|
||||
return queryCreator.createQuery(getDynamicSort(accessor));
|
||||
});
|
||||
}
|
||||
|
||||
private Sort getDynamicSort(RelationalParameterAccessor accessor) {
|
||||
return parameters.potentiallySortsDynamically() ? accessor.getSort() : Sort.unsorted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getClass().getSimpleName());
|
||||
sb.append(" [").append(getQueryMethod().getName());
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class PreparedOperationBindableQuery implements BindableQuery {
|
||||
* This class adapts {@link DatabaseClient.GenericExecuteSpec} to {@link BindTarget} allowing easy binding of query
|
||||
* parameters using {@link PreparedOperation}.
|
||||
*/
|
||||
private static class BindSpecBindTargetAdapter implements BindTarget {
|
||||
static class BindSpecBindTargetAdapter implements BindTarget {
|
||||
|
||||
DatabaseClient.GenericExecuteSpec bindSpec;
|
||||
|
||||
|
||||
@@ -25,12 +25,11 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.EntityInstantiators;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.data.relational.repository.query.DtoInstantiatingConverter;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
import org.springframework.data.util.ReflectionUtils;
|
||||
import org.springframework.r2dbc.core.FetchSpec;
|
||||
import org.springframework.r2dbc.core.RowsFetchSpec;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -41,7 +40,7 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
interface R2dbcQueryExecution {
|
||||
|
||||
Publisher<?> execute(FetchSpec<Object> query, Class<?> type, SqlIdentifier tableName);
|
||||
Publisher<?> execute(RowsFetchSpec<Object> fetchSpec);
|
||||
|
||||
/**
|
||||
* An {@link R2dbcQueryExecution} that wraps the results of the given delegate with the given result processing.
|
||||
@@ -57,11 +56,11 @@ interface R2dbcQueryExecution {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution#execute(org.springframework.data.r2dbc.function.FetchSpec, java.lang.Class, java.lang.String)
|
||||
* @see org.springframework.data.r2dbc.repository.query.R2dbcQueryExecution#execute(org.springframework.data.r2dbc.function.FetchSpec)
|
||||
*/
|
||||
@Override
|
||||
public Publisher<Object> execute(FetchSpec<Object> query, Class<?> type, SqlIdentifier tableName) {
|
||||
return (Publisher<Object>) this.converter.convert(this.delegate.execute(query, type, tableName));
|
||||
public Publisher<Object> execute(RowsFetchSpec<Object> fetchSpec) {
|
||||
return (Publisher<Object>) this.converter.convert(this.delegate.execute(fetchSpec));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,16 @@ package org.springframework.data.r2dbc.repository.query;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
|
||||
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.r2dbc.repository.Query;
|
||||
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
@@ -31,6 +37,9 @@ import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.r2dbc.core.DatabaseClient;
|
||||
import org.springframework.r2dbc.core.Parameter;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.binding.BindTarget;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -48,24 +57,24 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
|
||||
private final ExpressionParser expressionParser;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ExpressionDependencies expressionDependencies;
|
||||
private final ReactiveDataAccessStrategy dataAccessStrategy;
|
||||
|
||||
/**
|
||||
* Creates a new {@link StringBasedR2dbcQuery} for the given {@link StringBasedR2dbcQuery}, {@link DatabaseClient},
|
||||
* {@link SpelExpressionParser}, and {@link QueryMethodEvaluationContextProvider}.
|
||||
*
|
||||
* @param queryMethod must not be {@literal null}.
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @param entityOperations must not be {@literal null}.
|
||||
* @param converter must not be {@literal null}.
|
||||
* @param dataAccessStrategy must not be {@literal null}.
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public StringBasedR2dbcQuery(R2dbcQueryMethod queryMethod, DatabaseClient databaseClient, R2dbcConverter converter,
|
||||
ReactiveDataAccessStrategy dataAccessStrategy,
|
||||
ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
this(queryMethod.getRequiredAnnotatedQuery(), queryMethod, databaseClient, converter, dataAccessStrategy,
|
||||
expressionParser,
|
||||
evaluationContextProvider);
|
||||
public StringBasedR2dbcQuery(R2dbcQueryMethod queryMethod, R2dbcEntityOperations entityOperations,
|
||||
R2dbcConverter converter, ReactiveDataAccessStrategy dataAccessStrategy, ExpressionParser expressionParser,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
this(queryMethod.getRequiredAnnotatedQuery(), queryMethod, entityOperations, converter, dataAccessStrategy,
|
||||
expressionParser, evaluationContextProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,22 +82,23 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
|
||||
* {@link DatabaseClient}, {@link SpelExpressionParser}, and {@link QueryMethodEvaluationContextProvider}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param databaseClient must not be {@literal null}.
|
||||
* @param entityOperations must not be {@literal null}.
|
||||
* @param converter must not be {@literal null}.
|
||||
* @param dataAccessStrategy must not be {@literal null}.
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public StringBasedR2dbcQuery(String query, R2dbcQueryMethod method, DatabaseClient databaseClient,
|
||||
public StringBasedR2dbcQuery(String query, R2dbcQueryMethod method, R2dbcEntityOperations entityOperations,
|
||||
R2dbcConverter converter, ReactiveDataAccessStrategy dataAccessStrategy, ExpressionParser expressionParser,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, databaseClient, converter);
|
||||
super(method, entityOperations, converter);
|
||||
this.expressionParser = expressionParser;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
|
||||
Assert.hasText(query, "Query must not be empty");
|
||||
|
||||
this.dataAccessStrategy = dataAccessStrategy;
|
||||
this.expressionQuery = ExpressionQuery.create(query);
|
||||
this.binder = new ExpressionEvaluatingParameterBinder(expressionQuery, dataAccessStrategy);
|
||||
this.expressionDependencies = createExpressionDependencies();
|
||||
@@ -141,20 +151,8 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
|
||||
* @see org.springframework.data.r2dbc.repository.query.AbstractR2dbcQuery#createQuery(org.springframework.data.relational.repository.query.RelationalParameterAccessor)
|
||||
*/
|
||||
@Override
|
||||
protected Mono<BindableQuery> createQuery(RelationalParameterAccessor accessor) {
|
||||
|
||||
return getSpelEvaluator(accessor).map(evaluator -> new BindableQuery() {
|
||||
|
||||
@Override
|
||||
public DatabaseClient.GenericExecuteSpec bind(DatabaseClient.GenericExecuteSpec bindSpec) {
|
||||
return binder.bind(bindSpec, accessor, evaluator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return expressionQuery.getQuery();
|
||||
}
|
||||
});
|
||||
protected Mono<PreparedOperation<?>> createQuery(RelationalParameterAccessor accessor) {
|
||||
return getSpelEvaluator(accessor).map(evaluator -> new ExpandedQuery(accessor, evaluator));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,4 +170,108 @@ public class StringBasedR2dbcQuery extends AbstractR2dbcQuery {
|
||||
context -> new DefaultR2dbcSpELExpressionEvaluator(expressionParser, context))
|
||||
.defaultIfEmpty(DefaultR2dbcSpELExpressionEvaluator.unsupported());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getClass().getSimpleName());
|
||||
sb.append(" [").append(expressionQuery.getQuery());
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private class ExpandedQuery implements PreparedOperation<String> {
|
||||
|
||||
private final BindTargetRecorder recordedBindings;
|
||||
|
||||
private final PreparedOperation<?> expanded;
|
||||
|
||||
private final Map<String, Parameter> remainderByName;
|
||||
|
||||
private final Map<Integer, Parameter> remainderByIndex;
|
||||
|
||||
public ExpandedQuery(RelationalParameterAccessor accessor, R2dbcSpELExpressionEvaluator evaluator) {
|
||||
|
||||
this.recordedBindings = new BindTargetRecorder();
|
||||
binder.bind(recordedBindings, accessor, evaluator);
|
||||
|
||||
remainderByName = new LinkedHashMap<>(recordedBindings.byName);
|
||||
remainderByIndex = new LinkedHashMap<>(recordedBindings.byIndex);
|
||||
expanded = dataAccessStrategy.processNamedParameters(expressionQuery.getQuery(), (index, name) -> {
|
||||
|
||||
if (recordedBindings.byName.containsKey(name)) {
|
||||
remainderByName.remove(name);
|
||||
return SettableValue.fromParameter(recordedBindings.byName.get(name));
|
||||
}
|
||||
|
||||
if (recordedBindings.byIndex.containsKey(index)) {
|
||||
remainderByIndex.remove(index);
|
||||
return SettableValue.fromParameter(recordedBindings.byIndex.get(index));
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSource() {
|
||||
return expressionQuery.getQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTo(BindTarget target) {
|
||||
|
||||
expanded.bindTo(target);
|
||||
|
||||
remainderByName.forEach(target::bind);
|
||||
remainderByIndex.forEach(target::bind);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toQuery() {
|
||||
return expanded.toQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Original: [%s], Expanded: [%s]", expressionQuery.getQuery(), expanded.toQuery());
|
||||
}
|
||||
}
|
||||
|
||||
private static class BindTargetRecorder implements BindTarget {
|
||||
|
||||
final Map<Integer, Parameter> byIndex = new LinkedHashMap<>();
|
||||
|
||||
final Map<String, Parameter> byName = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public void bind(String identifier, Object value) {
|
||||
byName.put(identifier, toParameter(value));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Parameter toParameter(Object value) {
|
||||
|
||||
if (value instanceof SettableValue) {
|
||||
return ((SettableValue) value).toParameter();
|
||||
}
|
||||
|
||||
return value instanceof Parameter ? (Parameter) value : Parameter.from(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(int index, Object value) {
|
||||
byIndex.put(index, toParameter(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(String identifier, Class<?> type) {
|
||||
byName.put(identifier, Parameter.empty(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(int index, Class<?> type) {
|
||||
byIndex.put(index, Parameter.empty(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class R2dbcRepositoryFactory extends ReactiveRepositoryFactorySupport {
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
return Optional.of(new R2dbcQueryLookupStrategy(this.databaseClient,
|
||||
return Optional.of(new R2dbcQueryLookupStrategy(this.operations,
|
||||
(ReactiveQueryMethodEvaluationContextProvider) evaluationContextProvider, this.converter,
|
||||
this.dataAccessStrategy));
|
||||
}
|
||||
@@ -158,16 +158,16 @@ public class R2dbcRepositoryFactory extends ReactiveRepositoryFactorySupport {
|
||||
*/
|
||||
private static class R2dbcQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final DatabaseClient databaseClient;
|
||||
private final R2dbcEntityOperations entityOperations;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final R2dbcConverter converter;
|
||||
private final ReactiveDataAccessStrategy dataAccessStrategy;
|
||||
private final ExpressionParser parser = new CachingExpressionParser(EXPRESSION_PARSER);
|
||||
|
||||
R2dbcQueryLookupStrategy(DatabaseClient databaseClient,
|
||||
R2dbcQueryLookupStrategy(R2dbcEntityOperations entityOperations,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider, R2dbcConverter converter,
|
||||
ReactiveDataAccessStrategy dataAccessStrategy) {
|
||||
this.databaseClient = databaseClient;
|
||||
this.entityOperations = entityOperations;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.converter = converter;
|
||||
this.dataAccessStrategy = dataAccessStrategy;
|
||||
@@ -188,15 +188,15 @@ public class R2dbcRepositoryFactory extends ReactiveRepositoryFactorySupport {
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
return new StringBasedR2dbcQuery(namedQuery, queryMethod, this.databaseClient, this.converter,
|
||||
return new StringBasedR2dbcQuery(namedQuery, queryMethod, this.entityOperations, this.converter,
|
||||
this.dataAccessStrategy,
|
||||
parser, this.evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new StringBasedR2dbcQuery(queryMethod, this.databaseClient, this.converter, this.dataAccessStrategy,
|
||||
return new StringBasedR2dbcQuery(queryMethod, this.entityOperations, this.converter, this.dataAccessStrategy,
|
||||
this.parser,
|
||||
this.evaluationContextProvider);
|
||||
} else {
|
||||
return new PartTreeR2dbcQuery(queryMethod, this.databaseClient, this.converter, this.dataAccessStrategy);
|
||||
return new PartTreeR2dbcQuery(queryMethod, this.entityOperations, this.converter, this.dataAccessStrategy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public abstract class AbstractR2dbcRepositoryIntegrationTests extends R2dbcInteg
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindItemsByNameLike() {
|
||||
void shouldFindItemsByNameContains() {
|
||||
|
||||
shouldInsertNewItems();
|
||||
|
||||
|
||||
@@ -25,12 +25,16 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -39,10 +43,12 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
|
||||
import org.springframework.data.r2dbc.mapping.event.AfterConvertCallback;
|
||||
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
|
||||
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory;
|
||||
import org.springframework.data.r2dbc.testing.H2TestSupport;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
@@ -59,6 +65,7 @@ public class H2R2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIn
|
||||
|
||||
@Autowired private H2LegoSetRepository repository;
|
||||
@Autowired private IdOnlyEntityRepository idOnlyEntityRepository;
|
||||
@Autowired private AfterConvertCallbackRecorder recorder;
|
||||
|
||||
@Configuration
|
||||
@EnableR2dbcRepositories(considerNestedRepositories = true,
|
||||
@@ -70,6 +77,16 @@ public class H2R2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIn
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return H2TestSupport.createConnectionFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AfterConvertCallbackRecorder afterConvertCallbackRecorder() {
|
||||
return new AfterConvertCallbackRecorder();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
recorder.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,6 +109,18 @@ public class H2R2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIn
|
||||
return H2LegoSetRepository.class;
|
||||
}
|
||||
|
||||
@Test // gh-591
|
||||
void shouldFindItemsByManual() {
|
||||
super.shouldFindItemsByManual();
|
||||
assertThat(recorder.seenEntities).hasSize(1);
|
||||
}
|
||||
|
||||
@Test // gh-591
|
||||
void shouldFindItemsByNameContains() {
|
||||
super.shouldFindItemsByNameContains();
|
||||
assertThat(recorder.seenEntities).hasSize(2);
|
||||
}
|
||||
|
||||
@Test // gh-469
|
||||
void shouldSuppressNullValues() {
|
||||
repository.findMax("doo").as(StepVerifier::create).verifyComplete();
|
||||
@@ -196,4 +225,19 @@ public class H2R2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIn
|
||||
static class IdOnlyEntity {
|
||||
@Id Integer id;
|
||||
}
|
||||
|
||||
static class AfterConvertCallbackRecorder implements AfterConvertCallback<LegoSet> {
|
||||
|
||||
List<LegoSet> seenEntities = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public Publisher<LegoSet> onAfterConvert(LegoSet entity, SqlIdentifier table) {
|
||||
seenEntities.add(entity);
|
||||
return Mono.just(entity);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
seenEntities.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.r2dbc.convert.R2dbcConverter;
|
||||
import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy;
|
||||
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.dialect.DialectResolver;
|
||||
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
|
||||
@@ -52,6 +54,8 @@ import org.springframework.data.relational.repository.query.RelationalParameters
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.r2dbc.core.DatabaseClient;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.binding.BindTarget;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PartTreeR2dbcQuery}.
|
||||
@@ -75,7 +79,7 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
|
||||
private RelationalMappingContext mappingContext;
|
||||
private ReactiveDataAccessStrategy dataAccessStrategy;
|
||||
private DatabaseClient databaseClient;
|
||||
private R2dbcEntityOperations operations;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -92,18 +96,19 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
R2dbcDialect dialect = DialectResolver.getDialect(connectionFactory);
|
||||
dataAccessStrategy = new DefaultReactiveDataAccessStrategy(dialect, r2dbcConverter);
|
||||
|
||||
databaseClient = DatabaseClient.builder().connectionFactory(connectionFactory).build();
|
||||
operations = new R2dbcEntityTemplate(DatabaseClient.builder().connectionFactory(connectionFactory).build(),
|
||||
dataAccessStrategy);
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByStringAttribute() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery bindableQuery = createQuery(queryMethod, r2dbcQuery, "John");
|
||||
PreparedOperation<?> preparedOperation = createQuery(queryMethod, r2dbcQuery, "John");
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1");
|
||||
}
|
||||
|
||||
@@ -111,11 +116,11 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryWithIsNullCondition() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery bindableQuery = createQuery(queryMethod, r2dbcQuery, new Object[] { null });
|
||||
PreparedOperation<?> preparedOperation = createQuery(queryMethod, r2dbcQuery, new Object[] { null });
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name IS NULL");
|
||||
}
|
||||
|
||||
@@ -123,9 +128,9 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryWithLimitForExistsProjection() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("existsByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery query = createQuery(queryMethod, r2dbcQuery, "John");
|
||||
PreparedOperation<?> query = createQuery(queryMethod, r2dbcQuery, "John");
|
||||
|
||||
assertThat(query.get())
|
||||
.isEqualTo("SELECT " + TABLE + ".id FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1 LIMIT 1");
|
||||
@@ -135,11 +140,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByTwoStringAttributes() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByLastNameAndFirstName", String.class, String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, getAccessor(queryMethod, new Object[] { "Doe", "John" }));
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery,
|
||||
getAccessor(queryMethod, new Object[] { "Doe", "John" }));
|
||||
|
||||
assertThat(bindableQuery.get()).isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE
|
||||
assertThat(preparedOperation.get()).isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE
|
||||
+ ".last_name = $1 AND (" + TABLE + ".first_name = $2)");
|
||||
}
|
||||
|
||||
@@ -147,11 +153,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByOneOfTwoStringAttributes() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByLastNameOrFirstName", String.class, String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, getAccessor(queryMethod, new Object[] { "Doe", "John" }));
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery,
|
||||
getAccessor(queryMethod, new Object[] { "Doe", "John" }));
|
||||
|
||||
assertThat(bindableQuery.get()).isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE
|
||||
assertThat(preparedOperation.get()).isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE
|
||||
+ ".last_name = $1 OR (" + TABLE + ".first_name = $2)");
|
||||
}
|
||||
|
||||
@@ -159,34 +166,33 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByDateAttributeBetween() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByDateOfBirthBetween", Date.class, Date.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
Date from = new Date();
|
||||
Date to = new Date();
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { from, to });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".date_of_birth BETWEEN $1 AND $2");
|
||||
|
||||
DatabaseClient.GenericExecuteSpec bindSpecMock = mock(DatabaseClient.GenericExecuteSpec.class);
|
||||
when(bindSpecMock.bind(anyInt(), any())).thenReturn(bindSpecMock);
|
||||
bindableQuery.bind(bindSpecMock);
|
||||
BindTarget bindTarget = mock(BindTarget.class);
|
||||
preparedOperation.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpecMock, times(1)).bind(0, from);
|
||||
verify(bindSpecMock, times(1)).bind(1, to);
|
||||
verify(bindTarget, times(1)).bind(0, from);
|
||||
verify(bindTarget, times(1)).bind(1, to);
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeLessThan() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeLessThan", Integer.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { 30 });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age < $1");
|
||||
}
|
||||
|
||||
@@ -194,12 +200,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeLessThanEqual() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeLessThanEqual", Integer.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { 30 });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age <= $1");
|
||||
}
|
||||
|
||||
@@ -207,12 +213,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeGreaterThan() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeGreaterThan", Integer.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { 30 });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age > $1");
|
||||
}
|
||||
|
||||
@@ -220,12 +226,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeGreaterThanEqual() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeGreaterThanEqual", Integer.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { 30 });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age >= $1");
|
||||
}
|
||||
|
||||
@@ -233,24 +239,24 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByDateAttributeAfter() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByDateOfBirthAfter", Date.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { new Date() });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".date_of_birth > $1");
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByDateAttributeBefore() throws Exception {
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByDateOfBirthBefore", Date.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { new Date() });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".date_of_birth < $1");
|
||||
}
|
||||
|
||||
@@ -258,12 +264,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeIsNull() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeIsNull");
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[0]);
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age IS NULL");
|
||||
}
|
||||
|
||||
@@ -271,12 +277,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeIsNotNull() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeIsNotNull");
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[0]);
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age IS NOT NULL");
|
||||
}
|
||||
|
||||
@@ -284,12 +290,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByStringAttributeLike() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameLike", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "%John%" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name LIKE $1");
|
||||
}
|
||||
|
||||
@@ -297,12 +303,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByStringAttributeNotLike() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameNotLike", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "%John%" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name NOT LIKE $1");
|
||||
}
|
||||
|
||||
@@ -310,148 +316,144 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByStringAttributeStartingWith() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameStartingWith", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "Jo" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name LIKE $1");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Test // gh-282
|
||||
void appendsLikeOperatorParameterWithPercentSymbolForStartingWithQuery() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameStartingWith", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "Jo" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
DatabaseClient.GenericExecuteSpec bindSpecMock = mock(DatabaseClient.GenericExecuteSpec.class);
|
||||
bindableQuery.bind(bindSpecMock);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
BindTarget bindTarget = mock(BindTarget.class);
|
||||
preparedOperation.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpecMock, times(1)).bind(0, "Jo%");
|
||||
verify(bindTarget, times(1)).bind(0, "Jo%");
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByStringAttributeEndingWith() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameEndingWith", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "hn" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name LIKE $1");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Test // gh-282
|
||||
void prependsLikeOperatorParameterWithPercentSymbolForEndingWithQuery() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameEndingWith", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "hn" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
DatabaseClient.GenericExecuteSpec bindSpecMock = mock(DatabaseClient.GenericExecuteSpec.class);
|
||||
bindableQuery.bind(bindSpecMock);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
BindTarget bindTarget = mock(BindTarget.class);
|
||||
preparedOperation.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpecMock, times(1)).bind(0, "%hn");
|
||||
verify(bindTarget, times(1)).bind(0, "%hn");
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByStringAttributeContaining() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameContaining", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "oh" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name LIKE $1");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Test // gh-282
|
||||
void wrapsLikeOperatorParameterWithPercentSymbolsForContainingQuery() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameContaining", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "oh" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
DatabaseClient.GenericExecuteSpec bindSpecMock = mock(DatabaseClient.GenericExecuteSpec.class);
|
||||
bindableQuery.bind(bindSpecMock);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
BindTarget bindTarget = mock(BindTarget.class);
|
||||
preparedOperation.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpecMock, times(1)).bind(0, "%oh%");
|
||||
verify(bindTarget, times(1)).bind(0, "%oh%");
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByStringAttributeNotContaining() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameNotContaining", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "oh" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name NOT LIKE $1");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Test // gh-282
|
||||
void wrapsLikeOperatorParameterWithPercentSymbolsForNotContainingQuery() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameNotContaining", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "oh" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
DatabaseClient.GenericExecuteSpec bindSpecMock = mock(DatabaseClient.GenericExecuteSpec.class);
|
||||
bindableQuery.bind(bindSpecMock);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
BindTarget bindTarget = mock(BindTarget.class);
|
||||
preparedOperation.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpecMock, times(1)).bind(0, "%oh%");
|
||||
verify(bindTarget, times(1)).bind(0, "%oh%");
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeWithDescendingOrderingByStringAttribute()
|
||||
throws Exception {
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeOrderByLastNameDesc", Integer.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "oh" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age = $1 ORDER BY last_name DESC");
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeWithAscendingOrderingByStringAttribute() throws Exception {
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeOrderByLastNameAsc", Integer.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "oh" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age = $1 ORDER BY last_name ASC");
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByStringAttributeNot() throws Exception {
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByLastNameNot", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "Doe" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".last_name != $1");
|
||||
}
|
||||
|
||||
@@ -459,26 +461,26 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeIn() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeIn", Collection.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod,
|
||||
new Object[] { Collections.singleton(25) });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age IN ($1)");
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void createsQueryToFindAllEntitiesByIntegerAttributeNotIn() throws Exception {
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByAgeNotIn", Collection.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod,
|
||||
new Object[] { Collections.singleton(25) });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".age NOT IN ($1)");
|
||||
}
|
||||
|
||||
@@ -486,12 +488,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByBooleanAttributeTrue() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByActiveTrue");
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[0]);
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".active = TRUE");
|
||||
}
|
||||
|
||||
@@ -499,12 +501,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByBooleanAttributeFalse() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByActiveFalse");
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[0]);
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".active = FALSE");
|
||||
}
|
||||
|
||||
@@ -512,12 +514,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindAllEntitiesByStringAttributeIgnoringCase() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameIgnoreCase", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "John" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE UPPER(" + TABLE + ".first_name) = UPPER($1)");
|
||||
}
|
||||
|
||||
@@ -525,7 +527,7 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void throwsExceptionWhenIgnoringCaseIsImpossible() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findByIdIgnoringCase", Long.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
|
||||
assertThatIllegalStateException()
|
||||
@@ -538,7 +540,7 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByIdIn", Long.class);
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter, dataAccessStrategy));
|
||||
.isThrownBy(() -> new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter, dataAccessStrategy));
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
@@ -547,14 +549,14 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllById", Collection.class);
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter, dataAccessStrategy));
|
||||
.isThrownBy(() -> new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter, dataAccessStrategy));
|
||||
}
|
||||
|
||||
@Test // gh-282
|
||||
void throwsExceptionWhenConditionKeywordIsUnsupported() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByIdIsEmpty");
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -565,7 +567,7 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void throwsExceptionWhenInvalidNumberOfParameterIsGiven() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAllByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
@@ -576,12 +578,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryWithLimitToFindEntitiesByStringAttribute() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findTop3ByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "John" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1 LIMIT 3");
|
||||
}
|
||||
|
||||
@@ -589,12 +591,12 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindFirstEntityByStringAttribute() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findFirstByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "John" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get())
|
||||
assertThat(preparedOperation.get())
|
||||
.isEqualTo("SELECT " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1 LIMIT 1");
|
||||
}
|
||||
|
||||
@@ -602,23 +604,23 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToDeleteByFirstName() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("deleteByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[] { "John" });
|
||||
BindableQuery bindableQuery = createQuery(r2dbcQuery, accessor);
|
||||
PreparedOperation<?> preparedOperation = createQuery(r2dbcQuery, accessor);
|
||||
|
||||
assertThat(bindableQuery.get()).isEqualTo("DELETE FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1");
|
||||
assertThat(preparedOperation.get()).isEqualTo("DELETE FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1");
|
||||
}
|
||||
|
||||
@Test // gh-344
|
||||
void createsQueryToFindAllEntitiesByStringAttributeWithDistinct() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findDistinctByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery bindableQuery = createQuery(queryMethod, r2dbcQuery, "John");
|
||||
PreparedOperation<?> preparedOperation = createQuery(queryMethod, r2dbcQuery, "John");
|
||||
|
||||
assertThat(bindableQuery.get()).isEqualTo("SELECT " + DISTINCT + " " + TABLE + ".first_name, " + TABLE
|
||||
assertThat(preparedOperation.get()).isEqualTo("SELECT " + DISTINCT + " " + TABLE + ".first_name, " + TABLE
|
||||
+ ".foo FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1");
|
||||
}
|
||||
|
||||
@@ -626,11 +628,11 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryToFindByOpenProjection() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findOpenProjectionBy");
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery bindableQuery = createQuery(queryMethod, r2dbcQuery);
|
||||
PreparedOperation<?> preparedOperation = createQuery(queryMethod, r2dbcQuery);
|
||||
|
||||
assertThat(bindableQuery.get()).isEqualTo(
|
||||
assertThat(preparedOperation.get()).isEqualTo(
|
||||
"SELECT users.id, users.first_name, users.last_name, users.date_of_birth, users.age, users.active FROM "
|
||||
+ TABLE);
|
||||
}
|
||||
@@ -639,11 +641,11 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsDtoProjectionQuery() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("findAsDtoProjectionBy");
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery bindableQuery = createQuery(queryMethod, r2dbcQuery);
|
||||
PreparedOperation<?> preparedOperation = createQuery(queryMethod, r2dbcQuery);
|
||||
|
||||
assertThat(bindableQuery.get()).isEqualTo(
|
||||
assertThat(preparedOperation.get()).isEqualTo(
|
||||
"SELECT users.id, users.first_name, users.last_name, users.date_of_birth, users.age, users.active FROM "
|
||||
+ TABLE);
|
||||
}
|
||||
@@ -652,19 +654,21 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
void createsQueryForCountProjection() throws Exception {
|
||||
|
||||
R2dbcQueryMethod queryMethod = getQueryMethod("countByFirstName", String.class);
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
|
||||
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, operations, r2dbcConverter,
|
||||
dataAccessStrategy);
|
||||
BindableQuery query = createQuery(queryMethod, r2dbcQuery, "John");
|
||||
PreparedOperation<?> query = createQuery(queryMethod, r2dbcQuery, "John");
|
||||
|
||||
assertThat(query.get())
|
||||
.isEqualTo("SELECT COUNT(users.id) FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1");
|
||||
}
|
||||
|
||||
private BindableQuery createQuery(R2dbcQueryMethod queryMethod, PartTreeR2dbcQuery r2dbcQuery, Object... parameters) {
|
||||
private PreparedOperation<?> createQuery(R2dbcQueryMethod queryMethod, PartTreeR2dbcQuery r2dbcQuery,
|
||||
Object... parameters) {
|
||||
return createQuery(r2dbcQuery, getAccessor(queryMethod, parameters));
|
||||
}
|
||||
|
||||
private BindableQuery createQuery(PartTreeR2dbcQuery r2dbcQuery, RelationalParametersParameterAccessor accessor) {
|
||||
private PreparedOperation<?> createQuery(PartTreeR2dbcQuery r2dbcQuery,
|
||||
RelationalParametersParameterAccessor accessor) {
|
||||
return r2dbcQuery.createQuery(accessor).block();
|
||||
}
|
||||
|
||||
@@ -678,6 +682,7 @@ class PartTreeR2dbcQueryUnitTests {
|
||||
return new RelationalParametersParameterAccessor(queryMethod, values);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
interface UserRepository extends Repository<User, Long> {
|
||||
|
||||
Flux<User> findAllByFirstName(String firstName);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.data.r2dbc.repository.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -34,6 +33,7 @@ import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.r2dbc.convert.MappingR2dbcConverter;
|
||||
import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy;
|
||||
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
|
||||
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
|
||||
import org.springframework.data.r2dbc.dialect.PostgresDialect;
|
||||
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
|
||||
@@ -45,8 +45,9 @@ import org.springframework.data.repository.core.support.AbstractRepositoryMetada
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.r2dbc.core.DatabaseClient;
|
||||
import org.springframework.r2dbc.core.DatabaseClient.GenericExecuteSpec;
|
||||
import org.springframework.r2dbc.core.Parameter;
|
||||
import org.springframework.r2dbc.core.PreparedOperation;
|
||||
import org.springframework.r2dbc.core.binding.BindTarget;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
@@ -60,8 +61,8 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
|
||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
@Mock private DatabaseClient databaseClient;
|
||||
@Mock private GenericExecuteSpec bindSpec;
|
||||
@Mock private R2dbcEntityOperations entityOperations;
|
||||
@Mock private BindTarget bindTarget;
|
||||
|
||||
private RelationalMappingContext mappingContext;
|
||||
private MappingR2dbcConverter converter;
|
||||
@@ -77,9 +78,6 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
this.accessStrategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE, converter);
|
||||
this.metadata = AbstractRepositoryMetadata.getMetadata(SampleRepository.class);
|
||||
this.factory = new SpelAwareProxyProjectionFactory();
|
||||
|
||||
when(bindSpec.bind(anyInt(), any())).thenReturn(bindSpec);
|
||||
when(bindSpec.bind(anyString(), any())).thenReturn(bindSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -88,12 +86,12 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("findByLastname", String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind(0, "White");
|
||||
verify(bindTarget).bind(0, Parameter.from("White"));
|
||||
}
|
||||
|
||||
@Test // gh-164
|
||||
@@ -102,12 +100,12 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("findByLastnamePositional", String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind(0, "White");
|
||||
verify(bindTarget).bind(0, Parameter.from("White"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,12 +114,12 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("findByNamedParameter", String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = :lastname");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind("lastname", "White");
|
||||
verify(bindTarget).bind(0, "White");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,12 +128,12 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("findByNamedBindMarker", String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = @lastname");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind("lastname", "White");
|
||||
verify(bindTarget).bind("lastname", Parameter.from("White"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -144,12 +142,13 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("findNotByNamedBindMarker", String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = :unknown");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
assertThat(stringQuery.getSource()).isEqualTo("SELECT * FROM person WHERE lastname = :unknown");
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind(0, "White");
|
||||
verify(bindTarget).bind(0, "White");
|
||||
}
|
||||
|
||||
@Test // gh-164
|
||||
@@ -158,12 +157,13 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("simpleSpel");
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod());
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = :__synthetic_0__");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
assertThat(stringQuery.getSource()).isEqualTo("SELECT * FROM person WHERE lastname = :__synthetic_0__");
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind("__synthetic_0__", "hello");
|
||||
verify(bindTarget).bind(0, "hello");
|
||||
}
|
||||
|
||||
@Test // gh-164
|
||||
@@ -172,13 +172,14 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("simpleIndexedSpel", String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = :__synthetic_0__");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
assertThat(stringQuery.getSource()).isEqualTo("SELECT * FROM person WHERE lastname = :__synthetic_0__");
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind("__synthetic_0__", "White");
|
||||
verifyNoMoreInteractions(bindSpec);
|
||||
verify(bindTarget).bind(0, "White");
|
||||
verifyNoMoreInteractions(bindTarget);
|
||||
}
|
||||
|
||||
@Test // gh-164
|
||||
@@ -187,15 +188,15 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("simplePositionalSpel", String.class, String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White", "Walter");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get())
|
||||
assertThat(stringQuery.getSource())
|
||||
.isEqualTo("SELECT * FROM person WHERE lastname = :__synthetic_0__ and firstname = :firstname");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind("__synthetic_0__", "White");
|
||||
verify(bindSpec).bind("firstname", "Walter");
|
||||
verifyNoMoreInteractions(bindSpec);
|
||||
verify(bindTarget).bind(0, "White");
|
||||
verify(bindTarget).bind(1, "Walter");
|
||||
verifyNoMoreInteractions(bindTarget);
|
||||
}
|
||||
|
||||
@Test // gh-164
|
||||
@@ -204,15 +205,15 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("simpleNamedSpel", String.class, String.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "White", "Walter");
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get())
|
||||
.isEqualTo("SELECT * FROM person WHERE lastname = :__synthetic_0__ and firstname = :firstname");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
.isEqualTo("SELECT * FROM person WHERE lastname = $1 and firstname = $2");
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind("__synthetic_0__", "White");
|
||||
verify(bindSpec).bind("firstname", "Walter");
|
||||
verifyNoMoreInteractions(bindSpec);
|
||||
verify(bindTarget).bind(0, "White");
|
||||
verify(bindTarget).bind(1, "Walter");
|
||||
verifyNoMoreInteractions(bindTarget);
|
||||
}
|
||||
|
||||
@Test // gh-164
|
||||
@@ -221,13 +222,13 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("queryWithSpelObject", Person.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), new Person("Walter"));
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = :__synthetic_0__");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind("__synthetic_0__", "Walter");
|
||||
verifyNoMoreInteractions(bindSpec);
|
||||
verify(bindTarget).bind(0, "Walter");
|
||||
verifyNoMoreInteractions(bindTarget);
|
||||
}
|
||||
|
||||
@Test // gh-321
|
||||
@@ -236,13 +237,13 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("queryWithUnusedParameter", String.class, Sort.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), "Walter", null);
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = :name");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind(0, "Walter");
|
||||
verifyNoMoreInteractions(bindSpec);
|
||||
verify(bindTarget).bind(0, "Walter");
|
||||
verifyNoMoreInteractions(bindTarget);
|
||||
}
|
||||
|
||||
@Test // gh-465
|
||||
@@ -251,11 +252,11 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
StringBasedR2dbcQuery query = getQueryMethod("queryWithEnum", MyEnum.class);
|
||||
R2dbcParameterAccessor accessor = new R2dbcParameterAccessor(query.getQueryMethod(), MyEnum.INSTANCE);
|
||||
|
||||
BindableQuery stringQuery = query.createQuery(accessor).block();
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
PreparedOperation<?> stringQuery = query.createQuery(accessor).block();
|
||||
stringQuery.bindTo(bindTarget);
|
||||
|
||||
verify(bindSpec).bind(0, "INSTANCE");
|
||||
verifyNoMoreInteractions(bindSpec);
|
||||
verify(bindTarget).bind(0, "INSTANCE");
|
||||
verifyNoMoreInteractions(bindTarget);
|
||||
}
|
||||
|
||||
@Test // gh-475
|
||||
@@ -280,7 +281,7 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
|
||||
R2dbcQueryMethod queryMethod = new R2dbcQueryMethod(method, metadata, factory, converter.getMappingContext());
|
||||
|
||||
return new StringBasedR2dbcQuery(queryMethod, databaseClient, converter, accessStrategy, PARSER,
|
||||
return new StringBasedR2dbcQuery(queryMethod, entityOperations, converter, accessStrategy, PARSER,
|
||||
ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user