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:
Oliver Gierke
2018-05-09 12:06:45 +02:00
parent 299f92788e
commit 0dd257e8ef
5 changed files with 65 additions and 4 deletions

View File

@@ -25,6 +25,7 @@ import org.springframework.data.jpa.provider.QueryExtractor;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.QueryCreationException;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.util.StringUtils;
/**
* Implementation of {@link RepositoryQuery} based on {@link javax.persistence.NamedQuery}s.
@@ -46,6 +47,7 @@ final class NamedQuery extends AbstractJpaQuery {
private final String countProjection;
private final QueryExtractor extractor;
private final boolean namedCountQueryIsPresent;
private final StringQuery declaredQuery;
/**
* Creates a new {@link NamedQuery}.
@@ -79,6 +81,11 @@ final class NamedQuery extends AbstractJpaQuery {
LOG.warn("Finder method {} is backed by a NamedQuery" + " but contains a Pageable parameter! Sorting delivered "
+ "via this Pageable will not be applied!", method);
}
Query query = em.createNamedQuery(queryName);
String queryString = extractor.extractQueryString(query);
this.declaredQuery = StringUtils.hasText(queryString) ? new StringQuery(queryString) : null;
}
/**
@@ -157,13 +164,29 @@ final class NamedQuery extends AbstractJpaQuery {
TypedQuery<Long> countQuery = null;
if (namedCountQueryIsPresent) {
countQuery = em.createNamedQuery(countQueryName, Long.class);
} else {
Query query = createQuery(values);
String queryString = extractor.extractQueryString(query);
countQuery = em.createQuery(QueryUtils.createCountQueryFor(queryString, countProjection), Long.class);
if (declaredQuery == null) {
throw new IllegalStateException("Cannot derive count query without an extracted source query!");
}
String countQueryString = QueryUtils.createCountQueryFor(declaredQuery.getQueryString(), countProjection);
countQuery = em.createQuery(countQueryString, Long.class);
}
return createBinder(values).bind(countQuery);
}
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#getTypeToRead()
*/
@Override
protected Class<?> getTypeToRead() {
return declaredQuery != null && !declaredQuery.hasConstructorExpression() ? super.getTypeToRead() : null;
}
}

View File

@@ -82,7 +82,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),

View File

@@ -223,4 +223,9 @@ public class UserRepositoryFinderTests {
public void rejectsStreamExecutionIfNoSurroundingTransactionActive() {
userRepository.findAllByCustomQueryAndStream();
}
@Test // DATAJPA-1334
public void executesNamedQueryWithConstructorExpression() {
userRepository.findByNamedQueryWithConstructorExpression();
}
}

View File

@@ -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
public class NameOnlyDto {
String firstname, lastname;
public NameOnlyDto(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
}

View File

@@ -494,6 +494,9 @@ public interface UserRepository
@Query("select firstname as firstname, lastname as lastname from User u where u.firstname = 'Oliver'")
Map<String, Object> findMapWithNullValues();
// DATAJPA-1334
List<NameOnlyDto> findByNamedQueryWithConstructorExpression();
interface RolesAndFirstname {
String getFirstname();