DATAJPA-1280 - Tweaked the type handling for named native queries.

We now only use Tuple as target type to execute a named native query if a projection interface, i.e. an interface that's not part of the entity type's type hierarchy is used as return type. If a type within the entity types hierarchy is returned we use the returned type as type hint. In all other cases (usually DTOs) we rely on the named native query being backed by a custom JPA 2.1 SQL result set mapping.
This commit is contained in:
Oliver Gierke
2018-05-09 15:42:18 +02:00
parent 0dd257e8ef
commit 6098d01825
5 changed files with 50 additions and 25 deletions

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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));