/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.repository.query;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
import org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryExecution.CollectionExecution;
import org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryExecution.ResultProcessingConverter;
import org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryExecution.ResultProcessingExecution;
import org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryExecution.SingleEntityExecution;
import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.util.Assert;
import org.reactivestreams.Publisher;
import com.datastax.driver.core.Statement;
/**
* Base class for reactive {@link RepositoryQuery} implementations for Cassandra.
*
* @author Mark Paluch
* @since 2.0
*/
public abstract class AbstractReactiveCassandraQuery implements RepositoryQuery {
private final ReactiveCassandraQueryMethod method;
private final ReactiveCassandraOperations operations;
private final EntityInstantiators instantiators;
/**
* Create a new {@link AbstractReactiveCassandraQuery} from the given {@link CassandraQueryMethod} and
* {@link CassandraOperations}.
*
* @param method must not be {@literal null}.
* @param operations must not be {@literal null}.
*/
public AbstractReactiveCassandraQuery(ReactiveCassandraQueryMethod method, ReactiveCassandraOperations operations) {
Assert.notNull(method, "ReactiveCassandraQueryMethod must not be null");
Assert.notNull(operations, "ReactiveCassandraOperations must not be null");
this.method = method;
this.operations = operations;
this.instantiators = new EntityInstantiators();
}
/* (non-Javadoc) */
protected EntityInstantiators getEntityInstantiators() {
return this.instantiators;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod()
*/
@Override
public ReactiveCassandraQueryMethod getQueryMethod() {
return this.method;
}
/* (non-Javadoc) */
protected ReactiveCassandraOperations getReactiveCassandraOperations() {
return this.operations;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[])
*/
@Override
public Object execute(Object[] parameters) {
return (getQueryMethod().hasReactiveWrapperParameter() ? executeDeferred(parameters)
: execute(new ReactiveCassandraParameterAccessor(getQueryMethod(), parameters)));
}
@SuppressWarnings("unchecked")
private Object executeDeferred(Object[] parameters) {
ReactiveCassandraParameterAccessor accessor = new ReactiveCassandraParameterAccessor(getQueryMethod(), parameters);
return (getQueryMethod().isCollectionQuery() ? Flux.defer(() -> (Publisher<Object>) execute(accessor))
: Mono.defer(() -> (Mono<Object>) execute(accessor)));
}
private Object execute(CassandraParameterAccessor parameterAccessor) {
CassandraParameterAccessor convertingParameterAccessor =
new ConvertingParameterAccessor(getReactiveCassandraOperations().getConverter(), parameterAccessor);
Statement statement = createQuery(convertingParameterAccessor);
ResultProcessor resultProcessor =
getQueryMethod().getResultProcessor().withDynamicProjection(convertingParameterAccessor);
ReactiveCassandraQueryExecution queryExecution = getExecution(new ResultProcessingConverter(
resultProcessor, getReactiveCassandraOperations().getConverter().getMappingContext(),
getEntityInstantiators()));
CassandraReturnedType returnedType = new CassandraReturnedType(resultProcessor.getReturnedType(),
getReactiveCassandraOperations().getConverter().getCustomConversions());
Class<?> resultType = (returnedType.isProjecting() ? returnedType.getDomainType() : returnedType.getReturnedType());
return queryExecution.execute(statement, resultType);
}
/**
* Creates a string query using the given {@link ParameterAccessor}
*
* @param accessor must not be {@literal null}.
*/
protected abstract Statement createQuery(CassandraParameterAccessor accessor);
/**
* Returns the execution instance to use.
*
* @param resultProcessing must not be {@literal null}. @return
*/
private ReactiveCassandraQueryExecution getExecution(Converter<Object, Object> resultProcessing) {
return new ResultProcessingExecution(getExecutionToWrap(), resultProcessing);
}
/* (non-Javadoc) */
private ReactiveCassandraQueryExecution getExecutionToWrap() {
return (getQueryMethod().isCollectionQuery() ? new CollectionExecution(getReactiveCassandraOperations())
: new SingleEntityExecution(getReactiveCassandraOperations()));
}
}