diff --git a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java index f316c6d1d..d8dddd254 100644 --- a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java +++ b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java @@ -21,8 +21,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.data.annotation.QueryAnnotation; - /** * Annotation to configure the JPA 2.1 {@link javax.persistence.EntityGraph}s that should be used on repository methods. * @@ -31,7 +29,6 @@ import org.springframework.data.annotation.QueryAnnotation; */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) -@QueryAnnotation @Documented public @interface EntityGraph { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 2cb0c30fa..b251c6d2f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -28,6 +28,7 @@ import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExec import org.springframework.data.jpa.repository.query.JpaQueryExecution.ProcedureExecution; import org.springframework.data.jpa.repository.query.JpaQueryExecution.SingleEntityExecution; import org.springframework.data.jpa.repository.query.JpaQueryExecution.SlicedExecution; +import org.springframework.data.jpa.util.Jpa21Utils; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.util.Assert; @@ -180,7 +181,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { JpaEntityGraph entityGraph = method.getEntityGraph(); if (entityGraph != null) { - Jpa21QueryCustomizer.INSTANCE.tryConfigureFetchGraph(em, query, entityGraph); + Jpa21Utils.tryConfigureFetchGraph(em, query, entityGraph); } return query; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java index 0368d4de3..47c56f6ac 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java @@ -34,7 +34,7 @@ public class JpaEntityGraph { /** * Creates an {@link JpaEntityGraph}. * - * @param name must not be {@null}. + * @param name must not be {@null} or empty. * @param type must not be {@null}. */ public JpaEntityGraph(String name, EntityGraphType type) { diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java index f735e19b7..af4679414 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java @@ -19,11 +19,14 @@ import java.util.Map; import javax.persistence.LockModeType; +import org.springframework.data.jpa.repository.query.JpaEntityGraph; + /** * Interface to abstract {@link CrudMethodMetadata} that provide the {@link LockModeType} to be used for query * execution. * * @author Oliver Gierke + * @author Thomas Darimont */ public interface CrudMethodMetadata { @@ -40,4 +43,12 @@ public interface CrudMethodMetadata { * @return */ Map getQueryHints(); + + /** + * Returns the {@link JpaEntityGraph} to be used. + * + * @return + * @since 1.8 + */ + JpaEntityGraph getEntityGraph(); } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 93d98ace5..016a6a1d6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -29,8 +29,10 @@ import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.interceptor.ExposeInvocationInterceptor; import org.springframework.aop.target.AbstractLazyCreationTargetSource; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.QueryHints; +import org.springframework.data.jpa.repository.query.JpaEntityGraph; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -42,6 +44,7 @@ import org.springframework.util.Assert; * or query hints on them. * * @author Oliver Gierke + * @author Thomas Darimont */ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor { @@ -73,8 +76,8 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor { } /** - * {@link MethodInterceptor} to build and cache {@link DefaultCrudMethodMetadata} instances for the invoked - * methods. Will bind the found information to a {@link TransactionSynchronizationManager} for later lookup. + * {@link MethodInterceptor} to build and cache {@link DefaultCrudMethodMetadata} instances for the invoked methods. + * Will bind the found information to a {@link TransactionSynchronizationManager} for later lookup. * * @see DefaultCrudMethodMetadata * @author Oliver Gierke @@ -119,14 +122,16 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor { * Default implementation of {@link CrudMethodMetadata} that will inspect the backing method for annotations. * * @author Oliver Gierke + * @author Thomas Darimont */ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { private final LockModeType lockModeType; private final Map queryHints; + private final JpaEntityGraph entityGraph; /** - * Creates a new {@link DefaultCrudMethodMetadata} foir the given {@link Method}. + * Creates a new {@link DefaultCrudMethodMetadata} for the given {@link Method}. * * @param method must not be {@literal null}. */ @@ -136,6 +141,14 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor { this.lockModeType = findLockModeType(method); this.queryHints = findQueryHints(method); + this.entityGraph = findEntityGraph(method); + } + + private static final JpaEntityGraph findEntityGraph(Method method) { + + EntityGraph entityGraphAnnotation = AnnotationUtils.findAnnotation(method, EntityGraph.class); + return entityGraphAnnotation == null ? null : new JpaEntityGraph(entityGraphAnnotation.value(), + entityGraphAnnotation.type()); } private static final LockModeType findLockModeType(Method method) { @@ -182,6 +195,15 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor { public Map getQueryHints() { return queryHints; } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getEntityGraphHint() + */ + @Override + public JpaEntityGraph getEntityGraph() { + return this.entityGraph; + } } private static class ThreadBoundTargetSource extends AbstractLazyCreationTargetSource { diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java index 5eccacd2a..df12d35e2 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java @@ -20,12 +20,15 @@ import java.util.Collections; import java.util.List; import java.util.Map.Entry; +import javax.persistence.EntityGraph; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.query.JpaEntityGraph; +import org.springframework.data.jpa.util.Jpa21Utils; import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.querydsl.QSort; import org.springframework.data.querydsl.QueryDslPredicateExecutor; @@ -52,6 +55,7 @@ public class QueryDslJpaRepository extends SimpleJpa private final EntityPath path; private final PathBuilder builder; private final Querydsl querydsl; + private final EntityManager em; /** * Creates a new {@link QueryDslJpaRepository} from the given domain class and {@link EntityManager}. This will use @@ -76,7 +80,7 @@ public class QueryDslJpaRepository extends SimpleJpa EntityPathResolver resolver) { super(entityInformation, entityManager); - + this.em = entityManager; this.path = resolver.createPath(entityInformation.getJavaType()); this.builder = new PathBuilder(path.getType(), path.getMetadata()); this.querydsl = new Querydsl(entityManager, builder); @@ -154,6 +158,20 @@ public class QueryDslJpaRepository extends SimpleJpa query.setHint(hint.getKey(), hint.getValue()); } + JpaEntityGraph jpaEntityGraph = metadata.getEntityGraph(); + + if (jpaEntityGraph == null) { + return query; + } + + EntityGraph entityGraph = Jpa21Utils.tryGetFetchGraph(em, jpaEntityGraph); + + if (entityGraph == null) { + return query; + } + + query.setHint(jpaEntityGraph.getType().getKey(), entityGraph); + return query; } } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index c57e3ece8..249f4dc3f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -45,6 +45,7 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.query.QueryUtils; +import org.springframework.data.jpa.util.Jpa21Utils; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -68,7 +69,7 @@ public class SimpleJpaRepository implements JpaRepos private final EntityManager em; private final PersistenceProvider provider; - private CrudMethodMetadata crudMethodMetadata; + private CrudMethodMetadata metadata; /** * Creates a new {@link SimpleJpaRepository} to manage objects of the given {@link JpaEntityInformation}. @@ -103,11 +104,11 @@ public class SimpleJpaRepository implements JpaRepos * @param crudMethodMetadata */ public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) { - this.crudMethodMetadata = crudMethodMetadata; + this.metadata = crudMethodMetadata; } protected CrudMethodMetadata getRepositoryMethodMetadata() { - return crudMethodMetadata; + return metadata; } protected Class getDomainClass() { @@ -216,12 +217,12 @@ public class SimpleJpaRepository implements JpaRepos Class domainType = getDomainClass(); - if (crudMethodMetadata == null) { + if (metadata == null) { return em.find(domainType, id); } - LockModeType type = crudMethodMetadata.getLockModeType(); - Map hints = crudMethodMetadata.getQueryHints(); + LockModeType type = metadata.getLockModeType(); + Map hints = metadata.getQueryHints(); return type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints); } @@ -545,18 +546,18 @@ public class SimpleJpaRepository implements JpaRepos private TypedQuery applyRepositoryMethodMetadata(TypedQuery query) { - if (crudMethodMetadata == null) { + if (metadata == null) { return query; } - LockModeType type = crudMethodMetadata.getLockModeType(); + LockModeType type = metadata.getLockModeType(); TypedQuery toReturn = type == null ? query : query.setLockMode(type); - for (Entry hint : crudMethodMetadata.getQueryHints().entrySet()) { + for (Entry hint : metadata.getQueryHints().entrySet()) { query.setHint(hint.getKey(), hint.getValue()); } - return toReturn; + return Jpa21Utils.tryConfigureFetchGraph(em, toReturn, metadata.getEntityGraph()); } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21QueryCustomizer.java b/src/main/java/org/springframework/data/jpa/util/Jpa21Utils.java similarity index 60% rename from src/main/java/org/springframework/data/jpa/repository/query/Jpa21QueryCustomizer.java rename to src/main/java/org/springframework/data/jpa/util/Jpa21Utils.java index d33de0edb..a23df2c8b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21QueryCustomizer.java +++ b/src/main/java/org/springframework/data/jpa/util/Jpa21Utils.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.query; +package org.springframework.data.jpa.util; import java.lang.reflect.Method; @@ -21,23 +21,23 @@ import javax.persistence.EntityGraph; import javax.persistence.EntityManager; import javax.persistence.Query; +import org.springframework.data.jpa.repository.query.JpaEntityGraph; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** - * Customizes a given JPA query with JPA 2.1 features. + * Utils for bridging various JPA 2.1 features. * * @author Thomas Darimont + * @author Oliver Gierke * @since 1.6 */ -enum Jpa21QueryCustomizer { - - INSTANCE; +public class Jpa21Utils { private static final Method GET_ENTITY_GRAPH_METHOD; private static final boolean JPA21_AVAILABLE = ClassUtils.isPresent("javax.persistence.NamedEntityGraph", - Jpa21QueryCustomizer.class.getClassLoader()); + Jpa21Utils.class.getClassLoader()); static { @@ -52,26 +52,43 @@ enum Jpa21QueryCustomizer { * Adds a JPA 2.1 fetch-graph or load-graph hint to the given {@link Query} if running under JPA 2.1. * * @see JPA 2.1 Specfication 3.7.4 - Use of Entity Graphs in find and query operations P.117 - * @param em must not be {@literal null} - * @param query must not be {@literal null} - * @param entityGraph must not be {@literal null} + * @param em must not be {@literal null}. + * @param query must not be {@literal null}. + * @param entityGraph can be {@literal null}. */ - public void tryConfigureFetchGraph(EntityManager em, Query query, JpaEntityGraph entityGraph) { + public static T tryConfigureFetchGraph(EntityManager em, T query, JpaEntityGraph entityGraph) { + + if (entityGraph == null) { + return query; + } + + EntityGraph graph = tryGetFetchGraph(em, entityGraph); + + if (graph == null) { + return query; + } + + query.setHint(entityGraph.getType().getKey(), graph); + return query; + } + + /** + * Adds a JPA 2.1 fetch-graph or load-graph hint to the given {@link Query} if running under JPA 2.1. + * + * @see JPA 2.1 Specfication 3.7.4 - Use of Entity Graphs in find and query operations P.117 + * @param em must not be {@literal null}. + * @param jpaEntityGraph must not be {@literal null}. + * @return the {@link EntityGraph} described by the given {@code entityGraph}. + */ + public static EntityGraph tryGetFetchGraph(EntityManager em, JpaEntityGraph jpaEntityGraph) { Assert.notNull(em, "EntityManager must not be null!"); - Assert.notNull(query, "Query must not be null!"); - Assert.notNull(entityGraph, "EntityGraph must not be null!"); + Assert.notNull(jpaEntityGraph, "EntityGraph must not be null!"); Assert.isTrue(JPA21_AVAILABLE, "The EntityGraph-Feature requires at least a JPA 2.1 persistence provider!"); Assert.isTrue(GET_ENTITY_GRAPH_METHOD != null, "It seems that you have the JPA 2.1 API but a JPA 2.0 implementation on the classpath!"); - EntityGraph graph = em.getEntityGraph(entityGraph.getName()); - - if (graph == null) { - return; - } - - query.setHint(entityGraph.getType().getKey(), graph); + return em.getEntityGraph(jpaEntityGraph.getName()); } } diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java new file mode 100644 index 000000000..aeeb223cb --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 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; + +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Oliver Gierke + */ +@ContextConfiguration("classpath:eclipselink.xml") +public class EclipseLinkEntityGraphRepositoryMethodsIntegrationTests extends + EntityGraphRepositoryMethodsIntegrationTests { + +} diff --git a/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java new file mode 100644 index 000000000..5742fb5ab --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2014 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; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.Persistence; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.sample.Role; +import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.repository.sample.RepositoryMethodsWithEntityGraphConfigJpaRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Thomas Darimont + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:config/namespace-autoconfig-context.xml") +@Transactional +public class EntityGraphRepositoryMethodsIntegrationTests { + + @Autowired EntityManager em; + @Autowired RepositoryMethodsWithEntityGraphConfigJpaRepository repository; + + User tom; + Role role; + + @Before + public void setup() { + + tom = new User("Thomas", "Darimont", "tdarimont@example.org"); + role = new Role("Developer"); + em.persist(role); + tom.getRoles().add(role); + } + + /** + * @see DATAJPA-612 + */ + @Test + public void shouldRespectConfiguredJpaEntityGraph() { + + Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + + tom = repository.save(tom); + + List result = repository.findAll(); + + assertThat(result.size(), is(1)); + assertThat(Persistence.getPersistenceUtil().isLoaded(result.get(0).getRoles()), is(true)); + assertThat(result.get(0), is(tom)); + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java new file mode 100644 index 000000000..35e9f5e18 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 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; + +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Oliver Gierke + */ +@ContextConfiguration("classpath:openjpa.xml") +public class OpenJpaEntityGraphRepositoryMethodsIntegrationTests extends EntityGraphRepositoryMethodsIntegrationTests {} diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index ba142dc53..24d07fd51 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository.query; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; import java.lang.reflect.Method; import java.util.List; @@ -43,12 +44,12 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.ReflectionUtils; /** * Integration test for {@link AbstractJpaQuery}. * * @author Oliver Gierke + * @author Thomas Darimont */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -133,7 +134,7 @@ public class AbstractJpaQueryTests { @Transactional public void shouldAddEntityGraphHintForFetch() throws Exception { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager()); + Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); Method findAllMethod = SampleRepository.class.getMethod("findAll"); QueryExtractor provider = PersistenceProvider.fromEntityManager(em); @@ -155,7 +156,7 @@ public class AbstractJpaQueryTests { @Transactional public void shouldAddEntityGraphHintForLoad() throws Exception { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager()); + Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); Method getByIdMethod = SampleRepository.class.getMethod("getById", Integer.class); QueryExtractor provider = PersistenceProvider.fromEntityManager(em); @@ -170,11 +171,6 @@ public class AbstractJpaQueryTests { verify(result).setHint("javax.persistence.loadgraph", entityGraph); } - private boolean currentEntityManagerIsAJpa21EntityManager() { - return ReflectionUtils.findMethod(((org.springframework.orm.jpa.EntityManagerProxy) em).getTargetEntityManager() - .getClass(), "getEntityGraph", String.class) != null; - } - interface SampleRepository extends Repository { @QueryHints({ @QueryHint(name = "foo", value = "bar") }) diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 00026864b..6e0db24d6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -38,6 +38,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -52,6 +53,7 @@ import org.springframework.data.repository.query.QueryMethod; * Unit test for {@link QueryMethod}. * * @author Oliver Gierke + * @author Thomas Darimont */ @RunWith(MockitoJUnitRunner.class) public class JpaQueryMethodUnitTests { @@ -329,6 +331,19 @@ public class JpaQueryMethodUnitTests { assertThat(method.getEntityGraph().getType(), is(EntityGraphType.LOAD)); } + /** + * @see DATAJPA-612 + */ + @Test + public void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethod() throws Exception { + + JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findAll"), metadata, extractor); + + assertThat(method.getEntityGraph(), is(notNullValue())); + assertThat(method.getEntityGraph().getName(), is("User.detail")); + assertThat(method.getEntityGraph().getType(), is(EntityGraphType.FETCH)); + } + /** * Interface to define invalid repository methods for testing. * @@ -391,6 +406,16 @@ public class JpaQueryMethodUnitTests { User queryMethodWithCustomEntityFetchGraph(Integer id); } + static interface JpaRepositoryOverride extends JpaRepository { + + /** + * DATAJPA-612 + */ + @Override + @EntityGraph("User.detail") + public List findAll(); + } + @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT) @QueryHints(@QueryHint(name = "foo", value = "bar")) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigJpaRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigJpaRepository.java new file mode 100644 index 000000000..206cdf65a --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigJpaRepository.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014 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; + +import java.util.List; + +import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Custom repository interface that customizes the fetching behavior of querys of well known repository interface methods via {@link EntityGraph} + * annotation. + * + * @author Thomas Darimont + */ +public interface RepositoryMethodsWithEntityGraphConfigJpaRepository extends JpaRepository { + + /** + * Should find all users. + */ + @EntityGraph(type = EntityGraphType.LOAD, value = "User.overview") + List findAll(); +}