DATAJPA-804 - Support for projections on query methods.

Based on the work for DATACMNS-89 we now use the metadata exposed by ResourceProcessor to optimize queries that are to be projected on the query execution level.

If a projection interface is used that's not using any dynamic expression, we now explicitly query for a JPA Tuple consisting of all properties required for the projection interface. The same applies to DTOs that use an @PersistenceConstructor.

Related tickets: DATACMNS-89.
This commit is contained in:
Oliver Gierke
2015-11-27 23:11:10 +01:00
parent 8216f4d925
commit d88f823c75
19 changed files with 344 additions and 180 deletions

View File

@@ -15,14 +15,18 @@
*/
package org.springframework.data.jpa.repository.query;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.QueryHint;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import javax.persistence.TypedQuery;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.CollectionExecution;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
@@ -31,7 +35,9 @@ import org.springframework.data.jpa.repository.query.JpaQueryExecution.Procedure
import org.springframework.data.jpa.repository.query.JpaQueryExecution.SingleEntityExecution;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.SlicedExecution;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.StreamExecution;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.util.Assert;
/**
@@ -49,6 +55,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
* Creates a new {@link AbstractJpaQuery} from the given {@link JpaQueryMethod}.
*
* @param method
* @param resultFactory
* @param em
*/
public AbstractJpaQuery(JpaQueryMethod method, EntityManager em) {
@@ -62,30 +69,24 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.query.RepositoryQuery#getQueryMethod
* ()
* @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod()
*/
public JpaQueryMethod getQueryMethod() {
return method;
}
/**
* @return the em
* Returns the {@link EntityManager}.
*
* @return will never be {@literal null}.
*/
protected EntityManager getEntityManager() {
return em;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.query.RepositoryQuery#execute(java
* .lang.Object[])
* @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[])
*/
public Object execute(Object[] parameters) {
return doExecute(getExecution(), parameters);
@@ -97,7 +98,13 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
* @return
*/
private Object doExecute(JpaQueryExecution execution, Object[] values) {
return execution.execute(this, values);
Object result = execution.execute(this, values);
ParametersParameterAccessor accessor = new ParametersParameterAccessor(method.getParameters(), values);
ResultProcessor withDynamicProjection = method.getResultProcessor().withDynamicProjection(accessor);
return withDynamicProjection.processResult(result, TupleConverter.INSTANCE);
}
protected JpaQueryExecution getExecution() {
@@ -212,4 +219,47 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
* @return
*/
protected abstract Query doCreateCountQuery(Object[] values);
private static enum TupleConverter implements Converter<Object, Object> {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public Object convert(Object source) {
if (!(source instanceof Tuple)) {
return source;
}
Tuple tuple = (Tuple) source;
Map<String, Object> result = new HashMap<String, Object>();
for (TupleElement<?> element : tuple.getElements()) {
String alias = element.getAlias();
if (alias == null || isIndexAsString(alias)) {
throw new IllegalStateException("No aliases found in result tuple! Make sure your query defines aliases!");
}
result.put(element.getAlias(), tuple.get(element));
}
return result;
}
private static boolean isIndexAsString(String source) {
try {
Integer.parseInt(source);
return true;
} catch (NumberFormatException o_O) {
return false;
}
}
}
}

View File

@@ -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.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;
@@ -96,7 +99,12 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
* @return
*/
public Query createJpaQuery(String queryString) {
return getEntityManager().createQuery(queryString);
ResultProcessor resultFactory = getQueryMethod().getResultProcessor();
ReturnedType returnedType = resultFactory.getReturnedType();
EntityManager em = getEntityManager();
return returnedType.isProjecting() ? em.createQuery(queryString, Tuple.class) : em.createQuery(queryString);
}
/*

View File

@@ -17,10 +17,12 @@ package org.springframework.data.jpa.repository.query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.PartTree;
/**
@@ -38,9 +40,9 @@ public class JpaCountQueryCreator extends JpaQueryCreator {
* @param parameters
* @param em
*/
public JpaCountQueryCreator(PartTree tree, Class<?> domainClass, CriteriaBuilder builder,
public JpaCountQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder,
ParameterMetadataProvider provider) {
super(tree, domainClass, builder, provider);
super(tree, type, builder, provider);
}
/*
@@ -48,10 +50,10 @@ public class JpaCountQueryCreator extends JpaQueryCreator {
* @see org.springframework.data.jpa.repository.query.JpaQueryCreator#complete(javax.persistence.criteria.Predicate, org.springframework.data.domain.Sort, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder, javax.persistence.criteria.Root)
*/
@Override
protected CriteriaQuery<Object> complete(Predicate predicate, Sort sort, CriteriaQuery<Object> query,
CriteriaBuilder builder, Root<?> root) {
protected CriteriaQuery<? extends Object> complete(Predicate predicate, Sort sort,
CriteriaQuery<? extends Object> query, CriteriaBuilder builder, Root<?> root) {
CriteriaQuery<Object> select = query.select(builder.count(root));
CriteriaQuery<? extends Object> select = query.select((Expression) builder.count(root));
return predicate == null ? select : select.where(predicate);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.query;
import static org.springframework.data.jpa.repository.query.QueryUtils.*;
import static org.springframework.data.repository.query.parser.Part.Type.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -28,10 +29,12 @@ import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.Type;
@@ -43,12 +46,13 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
*/
public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>, Predicate> {
public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<? extends Object>, Predicate> {
private final CriteriaBuilder builder;
private final Root<?> root;
private final CriteriaQuery<Object> query;
private final CriteriaQuery<? extends Object> query;
private final ParameterMetadataProvider provider;
private final ReturnedType returnedType;
/**
* Create a new {@link JpaQueryCreator}.
@@ -58,15 +62,21 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
* @param accessor
* @param em
*/
public JpaQueryCreator(PartTree tree, Class<?> domainClass, CriteriaBuilder builder,
public JpaQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder,
ParameterMetadataProvider provider) {
super(tree);
Class<?> typeToRead = type.getTypeToRead();
CriteriaQuery<? extends Object> criteriaQuery = typeToRead == null ? builder.createTupleQuery()
: builder.createQuery(typeToRead);
this.builder = builder;
this.query = builder.createQuery().distinct(tree.isDistinct());
this.root = query.from(domainClass);
this.query = criteriaQuery.distinct(tree.isDistinct());
this.root = query.from(type.getDomainType());
this.provider = provider;
this.returnedType = type;
}
/**
@@ -94,7 +104,6 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
*/
@Override
protected Predicate and(Part part, Predicate base, Iterator<Object> iterator) {
return builder.and(base, toPredicate(part, root));
}
@@ -104,7 +113,6 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
*/
@Override
protected Predicate or(Predicate base, Predicate predicate) {
return builder.or(base, predicate);
}
@@ -114,8 +122,7 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
* and {@link CriteriaBuilder}.
*/
@Override
protected final CriteriaQuery<Object> complete(Predicate predicate, Sort sort) {
protected final CriteriaQuery<? extends Object> complete(Predicate predicate, Sort sort) {
return complete(predicate, sort, query, builder, root);
}
@@ -129,10 +136,24 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
* @param builder
* @return
*/
protected CriteriaQuery<Object> complete(Predicate predicate, Sort sort, CriteriaQuery<Object> query,
CriteriaBuilder builder, Root<?> root) {
@SuppressWarnings({ "unchecked", "rawtypes" })
protected CriteriaQuery<? extends Object> complete(Predicate predicate, Sort sort,
CriteriaQuery<? extends Object> query, CriteriaBuilder builder, Root<?> root) {
CriteriaQuery<Object> select = this.query.select(root).orderBy(QueryUtils.toOrders(sort, root, builder));
if (returnedType.needsCustomConstruction()) {
List<Selection<?>> selections = new ArrayList<Selection<?>>();
for (String property : returnedType.getInputProperties()) {
selections.add(root.get(property).alias(property));
}
query = query.multiselect(selections);
} else {
query = query.select((Root) root);
}
CriteriaQuery<? extends Object> select = query.orderBy(QueryUtils.toOrders(sort, root, builder));
return predicate == null ? select : select.where(predicate);
}
@@ -148,6 +169,23 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
return new PredicateBuilder(part, root).build();
}
/**
* Returns a path to a {@link Comparable}.
*
* @param root
* @param part
* @return
*/
@SuppressWarnings({ "rawtypes" })
private Expression<? extends Comparable> getComparablePath(Root<?> root, Part part) {
return getTypedPath(root, part);
}
private <T> Expression<T> getTypedPath(Root<?> root, Part part) {
return toExpressionRecursively(root, part.getProperty());
}
/**
* Simple builder to contain logic to create JPA {@link Predicate}s from {@link Part}s.
*

View File

@@ -41,16 +41,16 @@ enum JpaQueryFactory {
* Creates a {@link RepositoryQuery} from the given {@link QueryMethod} that is potentially annotated with
* {@link Query}.
*
* @param queryMethod must not be {@literal null}.
* @param method must not be {@literal null}.
* @param em must not be {@literal null}.
* @param evaluationContextProvider
* @return the {@link RepositoryQuery} derived from the annotation or {@code null} if no annotation found.
*/
AbstractJpaQuery fromQueryAnnotation(JpaQueryMethod queryMethod, EntityManager em,
AbstractJpaQuery fromQueryAnnotation(JpaQueryMethod method, EntityManager em,
EvaluationContextProvider evaluationContextProvider) {
LOG.debug("Looking up query for method {}", queryMethod.getName());
return fromMethodWithQueryString(queryMethod, em, queryMethod.getAnnotatedQuery(), evaluationContextProvider);
LOG.debug("Looking up query for method {}", method.getName());
return fromMethodWithQueryString(method, em, method.getAnnotatedQuery(), evaluationContextProvider);
}
/**
@@ -69,8 +69,8 @@ enum JpaQueryFactory {
return null;
}
return method.isNativeQuery() ? new NativeJpaQuery(method, em, queryString, evaluationContextProvider, PARSER) : //
new SimpleJpaQuery(method, em, queryString, evaluationContextProvider, PARSER);
return method.isNativeQuery() ? new NativeJpaQuery(method, em, queryString, evaluationContextProvider, PARSER)
: new SimpleJpaQuery(method, em, queryString, evaluationContextProvider, PARSER);
}
/**

View File

@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.provider.QueryExtractor;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.EvaluationContextProvider;
@@ -65,12 +66,14 @@ public final class JpaQueryLookupStrategy {
this.provider = extractor;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.repository.core.NamedQueries)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
*/
public final RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
return resolveQuery(new JpaQueryMethod(method, metadata, provider), em, namedQueries);
@Override
public final RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
NamedQueries namedQueries) {
return resolveQuery(new JpaQueryMethod(method, metadata, factory, provider), em, namedQueries);
}
protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries);
@@ -94,8 +97,8 @@ public final class JpaQueryLookupStrategy {
try {
return new PartTreeJpaQuery(method, em);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(String.format("Could not create query metamodel for method %s!",
method.toString()), e);
throw new IllegalArgumentException(
String.format("Could not create query metamodel for method %s!", method.toString()), e);
}
}
@@ -157,8 +160,8 @@ public final class JpaQueryLookupStrategy {
return query;
}
throw new IllegalStateException(String.format(
"Did neither find a NamedQuery nor an annotated query for method %s!", method));
throw new IllegalStateException(
String.format("Did neither find a NamedQuery nor an annotated query for method %s!", method));
}
}

View File

@@ -35,6 +35,7 @@ import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
@@ -77,9 +78,10 @@ public class JpaQueryMethod extends QueryMethod {
* @param extractor must not be {@literal null}
* @param metadata must not be {@literal null}
*/
public JpaQueryMethod(Method method, RepositoryMetadata metadata, QueryExtractor extractor) {
public JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
QueryExtractor extractor) {
super(method, metadata);
super(method, metadata, factory);
Assert.notNull(method, "Method must not be null!");
Assert.notNull(extractor, "Query extractor must not be null!");
@@ -108,9 +110,9 @@ public class JpaQueryMethod extends QueryMethod {
if (!annotatedQuery.contains(String.format(":%s", parameter.getName()))
&& !annotatedQuery.contains(String.format("#%s", parameter.getName()))) {
throw new IllegalStateException(String.format(
"Using named parameters for method %s but parameter '%s' not found in annotated query '%s'!", method,
parameter.getName(), annotatedQuery));
throw new IllegalStateException(
String.format("Using named parameters for method %s but parameter '%s' not found in annotated query '%s'!",
method, parameter.getName(), annotatedQuery));
}
}
}
@@ -298,8 +300,8 @@ public class JpaQueryMethod extends QueryMethod {
private <T> T getAnnotationValue(String attribute, Class<T> type) {
Query annotation = method.getAnnotation(Query.class);
Object value = annotation == null ? AnnotationUtils.getDefaultValue(Query.class, attribute) : AnnotationUtils
.getValue(annotation, attribute);
Object value = annotation == null ? AnnotationUtils.getDefaultValue(Query.class, attribute)
: AnnotationUtils.getValue(annotation, attribute);
return type.cast(value);
}

View File

@@ -28,6 +28,7 @@ import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.DeleteExecution;
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.parser.PartTree;
/**
@@ -50,19 +51,21 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
* Creates a new {@link PartTreeJpaQuery}.
*
* @param method must not be {@literal null}.
* @param factory must not be {@literal null}.
* @param em must not be {@literal null}.
*/
public PartTreeJpaQuery(JpaQueryMethod method, EntityManager em) {
super(method, em);
this.em = em;
this.domainClass = method.getEntityInformation().getJavaType();
this.tree = new PartTree(method.getName(), domainClass);
this.parameters = method.getParameters();
this.countQuery = new CountQueryPreparer(parameters.potentiallySortsDynamically());
this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(parameters.potentiallySortsDynamically());
boolean recreationRequired = parameters.hasDynamicProjection() || parameters.potentiallySortsDynamically();
this.countQuery = new CountQueryPreparer(recreationRequired);
this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(recreationRequired);
}
/*
@@ -189,10 +192,13 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
PersistenceProvider persistenceProvider = PersistenceProvider.fromEntityManager(entityManager);
ParameterMetadataProvider provider = accessor == null ? new ParameterMetadataProvider(builder, parameters,
persistenceProvider) : new ParameterMetadataProvider(builder, accessor, persistenceProvider);
ParameterMetadataProvider provider = accessor == null
? new ParameterMetadataProvider(builder, parameters, persistenceProvider)
: new ParameterMetadataProvider(builder, accessor, persistenceProvider);
return new JpaQueryCreator(tree, domainClass, builder, provider);
ResultProcessor resultFactory = getQueryMethod().getResultProcessor().withDynamicProjection(accessor);
return new JpaQueryCreator(tree, resultFactory.getReturnedType(), builder, provider);
}
/**
@@ -241,10 +247,11 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
PersistenceProvider persistenceProvider = PersistenceProvider.fromEntityManager(entityManager);
ParameterMetadataProvider provider = accessor == null ? new ParameterMetadataProvider(builder, parameters,
persistenceProvider) : new ParameterMetadataProvider(builder, accessor, persistenceProvider);
ParameterMetadataProvider provider = accessor == null
? new ParameterMetadataProvider(builder, parameters, persistenceProvider)
: new ParameterMetadataProvider(builder, accessor, persistenceProvider);
return new JpaCountQueryCreator(tree, domainClass, builder, provider);
return new JpaCountQueryCreator(tree, getQueryMethod().getResultProcessor().getReturnedType(), builder, provider);
}
/**

View File

@@ -116,7 +116,7 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
JpaParameters parameters = getQueryMethod().getParameters();
return useNamedParameters && StringUtils.hasText(outputParameterName) ? //
storedProcedureQuery.getOutputParameterValue(outputParameterName)
storedProcedureQuery.getOutputParameterValue(outputParameterName)
: storedProcedureQuery.getOutputParameterValue(parameters.getNumberOfParameters() + 1);
}

View File

@@ -42,4 +42,12 @@ public class EclipseLinkMetamodelIntegrationTests extends MetamodelIntegrationTe
@Ignore
@Override
public void pathToEntityIsOfBindableTypeEntityType() {}
/**
* TODO: Remove, once https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 is fixed.
*/
@Test
@Ignore
@Override
public void doesNotExposeAliasForTupleIfNoneDefined() {}
}

View File

@@ -35,4 +35,12 @@ public class HibernateMetamodelIntegrationTests extends MetamodelIntegrationTest
@Ignore
@Override
public void considersOneToOneAttributeAnAssociation() {}
/**
* @see https://hibernate.atlassian.net/browse/HHH-10341
*/
@Test
@Ignore
@Override
public void doesNotExposeAliasForTupleIfNoneDefined() {}
}

View File

@@ -15,12 +15,17 @@
*/
package org.springframework.data.jpa.infrastructure;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
@@ -35,6 +40,7 @@ import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Oliver Gierke
@@ -74,4 +80,23 @@ public abstract class MetamodelIntegrationTests {
assertThat(query.getParameter(1), is(notNullValue()));
}
@Test
@Transactional
public void doesNotExposeAliasForTupleIfNoneDefined() {
User user = new User();
user.setFirstname("Dave");
user.setEmailAddress("email");
em.persist(user);
TypedQuery<Tuple> query = em.createQuery("SELECT u.firstname from User u", Tuple.class);
List<Tuple> result = query.getResultList();
List<TupleElement<?>> elements = result.get(0).getElements();
assertThat(elements, hasSize(1));
assertThat(elements.get(0).getAlias(), is(nullValue()));
}
}

View File

@@ -31,4 +31,12 @@ public class OpenJpaMetamodelIntegrationTests extends MetamodelIntegrationTests
@Ignore
@Override
public void canAccessParametersByIndexForNativeQueries() {}
/**
* TODO: Remove once https://issues.apache.org/jira/browse/OPENJPA-2618 is fixed.
*/
@Test
@Ignore
@Override
public void doesNotExposeAliasForTupleIfNoneDefined() {}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2011 the original author or authors.
* Copyright 2008-2015 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.
@@ -35,11 +35,11 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.provider.QueryExtractor;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.test.context.ContextConfiguration;
@@ -75,10 +75,7 @@ public class AbstractJpaQueryTests {
@Test
public void addsHintsToQueryObject() throws Exception {
Method method = SampleRepository.class.getMethod("findByLastname", String.class);
QueryExtractor provider = PersistenceProvider.fromEntityManager(em);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
provider);
JpaQueryMethod queryMethod = getMethod("findByLastname", String.class);
AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em);
@@ -96,11 +93,7 @@ public class AbstractJpaQueryTests {
@Test
public void skipsHintsForCountQueryIfConfigured() throws Exception {
Method method = SampleRepository.class.getMethod("findByFirstname", String.class);
QueryExtractor provider = PersistenceProvider.fromEntityManager(em);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
provider);
JpaQueryMethod queryMethod = getMethod("findByFirstname", String.class);
AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em);
Query result = jpaQuery.createQuery(new Object[] { "Dave" });
@@ -118,10 +111,7 @@ public class AbstractJpaQueryTests {
when(query.setLockMode(any(LockModeType.class))).thenReturn(query);
Method method = SampleRepository.class.getMethod("findOneLocked", Integer.class);
QueryExtractor provider = PersistenceProvider.fromEntityManager(em);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
provider);
JpaQueryMethod queryMethod = getMethod("findOneLocked", Integer.class);
AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em);
Query result = jpaQuery.createQuery(new Object[] { Integer.valueOf(1) });
@@ -137,10 +127,7 @@ public class AbstractJpaQueryTests {
Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
Method findAllMethod = SampleRepository.class.getMethod("findAll");
QueryExtractor provider = PersistenceProvider.fromEntityManager(em);
JpaQueryMethod queryMethod = new JpaQueryMethod(findAllMethod,
new DefaultRepositoryMetadata(SampleRepository.class), provider);
JpaQueryMethod queryMethod = getMethod("findAll");
javax.persistence.EntityGraph<?> entityGraph = em.getEntityGraph("User.overview");
@@ -159,10 +146,7 @@ public class AbstractJpaQueryTests {
Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
Method getByIdMethod = SampleRepository.class.getMethod("getById", Integer.class);
QueryExtractor provider = PersistenceProvider.fromEntityManager(em);
JpaQueryMethod queryMethod = new JpaQueryMethod(getByIdMethod,
new DefaultRepositoryMetadata(SampleRepository.class), provider);
JpaQueryMethod queryMethod = getMethod("getById", Integer.class);
javax.persistence.EntityGraph<?> entityGraph = em.getEntityGraph("User.detail");
@@ -172,6 +156,15 @@ public class AbstractJpaQueryTests {
verify(result).setHint("javax.persistence.loadgraph", entityGraph);
}
private JpaQueryMethod getMethod(String name, Class<?>... parameterTypes) throws Exception {
Method method = SampleRepository.class.getMethod(name, parameterTypes);
PersistenceProvider persistenceProvider = PersistenceProvider.fromEntityManager(em);
return new JpaQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
new SpelAwareProxyProjectionFactory(), persistenceProvider);
}
interface SampleRepository extends Repository<User, Integer> {
@QueryHints({ @QueryHint(name = "foo", value = "bar") })

View File

@@ -37,6 +37,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.provider.QueryExtractor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
@@ -60,6 +61,7 @@ public class JpaQueryLookupStrategyUnitTests {
@Mock EntityManagerFactory emf;
@Mock QueryExtractor extractor;
@Mock NamedQueries namedQueries;
@Mock ProjectionFactory projectionFactory;
public @Rule ExpectedException exception = ExpectedException.none();
@@ -85,7 +87,7 @@ public class JpaQueryLookupStrategyUnitTests {
when(em.createQuery(anyString())).thenThrow(reference);
try {
strategy.resolveQuery(method, metadata, namedQueries);
strategy.resolveQuery(method, metadata, projectionFactory, namedQueries);
} catch (Exception e) {
assertThat(e, is(instanceOf(IllegalArgumentException.class)));
assertThat(e.getCause(), is(reference));
@@ -107,7 +109,7 @@ public class JpaQueryLookupStrategyUnitTests {
exception.expectMessage("Cannot use native queries with dynamic sorting and/or pagination in method");
exception.expectMessage(method.toString());
strategy.resolveQuery(method, metadata, namedQueries);
strategy.resolveQuery(method, metadata, projectionFactory, namedQueries);
}
interface UserRepository extends Repository<User, Long> {

View File

@@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.lang.annotation.Retention;
@@ -45,6 +46,9 @@ import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.data.jpa.repository.sample.UserRepository;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.Param;
@@ -64,10 +68,10 @@ public class JpaQueryMethodUnitTests {
@Mock QueryExtractor extractor;
@Mock RepositoryMetadata metadata;
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
Method repositoryMethod, invalidReturnType, pageableAndSort, pageableTwice, sortableTwice, modifyingMethod,
nativeQuery, namedQuery, findWithLockMethod, invalidNamedParameter, findsProjections, findsProjection,
withMetaAnnotation, queryMethodWithCustomEntityFetchGraph;
Method invalidReturnType, pageableAndSort, pageableTwice, sortableTwice, findWithLockMethod, findsProjections,
findsProjection, queryMethodWithCustomEntityFetchGraph;
/**
* @throws Exception
@@ -75,35 +79,25 @@ public class JpaQueryMethodUnitTests {
@Before
public void setUp() throws Exception {
repositoryMethod = UserRepository.class.getMethod("findByLastname", String.class);
invalidReturnType = InvalidRepository.class.getMethod(METHOD_NAME, String.class, Pageable.class);
pageableAndSort = InvalidRepository.class.getMethod(METHOD_NAME, String.class, Pageable.class, Sort.class);
pageableTwice = InvalidRepository.class.getMethod(METHOD_NAME, String.class, Pageable.class, Pageable.class);
sortableTwice = InvalidRepository.class.getMethod(METHOD_NAME, String.class, Sort.class, Sort.class);
modifyingMethod = UserRepository.class.getMethod("renameAllUsersTo", String.class);
nativeQuery = ValidRepository.class.getMethod("findByLastname", String.class);
namedQuery = ValidRepository.class.getMethod("findByNamedQuery");
findWithLockMethod = ValidRepository.class.getMethod("findOneLocked", Integer.class);
invalidNamedParameter = InvalidRepository.class.getMethod("findByAnnotatedQuery", String.class);
findsProjections = ValidRepository.class.getMethod("findsProjections");
findsProjection = ValidRepository.class.getMethod("findsProjection");
withMetaAnnotation = ValidRepository.class.getMethod("withMetaAnnotation");
queryMethodWithCustomEntityFetchGraph = ValidRepository.class.getMethod("queryMethodWithCustomEntityFetchGraph",
Integer.class);
}
@Test
public void testname() {
public void testname() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, new DefaultRepositoryMetadata(UserRepository.class),
extractor);
JpaQueryMethod method = getQueryMethod(UserRepository.class, "findByLastname", String.class);
assertEquals("User.findByLastname", method.getNamedQueryName());
assertThat(method.isCollectionQuery(), is(true));
@@ -114,62 +108,61 @@ public class JpaQueryMethodUnitTests {
@Test(expected = IllegalArgumentException.class)
public void preventsNullRepositoryMethod() {
new JpaQueryMethod(null, metadata, extractor);
new JpaQueryMethod(null, metadata, factory, extractor);
}
@Test(expected = IllegalArgumentException.class)
public void preventsNullQueryExtractor() {
public void preventsNullQueryExtractor() throws Exception {
new JpaQueryMethod(repositoryMethod, metadata, null);
Method method = UserRepository.class.getMethod("findByLastname", String.class);
new JpaQueryMethod(method, metadata, factory, null);
}
@Test
public void returnsCorrectName() {
public void returnsCorrectName() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, metadata, extractor);
assertEquals(repositoryMethod.getName(), method.getName());
JpaQueryMethod method = getQueryMethod(UserRepository.class, "findByLastname", String.class);
assertThat(method.getName(), is("findByLastname"));
}
@Test
public void returnsQueryIfAvailable() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, metadata, extractor);
JpaQueryMethod method = getQueryMethod(UserRepository.class, "findByLastname", String.class);
assertThat(method.getAnnotatedQuery(), is(nullValue()));
assertNull(method.getAnnotatedQuery());
Method repositoryMethod = UserRepository.class.getMethod("findByAnnotatedQuery", String.class);
assertNotNull(new JpaQueryMethod(repositoryMethod, metadata, extractor).getAnnotatedQuery());
method = getQueryMethod(UserRepository.class, "findByAnnotatedQuery", String.class);
assertThat(method.getAnnotatedQuery(), is(notNullValue()));
}
@Test(expected = IllegalStateException.class)
public void rejectsInvalidReturntypeOnPagebleFinder() {
new JpaQueryMethod(invalidReturnType, metadata, extractor);
new JpaQueryMethod(invalidReturnType, metadata, factory, extractor);
}
@Test(expected = IllegalStateException.class)
public void rejectsPageableAndSortInFinderMethod() {
new JpaQueryMethod(pageableAndSort, metadata, extractor);
new JpaQueryMethod(pageableAndSort, metadata, factory, extractor);
}
@Test(expected = IllegalStateException.class)
public void rejectsTwoPageableParameters() {
new JpaQueryMethod(pageableTwice, metadata, extractor);
new JpaQueryMethod(pageableTwice, metadata, factory, extractor);
}
@Test(expected = IllegalStateException.class)
public void rejectsTwoSortableParameters() {
new JpaQueryMethod(sortableTwice, metadata, extractor);
new JpaQueryMethod(sortableTwice, metadata, factory, extractor);
}
@Test
public void recognizesModifyingMethod() {
public void recognizesModifyingMethod() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(modifyingMethod, metadata, extractor);
JpaQueryMethod method = getQueryMethod(UserRepository.class, "renameAllUsersTo", String.class);
assertTrue(method.isModifyingQuery());
}
@@ -178,7 +171,7 @@ public class JpaQueryMethodUnitTests {
Method method = InvalidRepository.class.getMethod("updateMethod", String.class, Pageable.class);
new JpaQueryMethod(method, metadata, extractor);
new JpaQueryMethod(method, metadata, factory, extractor);
}
@Test(expected = IllegalArgumentException.class)
@@ -186,13 +179,13 @@ public class JpaQueryMethodUnitTests {
Method method = InvalidRepository.class.getMethod("updateMethod", String.class, Sort.class);
new JpaQueryMethod(method, metadata, extractor);
new JpaQueryMethod(method, metadata, factory, extractor);
}
@Test
public void discoversHintsCorrectly() {
public void discoversHintsCorrectly() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, metadata, extractor);
JpaQueryMethod method = getQueryMethod(UserRepository.class, "findByLastname", String.class);
List<QueryHint> hints = method.getHints();
assertNotNull(hints);
@@ -200,20 +193,28 @@ public class JpaQueryMethodUnitTests {
assertThat(hints.get(0).value(), is("bar"));
}
private JpaQueryMethod getQueryMethod(Class<?> repositoryInterface, String methodName, Class<?>... parameterTypes)
throws Exception {
Method method = repositoryInterface.getMethod(methodName, parameterTypes);
DefaultRepositoryMetadata repositoryMetadata = new DefaultRepositoryMetadata(repositoryInterface);
return new JpaQueryMethod(method, repositoryMetadata, factory, extractor);
}
@Test
public void calculatesNamedQueryNamesCorrectly() throws SecurityException, NoSuchMethodException {
public void calculatesNamedQueryNamesCorrectly() throws Exception {
RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class);
JpaQueryMethod queryMethod = new JpaQueryMethod(repositoryMethod, metadata, extractor);
JpaQueryMethod queryMethod = getQueryMethod(UserRepository.class, "findByLastname", String.class);
assertThat(queryMethod.getNamedQueryName(), is("User.findByLastname"));
Method method = UserRepository.class.getMethod("renameAllUsersTo", String.class);
queryMethod = new JpaQueryMethod(method, metadata, extractor);
queryMethod = new JpaQueryMethod(method, metadata, factory, extractor);
assertThat(queryMethod.getNamedQueryName(), is("User.renameAllUsersTo"));
method = UserRepository.class.getMethod("findSpecialUsersByLastname", String.class);
queryMethod = new JpaQueryMethod(method, metadata, extractor);
queryMethod = new JpaQueryMethod(method, metadata, factory, extractor);
assertThat(queryMethod.getNamedQueryName(), is("SpecialUser.findSpecialUsersByLastname"));
}
@@ -221,9 +222,9 @@ public class JpaQueryMethodUnitTests {
* @see DATAJPA-117
*/
@Test
public void discoversNativeQuery() {
public void discoversNativeQuery() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(nativeQuery, metadata, extractor);
JpaQueryMethod method = getQueryMethod(ValidRepository.class, "findByLastname", String.class);
assertThat(method.isNativeQuery(), is(true));
}
@@ -231,8 +232,9 @@ public class JpaQueryMethodUnitTests {
* @see DATAJPA-129
*/
@Test
public void considersAnnotatedNamedQueryName() {
JpaQueryMethod queryMethod = new JpaQueryMethod(namedQuery, metadata, extractor);
public void considersAnnotatedNamedQueryName() throws Exception {
JpaQueryMethod queryMethod = getQueryMethod(ValidRepository.class, "findByNamedQuery");
assertThat(queryMethod.getNamedQueryName(), is("HateoasAwareSpringDataWebConfiguration.bar"));
}
@@ -242,7 +244,7 @@ public class JpaQueryMethodUnitTests {
@Test
public void discoversLockModeCorrectly() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(findWithLockMethod, metadata, extractor);
JpaQueryMethod method = getQueryMethod(ValidRepository.class, "findOneLocked", Integer.class);
LockModeType lockMode = method.getLockModeType();
assertEquals(LockModeType.PESSIMISTIC_WRITE, lockMode);
@@ -252,12 +254,9 @@ public class JpaQueryMethodUnitTests {
* @see DATAJPA-142
*/
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void returnsDefaultCountQueryName() {
public void returnsDefaultCountQueryName() throws Exception {
when(metadata.getReturnedDomainClass(repositoryMethod)).thenReturn((Class) User.class);
JpaQueryMethod method = new JpaQueryMethod(repositoryMethod, metadata, extractor);
JpaQueryMethod method = getQueryMethod(UserRepository.class, "findByLastname", String.class);
assertThat(method.getNamedCountQueryName(), is("User.findByLastname.count"));
}
@@ -265,9 +264,9 @@ public class JpaQueryMethodUnitTests {
* @see DATAJPA-142
*/
@Test
public void returnsDefaultCountQueryNameBasedOnConfiguredNamedQueryName() {
public void returnsDefaultCountQueryNameBasedOnConfiguredNamedQueryName() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(namedQuery, metadata, extractor);
JpaQueryMethod method = getQueryMethod(ValidRepository.class, "findByNamedQuery");
assertThat(method.getNamedCountQueryName(), is("HateoasAwareSpringDataWebConfiguration.bar.count"));
}
@@ -275,10 +274,10 @@ public class JpaQueryMethodUnitTests {
* @see DATAJPA-185
*/
@Test
public void rejectsInvalidNamedParameter() {
public void rejectsInvalidNamedParameter() throws Exception {
try {
new JpaQueryMethod(invalidNamedParameter, metadata, extractor);
getQueryMethod(InvalidRepository.class, "findByAnnotatedQuery", String.class);
fail();
} catch (IllegalStateException e) {
// Parameter from query
@@ -301,17 +300,17 @@ public class JpaQueryMethodUnitTests {
when(metadata.getReturnedDomainClass(findsProjections)).thenReturn((Class) Integer.class);
when(metadata.getReturnedDomainClass(findsProjection)).thenReturn((Class) Integer.class);
assertThat(new JpaQueryMethod(findsProjections, metadata, extractor).isQueryForEntity(), is(false));
assertThat(new JpaQueryMethod(findsProjection, metadata, extractor).isQueryForEntity(), is(false));
assertThat(new JpaQueryMethod(findsProjections, metadata, factory, extractor).isQueryForEntity(), is(false));
assertThat(new JpaQueryMethod(findsProjection, metadata, factory, extractor).isQueryForEntity(), is(false));
}
/**
* @see DATAJPA-345
*/
@Test
public void detectsLockAndQueryHintsOnIfUsedAsMetaAnnotation() {
public void detectsLockAndQueryHintsOnIfUsedAsMetaAnnotation() throws Exception {
JpaQueryMethod method = new JpaQueryMethod(withMetaAnnotation, metadata, extractor);
JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotation");
assertThat(method.getLockModeType(), is(LockModeType.OPTIMISTIC_FORCE_INCREMENT));
assertThat(method.getHints(), hasSize(1));
@@ -324,11 +323,11 @@ public class JpaQueryMethodUnitTests {
*/
@Test
public void shouldStoreJpa21FetchGraphInformationAsHint() {
doReturn(User.class).when(metadata).getDomainType();
doReturn(User.class).when(metadata).getReturnedDomainClass(queryMethodWithCustomEntityFetchGraph);
JpaQueryMethod method = new JpaQueryMethod(queryMethodWithCustomEntityFetchGraph, metadata, extractor);
JpaQueryMethod method = new JpaQueryMethod(queryMethodWithCustomEntityFetchGraph, metadata, factory, extractor);
assertThat(method.getEntityGraph(), is(notNullValue()));
assertThat(method.getEntityGraph().getName(), is("User.propertyLoadPath"));
@@ -342,9 +341,10 @@ public class JpaQueryMethodUnitTests {
public void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethod() throws Exception {
doReturn(User.class).when(metadata).getDomainType();
doReturn(User.class).when(metadata).getReturnedDomainClass((Method)any());
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findAll"), metadata, extractor);
doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any());
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findAll"), metadata, factory,
extractor);
assertThat(method.getEntityGraph(), is(notNullValue()));
assertThat(method.getEntityGraph().getName(), is("User.detail"));
@@ -358,15 +358,16 @@ public class JpaQueryMethodUnitTests {
public void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethodFindOne() throws Exception {
doReturn(User.class).when(metadata).getDomainType();
doReturn(User.class).when(metadata).getReturnedDomainClass((Method)any());
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne", Long.class), metadata, extractor);
doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any());
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne", Long.class), metadata,
factory, extractor);
assertThat(method.getEntityGraph(), is(notNullValue()));
assertThat(method.getEntityGraph().getName(), is("User.detail"));
assertThat(method.getEntityGraph().getType(), is(EntityGraphType.FETCH));
}
/**
* DATAJPA-696
*/
@@ -374,9 +375,10 @@ public class JpaQueryMethodUnitTests {
public void shouldFindEntityGraphAnnotationOnQueryMethodGetOneByWithDerivedName() throws Exception {
doReturn(User.class).when(metadata).getDomainType();
doReturn(User.class).when(metadata).getReturnedDomainClass((Method)any());
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("getOneById", Long.class), metadata, extractor);
doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any());
JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("getOneById", Long.class),
metadata, factory, extractor);
assertThat(method.getEntityGraph(), is(notNullValue()));
assertThat(method.getEntityGraph().getName(), is("User.getOneById"));
@@ -388,9 +390,7 @@ public class JpaQueryMethodUnitTests {
*/
@Test
public void allowsPositionalBindingEvenIfParametersAreNamed() throws Exception {
new JpaQueryMethod(ValidRepository.class.getMethod("queryWithPositionalBinding", String.class), metadata,
extractor);
getQueryMethod(ValidRepository.class, "queryWithPositionalBinding", String.class);
}
/**
@@ -398,7 +398,7 @@ public class JpaQueryMethodUnitTests {
*
* @author Oliver Gierke
*/
static interface InvalidRepository {
static interface InvalidRepository extends Repository<User, Long> {
// Invalid return type
User findByFirstname(String firstname, Pageable pageable);
@@ -429,7 +429,7 @@ public class JpaQueryMethodUnitTests {
List<User> findByAnnotatedQuery(@Param("param") String param);
}
static interface ValidRepository {
static interface ValidRepository extends Repository<User, Long> {
@Query(value = "query", nativeQuery = true)
List<User> findByLastname(String lastname);
@@ -472,7 +472,7 @@ public class JpaQueryMethodUnitTests {
*/
@EntityGraph("User.detail")
User findOne(Long id);
/**
* DATAJPA-696
*/

View File

@@ -31,6 +31,8 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.provider.QueryExtractor;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryCreationException;
@@ -47,6 +49,7 @@ public class NamedQueryUnitTests {
@Mock QueryExtractor extractor;
@Mock EntityManager em;
@Mock EntityManagerFactory emf;
ProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
Method method;
@@ -57,7 +60,7 @@ public class NamedQueryUnitTests {
method = SampleRepository.class.getMethod("foo", Pageable.class);
when(metadata.getDomainType()).thenReturn((Class) String.class);
when(metadata.getReturnedDomainClass(method)).thenReturn((Class) String.class);
when(em.getEntityManagerFactory()).thenReturn(emf);
when(emf.createEntityManager()).thenReturn(em);
}
@@ -66,7 +69,7 @@ public class NamedQueryUnitTests {
public void rejectsPersistenceProviderIfIncapableOfExtractingQueriesAndPagebleBeingUsed() {
when(extractor.canExtractQuery()).thenReturn(false);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, extractor);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, projectionFactory, extractor);
when(em.createNamedQuery(queryMethod.getNamedCountQueryName())).thenThrow(new IllegalArgumentException());
NamedQuery.lookupFrom(queryMethod, em);
@@ -79,7 +82,7 @@ public class NamedQueryUnitTests {
public void doesNotRejectPersistenceProviderIfNamedCountQueryIsAvailable() {
when(extractor.canExtractQuery()).thenReturn(false);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, extractor);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, projectionFactory, extractor);
when(em.createNamedQuery(queryMethod.getNamedCountQueryName())).thenReturn(null);
NamedQuery query = (NamedQuery) NamedQuery.lookupFrom(queryMethod, em);

View File

@@ -44,6 +44,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.repository.Temporal;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.Param;
@@ -72,9 +73,7 @@ public class PartTreeJpaQueryIntegrationTests {
@Test
public void test() throws Exception {
Method method = UserRepository.class.getMethod("findByFirstname", String.class, Pageable.class);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
PersistenceProvider.fromEntityManager(entityManager));
JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
jpaQuery.createQuery(new Object[] { "Matthews", new PageRequest(0, 1) });
@@ -101,9 +100,7 @@ public class PartTreeJpaQueryIntegrationTests {
@Test
public void recreatesQueryIfNullValueIsGiven() throws Exception {
Method method = UserRepository.class.getMethod("findByFirstname", String.class, Pageable.class);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
PersistenceProvider.fromEntityManager(entityManager));
JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
Query query = jpaQuery.createQuery(new Object[] { "Matthews", new PageRequest(0, 1) });
@@ -124,12 +121,18 @@ public class PartTreeJpaQueryIntegrationTests {
parameterTypes[i] = values[i].getClass();
}
Method method = UserRepository.class.getMethod(methodName, parameterTypes);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
PersistenceProvider.fromEntityManager(entityManager));
JpaQueryMethod queryMethod = getQueryMethod(methodName, parameterTypes);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
jpaQuery.createQuery(values);
}
private JpaQueryMethod getQueryMethod(String methodName, Class<?>... parameterTypes) throws Exception {
Method method = UserRepository.class.getMethod(methodName, parameterTypes);
return new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory(), PersistenceProvider.fromEntityManager(entityManager));
}
@SuppressWarnings("unchecked")
private static <T> T getValue(Object source, String path) {

View File

@@ -43,6 +43,8 @@ import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.provider.QueryExtractor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.sample.UserRepository;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.ExtensionAwareEvaluationContextProvider;
@@ -72,6 +74,8 @@ public class SimpleJpaQueryUnitTests {
@Mock RepositoryMetadata metadata;
@Mock ParameterBinder binder;
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
public @Rule ExpectedException exception = ExpectedException.none();
@Before
@@ -86,14 +90,14 @@ public class SimpleJpaQueryUnitTests {
when(metadata.getReturnedDomainClass(Mockito.any(Method.class))).thenReturn((Class) User.class);
Method setUp = UserRepository.class.getMethod("findByLastname", String.class);
method = new JpaQueryMethod(setUp, metadata, extractor);
method = new JpaQueryMethod(setUp, metadata, factory, extractor);
}
@Test
public void prefersDeclaredCountQueryOverCreatingOne() throws Exception {
method = new JpaQueryMethod(SimpleJpaQueryUnitTests.class.getMethod("prefersDeclaredCountQueryOverCreatingOne"),
metadata, extractor);
metadata, factory, extractor);
when(em.createQuery("foo", Long.class)).thenReturn(typedQuery);
SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", EVALUATION_CONTEXT_PROVIDER,
@@ -111,7 +115,7 @@ public class SimpleJpaQueryUnitTests {
when(em.createQuery(Mockito.anyString())).thenReturn(query);
Method method = UserRepository.class.getMethod("findAllPaged", Pageable.class);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, extractor);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor);
AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", EVALUATION_CONTEXT_PROVIDER,
PARSER);
@@ -126,7 +130,7 @@ public class SimpleJpaQueryUnitTests {
public void discoversNativeQuery() throws Exception {
Method method = SampleRepository.class.getMethod("findNativeByLastname", String.class);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, extractor);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor);
AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromQueryAnnotation(queryMethod, em,
EVALUATION_CONTEXT_PROVIDER);
@@ -224,7 +228,7 @@ public class SimpleJpaQueryUnitTests {
private AbstractJpaQuery createJpaQuery(Method method) {
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, extractor);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor);
return JpaQueryFactory.INSTANCE.fromQueryAnnotation(queryMethod, em, EVALUATION_CONTEXT_PROVIDER);
}