DATAJPA-980 - Native query execution now also uses a Tuple query for projections.
If a projection interface is used as return type for a native query we now trigger a tuple query. Note that this requires Hibernate 5.2.11.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import static org.junit.Assert.*;
|
||||
import static org.springframework.data.domain.Example.*;
|
||||
import static org.springframework.data.domain.ExampleMatcher.*;
|
||||
import static org.springframework.data.domain.Sort.Direction.*;
|
||||
import static org.springframework.data.jpa.domain.Specifications.not;
|
||||
import static org.springframework.data.jpa.domain.Specifications.*;
|
||||
import static org.springframework.data.jpa.domain.Specifications.not;
|
||||
import static org.springframework.data.jpa.domain.sample.UserSpecifications.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -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;
|
||||
@@ -55,7 +56,8 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.ExampleMatcher;
|
||||
import org.springframework.data.domain.ExampleMatcher.*;
|
||||
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
|
||||
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
@@ -72,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;
|
||||
@@ -2155,6 +2158,21 @@ public class UserRepositoryTests {
|
||||
assertThat(users, hasSize(1));
|
||||
}
|
||||
|
||||
@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<User> executeSpecWithSort(Sort sort) {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
@@ -472,10 +472,20 @@ public interface UserRepository
|
||||
|
||||
List<RolesAndFirstname> 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<Role> getRoles();
|
||||
}
|
||||
|
||||
interface NameOnly {
|
||||
|
||||
String getFirstname();
|
||||
|
||||
String getLastname();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user