DATAJPA-1334 - Fixed named query execution using constructor expressions.
Our named query execution now refrains from using Tuple as return type in case the query uses a constructor expression.
This commit is contained in:
@@ -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<Long> 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<Class<?>> getTypeToRead() {
|
||||
return declaredQuery.hasConstructorExpression() ? Optional.empty() : super.getTypeToRead();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -172,8 +172,8 @@ public class UserRepositoryFinderTests {
|
||||
public void respectsPageableOrderOnQueryGenerateFromMethodName() throws Exception {
|
||||
Page<User> ascending = userRepository.findByLastnameIgnoringCase(PageRequest.of(0, 10, Sort.by(ASC, "firstname")),
|
||||
"Matthews");
|
||||
Page<User> descending = userRepository
|
||||
.findByLastnameIgnoringCase(PageRequest.of(0, 10, Sort.by(DESC, "firstname")), "Matthews");
|
||||
Page<User> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<NameOnlyDto> findByNamedQueryWithConstructorExpression();
|
||||
|
||||
interface RolesAndFirstname {
|
||||
|
||||
String getFirstname();
|
||||
|
||||
Reference in New Issue
Block a user