From 10d7339799c8fd8776c0bdc54136ce10bfca3c89 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 19 Jul 2019 12:11:01 +0200 Subject: [PATCH] DATAJPA-1575 - Consistent use of JpaParametersParameterAccessor. We now reuse JpaParametersParameterAccessor as much as possible to avoid constant recreation. This change required several signature changes to accept JpaParametersParameterAccessor instead of raw Object[] values. --- .../repository/query/AbstractJpaQuery.java | 27 +++--- .../query/AbstractStringBasedJpaQuery.java | 18 ++-- .../query/JpaParametersParameterAccessor.java | 6 ++ .../repository/query/JpaQueryExecution.java | 76 ++++++--------- .../data/jpa/repository/query/NamedQuery.java | 19 ++-- .../jpa/repository/query/ParameterBinder.java | 15 ++- .../query/ParameterMetadataProvider.java | 6 +- .../repository/query/PartTreeJpaQuery.java | 76 ++++++++------- .../query/QueryParameterSetter.java | 15 +-- .../query/QueryParameterSetterFactory.java | 21 +++-- .../query/StoredProcedureJpaQuery.java | 17 ++-- .../query/AbstractJpaQueryTests.java | 25 +++-- .../query/JpaQueryExecutionUnitTests.java | 93 ++++++++++--------- ...rIndexedQueryParameterSetterUnitTests.java | 17 +++- .../repository/query/NamedQueryUnitTests.java | 3 +- .../query/ParameterBinderUnitTests.java | 29 ++++-- .../PartTreeJpaQueryIntegrationTests.java | 24 +++-- .../query/SimpleJpaQueryUnitTests.java | 16 ++-- 18 files changed, 264 insertions(+), 239 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 3e1e17978..f8d526a97 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -69,7 +69,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { private final JpaMetamodel metamodel; private final PersistenceProvider provider; - Lazy parameterBinder = new Lazy<>(this::createBinder); + final Lazy parameterBinder = new Lazy<>(this::createBinder); /** * Creates a new {@link AbstractJpaQuery} from the given {@link JpaQueryMethod}. @@ -133,11 +133,10 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { @Nullable private Object doExecute(JpaQueryExecution execution, Object[] values) { - Object result = execution.execute(this, values); + JpaParametersParameterAccessor accessor = new JpaParametersParameterAccessor(method.getParameters(), values); + Object result = execution.execute(this, accessor); - ParametersParameterAccessor accessor = new ParametersParameterAccessor(method.getParameters(), values); ResultProcessor withDynamicProjection = method.getResultProcessor().withDynamicProjection(accessor); - return withDynamicProjection.processResult(result, new TupleConverter(withDynamicProjection.getReturnedType())); } @@ -150,9 +149,9 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { } else if (method.isCollectionQuery()) { return new CollectionExecution(); } else if (method.isSliceQuery()) { - return new SlicedExecution(method.getParameters()); + return new SlicedExecution(); } else if (method.isPageQuery()) { - return new PagedExecution(method.getParameters()); + return new PagedExecution(); } else if (method.isModifyingQuery()) { return new ModifyingExecution(method, em); } else { @@ -206,8 +205,8 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { return ParameterBinderFactory.createBinder(getQueryMethod().getParameters()); } - protected Query createQuery(Object[] values) { - return applyLockMode(applyEntityGraphConfiguration(applyHints(doCreateQuery(values), method), method), method); + protected Query createQuery(JpaParametersParameterAccessor parameters) { + return applyLockMode(applyEntityGraphConfiguration(applyHints(doCreateQuery(parameters), method), method), method); } /** @@ -233,14 +232,14 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { return query; } - protected Query createCountQuery(Object[] values) { + protected Query createCountQuery(JpaParametersParameterAccessor values) { Query countQuery = doCreateCountQuery(values); return method.applyHintsToCountQuery() ? applyHints(countQuery, method) : countQuery; } /** * Returns the type to be used when creating the JPA query. - * + * * @return * @since 2.0.5 */ @@ -258,18 +257,18 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Creates a {@link Query} instance for the given values. * - * @param values must not be {@literal null}. + * @param accessor must not be {@literal null}. * @return */ - protected abstract Query doCreateQuery(Object[] values); + protected abstract Query doCreateQuery(JpaParametersParameterAccessor accessor); /** * Creates a {@link TypedQuery} for counting using the given values. * - * @param values must not be {@literal null}. + * @param accessor must not be {@literal null}. * @return */ - protected abstract Query doCreateCountQuery(Object[] values); + protected abstract Query doCreateCountQuery(JpaParametersParameterAccessor accessor); static class TupleConverter implements Converter { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 25c7decc7..a3be59875 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -20,8 +20,6 @@ import static org.springframework.data.jpa.repository.query.QueryParameterSetter import javax.persistence.EntityManager; import javax.persistence.Query; -import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; @@ -36,6 +34,7 @@ import org.springframework.util.Assert; * @author Jens Schauder * @author Tom Hombergs * @author David Madden + * @author Mark Paluch */ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { @@ -77,12 +76,11 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(JpaParametersParameterAccessor) */ @Override - public Query doCreateQuery(Object[] values) { + public Query doCreateQuery(JpaParametersParameterAccessor accessor) { - ParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), values); String sortedQueryString = QueryUtils.applySorting(query.getQueryString(), accessor.getSort(), query.getAlias()); ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor); @@ -90,12 +88,12 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { // 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, values); + return parameterBinder.get().bindAndPrepare(query, accessor); } /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#createBinder(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#createBinder(JpaParametersParameterAccessor) */ @Override protected ParameterBinder createBinder() { @@ -106,10 +104,10 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(JpaParametersParameterAccessor) */ @Override - protected Query doCreateCountQuery(Object[] values) { + protected Query doCreateCountQuery(JpaParametersParameterAccessor accessor) { String queryString = countQuery.getQueryString(); EntityManager em = getEntityManager(); @@ -118,7 +116,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { ? em.createNativeQuery(queryString) // : em.createQuery(queryString, Long.class); - return parameterBinder.get().bind(query, values, LENIENT); + return parameterBinder.get().bind(query, accessor, LENIENT); } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java index f7ea18d24..8ed3a5101 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java @@ -25,6 +25,7 @@ import org.springframework.data.repository.query.ParametersParameterAccessor; * offers access to all the values, not just the bindable ones based on a {@link JpaParameter} instance. * * @author Jens Schauder + * @author Mark Paluch */ public class JpaParametersParameterAccessor extends ParametersParameterAccessor { @@ -41,4 +42,9 @@ public class JpaParametersParameterAccessor extends ParametersParameterAccessor public T getValue(Parameter parameter) { return super.getValue(parameter.getIndex()); } + + @Override + public Object[] getValues() { + return super.getValues(); + } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 3d94aa616..12e0285b9 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -34,9 +34,6 @@ import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor; -import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.data.repository.query.Parameters; -import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.support.PageableExecutionUtils; import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.StreamUtils; @@ -80,15 +77,15 @@ public abstract class JpaQueryExecution { * @return */ @Nullable - public Object execute(AbstractJpaQuery query, Object[] values) { + public Object execute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { Assert.notNull(query, "AbstractJpaQuery must not be null!"); - Assert.notNull(values, "Values must not be null!"); + Assert.notNull(accessor, "JpaParametersParameterAccessor must not be null!"); Object result; try { - result = doExecute(query, values); + result = doExecute(query, accessor); } catch (NoResultException e) { return null; } @@ -117,7 +114,7 @@ public abstract class JpaQueryExecution { * @return */ @Nullable - protected abstract Object doExecute(AbstractJpaQuery query, Object[] values); + protected abstract Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor); /** * Executes the query to return a simple collection of entities. @@ -125,8 +122,8 @@ public abstract class JpaQueryExecution { static class CollectionExecution extends JpaQueryExecution { @Override - protected Object doExecute(AbstractJpaQuery query, Object[] values) { - return query.createQuery(values).getResultList(); + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { + return query.createQuery(accessor).getResultList(); } } @@ -138,29 +135,16 @@ public abstract class JpaQueryExecution { */ static class SlicedExecution extends JpaQueryExecution { - private final Parameters parameters; - - /** - * Creates a new {@link SlicedExecution} using the given {@link Parameters}. - * - * @param parameters must not be {@literal null}. - */ - public SlicedExecution(Parameters parameters) { - this.parameters = parameters; - } - /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) */ @Override @SuppressWarnings("unchecked") - protected Object doExecute(AbstractJpaQuery query, Object[] values) { + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { - ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values); Pageable pageable = accessor.getPageable(); - - Query createQuery = query.createQuery(values); + Query createQuery = query.createQuery(accessor); int pageSize = 0; if (pageable.isPaged()) { @@ -184,28 +168,20 @@ public abstract class JpaQueryExecution { */ static class PagedExecution extends JpaQueryExecution { - private final Parameters parameters; - - public PagedExecution(Parameters parameters) { - - this.parameters = parameters; - } - @Override @SuppressWarnings("unchecked") - protected Object doExecute(final AbstractJpaQuery repositoryQuery, final Object[] values) { + protected Object doExecute(final AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) { - ParameterAccessor accessor = new ParametersParameterAccessor(parameters, values); - Query query = repositoryQuery.createQuery(values); + Query query = repositoryQuery.createQuery(accessor); return PageableExecutionUtils.getPage(query.getResultList(), accessor.getPageable(), - () -> count(repositoryQuery, values)); + () -> count(repositoryQuery, accessor)); } - private long count(AbstractJpaQuery repositoryQuery, Object[] values) { + private long count(AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) { - List totals = repositoryQuery.createCountQuery(values).getResultList(); + List totals = repositoryQuery.createCountQuery(accessor).getResultList(); return (totals.size() == 1 ? CONVERSION_SERVICE.convert(totals.get(0), Long.class) : totals.size()); } } @@ -216,9 +192,9 @@ public abstract class JpaQueryExecution { static class SingleEntityExecution extends JpaQueryExecution { @Override - protected Object doExecute(AbstractJpaQuery query, Object[] values) { + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { - return query.createQuery(values).getSingleResult(); + return query.createQuery(accessor).getSingleResult(); } } @@ -254,13 +230,13 @@ public abstract class JpaQueryExecution { } @Override - protected Object doExecute(AbstractJpaQuery query, Object[] values) { + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { if (flush) { em.flush(); } - int result = query.createQuery(values).executeUpdate(); + int result = query.createQuery(accessor).executeUpdate(); if (clear) { em.clear(); @@ -290,9 +266,9 @@ public abstract class JpaQueryExecution { * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) */ @Override - protected Object doExecute(AbstractJpaQuery jpaQuery, Object[] values) { + protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAccessor accessor) { - Query query = jpaQuery.createQuery(values); + Query query = jpaQuery.createQuery(accessor); List resultList = query.getResultList(); for (Object o : resultList) { @@ -312,8 +288,8 @@ public abstract class JpaQueryExecution { static class ExistsExecution extends JpaQueryExecution { @Override - protected Object doExecute(AbstractJpaQuery query, Object[] values) { - return !query.createQuery(values).getResultList().isEmpty(); + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { + return !query.createQuery(accessor).getResultList().isEmpty(); } } @@ -330,12 +306,12 @@ public abstract class JpaQueryExecution { * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) */ @Override - protected Object doExecute(AbstractJpaQuery jpaQuery, Object[] values) { + protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAccessor accessor) { Assert.isInstanceOf(StoredProcedureJpaQuery.class, jpaQuery); StoredProcedureJpaQuery storedProcedureJpaQuery = (StoredProcedureJpaQuery) jpaQuery; - StoredProcedureQuery storedProcedure = storedProcedureJpaQuery.createQuery(values); + StoredProcedureQuery storedProcedure = storedProcedureJpaQuery.createQuery(accessor); storedProcedure.execute(); return storedProcedureJpaQuery.extractOutputValue(storedProcedure); @@ -356,16 +332,16 @@ public abstract class JpaQueryExecution { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, JpaParametersParameterAccessor) */ @Override - protected Object doExecute(final AbstractJpaQuery query, Object[] values) { + protected Object doExecute(final AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) { throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); } - Query jpaQuery = query.createQuery(values); + Query jpaQuery = query.createQuery(accessor); // JPA 2.2 on the classpath if (streamMethod != null) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 73528f24a..e263475b1 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -26,9 +26,9 @@ import javax.persistence.TypedQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.repository.query.Parameters; -import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.QueryCreationException; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; @@ -152,30 +152,29 @@ final class NamedQuery extends AbstractJpaQuery { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(JpaParametersParameterAccessor) */ @Override - protected Query doCreateQuery(Object[] values) { + protected Query doCreateQuery(JpaParametersParameterAccessor accessor) { EntityManager em = getEntityManager(); JpaQueryMethod queryMethod = getQueryMethod(); - ResultProcessor processor = queryMethod.getResultProcessor() - .withDynamicProjection(new ParametersParameterAccessor(queryMethod.getParameters(), values)); + ResultProcessor processor = queryMethod.getResultProcessor().withDynamicProjection(accessor); Query query = getTypeToRead(processor.getReturnedType()) // . map(it -> em.createNamedQuery(queryName, it)) // .orElseGet(() -> em.createNamedQuery(queryName)); - return parameterBinder.get().bindAndPrepare(query, values); + return parameterBinder.get().bindAndPrepare(query, accessor); } /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(JpaParametersParameterAccessor) */ @Override - protected TypedQuery doCreateCountQuery(Object[] values) { + protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { EntityManager em = getEntityManager(); TypedQuery countQuery; @@ -191,10 +190,10 @@ final class NamedQuery extends AbstractJpaQuery { countQuery = em.createQuery(countQueryString, Long.class); } - return parameterBinder.get().bind(countQuery, values, LENIENT); + return parameterBinder.get().bind(countQuery, accessor, LENIENT); } - /* + /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#getTypeToRead() */ diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 774a4c281..db040921d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -18,7 +18,6 @@ package org.springframework.data.jpa.repository.query; import javax.persistence.Query; import org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling; -import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.util.Assert; /** @@ -70,13 +69,13 @@ public class ParameterBinder { this.useJpaForPaging = useJpaForPaging; } - public T bind(T jpaQuery, Object[] values) { - return bind(jpaQuery, values, ErrorHandling.STRICT); + public T bind(T jpaQuery, JpaParametersParameterAccessor accessor) { + return bind(jpaQuery, accessor, ErrorHandling.STRICT); } - public T bind(T jpaQuery, Object[] values, ErrorHandling errorHandling) { + public T bind(T jpaQuery, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling) { - parameterSetters.forEach(it -> it.setParameter(jpaQuery, values, errorHandling)); + parameterSetters.forEach(it -> it.setParameter(jpaQuery, accessor, errorHandling)); return jpaQuery; } @@ -87,13 +86,11 @@ 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, Object[] values) { + Query bindAndPrepare(Query query, JpaParametersParameterAccessor accessor) { Assert.notNull(query, "Query must not be null!"); - ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values); - - Query result = bind(query, values); + Query result = bind(query, accessor); if (!useJpaForPaging || !parameters.hasPageableParameter() || accessor.getPageable().isUnpaged()) { return result; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 1009e0bf2..aef364438 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -64,7 +64,7 @@ class ParameterMetadataProvider { * Creates a new {@link ParameterMetadataProvider} from the given {@link CriteriaBuilder} and * {@link ParametersParameterAccessor} with support for parameter value customizations via {@link PersistenceProvider} * . - * + * * @param builder must not be {@literal null}. * @param accessor must not be {@literal null}. * @param provider must not be {@literal null}. @@ -78,7 +78,7 @@ class ParameterMetadataProvider { /** * Creates a new {@link ParameterMetadataProvider} from the given {@link CriteriaBuilder} and {@link Parameters} with * support for parameter value customizations via {@link PersistenceProvider}. - * + * * @param builder must not be {@literal null}. * @param parameters must not be {@literal null}. * @param provider must not be {@literal null}. @@ -121,7 +121,7 @@ class ParameterMetadataProvider { * @return the expressions */ public List> getExpressions() { - return Collections.unmodifiableList(expressions); + return expressions; } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index def656f32..47ed7b936 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -17,7 +17,6 @@ package org.springframework.data.jpa.repository.query; import java.util.Collection; import java.util.List; -import java.util.Optional; import javax.persistence.EntityManager; import javax.persistence.Query; @@ -106,21 +105,21 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(JpaParametersParameterAccessor) */ @Override - public Query doCreateQuery(Object[] values) { - return query.createQuery(values); + public Query doCreateQuery(JpaParametersParameterAccessor accessor) { + return query.createQuery(accessor); } /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(JpaParametersParameterAccessor) */ @Override @SuppressWarnings("unchecked") - public TypedQuery doCreateCountQuery(Object[] values) { - return (TypedQuery) countQuery.createQuery(values); + public TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { + return (TypedQuery) countQuery.createQuery(accessor); } /* @@ -218,7 +217,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { this.persistenceProvider = persistenceProvider; - JpaQueryCreator creator = createCreator(persistenceProvider, Optional.empty()); + JpaQueryCreator creator = createCreator(persistenceProvider, null); if (recreateQueries) { this.cachedCriteriaQuery = null; @@ -234,15 +233,14 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { /** * Creates a new {@link Query} for the given parameter values. */ - public Query createQuery(Object[] values) { + public Query createQuery(JpaParametersParameterAccessor accessor) { CriteriaQuery criteriaQuery = cachedCriteriaQuery; ParameterBinder parameterBinder = cachedParameterBinder; - ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values); if (cachedCriteriaQuery == null || accessor.hasBindableNullValue()) { - JpaQueryCreator creator = createCreator(persistenceProvider, Optional.of(accessor)); - criteriaQuery = creator.createQuery(getDynamicSort(values)); + JpaQueryCreator creator = createCreator(persistenceProvider, accessor); + criteriaQuery = creator.createQuery(getDynamicSort(accessor)); List> expressions = creator.getParameterExpressions(); parameterBinder = getBinder(expressions); } @@ -251,7 +249,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { throw new IllegalStateException("ParameterBinder is null!"); } - return restrictMaxResultsIfNecessary(invokeBinding(parameterBinder, createQuery(criteriaQuery), values)); + return restrictMaxResultsIfNecessary(invokeBinding(parameterBinder, createQuery(criteriaQuery), accessor)); } /** @@ -303,18 +301,22 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { } protected JpaQueryCreator createCreator(PersistenceProvider persistenceProvider, - Optional accessor) { + @Nullable JpaParametersParameterAccessor accessor) { EntityManager entityManager = getEntityManager(); CriteriaBuilder builder = entityManager.getCriteriaBuilder(); - - ParameterMetadataProvider provider = accessor - .map(it -> new ParameterMetadataProvider(builder, it, persistenceProvider, escape))// - .orElseGet(() -> new ParameterMetadataProvider(builder, parameters, persistenceProvider, escape)); - ResultProcessor processor = getQueryMethod().getResultProcessor(); - ReturnedType returnedType = accessor.map(processor::withDynamicProjection)// - .orElse(processor).getReturnedType(); + + ParameterMetadataProvider provider; + ReturnedType returnedType; + + if (accessor != null) { + provider = new ParameterMetadataProvider(builder, accessor, persistenceProvider, escape); + returnedType = processor.withDynamicProjection(accessor).getReturnedType(); + } else { + provider = new ParameterMetadataProvider(builder, parameters, persistenceProvider, escape); + returnedType = processor.getReturnedType(); + } return new JpaQueryCreator(tree, returnedType, builder, provider); } @@ -322,19 +324,20 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { /** * Invokes parameter binding on the given {@link TypedQuery}. */ - protected Query invokeBinding(ParameterBinder binder, TypedQuery query, Object[] values) { + protected Query invokeBinding(ParameterBinder binder, TypedQuery query, + JpaParametersParameterAccessor accessor) { - return binder.bindAndPrepare(query, values); + return binder.bindAndPrepare(query, accessor); } private ParameterBinder getBinder(List> expressions) { return ParameterBinderFactory.createCriteriaBinder(parameters, expressions); } - private Sort getDynamicSort(Object[] values) { + private Sort getDynamicSort(JpaParametersParameterAccessor accessor) { return parameters.potentiallySortsDynamically() // - ? new ParametersParameterAccessor(parameters, values).getSort() // + ? accessor.getSort() // : Sort.unsorted(); } } @@ -351,20 +354,20 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { super(persistenceProvider, recreateQueries); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.PartTreeJpaQuery.QueryPreparer#createCreator(org.springframework.data.repository.query.ParametersParameterAccessor, org.springframework.data.jpa.provider.PersistenceProvider) - */ @Override protected JpaQueryCreator createCreator(PersistenceProvider persistenceProvider, - Optional accessor) { + JpaParametersParameterAccessor accessor) { EntityManager entityManager = getEntityManager(); CriteriaBuilder builder = entityManager.getCriteriaBuilder(); - ParameterMetadataProvider provider = accessor - .map(it -> new ParameterMetadataProvider(builder, it, persistenceProvider, escape))// - .orElseGet(() -> new ParameterMetadataProvider(builder, parameters, persistenceProvider, escape)); + ParameterMetadataProvider provider; + + if (accessor != null) { + provider = new ParameterMetadataProvider(builder, accessor, persistenceProvider, escape); + } else { + provider = new ParameterMetadataProvider(builder, parameters, persistenceProvider, escape); + } return new JpaCountQueryCreator(tree, getQueryMethod().getResultProcessor().getReturnedType(), builder, provider); } @@ -372,11 +375,12 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { /** * Customizes binding by skipping the pagination. * - * @see QueryPreparer#invokeBinding(ParameterBinder, TypedQuery, Object[]) + * @see QueryPreparer#invokeBinding(ParameterBinder, TypedQuery, JpaParametersParameterAccessor) */ @Override - protected Query invokeBinding(ParameterBinder binder, TypedQuery query, Object[] values) { - return binder.bind(query, values); + protected Query invokeBinding(ParameterBinder binder, TypedQuery query, + JpaParametersParameterAccessor accessor) { + return binder.bind(query, accessor); } } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index 458417f8c..b18319b00 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -28,6 +28,7 @@ import javax.persistence.criteria.ParameterExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -41,7 +42,7 @@ import org.springframework.util.Assert; */ interface QueryParameterSetter { - void setParameter(Query query, Object[] values, ErrorHandling errorHandling); + void setParameter(Query query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling); /** Noop implementation */ QueryParameterSetter NOOP = (query, values, errorHandling) -> {}; @@ -53,7 +54,7 @@ interface QueryParameterSetter { private static final Logger LOGGER = LoggerFactory.getLogger(NamedOrIndexedQueryParameterSetter.class); - private final Function valueExtractor; + private final Function valueExtractor; private final Parameter parameter; private final @Nullable TemporalType temporalType; @@ -62,8 +63,8 @@ interface QueryParameterSetter { * @param parameter must not be {@literal null}. * @param temporalType may be {@literal null}. */ - NamedOrIndexedQueryParameterSetter(Function valueExtractor, Parameter parameter, - @Nullable TemporalType temporalType) { + NamedOrIndexedQueryParameterSetter(Function valueExtractor, + Parameter parameter, @Nullable TemporalType temporalType) { Assert.notNull(valueExtractor, "ValueExtractor must not be null!"); @@ -78,9 +79,9 @@ interface QueryParameterSetter { */ @SuppressWarnings("unchecked") @Override - public void setParameter(Query query, Object[] values, ErrorHandling errorHandling) { + public void setParameter(Query query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling) { - Object value = valueExtractor.apply(values); + Object value = valueExtractor.apply(accessor); if (temporalType != null) { @@ -143,7 +144,7 @@ interface QueryParameterSetter { /** * 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. diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index 5f197730e..57e2cece3 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -106,7 +106,8 @@ abstract class QueryParameterSetterFactory { * @param binding the binding of the query parameter to be set. * @param parameter the method parameter to bind. */ - private static QueryParameterSetter createSetter(Function valueExtractor, ParameterBinding binding, + private static QueryParameterSetter createSetter(Function valueExtractor, + ParameterBinding binding, @Nullable JpaParameter parameter) { TemporalType temporalType = parameter != null && parameter.isTemporalParameter() // @@ -172,9 +173,9 @@ abstract class QueryParameterSetterFactory { * @return the result of the evaluation. */ @Nullable - private Object evaluateExpression(Expression expression, Object[] values) { + private Object evaluateExpression(Expression expression, JpaParametersParameterAccessor accessor) { - EvaluationContext context = evaluationContextProvider.getEvaluationContext(parameters, values); + EvaluationContext context = evaluationContextProvider.getEvaluationContext(parameters, accessor.getValues()); return expression.getValue(context, Object.class); } @@ -244,8 +245,8 @@ abstract class QueryParameterSetterFactory { .findFirst().orElse(null); } - private Object getValue(Object[] values, Parameter parameter) { - return new JpaParametersParameterAccessor(parameters, values).getValue(parameter); + private Object getValue(JpaParametersParameterAccessor accessor, Parameter parameter) { + return accessor.getValue(parameter); } private static String getName(JpaParameter p) { @@ -306,15 +307,15 @@ abstract class QueryParameterSetterFactory { JpaParameter parameter = parameters.getBindableParameter(parameterIndex); TemporalType temporalType = parameter.isTemporalParameter() ? parameter.getRequiredTemporalType() : null; - return new NamedOrIndexedQueryParameterSetter(values -> getAndPrepare(parameter, metadata, values), + return new NamedOrIndexedQueryParameterSetter(values -> { + return getAndPrepare(parameter, metadata, values); + }, metadata.getExpression(), temporalType); } @Nullable - private Object getAndPrepare(JpaParameter parameter, ParameterMetadata metadata, Object[] values) { - - JpaParametersParameterAccessor accessor = new JpaParametersParameterAccessor(parameters, values); - + private Object getAndPrepare(JpaParameter parameter, ParameterMetadata metadata, + JpaParametersParameterAccessor accessor) { return metadata.prepare(accessor.getValue(parameter)); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index 53502a685..fb13de6cf 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -28,6 +28,7 @@ import javax.persistence.TypedQuery; 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.lang.Nullable; import org.springframework.util.Assert; @@ -83,28 +84,28 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#createQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#createQuery(JpaParametersParameterAccessor) */ @Override - protected StoredProcedureQuery createQuery(Object[] values) { - return applyHints(doCreateQuery(values), getQueryMethod()); + protected StoredProcedureQuery createQuery(JpaParametersParameterAccessor accessor) { + return applyHints(doCreateQuery(accessor), getQueryMethod()); } /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(JpaParametersParameterAccessor) */ @Override - protected StoredProcedureQuery doCreateQuery(Object[] values) { - return parameterBinder.get().bind(createStoredProcedure(), values); + protected StoredProcedureQuery doCreateQuery(JpaParametersParameterAccessor accessor) { + return parameterBinder.get().bind(createStoredProcedure(), accessor); } /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(java.lang.Object[]) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(JpaParametersParameterAccessor) */ @Override - protected TypedQuery doCreateCountQuery(Object[] values) { + protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { throw new UnsupportedOperationException("StoredProcedureQuery does not support count queries!"); } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index 2576942bf..13b34c8b8 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -51,6 +51,7 @@ import org.springframework.transaction.annotation.Transactional; * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -75,10 +76,12 @@ public class AbstractJpaQueryTests { AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); - Query result = jpaQuery.createQuery(new Object[] { "Matthews" }); + Query result = jpaQuery + .createQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { "Matthews" })); verify(result).setHint("foo", "bar"); - result = jpaQuery.createCountQuery(new Object[] { "Matthews" }); + result = jpaQuery + .createCountQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { "Matthews" })); verify(result).setHint("foo", "bar"); } @@ -88,10 +91,12 @@ public class AbstractJpaQueryTests { JpaQueryMethod queryMethod = getMethod("findByFirstname", String.class); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); - Query result = jpaQuery.createQuery(new Object[] { "Dave" }); + Query result = jpaQuery + .createQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { "Dave" })); verify(result).setHint("bar", "foo"); - result = jpaQuery.createCountQuery(new Object[] { "Dave" }); + result = jpaQuery + .createCountQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { "Dave" })); verify(result, never()).setHint("bar", "foo"); } @@ -103,7 +108,8 @@ public class AbstractJpaQueryTests { JpaQueryMethod queryMethod = getMethod("findOneLocked", Integer.class); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); - Query result = jpaQuery.createQuery(new Object[] { Integer.valueOf(1) }); + Query result = jpaQuery.createQuery( + new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { Integer.valueOf(1) })); verify(result).setLockMode(LockModeType.PESSIMISTIC_WRITE); } @@ -118,7 +124,7 @@ public class AbstractJpaQueryTests { javax.persistence.EntityGraph entityGraph = em.getEntityGraph("User.overview"); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); - Query result = jpaQuery.createQuery(new Object[0]); + Query result = jpaQuery.createQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[0])); verify(result).setHint("javax.persistence.fetchgraph", entityGraph); } @@ -134,7 +140,8 @@ public class AbstractJpaQueryTests { javax.persistence.EntityGraph entityGraph = em.getEntityGraph("User.detail"); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); - Query result = jpaQuery.createQuery(new Object[] { 1 }); + Query result = jpaQuery + .createQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { 1 })); verify(result).setHint("javax.persistence.loadgraph", entityGraph); } @@ -176,12 +183,12 @@ public class AbstractJpaQueryTests { } @Override - protected Query doCreateQuery(Object[] values) { + protected Query doCreateQuery(JpaParametersParameterAccessor accessor) { return query; } @Override - protected TypedQuery doCreateCountQuery(Object[] values) { + protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { return (TypedQuery) countQuery; } } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java index 6c1bd60e4..4ba8692d3 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java @@ -33,12 +33,11 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; + import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution; import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution; -import org.springframework.data.repository.query.DefaultParameters; -import org.springframework.data.repository.query.Parameters; /** * Unit test for {@link JpaQueryExecution}. @@ -56,6 +55,7 @@ public class JpaQueryExecutionUnitTests { @Mock AbstractStringBasedJpaQuery jpaQuery; @Mock Query query; @Mock JpaQueryMethod method; + @Mock JpaParametersParameterAccessor accessor; @Mock TypedQuery countQuery; @@ -65,14 +65,14 @@ public class JpaQueryExecutionUnitTests { public void setUp() { when(query.executeUpdate()).thenReturn(0); - when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query); + when(jpaQuery.createQuery(Mockito.any(JpaParametersParameterAccessor.class))).thenReturn(query); when(jpaQuery.getQueryMethod()).thenReturn(method); } @Test(expected = IllegalArgumentException.class) public void rejectsNullQuery() { - new StubQueryExecution().execute(null, new Object[] {}); + new StubQueryExecution().execute(null, accessor); } @Test(expected = IllegalArgumentException.class) @@ -87,11 +87,11 @@ public class JpaQueryExecutionUnitTests { assertThat(new JpaQueryExecution() { @Override - protected Object doExecute(AbstractJpaQuery query, Object[] values) { + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { return null; } - }.execute(jpaQuery, new Object[] {})).isNull(); + }.execute(jpaQuery, accessor)).isNull(); } @Test // DATAJPA-806 @@ -102,7 +102,7 @@ public class JpaQueryExecutionUnitTests { when(method.getFlushAutomatically()).thenReturn(true); ModifyingExecution execution = new ModifyingExecution(method, em); - execution.execute(jpaQuery, new Object[] {}); + execution.execute(jpaQuery, accessor); verify(em, times(1)).flush(); verify(em, times(0)).clear(); @@ -116,7 +116,7 @@ public class JpaQueryExecutionUnitTests { when(method.getClearAutomatically()).thenReturn(true); ModifyingExecution execution = new ModifyingExecution(method, em); - execution.execute(jpaQuery, new Object[] {}); + execution.execute(jpaQuery, accessor); verify(em, times(0)).flush(); verify(em, times(1)).clear(); @@ -144,13 +144,14 @@ public class JpaQueryExecutionUnitTests { @Test // DATAJPA-124, DATAJPA-912 public void pagedExecutionRetrievesObjectsForPageableOutOfRange() throws Exception { - Parameters parameters = new DefaultParameters(getClass().getMethod("sampleMethod", Pageable.class)); - when(jpaQuery.createCountQuery(Mockito.any(Object[].class))).thenReturn(countQuery); - when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query); + JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + when(jpaQuery.createCountQuery(Mockito.any())).thenReturn(countQuery); + when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(countQuery.getResultList()).thenReturn(Arrays.asList(20L)); - PagedExecution execution = new PagedExecution(parameters); - execution.doExecute(jpaQuery, new Object[] { PageRequest.of(2, 10) }); + PagedExecution execution = new PagedExecution(); + execution.doExecute(jpaQuery, + new JpaParametersParameterAccessor(parameters, new Object[] { PageRequest.of(2, 10) })); verify(query).getResultList(); verify(countQuery).getResultList(); @@ -159,73 +160,78 @@ public class JpaQueryExecutionUnitTests { @Test // DATAJPA-477, DATAJPA-912 public void pagedExecutionShouldNotGenerateCountQueryIfQueryReportedNoResults() throws Exception { - Parameters parameters = new DefaultParameters(getClass().getMethod("sampleMethod", Pageable.class)); - when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query); + JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Arrays.asList(0L)); - PagedExecution execution = new PagedExecution(parameters); - execution.doExecute(jpaQuery, new Object[] { PageRequest.of(0, 10) }); + PagedExecution execution = new PagedExecution(); + execution.doExecute(jpaQuery, + new JpaParametersParameterAccessor(parameters, new Object[] { PageRequest.of(0, 10) })); verify(countQuery, times(0)).getResultList(); - verify(jpaQuery, times(0)).createCountQuery((Object[]) any()); + verify(jpaQuery, times(0)).createCountQuery(any()); } @Test // DATAJPA-912 public void pagedExecutionShouldUseCountFromResultIfOffsetIsZeroAndResultsWithinPageSize() throws Exception { - Parameters parameters = new DefaultParameters(getClass().getMethod("sampleMethod", Pageable.class)); - when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query); + JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Arrays.asList(new Object(), new Object(), new Object(), new Object())); - PagedExecution execution = new PagedExecution(parameters); - execution.doExecute(jpaQuery, new Object[] { PageRequest.of(0, 10) }); + PagedExecution execution = new PagedExecution(); + execution.doExecute(jpaQuery, + new JpaParametersParameterAccessor(parameters, new Object[] { PageRequest.of(0, 10) })); - verify(jpaQuery, times(0)).createCountQuery((Object[]) any()); + verify(jpaQuery, times(0)).createCountQuery(any()); } @Test // DATAJPA-912 public void pagedExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSize() throws Exception { - Parameters parameters = new DefaultParameters(getClass().getMethod("sampleMethod", Pageable.class)); - when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query); + JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Arrays.asList(new Object(), new Object(), new Object(), new Object())); - PagedExecution execution = new PagedExecution(parameters); - execution.doExecute(jpaQuery, new Object[] { PageRequest.of(5, 10) }); + PagedExecution execution = new PagedExecution(); + execution.doExecute(jpaQuery, + new JpaParametersParameterAccessor(parameters, new Object[] { PageRequest.of(5, 10) })); - verify(jpaQuery, times(0)).createCountQuery((Object[]) any()); + verify(jpaQuery, times(0)).createCountQuery(any()); } @Test // DATAJPA-912 public void pagedExecutionShouldUseRequestCountFromResultWithOffsetAndResultsHitLowerPageSizeBounds() throws Exception { - Parameters parameters = new DefaultParameters(getClass().getMethod("sampleMethod", Pageable.class)); - when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query); + JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Collections.emptyList()); - when(jpaQuery.createCountQuery(Mockito.any(Object[].class))).thenReturn(query); + when(jpaQuery.createCountQuery(Mockito.any())).thenReturn(query); when(countQuery.getResultList()).thenReturn(Arrays.asList(20L)); - PagedExecution execution = new PagedExecution(parameters); - execution.doExecute(jpaQuery, new Object[] { PageRequest.of(4, 4) }); + PagedExecution execution = new PagedExecution(); + execution.doExecute(jpaQuery, + new JpaParametersParameterAccessor(parameters, new Object[] { PageRequest.of(4, 4) })); - verify(jpaQuery).createCountQuery((Object[]) any()); + verify(jpaQuery).createCountQuery(any()); } @Test // DATAJPA-912 public void pagedExecutionShouldUseRequestCountFromResultWithOffsetAndResultsHitUpperPageSizeBounds() throws Exception { - Parameters parameters = new DefaultParameters(getClass().getMethod("sampleMethod", Pageable.class)); - when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query); + JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Arrays.asList(new Object(), new Object(), new Object(), new Object())); - when(jpaQuery.createCountQuery(Mockito.any(Object[].class))).thenReturn(query); + when(jpaQuery.createCountQuery(Mockito.any())).thenReturn(query); when(countQuery.getResultList()).thenReturn(Arrays.asList(20L)); - PagedExecution execution = new PagedExecution(parameters); - execution.doExecute(jpaQuery, new Object[] { PageRequest.of(4, 4) }); + PagedExecution execution = new PagedExecution(); + execution.doExecute(jpaQuery, + new JpaParametersParameterAccessor(parameters, new Object[] { PageRequest.of(4, 4) })); - verify(jpaQuery).createCountQuery((Object[]) any()); + verify(jpaQuery).createCountQuery(any()); } @Test // DATAJPA-951 @@ -233,15 +239,16 @@ public class JpaQueryExecutionUnitTests { doReturn(method).when(jpaQuery).getQueryMethod(); doReturn(Optional.class).when(method).getReturnType(); + JpaParametersParameterAccessor accessor = mock(JpaParametersParameterAccessor.class); StubQueryExecution execution = new StubQueryExecution() { @Override - protected Object doExecute(AbstractJpaQuery query, Object[] values) { + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { return "result"; } }; - Object result = execution.execute(jpaQuery, new Object[0]); + Object result = execution.execute(jpaQuery, accessor); assertThat(result).isInstanceOf(String.class); } @@ -249,7 +256,7 @@ public class JpaQueryExecutionUnitTests { static class StubQueryExecution extends JpaQueryExecution { @Override - protected Object doExecute(AbstractJpaQuery query, Object[] values) { + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { return null; } } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java index 725d545cc..cf2bb21b5 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java @@ -35,20 +35,22 @@ import javax.persistence.TemporalType; import javax.persistence.criteria.ParameterExpression; import org.assertj.core.api.SoftAssertions; +import org.junit.Before; import org.junit.Test; import org.springframework.data.jpa.repository.query.QueryParameterSetter.NamedOrIndexedQueryParameterSetter; /** * Unit tests fir {@link NamedOrIndexedQueryParameterSetter}. - * + * * @author Jens Schauder * @author Oliver Gierke + * @author Mark Paluch */ public class NamedOrIndexedQueryParameterSetterUnitTests { static final String EXCEPTION_MESSAGE = "mock exception"; - Function firstValueExtractor = args -> args[0]; - Object[] methodArguments = { new Date() }; + Function firstValueExtractor = args -> args.getValues()[0]; + JpaParametersParameterAccessor methodArguments; List temporalTypes = asList(null, TIME); List> parameters = Arrays.> asList( // @@ -59,6 +61,15 @@ public class NamedOrIndexedQueryParameterSetterUnitTests { SoftAssertions softly = new SoftAssertions(); + @Before + public void before() { + + JpaParametersParameterAccessor accessor = mock(JpaParametersParameterAccessor.class); + when(accessor.getValues()).thenReturn(new Object[] { new Date() }); + + this.methodArguments = accessor; + } + @Test // DATAJPA-1233 public void strictErrorHandlingThrowsExceptionForAllVariationsOfParameters() { diff --git a/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java index 3a892da79..58abca6db 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java @@ -43,6 +43,7 @@ import org.springframework.data.repository.query.QueryCreationException; * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) public class NamedQueryUnitTests { @@ -92,7 +93,7 @@ public class NamedQueryUnitTests { when(em.createNamedQuery(eq(queryMethod.getNamedCountQueryName()), eq(Long.class))).thenReturn(countQuery); NamedQuery query = (NamedQuery) NamedQuery.lookupFrom(queryMethod, em); - query.doCreateCountQuery(new Object[1]); + query.doCreateCountQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[1])); verify(em, times(1)).createNamedQuery(queryMethod.getNamedCountQueryName(), Long.class); verify(em, never()).createQuery(any(String.class), eq(Long.class)); } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java index 6d0bd22df..11032b0e4 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java @@ -47,6 +47,7 @@ import org.springframework.data.repository.query.Param; * @author Oliver Gierke * @author Thomas Darimont * @author Jens Schauder + * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) public class ParameterBinderUnitTests { @@ -101,7 +102,8 @@ public class ParameterBinderUnitTests { Method validWithSort = SampleRepository.class.getMethod("validWithSort", String.class, Sort.class); Object[] values = { "foo", null }; - ParameterBinderFactory.createBinder(new JpaParameters(validWithSort)).bind(query, values); + ParameterBinderFactory.createBinder(new JpaParameters(validWithSort)).bind(query, + new JpaParametersParameterAccessor(new JpaParameters(validWithSort), values)); verify(query).setParameter(eq(1), eq("foo")); } @@ -111,7 +113,8 @@ public class ParameterBinderUnitTests { Method validWithPageable = SampleRepository.class.getMethod("validWithPageable", String.class, Pageable.class); Object[] values = { "foo", null }; - ParameterBinderFactory.createBinder(new JpaParameters(validWithPageable)).bind(query, values); + ParameterBinderFactory.createBinder(new JpaParameters(validWithPageable)).bind(query, + new JpaParametersParameterAccessor(new JpaParameters(validWithPageable), values)); verify(query).setParameter(eq(1), eq("foo")); } @@ -119,7 +122,8 @@ public class ParameterBinderUnitTests { public void usesIndexedParametersIfNoParamAnnotationPresent() throws Exception { Object[] values = { "foo" }; - ParameterBinderFactory.createBinder(new JpaParameters(useIndexedParameters)).bind(query, values); + ParameterBinderFactory.createBinder(new JpaParameters(useIndexedParameters)).bind(query, + new JpaParametersParameterAccessor(new JpaParameters(useIndexedParameters), values)); verify(query).setParameter(eq(1), any()); } @@ -134,7 +138,8 @@ public class ParameterBinderUnitTests { when(query.getParameters()).thenReturn(singleton(parameter)); Object[] values = { "foo" }; - ParameterBinderFactory.createBinder(new JpaParameters(valid)).bind(query, values); + ParameterBinderFactory.createBinder(new JpaParameters(valid)).bind(query, + new JpaParametersParameterAccessor(new JpaParameters(valid), values)); verify(query).setParameter(eq("username"), any()); } @@ -147,7 +152,7 @@ public class ParameterBinderUnitTests { SampleEmbeddable embeddable = new SampleEmbeddable(); Object[] values = { embeddable }; - ParameterBinderFactory.createBinder(parameters).bind(query, values); + ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values)); verify(query).setParameter(1, embeddable); } @@ -160,7 +165,7 @@ public class ParameterBinderUnitTests { Date date = new Date(); Object[] values = { date }; - ParameterBinderFactory.createBinder(parameters).bind(query, values); + ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values)); verify(query).setParameter(eq(1), eq(date), eq(TemporalType.DATE)); } @@ -173,7 +178,7 @@ public class ParameterBinderUnitTests { Date date = new Date(); Object[] values = { date }; - ParameterBinderFactory.createBinder(parameters).bind(query, values); + ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values)); verify(query).setParameter(eq(1), eq(date), eq(TemporalType.TIMESTAMP)); } @@ -194,7 +199,7 @@ public class ParameterBinderUnitTests { JpaParameters parameters = new JpaParameters(method); Integer[] ids = new Integer[] { 1, 2, 3 }; Object[] values = { ids }; - ParameterBinderFactory.createBinder(parameters).bind(query, values); + ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values)); verify(query).setParameter(eq(1), eq(ids)); } @@ -206,7 +211,7 @@ public class ParameterBinderUnitTests { JpaParameters parameters = new JpaParameters(method); Object[] values = { Optional.of("Foo") }; - ParameterBinderFactory.createBinder(parameters).bind(query, values); + ParameterBinderFactory.createBinder(parameters).bind(query, getAccessor(method, values)); verify(query).setParameter(eq(1), eq("Foo")); } @@ -217,12 +222,16 @@ public class ParameterBinderUnitTests { Method method = SampleRepository.class.getMethod("withQuery", String.class, String.class); Object[] values = { "foo", "superfluous" }; - ParameterBinderFactory.createBinder(new JpaParameters(method)).bind(query, values); + ParameterBinderFactory.createBinder(new JpaParameters(method)).bind(query, getAccessor(method, values)); verify(query).setParameter(eq(1), any()); verify(query, never()).setParameter(eq(2), any()); } + private JpaParametersParameterAccessor getAccessor(Method method, Object... values) { + return new JpaParametersParameterAccessor(new JpaParameters(method), values); + } + public SampleEntity findByEmbeddable(SampleEmbeddable embeddable) { return null; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 0911c76b3..f1036e09a 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -85,8 +85,8 @@ public class PartTreeJpaQueryIntegrationTests { JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, provider); - jpaQuery.createQuery(new Object[] { "Matthews", PageRequest.of(0, 1) }); - jpaQuery.createQuery(new Object[] { "Matthews", PageRequest.of(0, 1) }); + jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) })); + jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) }))); } @Test @@ -109,11 +109,11 @@ public class PartTreeJpaQueryIntegrationTests { JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, provider); - Query query = jpaQuery.createQuery(new Object[] { "Matthews", PageRequest.of(0, 1) }); + Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) }))); assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).endsWith("firstname=:param0"); - query = jpaQuery.createQuery(new Object[] { null, PageRequest.of(0, 1) }); + query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { null, PageRequest.of(0, 1) }))); assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).endsWith("firstname is null"); } @@ -124,7 +124,7 @@ public class PartTreeJpaQueryIntegrationTests { JpaQueryMethod queryMethod = getQueryMethod("existsByFirstname", String.class); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, provider); - Query query = jpaQuery.createQuery(new Object[] { "Matthews" }); + Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews" }))); assertThat(query.getMaxResults()).isEqualTo(1); } @@ -135,7 +135,7 @@ public class PartTreeJpaQueryIntegrationTests { JpaQueryMethod queryMethod = getQueryMethod("existsByFirstname", String.class); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, provider); - Query query = jpaQuery.createQuery(new Object[] { "Matthews" }); + Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews" }))); assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).contains(".id from User as"); } @@ -146,7 +146,7 @@ public class PartTreeJpaQueryIntegrationTests { JpaQueryMethod queryMethod = getQueryMethod("findByRolesIsEmpty"); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, provider); - Query query = jpaQuery.createQuery(new Object[] {}); + Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] {}))); assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).endsWith("roles is empty"); } @@ -157,7 +157,7 @@ public class PartTreeJpaQueryIntegrationTests { JpaQueryMethod queryMethod = getQueryMethod("findByRolesIsNotEmpty"); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, provider); - Query query = jpaQuery.createQuery(new Object[] {}); + Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] {}))); assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).endsWith("roles is not empty"); } @@ -168,7 +168,7 @@ public class PartTreeJpaQueryIntegrationTests { JpaQueryMethod method = getQueryMethod("findByFirstnameIsEmpty"); AbstractJpaQuery jpaQuery = new PartTreeJpaQuery(method, entityManager, provider); - jpaQuery.createQuery(new Object[] { "Oliver" }); + jpaQuery.createQuery((getAccessor(method, new Object[] { "Oliver" }))); } @Test // DATAJPA-1182 @@ -232,7 +232,7 @@ public class PartTreeJpaQueryIntegrationTests { JpaQueryMethod queryMethod = getQueryMethod(methodName, parameterTypes); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, PersistenceProvider.fromEntityManager(entityManager)); - jpaQuery.createQuery(values); + jpaQuery.createQuery((getAccessor(queryMethod, values))); } private JpaQueryMethod getQueryMethod(String methodName, Class... parameterTypes) throws Exception { @@ -258,6 +258,10 @@ public class PartTreeJpaQueryIntegrationTests { return (T) result; } + private JpaParametersParameterAccessor getAccessor(JpaQueryMethod queryMethod, Object[] values) { + return new JpaParametersParameterAccessor(queryMethod.getParameters(), values); + } + private static String getQueryProperty() { return isHibernate43() || isHibernate5() ? "jpqlQuery" : "val$jpaqlQuery"; } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 98a621ce0..851f67d78 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -111,7 +111,8 @@ public class SimpleJpaQueryUnitTests { SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", EVALUATION_CONTEXT_PROVIDER, PARSER); - assertThat(jpaQuery.createCountQuery(new Object[] {})).isEqualTo((javax.persistence.Query) typedQuery); + assertThat(jpaQuery.createCountQuery(new JpaParametersParameterAccessor(method.getParameters(), new Object[] {}))) + .isEqualTo(typedQuery); } @Test // DATAJPA-77 @@ -124,7 +125,8 @@ public class SimpleJpaQueryUnitTests { AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", EVALUATION_CONTEXT_PROVIDER, PARSER); - jpaQuery.createCountQuery(new Object[] { PageRequest.of(1, 10) }); + jpaQuery.createCountQuery( + new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); verify(query, times(0)).setFirstResult(anyInt()); verify(query, times(0)).setMaxResults(anyInt()); @@ -144,7 +146,7 @@ public class SimpleJpaQueryUnitTests { when(em.createNativeQuery(anyString(), eq(User.class))).thenReturn(query); when(metadata.getReturnedDomainClass(method)).thenReturn((Class) User.class); - jpaQuery.createQuery(new Object[] { "Matthews" }); + jpaQuery.createQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { "Matthews" })); verify(em).createNativeQuery("SELECT u FROM User u WHERE u.lastname = ?1", User.class); } @@ -202,7 +204,8 @@ public class SimpleJpaQueryUnitTests { AbstractJpaQuery jpaQuery = createJpaQuery( UserRepository.class.getMethod("findUsersInNativeQueryWithPagination", Pageable.class)); - jpaQuery.doCreateCountQuery(new Object[] { PageRequest.of(0, 10) }); + jpaQuery.doCreateCountQuery(new JpaParametersParameterAccessor(jpaQuery.getQueryMethod().getParameters(), + new Object[] { PageRequest.of(0, 10) })); verify(em).createNativeQuery(anyString()); } @@ -212,7 +215,7 @@ public class SimpleJpaQueryUnitTests { AbstractJpaQuery jpaQuery = createJpaQuery(SampleRepository.class.getMethod("projectWithExplicitQuery")); - jpaQuery.createQuery(new Object[0]); + jpaQuery.createQuery(new JpaParametersParameterAccessor(jpaQuery.getQueryMethod().getParameters(), new Object[0])); verify(em, times(0)).createQuery(anyString(), eq(Tuple.class)); @@ -242,7 +245,8 @@ public class SimpleJpaQueryUnitTests { AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", EVALUATION_CONTEXT_PROVIDER, PARSER); - jpaQuery.createCountQuery(new Object[] { PageRequest.of(1, 10) }); + jpaQuery.createCountQuery( + new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); verify(em).createQuery(eq("select u from User u")); verify(em).createQuery(eq("select count(u.id) from User u"), eq(Long.class));