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 f32907c5e..91745256c 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,6 +17,8 @@ package org.springframework.data.jpa.repository.query; import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.*; +import java.util.Optional; + import javax.persistence.EntityManager; import javax.persistence.Query; import javax.persistence.TypedQuery; @@ -50,6 +52,7 @@ final class NamedQuery extends AbstractJpaQuery { private final @Nullable String countProjection; private final QueryExtractor extractor; private final boolean namedCountQueryIsPresent; + private final DeclaredQuery declaredQuery; /** * Creates a new {@link NamedQuery}. @@ -72,6 +75,11 @@ final class NamedQuery extends AbstractJpaQuery { this.namedCountQueryIsPresent = hasNamedQuery(em, countQueryName); + Query query = em.createNamedQuery(queryName); + String queryString = extractor.extractQueryString(query); + + this.declaredQuery = DeclaredQuery.of(queryString); + boolean weNeedToCreateCountQuery = !namedCountQueryIsPresent && method.getParameters().hasPageableParameter(); boolean cantExtractQuery = !this.extractor.canExtractQuery(); @@ -165,18 +173,25 @@ final class NamedQuery extends AbstractJpaQuery { TypedQuery countQuery; if (namedCountQueryIsPresent) { + countQuery = em.createNamedQuery(countQueryName, Long.class); + } else { - Query query = createQuery(values); - String queryString = extractor.extractQueryString(query); - if (queryString == null) { - throw new IllegalStateException(String.format("Cannot extract query string for query %s is null!", query)); - } + String countQueryString = declaredQuery.deriveCountQuery(null, countProjection).getQueryString(); - countQuery = em.createQuery(QueryUtils.createCountQueryFor(queryString, countProjection), Long.class); + countQuery = em.createQuery(countQueryString, Long.class); } return parameterBinder.get().bind(countQuery, values, LENIENT); } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#getTypeToRead() + */ + @Override + protected Optional> getTypeToRead() { + return declaredQuery.hasConstructorExpression() ? Optional.empty() : super.getTypeToRead(); + } } diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/src/test/java/org/springframework/data/jpa/domain/sample/User.java index a5bcfa231..d52fc37f1 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -85,7 +85,10 @@ import javax.persistence.TemporalType; @NamedQuery(name = "User.findByEmailAddress", // query = "SELECT u FROM User u WHERE u.emailAddress = ?1"), // @NamedQuery(name = "User.findByNamedQueryWithAliasInInvertedOrder", // - query = "SELECT u.lastname AS lastname, u.firstname AS firstname FROM User u ORDER BY u.lastname ASC") }) + query = "SELECT u.lastname AS lastname, u.firstname AS firstname FROM User u ORDER BY u.lastname ASC"), + @NamedQuery(name = "User.findByNamedQueryWithConstructorExpression", + query = "SELECT new org.springframework.data.jpa.repository.sample.NameOnlyDto(u.firstname, u.lastname) from User u") }) + @NamedStoredProcedureQueries({ // @NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 2eb33ace5..444933ab2 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -172,8 +172,8 @@ public class UserRepositoryFinderTests { public void respectsPageableOrderOnQueryGenerateFromMethodName() throws Exception { Page ascending = userRepository.findByLastnameIgnoringCase(PageRequest.of(0, 10, Sort.by(ASC, "firstname")), "Matthews"); - Page descending = userRepository - .findByLastnameIgnoringCase(PageRequest.of(0, 10, Sort.by(DESC, "firstname")), "Matthews"); + Page descending = userRepository.findByLastnameIgnoringCase(PageRequest.of(0, 10, Sort.by(DESC, "firstname")), + "Matthews"); assertThat(ascending.getTotalElements(), is(2L)); assertThat(descending.getTotalElements(), is(2L)); assertThat(ascending.getContent().get(0).getFirstname(), @@ -223,4 +223,9 @@ public class UserRepositoryFinderTests { public void rejectsStreamExecutionIfNoSurroundingTransactionActive() { userRepository.findAllByCustomQueryAndStream(); } + + @Test // DATAJPA-1334 + public void executesNamedQueryWithConstructorExpression() { + userRepository.findByNamedQueryWithConstructorExpression(); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java b/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java new file mode 100644 index 000000000..a53966ce5 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java @@ -0,0 +1,27 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.sample; + +// DATAJPA-1334 +class NameOnlyDto { + + String firstname, lastname; + + public NameOnlyDto(String firstname, String lastname) { + this.firstname = firstname; + this.lastname = lastname; + } +} 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 7f65cb47b..f33c0222b 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 @@ -553,6 +553,9 @@ public interface UserRepository @Query(value = "select * from SD_User u where u.emailAddress = ?", nativeQuery = true) User findByEmailNativeAddressJdbcStyleParameter(String emailAddress); + // DATAJPA-1334 + List findByNamedQueryWithConstructorExpression(); + interface RolesAndFirstname { String getFirstname();