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 7a0392edb..a737193c6 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 @@ -137,9 +137,8 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { ResultProcessor resultFactory = getQueryMethod().getResultProcessor(); ReturnedType returnedType = resultFactory.getReturnedType(); - getMetamodel().isJpaManaged(returnedType.getReturnedType()); - - return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) - ? em.createQuery(queryString, Tuple.class) : em.createQuery(queryString); + return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) // + ? em.createQuery(queryString, Tuple.class) // + : em.createQuery(queryString); } } 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 46f08fdac..d025d4a0d 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 @@ -17,10 +17,13 @@ package org.springframework.data.jpa.repository.query; import javax.persistence.EntityManager; import javax.persistence.Query; +import javax.persistence.Tuple; 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; /** @@ -33,6 +36,8 @@ 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}. * @@ -55,6 +60,8 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { throw new InvalidJpaQueryMethodException( "Cannot use native queries with dynamic sorting and/or pagination in method " + method); } + + this.resultType = getTypeToQueryFor(); } /* @@ -63,8 +70,25 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { */ @Override protected Query createJpaQuery(String queryString) { - return getQueryMethod().isQueryForEntity() - ? getEntityManager().createNativeQuery(queryString, getQueryMethod().getReturnedObjectType()) - : getEntityManager().createNativeQuery(queryString); + + EntityManager em = getEntityManager(); + + return this.resultType == null ? em.createNativeQuery(queryString) + : em.createNativeQuery(queryString, this.resultType); + } + + private Class getTypeToQueryFor() { + + ResultProcessor resultFactory = getQueryMethod().getResultProcessor(); + ReturnedType returnedType = resultFactory.getReturnedType(); + + Class result = getQueryMethod().isQueryForEntity() ? returnedType.getDomainType() : null; + + if (this.getQuery().hasConstructorExpression() || this.getQuery().isDefaultProjection()) { + return result; + } + + return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) ? Tuple.class + : result; } } diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 7db722c46..5bdf4fc8d 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -44,6 +44,7 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.hamcrest.Matchers; +import org.hibernate.Version; import org.junit.Assume; import org.junit.Before; import org.junit.Ignore; @@ -73,6 +74,7 @@ import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.sample.SampleEvaluationContextExtension.SampleSecurityContextHolder; import org.springframework.data.jpa.repository.sample.UserRepository; +import org.springframework.data.jpa.repository.sample.UserRepository.NameOnly; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; @@ -2405,6 +2407,21 @@ public class UserRepositoryTests { assertThat(query.getParameters(),hasSize(2)); } + @Test // DATAJPA-980 + public void supportsProjectionsWithNativeQueries() { + + Assume.assumeTrue(Version.getVersionString().startsWith("5.2")); + + flushTestUsers(); + + User user = repository.findAll().get(0); + + NameOnly result = repository.findByNativeQuery(user.getId()); + + assertThat(result.getFirstname(), is(user.getFirstname())); + assertThat(result.getLastname(), is(user.getLastname())); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index b01d19ee1..a74fb1ab9 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -599,10 +599,20 @@ public interface UserRepository List findRolesAndFirstnameBy(); - static interface RolesAndFirstname { + @Query(value = "SELECT firstname, lastname from SD_User WHERE id = ?1", nativeQuery = true) + NameOnly findByNativeQuery(Integer id); + + interface RolesAndFirstname { String getFirstname(); Set getRoles(); } + + interface NameOnly { + + String getFirstname(); + + String getLastname(); + } }