Skip to content

Commit

Permalink
DATAJPA-689 - Allow @entitygraph on CrudRepository.findOne(…).
Browse files Browse the repository at this point in the history
We now honor @entitygraph definitions on CrudRepository.findOne(…) which was previously only the case for methods that created a Query explicitly. 

Extracted tryGetFetchGraphHints(…) method from tryConfigureFetchGraph(…) method in Jpa21Utils to allow EntityGraph hints to be used in SimpleJpaRepository.findOne(…). Construction of query hints from context information in SimpleJpaRepository is now performed via the getQueryHints(…) method. Adjusted QueryDslJpaRepository to use query hints as well.

Added unit and integration tests to verify that @entitygraph information is propagated to findOne executions.

Original pull request: spring-projects#137.
  • Loading branch information
Thomas Darimont authored and odrotbohm committed Mar 19, 2015
1 parent febd41b commit d07aeae
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2008-2014 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.
Expand All @@ -15,6 +15,8 @@
*/
package org.springframework.data.jpa.repository.query;

import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.Query;
Expand Down Expand Up @@ -180,10 +182,10 @@ private Query applyEntityGraphConfiguration(Query query, JpaQueryMethod method)
Assert.notNull(query, "Query must not be null!");
Assert.notNull(method, "JpaQueryMethod must not be null!");

JpaEntityGraph entityGraph = method.getEntityGraph();
Map<String, Object> hints = Jpa21Utils.tryGetFetchGraphHints(em, method.getEntityGraph());

if (entityGraph != null) {
Jpa21Utils.tryConfigureFetchGraph(em, query, entityGraph);
for (Map.Entry<String, Object> hint : hints.entrySet()) {
query.setHint(hint.getKey(), hint.getValue());
}

return query;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-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.
Expand All @@ -16,6 +16,8 @@
package org.springframework.data.jpa.repository.query;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
Expand Down Expand Up @@ -52,27 +54,27 @@ private Jpa21Utils() {
}

/**
* Adds a JPA 2.1 fetch-graph or load-graph hint to the given {@link Query} if running under JPA 2.1.
* Returns a {@link Map} with hints for a JPA 2.1 fetch-graph or load-graph 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 can be {@literal null}.
* @param em must not be {@literal null}
* @param query must not be {@literal null}
* @param entityGraph can be {@literal null}
* @return a {@code Map} with the hints or an empty {@code Map} if no hints were found
* @since 1.8
*/
public static <T extends Query> T tryConfigureFetchGraph(EntityManager em, T query, JpaEntityGraph entityGraph) {
public static Map<String, Object> tryGetFetchGraphHints(EntityManager em, JpaEntityGraph entityGraph) {

if (entityGraph == null) {
return query;
return Collections.emptyMap();
}

EntityGraph<?> graph = tryGetFetchGraph(em, entityGraph);

if (graph == null) {
return query;
return Collections.emptyMap();
}

query.setHint(entityGraph.getType().getKey(), graph);
return query;
return Collections.<String, Object> singletonMap(entityGraph.getType().getKey(), graph);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,13 @@
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.domain.Sort;
import org.springframework.data.jpa.repository.query.Jpa21Utils;
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.QSort;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
Expand Down Expand Up @@ -57,7 +54,6 @@ 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
Expand All @@ -82,7 +78,6 @@ public QueryDslJpaRepository(JpaEntityInformation<T, ID> entityInformation, Enti
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);
Expand Down Expand Up @@ -185,24 +180,10 @@ protected JPQLQuery createQuery(Predicate... predicate) {
LockModeType type = metadata.getLockModeType();
query = type == null ? query : query.setLockMode(type);

for (Entry<String, Object> hint : metadata.getQueryHints().entrySet()) {
for (Entry<String, Object> hint : getQueryHints().entrySet()) {
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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2008-2014 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.
Expand All @@ -20,6 +20,7 @@
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -28,6 +29,7 @@
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
import javax.persistence.Parameter;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
Expand All @@ -43,6 +45,7 @@
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.query.Jpa21Utils;
Expand Down Expand Up @@ -225,11 +228,31 @@ public T findOne(ID id) {
}

LockModeType type = metadata.getLockModeType();
Map<String, Object> hints = metadata.getQueryHints();

Map<String, Object> hints = getQueryHints();

return type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints);
}

/**
* Returns a {@link Map} with the query hints based on the current {@link CrudMethodMetadata} and potential
* {@link EntityGraph} information.
*
* @return
*/
protected Map<String, Object> getQueryHints() {

if (metadata.getEntityGraph() == null) {
return metadata.getQueryHints();
}

Map<String, Object> hints = new HashMap<String, Object>();
hints.putAll(metadata.getQueryHints());
hints.putAll(Jpa21Utils.tryGetFetchGraphHints(em, metadata.getEntityGraph()));

return hints;
}

/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.JpaRepository#getOne(java.io.Serializable)
Expand Down Expand Up @@ -567,11 +590,16 @@ private TypedQuery<T> applyRepositoryMethodMetadata(TypedQuery<T> query) {
LockModeType type = metadata.getLockModeType();
TypedQuery<T> toReturn = type == null ? query : query.setLockMode(type);

for (Entry<String, Object> hint : metadata.getQueryHints().entrySet()) {
applyQueryHints(toReturn);

return toReturn;
}

private void applyQueryHints(Query query) {

for (Entry<String, Object> hint : getQueryHints().entrySet()) {
query.setHint(hint.getKey(), hint.getValue());
}

return Jpa21Utils.tryConfigureFetchGraph(em, toReturn, metadata.getEntityGraph());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@ public class EntityGraphRepositoryMethodsIntegrationTests {
@Autowired RepositoryMethodsWithEntityGraphConfigJpaRepository repository;

User tom;
User olli;
Role role;

@Before
public void setup() {

tom = new User("Thomas", "Darimont", "[email protected]");
olli = new User("Oliver", "Gierke", "[email protected]");

role = new Role("Developer");
em.persist(role);
tom.getRoles().add(role);
Expand All @@ -75,4 +78,25 @@ public void shouldRespectConfiguredJpaEntityGraph() {
assertThat(Persistence.getPersistenceUtil().isLoaded(result.get(0).getRoles()), is(true));
assertThat(result.get(0), is(tom));
}

/**
* @see DATAJPA-689
*/
@Test
public void shouldRespectConfiguredJpaEntityGraphInFindOne() {

Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));

olli = repository.save(olli);
tom.getColleagues().add(olli);
tom = repository.save(tom);

em.flush();

User user = repository.findOne(tom.getId());

assertThat(user, is(notNullValue()));
assertThat("colleages should be fetched with 'user.detail' fetchgraph",
Persistence.getPersistenceUtil().isLoaded(user.getColleagues()), is(true));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,19 @@ public void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethod
assertThat(method.getEntityGraph().getType(), is(EntityGraphType.FETCH));
}

/**
* @see DATAJPA-689
*/
@Test
public void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethodFindOne() throws Exception {

JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne"), 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.
*
Expand Down Expand Up @@ -414,7 +427,13 @@ static interface JpaRepositoryOverride extends JpaRepository<User, Long> {
*/
@Override
@EntityGraph("User.detail")
public List<User> findAll();
List<User> findAll();

/**
* DATAJPA-689
*/
@EntityGraph("User.detail")
User findOne();
}

@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-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.
Expand All @@ -23,16 +23,22 @@
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.
* 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> {
public interface RepositoryMethodsWithEntityGraphConfigJpaRepository extends JpaRepository<User, Integer> {

/**
* Should find all users.
*/
@EntityGraph(type = EntityGraphType.LOAD, value = "User.overview")
List<User> findAll();

/**
* Should fetch all user details
*/
@EntityGraph(type = EntityGraphType.FETCH, value = "User.detail")
User findOne(Integer id);
}
Loading

0 comments on commit d07aeae

Please sign in to comment.