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 a19cdfb8d..e813e5d9a 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 @@ -236,15 +236,12 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { * @return * @since 2.0.5 */ - protected Class getTypeToRead() { + protected Class getTypeToRead(ReturnedType returnedType) { if (PersistenceProvider.ECLIPSELINK.equals(provider)) { return null; } - ResultProcessor resultFactory = getQueryMethod().getResultProcessor(); - ReturnedType returnedType = resultFactory.getReturnedType(); - return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) // ? HibernateUtils.isVersionOrBetter(HIBERNATE_VERSION_SUPPORTING_TUPLES) ? Tuple.class : null // : null; 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 94eba6a92..f31a1eb59 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 @@ -21,6 +21,8 @@ import javax.persistence.Query; import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.data.repository.query.ResultProcessor; +import org.springframework.data.repository.query.ReturnedType; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.Assert; @@ -72,8 +74,9 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { ParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), values); String sortedQueryString = QueryUtils.applySorting(query.getQueryString(), accessor.getSort(), query.getAlias()); + ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor); - Query query = createJpaQuery(sortedQueryString); + Query query = createJpaQuery(sortedQueryString, processor.getReturnedType()); return createBinder(values).bindAndPrepare(query); } @@ -98,8 +101,11 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { String queryString = countQuery.getQueryString(); EntityManager em = getEntityManager(); - return createBinder(values).bind( - getQueryMethod().isNativeQuery() ? em.createNativeQuery(queryString) : em.createQuery(queryString, Long.class)); + Query query = getQueryMethod().isNativeQuery() // + ? em.createNativeQuery(queryString) // + : em.createQuery(queryString, Long.class); + + return createBinder(values).bind(query); } /** @@ -123,7 +129,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { * @param queryString * @return */ - protected Query createJpaQuery(String queryString) { + protected Query createJpaQuery(String queryString, ReturnedType returnedType) { EntityManager em = getEntityManager(); @@ -131,7 +137,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { return em.createQuery(queryString); } - Class typeToRead = getTypeToRead(); + Class typeToRead = getTypeToRead(returnedType); return typeToRead == null ? em.createQuery(queryString) : em.createQuery(queryString, typeToRead); } 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 a6465adb3..ac2f83030 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 @@ -17,14 +17,18 @@ package org.springframework.data.jpa.repository.query; import javax.persistence.EntityManager; import javax.persistence.Query; +import javax.persistence.Tuple; 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; +import org.springframework.data.repository.query.ReturnedType; import org.springframework.util.StringUtils; /** @@ -147,7 +151,11 @@ final class NamedQuery extends AbstractJpaQuery { EntityManager em = getEntityManager(); - Class typeToRead = getTypeToRead(); + JpaQueryMethod queryMethod = getQueryMethod(); + ResultProcessor processor = queryMethod.getResultProcessor() + .withDynamicProjection(new ParametersParameterAccessor(queryMethod.getParameters(), values)); + + Class typeToRead = getTypeToRead(processor.getReturnedType()); Query query = typeToRead == null ? em.createNamedQuery(queryName) : em.createNamedQuery(queryName, typeToRead); return createBinder(values).bindAndPrepare(query); @@ -186,7 +194,29 @@ final class NamedQuery extends AbstractJpaQuery { * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#getTypeToRead() */ @Override - protected Class getTypeToRead() { - return declaredQuery != null && !declaredQuery.hasConstructorExpression() ? super.getTypeToRead() : null; + protected Class getTypeToRead(ReturnedType returnedType) { + + if (getQueryMethod().isNativeQuery()) { + + Class type = returnedType.getReturnedType(); + Class domainType = returnedType.getDomainType(); + + // Domain or subtype -> use return type + if (domainType.isAssignableFrom(type)) { + return type; + } + + // Domain type supertype -> use domain type + if (type.isAssignableFrom(domainType)) { + return domainType; + } + + // Tuples for projection interfaces or explicit SQL mappings for everything else + return type.isInterface() ? Tuple.class : null; + } + + return declaredQuery != null && !declaredQuery.hasConstructorExpression() // + ? super.getTypeToRead(returnedType) // + : null; } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index d312ed970..b5b10dc9e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -23,7 +23,6 @@ import org.springframework.data.jpa.provider.HibernateUtils; import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.RepositoryQuery; -import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -37,8 +36,6 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; */ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { - private final Class resultType; - /** * Creates a new {@link NativeJpaQuery} encapsulating the query annotated on the given {@link JpaQueryMethod}. * @@ -61,8 +58,6 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { throw new InvalidJpaQueryMethodException( "Cannot use native queries with dynamic sorting and/or pagination in method " + method); } - - this.resultType = getTypeToQueryFor(); } /* @@ -70,18 +65,15 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { * @see org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery#createJpaQuery(java.lang.String) */ @Override - protected Query createJpaQuery(String queryString) { + protected Query createJpaQuery(String queryString, ReturnedType returnedType) { EntityManager em = getEntityManager(); + Class type = getTypeToQueryFor(returnedType); - return this.resultType == null ? em.createNativeQuery(queryString) - : em.createNativeQuery(queryString, this.resultType); + return type == null ? em.createNativeQuery(queryString) : em.createNativeQuery(queryString, type); } - private Class getTypeToQueryFor() { - - ResultProcessor resultFactory = getQueryMethod().getResultProcessor(); - ReturnedType returnedType = resultFactory.getReturnedType(); + private Class getTypeToQueryFor(ReturnedType returnedType) { Class result = getQueryMethod().isQueryForEntity() ? returnedType.getDomainType() : null; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index 2a9cbcfec..3f8bdfcea 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -62,7 +62,7 @@ public class AbstractStringBasedJpaQueryIntegrationTests { AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, DefaultEvaluationContextProvider.INSTANCE, new SpelExpressionParser()); - jpaQuery.createJpaQuery(method.getAnnotatedQuery()); + jpaQuery.createJpaQuery(method.getAnnotatedQuery(), method.getResultProcessor().getReturnedType()); verify(mock, times(1)).createQuery(anyString()); verify(mock, times(0)).createQuery(anyString(), eq(Tuple.class));