DATAJPA-1575 - Introduce QueryMetadataCache.
This commit is contained in:
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.*;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
@@ -42,6 +40,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
|
||||
private final DeclaredQuery countQuery;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final SpelExpressionParser parser;
|
||||
private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache();
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractStringBasedJpaQuery} from the given {@link JpaQueryMethod}, {@link EntityManager} and
|
||||
@@ -86,9 +85,11 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
|
||||
|
||||
Query query = createJpaQuery(sortedQueryString, processor.getReturnedType());
|
||||
|
||||
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(sortedQueryString, query);
|
||||
|
||||
// it is ok to reuse the binding contained in the ParameterBinder although we create a new query String because the
|
||||
// parameters in the query do not change.
|
||||
return parameterBinder.get().bindAndPrepare(query, accessor);
|
||||
return parameterBinder.get().bindAndPrepare(query, metadata, accessor);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -116,7 +117,11 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
|
||||
? em.createNativeQuery(queryString) //
|
||||
: em.createQuery(queryString, Long.class);
|
||||
|
||||
return parameterBinder.get().bind(query, accessor, LENIENT);
|
||||
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(queryString, query);
|
||||
|
||||
parameterBinder.get().bind(metadata.withQuery(query), accessor, QueryParameterSetter.ErrorHandling.LENIENT);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,7 +176,6 @@ public abstract class JpaQueryExecution {
|
||||
|
||||
return PageableExecutionUtils.getPage(query.getResultList(), accessor.getPageable(),
|
||||
() -> count(repositoryQuery, accessor));
|
||||
|
||||
}
|
||||
|
||||
private long count(AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) {
|
||||
|
||||
@@ -55,6 +55,7 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
private final QueryExtractor extractor;
|
||||
private final boolean namedCountQueryIsPresent;
|
||||
private final DeclaredQuery declaredQuery;
|
||||
private final QueryParameterSetter.QueryMetadataCache metadataCache;
|
||||
|
||||
/**
|
||||
* Creates a new {@link NamedQuery}.
|
||||
@@ -93,6 +94,8 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
LOG.warn("Finder method {} is backed by a NamedQuery" + " but contains a Pageable parameter! Sorting delivered "
|
||||
+ "via this Pageable will not be applied!", method);
|
||||
}
|
||||
|
||||
this.metadataCache = new QueryParameterSetter.QueryMetadataCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +169,9 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
? em.createNamedQuery(queryName) //
|
||||
: em.createNamedQuery(queryName, typeToRead);
|
||||
|
||||
return parameterBinder.get().bindAndPrepare(query, accessor);
|
||||
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(queryName, query);
|
||||
|
||||
return parameterBinder.get().bindAndPrepare(query, metadata, accessor);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -179,18 +184,21 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
EntityManager em = getEntityManager();
|
||||
TypedQuery<Long> countQuery;
|
||||
|
||||
String cacheKey;
|
||||
if (namedCountQueryIsPresent) {
|
||||
|
||||
cacheKey = countQueryName;
|
||||
countQuery = em.createNamedQuery(countQueryName, Long.class);
|
||||
|
||||
} else {
|
||||
|
||||
String countQueryString = declaredQuery.deriveCountQuery(null, countProjection).getQueryString();
|
||||
|
||||
cacheKey = countQueryString;
|
||||
countQuery = em.createQuery(countQueryString, Long.class);
|
||||
}
|
||||
|
||||
return parameterBinder.get().bind(countQuery, accessor, LENIENT);
|
||||
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(cacheKey, countQuery);
|
||||
|
||||
return parameterBinder.get().bind(countQuery, metadata, accessor);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -69,17 +69,18 @@ public class ParameterBinder {
|
||||
this.useJpaForPaging = useJpaForPaging;
|
||||
}
|
||||
|
||||
public <T extends Query> T bind(T jpaQuery, JpaParametersParameterAccessor accessor) {
|
||||
return bind(jpaQuery, accessor, ErrorHandling.STRICT);
|
||||
public <T extends Query> T bind(T jpaQuery, QueryParameterSetter.QueryMetadata metadata,
|
||||
JpaParametersParameterAccessor accessor) {
|
||||
bind(metadata.withQuery(jpaQuery), accessor, ErrorHandling.STRICT);
|
||||
return jpaQuery;
|
||||
}
|
||||
|
||||
public <T extends Query> T bind(T jpaQuery, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling) {
|
||||
public void bind(QueryParameterSetter.BindableQuery query, JpaParametersParameterAccessor accessor,
|
||||
ErrorHandling errorHandling) {
|
||||
|
||||
for (QueryParameterSetter setter : parameterSetters) {
|
||||
setter.setParameter(jpaQuery, accessor, errorHandling);
|
||||
setter.setParameter(query, accessor, errorHandling);
|
||||
}
|
||||
|
||||
return jpaQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,19 +89,18 @@ public class ParameterBinder {
|
||||
* @param query must not be {@literal null}.
|
||||
* @param values values of method parameters to be assigned to the query parameters.
|
||||
*/
|
||||
Query bindAndPrepare(Query query, JpaParametersParameterAccessor accessor) {
|
||||
Query bindAndPrepare(Query query, QueryParameterSetter.QueryMetadata metadata,
|
||||
JpaParametersParameterAccessor accessor) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
Query result = bind(query, accessor);
|
||||
bind(query, metadata, accessor);
|
||||
|
||||
if (!useJpaForPaging || !parameters.hasPageableParameter() || accessor.getPageable().isUnpaged()) {
|
||||
return result;
|
||||
return query;
|
||||
}
|
||||
|
||||
result.setFirstResult((int) accessor.getPageable().getOffset());
|
||||
result.setMaxResults(accessor.getPageable().getPageSize());
|
||||
query.setFirstResult((int) accessor.getPageable().getOffset());
|
||||
query.setMaxResults(accessor.getPageable().getPageSize());
|
||||
|
||||
return result;
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +214,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
private final @Nullable List<ParameterMetadata<?>> expressions;
|
||||
private final PersistenceProvider persistenceProvider;
|
||||
private final Map<List<ParameterMetadata<?>>, ParameterBinder> binderCache = new ConcurrentHashMap<>();
|
||||
private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache();
|
||||
|
||||
QueryPreparer(PersistenceProvider persistenceProvider, boolean recreateQueries) {
|
||||
|
||||
@@ -251,7 +252,9 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
throw new IllegalStateException("ParameterBinder is null!");
|
||||
}
|
||||
|
||||
return restrictMaxResultsIfNecessary(invokeBinding(parameterBinder, createQuery(criteriaQuery), accessor));
|
||||
TypedQuery<?> query = createQuery(criteriaQuery);
|
||||
|
||||
return restrictMaxResultsIfNecessary(invokeBinding(parameterBinder, query, accessor, this.metadataCache));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,10 +330,12 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
/**
|
||||
* Invokes parameter binding on the given {@link TypedQuery}.
|
||||
*/
|
||||
protected Query invokeBinding(ParameterBinder binder, TypedQuery<?> query,
|
||||
JpaParametersParameterAccessor accessor) {
|
||||
protected Query invokeBinding(ParameterBinder binder, TypedQuery<?> query, JpaParametersParameterAccessor accessor,
|
||||
QueryParameterSetter.QueryMetadataCache metadataCache) {
|
||||
|
||||
return binder.bindAndPrepare(query, accessor);
|
||||
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata("query", query);
|
||||
|
||||
return binder.bindAndPrepare(query, metadata, accessor);
|
||||
}
|
||||
|
||||
private ParameterBinder getBinder(List<ParameterMetadata<?>> expressions) {
|
||||
@@ -382,9 +387,12 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
* @see QueryPreparer#invokeBinding(ParameterBinder, TypedQuery, JpaParametersParameterAccessor)
|
||||
*/
|
||||
@Override
|
||||
protected Query invokeBinding(ParameterBinder binder, TypedQuery<?> query,
|
||||
JpaParametersParameterAccessor accessor) {
|
||||
return binder.bind(query, accessor);
|
||||
protected Query invokeBinding(ParameterBinder binder, TypedQuery<?> query, JpaParametersParameterAccessor accessor,
|
||||
QueryParameterSetter.QueryMetadataCache metadataCache) {
|
||||
|
||||
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata("countquery", query);
|
||||
|
||||
return binder.bind(query, metadata, accessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ package org.springframework.data.jpa.repository.query;
|
||||
import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.*;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.persistence.Parameter;
|
||||
@@ -42,7 +46,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
interface QueryParameterSetter {
|
||||
|
||||
void setParameter(Query query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling);
|
||||
void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling);
|
||||
|
||||
/** Noop implementation */
|
||||
QueryParameterSetter NOOP = (query, values, errorHandling) -> {};
|
||||
@@ -52,8 +56,6 @@ interface QueryParameterSetter {
|
||||
*/
|
||||
class NamedOrIndexedQueryParameterSetter implements QueryParameterSetter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NamedOrIndexedQueryParameterSetter.class);
|
||||
|
||||
private final Function<JpaParametersParameterAccessor, Object> valueExtractor;
|
||||
private final Parameter<?> parameter;
|
||||
private final @Nullable TemporalType temporalType;
|
||||
@@ -79,7 +81,8 @@ interface QueryParameterSetter {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void setParameter(Query query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling) {
|
||||
public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor,
|
||||
ErrorHandling errorHandling) {
|
||||
|
||||
Object value = valueExtractor.apply(accessor);
|
||||
|
||||
@@ -92,7 +95,7 @@ interface QueryParameterSetter {
|
||||
|
||||
if (parameter instanceof ParameterExpression) {
|
||||
errorHandling.execute(() -> query.setParameter((Parameter<Date>) parameter, (Date) value, temporalType));
|
||||
} else if (parameter.getName() != null && QueryUtils.hasNamedParameter(query)) {
|
||||
} else if (query.hasNamedParameters() && parameter.getName() != null) {
|
||||
errorHandling.execute(() -> query.setParameter(parameter.getName(), (Date) value, temporalType));
|
||||
} else {
|
||||
|
||||
@@ -100,7 +103,7 @@ interface QueryParameterSetter {
|
||||
|
||||
if (position != null //
|
||||
&& (query.getParameters().size() >= parameter.getPosition() //
|
||||
|| registerExcessParameters(query) //
|
||||
|| query.registerExcessParameters() //
|
||||
|| errorHandling == LENIENT)) {
|
||||
|
||||
errorHandling.execute(() -> query.setParameter(parameter.getPosition(), (Date) value, temporalType));
|
||||
@@ -111,7 +114,7 @@ interface QueryParameterSetter {
|
||||
|
||||
if (parameter instanceof ParameterExpression) {
|
||||
errorHandling.execute(() -> query.setParameter((Parameter<Object>) parameter, value));
|
||||
} else if (parameter.getName() != null && QueryUtils.hasNamedParameter(query)) {
|
||||
} else if (query.hasNamedParameters() && parameter.getName() != null) {
|
||||
errorHandling.execute(() -> query.setParameter(parameter.getName(), value));
|
||||
|
||||
} else {
|
||||
@@ -121,51 +124,12 @@ interface QueryParameterSetter {
|
||||
if (position != null //
|
||||
&& (query.getParameters().size() >= position //
|
||||
|| errorHandling == LENIENT //
|
||||
|| registerExcessParameters(query))) {
|
||||
|
||||
|| query.registerExcessParameters())) {
|
||||
errorHandling.execute(() -> query.setParameter(position, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean registerExcessParameters(Query query) {
|
||||
|
||||
// DATAJPA-1172
|
||||
// Since EclipseLink doesn't reliably report whether a query has parameters
|
||||
// we simply try to set the parameters and ignore possible failures.
|
||||
// this is relevant for native queries with SpEL expressions, where the method parameters don't have to match the
|
||||
// parameters in the query.
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=521915
|
||||
|
||||
return query.getParameters().size() == 0 && unwrapClass(query).getName().startsWith("org.eclipse");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual target {@link Query} instance, even if the provided query is a {@link Proxy} based on
|
||||
* {@link org.springframework.orm.jpa.SharedEntityManagerCreator.DeferredQueryInvocationHandler}.
|
||||
*
|
||||
* @param query a {@link Query} instance, possibly a Proxy.
|
||||
* @return the class of the actual underlying class if it can be determined, the class of the passed in instance
|
||||
* otherwise.
|
||||
*/
|
||||
private static Class<?> unwrapClass(Query query) {
|
||||
|
||||
Class<? extends Query> queryType = query.getClass();
|
||||
|
||||
try {
|
||||
|
||||
return Proxy.isProxyClass(queryType) //
|
||||
? query.unwrap(null).getClass() //
|
||||
: queryType;
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
LOGGER.warn("Failed to unwrap actual class for Query proxy.", e);
|
||||
|
||||
return queryType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ErrorHandling {
|
||||
@@ -195,4 +159,184 @@ interface QueryParameterSetter {
|
||||
|
||||
abstract void execute(Runnable block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache for {@link QueryMetadata}. Optimizes for small cache sizes on a best-effort basis.
|
||||
*/
|
||||
class QueryMetadataCache {
|
||||
|
||||
private Map<String, QueryMetadata> cache = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* Retrieve the {@link QueryMetadata} for a given {@code cacheKey}.
|
||||
*
|
||||
* @param cacheKey
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
public QueryMetadata getMetadata(String cacheKey, Query query) {
|
||||
|
||||
QueryMetadata queryMetadata = cache.get(cacheKey);
|
||||
|
||||
if (queryMetadata == null) {
|
||||
|
||||
queryMetadata = new QueryMetadata(query);
|
||||
|
||||
Map<String, QueryMetadata> cache;
|
||||
|
||||
if (this.cache.isEmpty()) {
|
||||
cache = Collections.singletonMap(cacheKey, queryMetadata);
|
||||
} else {
|
||||
cache = new HashMap<>(this.cache);
|
||||
cache.put(cacheKey, queryMetadata);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
this.cache = cache;
|
||||
}
|
||||
}
|
||||
|
||||
return queryMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata for a JPA {@link Query}.
|
||||
*/
|
||||
class QueryMetadata {
|
||||
|
||||
private final boolean namedParameters;
|
||||
private final Set<Parameter<?>> parameters;
|
||||
private final boolean registerExcessParameters;
|
||||
|
||||
QueryMetadata(Query query) {
|
||||
|
||||
this.namedParameters = QueryUtils.hasNamedParameter(query);
|
||||
this.parameters = query.getParameters();
|
||||
|
||||
// DATAJPA-1172
|
||||
// Since EclipseLink doesn't reliably report whether a query has parameters
|
||||
// we simply try to set the parameters and ignore possible failures.
|
||||
// this is relevant for native queries with SpEL expressions, where the method parameters don't have to match the
|
||||
// parameters in the query.
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=521915
|
||||
|
||||
this.registerExcessParameters = query.getParameters().size() == 0
|
||||
&& unwrapClass(query).getName().startsWith("org.eclipse");
|
||||
}
|
||||
|
||||
QueryMetadata(QueryMetadata metadata) {
|
||||
|
||||
this.namedParameters = metadata.namedParameters;
|
||||
this.parameters = metadata.parameters;
|
||||
this.registerExcessParameters = metadata.registerExcessParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link BindableQuery} for a {@link Query}.
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
public BindableQuery withQuery(Query query) {
|
||||
return new BindableQuery(this, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Set<Parameter<?>> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the underlying query uses named parameters.
|
||||
*/
|
||||
public boolean hasNamedParameters() {
|
||||
return this.namedParameters;
|
||||
}
|
||||
|
||||
public boolean registerExcessParameters() {
|
||||
return this.registerExcessParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual target {@link Query} instance, even if the provided query is a {@link Proxy} based on
|
||||
* {@link org.springframework.orm.jpa.SharedEntityManagerCreator.DeferredQueryInvocationHandler}.
|
||||
*
|
||||
* @param query a {@link Query} instance, possibly a Proxy.
|
||||
* @return the class of the actual underlying class if it can be determined, the class of the passed in instance
|
||||
* otherwise.
|
||||
*/
|
||||
private static Class<?> unwrapClass(Query query) {
|
||||
|
||||
Class<? extends Query> queryType = query.getClass();
|
||||
|
||||
try {
|
||||
|
||||
return Proxy.isProxyClass(queryType) //
|
||||
? query.unwrap(null).getClass() //
|
||||
: queryType;
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
LoggerFactory.getLogger(QueryMetadata.class).warn("Failed to unwrap actual class for Query proxy.", e);
|
||||
|
||||
return queryType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A bindable {@link Query}.
|
||||
*/
|
||||
class BindableQuery extends QueryMetadata {
|
||||
|
||||
private final Query query;
|
||||
private final Query unwrapped;
|
||||
|
||||
BindableQuery(QueryMetadata metadata, Query query) {
|
||||
super(metadata);
|
||||
this.query = query;
|
||||
this.unwrapped = Proxy.isProxyClass(query.getClass()) ? query.unwrap(null) : query;
|
||||
}
|
||||
|
||||
private BindableQuery(Query query) {
|
||||
super(query);
|
||||
this.query = query;
|
||||
this.unwrapped = Proxy.isProxyClass(query.getClass()) ? query.unwrap(null) : query;
|
||||
}
|
||||
|
||||
public static BindableQuery from(Query query) {
|
||||
return new BindableQuery(query);
|
||||
}
|
||||
|
||||
public Query getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public <T> Query setParameter(Parameter<T> param, T value) {
|
||||
return unwrapped.setParameter(param, value);
|
||||
}
|
||||
|
||||
public Query setParameter(Parameter<Date> param, Date value, TemporalType temporalType) {
|
||||
return unwrapped.setParameter(param, value, temporalType);
|
||||
}
|
||||
|
||||
public Query setParameter(String name, Object value) {
|
||||
return unwrapped.setParameter(name, value);
|
||||
}
|
||||
|
||||
public Query setParameter(String name, Date value, TemporalType temporalType) {
|
||||
return query.setParameter(name, value, temporalType);
|
||||
}
|
||||
|
||||
public Query setParameter(int position, Object value) {
|
||||
return unwrapped.setParameter(position, value);
|
||||
}
|
||||
|
||||
public Query setParameter(int position, Date value, TemporalType temporalType) {
|
||||
return unwrapped.setParameter(position, value, temporalType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -51,6 +52,7 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
|
||||
|
||||
private final StoredProcedureAttributes procedureAttributes;
|
||||
private final boolean useNamedParameters;
|
||||
private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache();
|
||||
|
||||
/**
|
||||
* Creates a new {@link StoredProcedureJpaQuery}.
|
||||
@@ -97,7 +99,11 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
|
||||
*/
|
||||
@Override
|
||||
protected StoredProcedureQuery doCreateQuery(JpaParametersParameterAccessor accessor) {
|
||||
return parameterBinder.get().bind(createStoredProcedure(), accessor);
|
||||
|
||||
StoredProcedureQuery storedProcedure = createStoredProcedure();
|
||||
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata("singleton", storedProcedure);
|
||||
|
||||
return parameterBinder.get().bind(storedProcedure, metadata, accessor);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -105,7 +105,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
/**
|
||||
* Configures the {@link EntityPathResolver} to be used. Defaults to {@link SimpleEntityPathResolver#INSTANCE}.
|
||||
*
|
||||
*
|
||||
* @param entityPathResolver must not be {@literal null}.
|
||||
*/
|
||||
public void setEntityPathResolver(EntityPathResolver entityPathResolver) {
|
||||
@@ -252,7 +252,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
/**
|
||||
* Creates a new {@link EclipseLinkProjectionQueryCreationListener} for the given {@link EntityManager}.
|
||||
*
|
||||
*
|
||||
* @param em must not be {@literal null}.
|
||||
*/
|
||||
public EclipseLinkProjectionQueryCreationListener(EntityManager em) {
|
||||
@@ -262,7 +262,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
this.metamodel = JpaMetamodel.of(em.getMetamodel());
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.QueryCreationListener#onCreation(org.springframework.data.repository.query.RepositoryQuery)
|
||||
*/
|
||||
|
||||
@@ -84,7 +84,9 @@ public class NamedOrIndexedQueryParameterSetterUnitTests {
|
||||
temporalType //
|
||||
);
|
||||
|
||||
softly.assertThatThrownBy(() -> setter.setParameter(query, methodArguments, STRICT)) //
|
||||
softly
|
||||
.assertThatThrownBy(
|
||||
() -> setter.setParameter(QueryParameterSetter.BindableQuery.from(query), methodArguments, STRICT)) //
|
||||
.describedAs("p-type: %s, p-name: %s, p-position: %s, temporal: %s", //
|
||||
parameter.getClass(), //
|
||||
parameter.getName(), //
|
||||
@@ -111,7 +113,9 @@ public class NamedOrIndexedQueryParameterSetterUnitTests {
|
||||
temporalType //
|
||||
);
|
||||
|
||||
softly.assertThatCode(() -> setter.setParameter(query, methodArguments, LENIENT)) //
|
||||
softly
|
||||
.assertThatCode(
|
||||
() -> setter.setParameter(QueryParameterSetter.BindableQuery.from(query), methodArguments, LENIENT)) //
|
||||
.describedAs("p-type: %s, p-name: %s, p-position: %s, temporal: %s", //
|
||||
parameter.getClass(), //
|
||||
parameter.getName(), //
|
||||
@@ -142,7 +146,7 @@ public class NamedOrIndexedQueryParameterSetterUnitTests {
|
||||
temporalType //
|
||||
);
|
||||
|
||||
setter.setParameter(query, methodArguments, LENIENT);
|
||||
setter.setParameter(QueryParameterSetter.BindableQuery.from(query), methodArguments, LENIENT);
|
||||
|
||||
if (temporalType == null) {
|
||||
verify(query).setParameter(eq(11), any(Date.class));
|
||||
@@ -172,7 +176,7 @@ public class NamedOrIndexedQueryParameterSetterUnitTests {
|
||||
temporalType //
|
||||
);
|
||||
|
||||
setter.setParameter(query, methodArguments, LENIENT);
|
||||
setter.setParameter(QueryParameterSetter.BindableQuery.from(query), methodArguments, LENIENT);
|
||||
|
||||
if (temporalType == null) {
|
||||
verify(query, never()).setParameter(anyInt(), any(Date.class));
|
||||
|
||||
@@ -17,8 +17,9 @@ package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static javax.persistence.TemporalType.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Mockito.any;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
@@ -36,6 +37,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.repository.Temporal;
|
||||
@@ -102,8 +104,7 @@ public class ParameterBinderUnitTests {
|
||||
Method validWithSort = SampleRepository.class.getMethod("validWithSort", String.class, Sort.class);
|
||||
|
||||
Object[] values = { "foo", null };
|
||||
ParameterBinderFactory.createBinder(new JpaParameters(validWithSort)).bind(query,
|
||||
new JpaParametersParameterAccessor(new JpaParameters(validWithSort), values));
|
||||
bind(validWithSort, values);
|
||||
verify(query).setParameter(eq(1), eq("foo"));
|
||||
}
|
||||
|
||||
@@ -113,8 +114,7 @@ public class ParameterBinderUnitTests {
|
||||
Method validWithPageable = SampleRepository.class.getMethod("validWithPageable", String.class, Pageable.class);
|
||||
|
||||
Object[] values = { "foo", null };
|
||||
ParameterBinderFactory.createBinder(new JpaParameters(validWithPageable)).bind(query,
|
||||
new JpaParametersParameterAccessor(new JpaParameters(validWithPageable), values));
|
||||
bind(validWithPageable, values);
|
||||
verify(query).setParameter(eq(1), eq("foo"));
|
||||
}
|
||||
|
||||
@@ -122,8 +122,7 @@ public class ParameterBinderUnitTests {
|
||||
public void usesIndexedParametersIfNoParamAnnotationPresent() throws Exception {
|
||||
|
||||
Object[] values = { "foo" };
|
||||
ParameterBinderFactory.createBinder(new JpaParameters(useIndexedParameters)).bind(query,
|
||||
new JpaParametersParameterAccessor(new JpaParameters(useIndexedParameters), values));
|
||||
bind(useIndexedParameters, values);
|
||||
verify(query).setParameter(eq(1), any());
|
||||
}
|
||||
|
||||
@@ -138,8 +137,7 @@ public class ParameterBinderUnitTests {
|
||||
when(query.getParameters()).thenReturn(singleton(parameter));
|
||||
|
||||
Object[] values = { "foo" };
|
||||
ParameterBinderFactory.createBinder(new JpaParameters(valid)).bind(query,
|
||||
new JpaParametersParameterAccessor(new JpaParameters(valid), values));
|
||||
bind(valid, values);
|
||||
|
||||
verify(query).setParameter(eq("username"), any());
|
||||
}
|
||||
@@ -152,7 +150,7 @@ public class ParameterBinderUnitTests {
|
||||
SampleEmbeddable embeddable = new SampleEmbeddable();
|
||||
|
||||
Object[] values = { embeddable };
|
||||
ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values));
|
||||
bind(method, parameters, values);
|
||||
|
||||
verify(query).setParameter(1, embeddable);
|
||||
}
|
||||
@@ -165,7 +163,7 @@ public class ParameterBinderUnitTests {
|
||||
Date date = new Date();
|
||||
|
||||
Object[] values = { date };
|
||||
ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values));
|
||||
bind(method, parameters, values);
|
||||
|
||||
verify(query).setParameter(eq(1), eq(date), eq(TemporalType.DATE));
|
||||
}
|
||||
@@ -178,7 +176,7 @@ public class ParameterBinderUnitTests {
|
||||
Date date = new Date();
|
||||
|
||||
Object[] values = { date };
|
||||
ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values));
|
||||
bind(method, parameters, values);
|
||||
|
||||
verify(query).setParameter(eq(1), eq(date), eq(TemporalType.TIMESTAMP));
|
||||
}
|
||||
@@ -199,7 +197,7 @@ public class ParameterBinderUnitTests {
|
||||
JpaParameters parameters = new JpaParameters(method);
|
||||
Integer[] ids = new Integer[] { 1, 2, 3 };
|
||||
Object[] values = { ids };
|
||||
ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values));
|
||||
bind(method, parameters, values);
|
||||
|
||||
verify(query).setParameter(eq(1), eq(ids));
|
||||
}
|
||||
@@ -211,7 +209,7 @@ public class ParameterBinderUnitTests {
|
||||
JpaParameters parameters = new JpaParameters(method);
|
||||
|
||||
Object[] values = { Optional.of("Foo") };
|
||||
ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values));
|
||||
bind(method, parameters, values);
|
||||
|
||||
verify(query).setParameter(eq(1), eq("Foo"));
|
||||
}
|
||||
@@ -222,12 +220,21 @@ public class ParameterBinderUnitTests {
|
||||
Method method = SampleRepository.class.getMethod("withQuery", String.class, String.class);
|
||||
|
||||
Object[] values = { "foo", "superfluous" };
|
||||
ParameterBinderFactory.createBinder(new JpaParameters(method)).bind(query, getAccessor(method, values));
|
||||
bind(method, new JpaParameters(method), values);
|
||||
|
||||
verify(query).setParameter(eq(1), any());
|
||||
verify(query, never()).setParameter(eq(2), any());
|
||||
}
|
||||
|
||||
private void bind(Method method, Object[] values) {
|
||||
bind(method, new JpaParameters(method), values);
|
||||
}
|
||||
|
||||
private void bind(Method method, JpaParameters parameters, Object[] values) {
|
||||
ParameterBinderFactory.createBinder(parameters).bind(QueryParameterSetter.BindableQuery.from(query),
|
||||
getAccessor(method, values), QueryParameterSetter.ErrorHandling.STRICT);
|
||||
}
|
||||
|
||||
private JpaParametersParameterAccessor getAccessor(Method method, Object... values) {
|
||||
return new JpaParametersParameterAccessor(new JpaParameters(method), values);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user