DATAJPA-612 - Allow @EntityGraph on a method inherited from JpaRepository.

We now respect the @EntityGraph annotations on redeclared CRUD methods. CrudMethodMetadata now looks for @EntityGraph annotations, and - if present - the appropriate entity graph configuration is applied within SimpleJpaRepository.applyRepositoryMethodMetadata(…).

Previously we mistakenly treated the @EntityGraph annotation as a Query annotation which triggered a query resolution process that tried to find a named or create a derived query and failed. Removed @QueryAnnotation from @EntityGraph, since it should only be used to mark store specific @Query annotations.

Renamed Jpa21QueryCustomizer to Jpa21Utils. Moved Jpa21Utils to org.springframework.data.jpa.util to avoid potential dependency cycles.

Added EclipseLink and OpenJPA specific subclasses for the EntityGraphRepositoryMethodsIntegrationTests to make sure the tests are executed for EclipseLink and OpenJPA as well.

Original pull request: #109.
This commit is contained in:
Thomas Darimont
2014-10-06 09:51:41 +02:00
committed by Oliver Gierke
parent 6ea82fa1c5
commit bacf6cce26
14 changed files with 301 additions and 46 deletions

View File

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

View File

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

View File

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

View File

@@ -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<String, Object> getQueryHints();
/**
* Returns the {@link JpaEntityGraph} to be used.
*
* @return
* @since 1.8
*/
JpaEntityGraph getEntityGraph();
}

View File

@@ -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<String, Object> 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<String, Object> 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 {

View File

@@ -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<T, ID extends Serializable> extends SimpleJpa
private final EntityPath<T> path;
private final PathBuilder<T> 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<T, ID extends Serializable> extends SimpleJpa
EntityPathResolver resolver) {
super(entityInformation, entityManager);
this.em = entityManager;
this.path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.querydsl = new Querydsl(entityManager, builder);
@@ -154,6 +158,20 @@ public class QueryDslJpaRepository<T, ID extends Serializable> 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;
}
}

View File

@@ -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<T, ID extends Serializable> 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<T, ID extends Serializable> implements JpaRepos
* @param crudMethodMetadata
*/
public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) {
this.crudMethodMetadata = crudMethodMetadata;
this.metadata = crudMethodMetadata;
}
protected CrudMethodMetadata getRepositoryMethodMetadata() {
return crudMethodMetadata;
return metadata;
}
protected Class<T> getDomainClass() {
@@ -216,12 +217,12 @@ public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepos
Class<T> domainType = getDomainClass();
if (crudMethodMetadata == null) {
if (metadata == null) {
return em.find(domainType, id);
}
LockModeType type = crudMethodMetadata.getLockModeType();
Map<String, Object> hints = crudMethodMetadata.getQueryHints();
LockModeType type = metadata.getLockModeType();
Map<String, Object> hints = metadata.getQueryHints();
return type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints);
}
@@ -545,18 +546,18 @@ public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepos
private TypedQuery<T> applyRepositoryMethodMetadata(TypedQuery<T> query) {
if (crudMethodMetadata == null) {
if (metadata == null) {
return query;
}
LockModeType type = crudMethodMetadata.getLockModeType();
LockModeType type = metadata.getLockModeType();
TypedQuery<T> toReturn = type == null ? query : query.setLockMode(type);
for (Entry<String, Object> hint : crudMethodMetadata.getQueryHints().entrySet()) {
for (Entry<String, Object> hint : metadata.getQueryHints().entrySet()) {
query.setHint(hint.getKey(), hint.getValue());
}
return toReturn;
return Jpa21Utils.tryConfigureFetchGraph(em, toReturn, metadata.getEntityGraph());
}
/**

View File

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

View File

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

View File

@@ -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<User> result = repository.findAll();
assertThat(result.size(), is(1));
assertThat(Persistence.getPersistenceUtil().isLoaded(result.get(0).getRoles()), is(true));
assertThat(result.get(0), is(tom));
}
}

View File

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

View File

@@ -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<User, Integer> {
@QueryHints({ @QueryHint(name = "foo", value = "bar") })

View File

@@ -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<User, Long> {
/**
* DATAJPA-612
*/
@Override
@EntityGraph("User.detail")
public List<User> findAll();
}
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
@QueryHints(@QueryHint(name = "foo", value = "bar"))
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -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<User, Long> {
/**
* Should find all users.
*/
@EntityGraph(type = EntityGraphType.LOAD, value = "User.overview")
List<User> findAll();
}