diff --git a/pom.xml b/pom.xml
index 3e76b546e..d674e070a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -326,7 +326,7 @@
**/EclipseLink*Tests.java
- -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar
+ -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar
diff --git a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java
index eb5020ce2..a2278e11d 100644
--- a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java
+++ b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java
@@ -15,8 +15,17 @@
*/
package org.springframework.data.jpa.provider;
-import static org.springframework.data.jpa.provider.JpaClassUtils.*;
-import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*;
+import static org.springframework.data.jpa.provider.JpaClassUtils.isEntityManagerOfType;
+import static org.springframework.data.jpa.provider.JpaClassUtils.isMetamodelOfType;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.ECLIPSELINK_ENTITY_MANAGER_INTERFACE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.ECLIPSELINK_JPA_METAMODEL_TYPE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.GENERIC_JPA_ENTITY_MANAGER_INTERFACE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.HIBERNATE43_ENTITY_MANAGER_INTERFACE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.HIBERNATE43_JPA_METAMODEL_TYPE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.HIBERNATE_ENTITY_MANAGER_INTERFACE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.HIBERNATE_JPA_METAMODEL_TYPE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.OPENJPA_ENTITY_MANAGER_INTERFACE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.OPENJPA_JPA_METAMODEL_TYPE;
import java.util.Arrays;
import java.util.Collection;
@@ -24,8 +33,10 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.Query;
+import javax.persistence.Subgraph;
import javax.persistence.metamodel.Metamodel;
import org.apache.openjpa.enhance.PersistenceCapable;
@@ -42,10 +53,12 @@ import org.hibernate.ScrollableResults;
import org.hibernate.ejb.HibernateQuery;
import org.hibernate.proxy.HibernateProxy;
import org.springframework.beans.DirectFieldAccessor;
+import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.data.util.CloseableIterator;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
/**
* Enumeration representing persistence providers to be used.
@@ -354,6 +367,69 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor {
+ name());
}
+ /**
+ * Creates a dynamic {@link EntityGraph} from the given {@link JpaEntityGraph} information.
+ *
+ * @param em
+ * @param jpaEntityGraph
+ * @param entityType
+ * @return
+ *
+ * @since 1.9
+ */
+ public EntityGraph> createDynamicEntityGraph(EntityManager em, JpaEntityGraph jpaEntityGraph, Class> entityType) {
+
+ Assert.isTrue(jpaEntityGraph.isDynamicEntityGraph(), "The given " + jpaEntityGraph + " is not dynamic!");
+
+ EntityGraph> entityGraph = em.createEntityGraph(entityType);
+
+ configureFetchGraphFrom(jpaEntityGraph, entityGraph);
+
+ return entityGraph;
+ }
+
+
+ /**
+ * Configures the given {@link EntityGraph} with the fetch graph information stored in {@link JpaEntityGraph}.
+ *
+ * @param jpaEntityGraph
+ * @param entityGraph
+ */
+ /* visible for testing */
+ void configureFetchGraphFrom(JpaEntityGraph jpaEntityGraph, EntityGraph> entityGraph) {
+
+ String[] attributePaths = jpaEntityGraph.getAttributePaths().clone();
+
+ // sort to ensure that the intermediate entity subgraphs are created accordingly.
+ Arrays.sort(attributePaths);
+
+ // we build the entity graph based on the paths with highest depth first
+ for (int i = attributePaths.length - 1; i >= 0; i--) {
+
+ String path = attributePaths[i];
+
+ //fast path just single attribute
+ if (!path.contains(".")) {
+ entityGraph.addAttributeNodes(path);
+ continue;
+ }
+
+ //we need to build nested sub fetch graphs
+ String[] pathComponents = StringUtils.delimitedListToStringArray(path, ".");
+
+ Subgraph> parent = null;
+ for (int c = 0; c < pathComponents.length - 1; c++) {
+
+ if (c == 0) {
+ parent = entityGraph.addSubgraph(pathComponents[c]);
+ } else {
+ parent = parent.addSubgraph(pathComponents[c]);
+ }
+ }
+ parent.addAttributeNodes(pathComponents[pathComponents.length - 1]);
+ }
+ }
+
/**
* {@link CloseableIterator} for Hibernate.
*
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 d8dddd254..7a2bc5452 100644
--- a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java
+++ b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java
@@ -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.
@@ -21,9 +21,19 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import javax.persistence.NamedAttributeNode;
+
+import org.springframework.data.jpa.repository.query.JpaQueryMethod;
+
/**
* Annotation to configure the JPA 2.1 {@link javax.persistence.EntityGraph}s that should be used on repository methods.
*
+ * Since 1.9 we support the definition of dynamic {@link EntityGraph}s by allowing to customize the fetch-graph via
+ * via {@link #attributePaths()} ad-hoc fetch-graph configuration.
+ *
+ * If {@link #attributePaths()} are specified then we ignore the entity-graph name {@link #value()}
+ * and treat this {@link EntityGraph} as dynamic.
+ *
* @author Thomas Darimont
* @since 1.6
*/
@@ -34,10 +44,11 @@ public @interface EntityGraph {
/**
* The name of the EntityGraph to use.
+ * If empty we fall-back to {@link JpaQueryMethod#getNamedQueryName()} as the value.
*
* @return
*/
- String value();
+ String value() default "";
/**
* The {@link Type} of the EntityGraph to use, defaults to {@link Type#FETCH}.
@@ -45,6 +56,16 @@ public @interface EntityGraph {
* @return
*/
EntityGraphType type() default EntityGraphType.FETCH;
+
+ /**
+ * The paths of attributes of this {@link EntityGraph} to use, empty by default.
+ *
+ * You can refer to direct properties of the entity or nested properties via a {@code property.nestedProperty}.
+ *
+ * @return
+ * @since 1.9
+ */
+ String[] attributePaths() default {};
/**
* Enum for JPA 2.1 {@link javax.persistence.EntityGraph} types.
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 41269876e..d87ee193b 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
@@ -182,7 +182,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
Assert.notNull(query, "Query must not be null!");
Assert.notNull(method, "JpaQueryMethod must not be null!");
- Map hints = Jpa21Utils.tryGetFetchGraphHints(em, method.getEntityGraph());
+ Map hints = Jpa21Utils.tryGetFetchGraphHints(em, method.getEntityGraph(), getQueryMethod().getEntityInformation().getJavaType());
for (Map.Entry hint : hints.entrySet()) {
query.setHint(hint.getKey(), hint.getValue());
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java
index 63793443a..87aa84fbf 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java
@@ -23,6 +23,7 @@ import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.Query;
+import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -59,16 +60,18 @@ public class Jpa21Utils {
* @param em must not be {@literal null}
* @param query must not be {@literal null}
* @param entityGraph can be {@literal null}
+ * @param entityType must not 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 Map tryGetFetchGraphHints(EntityManager em, JpaEntityGraph entityGraph) {
+ public static Map tryGetFetchGraphHints(EntityManager em, JpaEntityGraph entityGraph,
+ Class> entityType) {
if (entityGraph == null) {
return Collections.emptyMap();
}
- EntityGraph> graph = tryGetFetchGraph(em, entityGraph);
+ EntityGraph> graph = tryGetFetchGraph(em, entityGraph, entityType);
if (graph == null) {
return Collections.emptyMap();
@@ -83,17 +86,25 @@ public class Jpa21Utils {
* @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}.
+ * @param entityType must not be {@literal null}.
* @return the {@link EntityGraph} described by the given {@code entityGraph}.
*/
- public static EntityGraph> tryGetFetchGraph(EntityManager em, JpaEntityGraph jpaEntityGraph) {
+ private static EntityGraph> tryGetFetchGraph(EntityManager em, JpaEntityGraph jpaEntityGraph, Class> entityType) {
Assert.notNull(em, "EntityManager must not be null!");
Assert.notNull(jpaEntityGraph, "EntityGraph must not be null!");
+ Assert.notNull(entityType, "EntityType 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!");
- return em.getEntityGraph(jpaEntityGraph.getName());
+ try {
+ // first check whether an entityGraph with that name is already registered.
+ return em.getEntityGraph(jpaEntityGraph.getName());
+ } catch (Exception ex) {
+ // try to create and dynamically register the entityGraph
+ return PersistenceProvider.fromEntityManager(em).createDynamicEntityGraph(em, jpaEntityGraph, entityType);
+ }
}
}
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 47c56f6ac..877ceb42a 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
@@ -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.
@@ -15,10 +15,12 @@
*/
package org.springframework.data.jpa.repository.query;
-import javax.persistence.EntityGraph;
+import java.util.Arrays;
+import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
/**
* EntityGraph configuration for JPA 2.1 {@link EntityGraph}s.
@@ -28,22 +30,38 @@ import org.springframework.util.Assert;
*/
public class JpaEntityGraph {
+ private static String[] EMPTY_ATTRIBUTE_PATHS = {};
+
private final String name;
private final EntityGraphType type;
+ private final String[] attributePaths;
/**
* Creates an {@link JpaEntityGraph}.
*
- * @param name must not be {@null} or empty.
- * @param type must not be {@null}.
+ * @param entityGraph must not be {@literal null}.
+ * @param nameFallback must not be {@literal null} or empty.
*/
- public JpaEntityGraph(String name, EntityGraphType type) {
+ public JpaEntityGraph(EntityGraph entityGraph, String nameFallback) {
+ this(StringUtils.hasText(entityGraph.value()) ? entityGraph.value() : nameFallback, entityGraph.type(), entityGraph
+ .attributePaths());
+ }
+
+ /**
+ * Creates an {@link JpaEntityGraph}.
+ *
+ * @param name must not be {@literal null} or empty.
+ * @param type must not be {@literal null}.
+ * @param attributePaths may be {@literal null}.
+ */
+ public JpaEntityGraph(String name, EntityGraphType type, String[] attributePaths) {
Assert.hasText(name, "The name of an EntityGraph must not be null or empty!");
Assert.notNull(type, "FetchGraphType must not be null!");
this.name = name;
this.type = type;
+ this.attributePaths = attributePaths == null ? EMPTY_ATTRIBUTE_PATHS : attributePaths;
}
/**
@@ -64,12 +82,32 @@ public class JpaEntityGraph {
return type;
}
+ /**
+ * Returns the attribute node names to be used for this {@link JpaEntityGraph}.
+ *
+ * @return
+ * @since 1.9
+ */
+ public String[] getAttributePaths() {
+ return attributePaths;
+ }
+
+ /**
+ * Return {@literal true} if this {@link JpaEntityGraph} needs to be generated on-the-fly.
+ *
+ * @return
+ */
+ public boolean isDynamicEntityGraph() {
+ return this.attributePaths.length > 0;
+ }
+
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
- return "JpaEntityGraph [name=" + name + ", type=" + type + "]";
+ return "JpaEntityGraph [name=" + name + ", type=" + type + ", attributePaths=" + Arrays.toString(attributePaths)
+ + "]";
}
}
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java
index 084c5e30b..5416a5850 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java
@@ -15,7 +15,8 @@
*/
package org.springframework.data.jpa.repository.query;
-import static org.springframework.core.annotation.AnnotationUtils.*;
+import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
+import static org.springframework.core.annotation.AnnotationUtils.getAnnotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -173,7 +174,7 @@ public class JpaQueryMethod extends QueryMethod {
JpaEntityGraph getEntityGraph() {
EntityGraph annotation = findAnnotation(method, EntityGraph.class);
- return annotation == null ? null : new JpaEntityGraph(annotation.value(), annotation.type());
+ return annotation == null ? null : new JpaEntityGraph(annotation, getNamedQueryName());
}
/**
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 af4679414..93b915f9f 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
@@ -15,10 +15,12 @@
*/
package org.springframework.data.jpa.repository.support;
+import java.lang.reflect.Method;
import java.util.Map;
import javax.persistence.LockModeType;
+import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
/**
@@ -45,10 +47,18 @@ public interface CrudMethodMetadata {
Map getQueryHints();
/**
- * Returns the {@link JpaEntityGraph} to be used.
+ * Returns the {@link EntityGraph} to be used.
*
* @return
- * @since 1.8
+ * @since 1.9
*/
- JpaEntityGraph getEntityGraph();
+ EntityGraph getEntityGraph();
+
+ /**
+ * Returns the {@link Method} to be used.
+ *
+ * @return
+ * @since 1.9
+ */
+ Method getMethod();
}
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 5c524c48d..bf4394a5c 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
@@ -37,6 +37,7 @@ 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.data.util.ReflectionUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
@@ -60,7 +61,7 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor {
public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) {
factory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
- factory.addAdvice(CrudMethodMetadataPopulatingMethodIntercceptor.INSTANCE);
+ factory.addAdvice(CrudMethodMetadataPopulatingMethodInterceptor.INSTANCE);
}
/**
@@ -85,7 +86,7 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor {
* @author Oliver Gierke
* @author Thomas Darimont
*/
- static enum CrudMethodMetadataPopulatingMethodIntercceptor implements MethodInterceptor {
+ static enum CrudMethodMetadataPopulatingMethodInterceptor implements MethodInterceptor {
INSTANCE;
@@ -136,7 +137,8 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor {
private final LockModeType lockModeType;
private final Map queryHints;
- private final JpaEntityGraph entityGraph;
+ private final EntityGraph entityGraph;
+ private final Method method;
/**
* Creates a new {@link DefaultCrudMethodMetadata} for the given {@link Method}.
@@ -150,13 +152,11 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor {
this.lockModeType = findLockModeType(method);
this.queryHints = findQueryHints(method);
this.entityGraph = findEntityGraph(method);
+ this.method = method;
}
- private static JpaEntityGraph findEntityGraph(Method method) {
-
- EntityGraph entityGraphAnnotation = AnnotationUtils.findAnnotation(method, EntityGraph.class);
- return entityGraphAnnotation == null ? null : new JpaEntityGraph(entityGraphAnnotation.value(),
- entityGraphAnnotation.type());
+ private static EntityGraph findEntityGraph(Method method) {
+ return AnnotationUtils.findAnnotation(method, EntityGraph.class);
}
private static LockModeType findLockModeType(Method method) {
@@ -203,14 +203,21 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor {
public Map getQueryHints() {
return queryHints;
}
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getEntityGraphHint()
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getEntityGraph()
*/
@Override
- public JpaEntityGraph getEntityGraph() {
- return this.entityGraph;
+ public EntityGraph getEntityGraph() {
+ return entityGraph;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getMethod()
+ */
+ @Override
+ public Method getMethod() {
+ return method;
}
}
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 8f6797395..6111a0157 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
@@ -15,7 +15,11 @@
*/
package org.springframework.data.jpa.repository.support;
-import static org.springframework.data.jpa.repository.query.QueryUtils.*;
+import static org.springframework.data.jpa.repository.query.QueryUtils.COUNT_QUERY_STRING;
+import static org.springframework.data.jpa.repository.query.QueryUtils.DELETE_ALL_QUERY_STRING;
+import static org.springframework.data.jpa.repository.query.QueryUtils.applyAndBind;
+import static org.springframework.data.jpa.repository.query.QueryUtils.getQueryString;
+import static org.springframework.data.jpa.repository.query.QueryUtils.toOrders;
import java.io.Serializable;
import java.util.ArrayList;
@@ -49,6 +53,7 @@ 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;
+import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.data.jpa.repository.query.QueryUtils;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@@ -248,11 +253,18 @@ public class SimpleJpaRepository implements JpaRepos
Map hints = new HashMap();
hints.putAll(metadata.getQueryHints());
- hints.putAll(Jpa21Utils.tryGetFetchGraphHints(em, metadata.getEntityGraph()));
+
+ hints.putAll(Jpa21Utils.tryGetFetchGraphHints(em, getEntityGraph(), getDomainClass()));
return hints;
}
+ private JpaEntityGraph getEntityGraph() {
+
+ String fallbackName = this.entityInformation.getEntityName() + "." + metadata.getMethod().getName();
+ return new JpaEntityGraph(metadata.getEntityGraph(), fallbackName);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.JpaRepository#getOne(java.io.Serializable)
diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/src/test/java/org/springframework/data/jpa/domain/sample/User.java
index 3ca89ed80..962972c46 100644
--- a/src/test/java/org/springframework/data/jpa/domain/sample/User.java
+++ b/src/test/java/org/springframework/data/jpa/domain/sample/User.java
@@ -52,6 +52,8 @@ import javax.persistence.TemporalType;
@NamedEntityGraphs({
@NamedEntityGraph(name = "User.overview", attributeNodes = { @NamedAttributeNode("roles") }),
@NamedEntityGraph(name = "User.detail", attributeNodes = { @NamedAttributeNode("roles"),
+ @NamedAttributeNode("manager"), @NamedAttributeNode("colleagues") }),
+ @NamedEntityGraph(name = "User.getOneWithDefinedEntityGraphById", attributeNodes = { @NamedAttributeNode("roles"),
@NamedAttributeNode("manager"), @NamedAttributeNode("colleagues") }) })
@NamedQuery(name = "User.findByEmailAddress", query = "SELECT u FROM User u WHERE u.emailAddress = ?1")
@NamedStoredProcedureQueries({ //
diff --git a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java
index 41f809bd6..24039fca1 100644
--- a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java
+++ b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java
@@ -15,22 +15,37 @@
*/
package org.springframework.data.jpa.provider;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-import static org.springframework.data.jpa.provider.PersistenceProvider.*;
-import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.springframework.data.jpa.provider.PersistenceProvider.ECLIPSELINK;
+import static org.springframework.data.jpa.provider.PersistenceProvider.GENERIC_JPA;
+import static org.springframework.data.jpa.provider.PersistenceProvider.HIBERNATE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.OPEN_JPA;
+import static org.springframework.data.jpa.provider.PersistenceProvider.fromEntityManager;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.ECLIPSELINK_ENTITY_MANAGER_INTERFACE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.HIBERNATE43_ENTITY_MANAGER_INTERFACE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.HIBERNATE_ENTITY_MANAGER_INTERFACE;
+import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.OPENJPA_ENTITY_MANAGER_INTERFACE;
import java.util.ArrayList;
import java.util.List;
+import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
+import javax.persistence.Subgraph;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.asm.ClassWriter;
import org.springframework.asm.Opcodes;
-import org.springframework.data.jpa.provider.PersistenceProvider;
+import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
+import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.instrument.classloading.ShadowingClassLoader;
import org.springframework.util.ClassUtils;
@@ -103,6 +118,26 @@ public class PersistenceProviderUnitTests {
assertThat(fromEntityManager(em), is(GENERIC_JPA));
}
+ /**
+ * @see DATAJPA-696
+ */
+ @Test
+ public void shouldBuildCorrectSubgraphForJpaEntityGraph() throws Exception {
+
+ EntityGraph> entityGraph = mock(EntityGraph.class);
+ Subgraph> subgraph = mock(Subgraph.class);
+ doReturn(subgraph).when(entityGraph).addSubgraph(anyString());
+
+ JpaEntityGraph jpaEntityGraph = new JpaEntityGraph("foo", EntityGraphType.FETCH,
+ new String[] { "foo", "gugu.gaga" });
+
+ PersistenceProvider.GENERIC_JPA.configureFetchGraphFrom(jpaEntityGraph, entityGraph);
+
+ verify(entityGraph, times(1)).addAttributeNodes("foo");
+ verify(entityGraph, times(1)).addSubgraph("gugu");
+ verify(subgraph, times(1)).addAttributeNodes("gaga");
+ }
+
private EntityManager mockProviderSpecificEntityManagerInterface(String interfaceName) throws ClassNotFoundException {
Class> providerSpecificEntityManagerInterface = InterfaceGenerator.generate(interfaceName, shadowingClassLoader,
diff --git a/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java
index 9dc459f30..bc2ba63d3 100644
--- a/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java
@@ -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.
@@ -60,6 +60,10 @@ public class EntityGraphRepositoryMethodsIntegrationTests {
role = new Role("Developer");
em.persist(role);
tom.getRoles().add(role);
+ tom = repository.save(tom);
+
+ olli = repository.save(olli);
+ tom.getColleagues().add(olli);
}
/**
@@ -70,11 +74,9 @@ public class EntityGraphRepositoryMethodsIntegrationTests {
Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
- tom = repository.save(tom);
-
List result = repository.findAll();
- assertThat(result.size(), is(1));
+ assertThat(result.size(), is(2));
assertThat(Persistence.getPersistenceUtil().isLoaded(result.get(0).getRoles()), is(true));
assertThat(result.get(0), is(tom));
}
@@ -87,16 +89,40 @@ public class EntityGraphRepositoryMethodsIntegrationTests {
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));
}
+
+ /**
+ * @see DATAJPA-696
+ */
+ @Test
+ public void shouldRespectInferFetchGraphFromMethodName() {
+
+ Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
+
+ User user = repository.getOneWithDefinedEntityGraphById(tom.getId());
+
+ assertThat(user, is(notNullValue()));
+ assertThat("colleages should be fetched with 'user.detail' fetchgraph",
+ Persistence.getPersistenceUtil().isLoaded(user.getColleagues()), is(true));
+ }
+
+ /**
+ * @see DATAJPA-696
+ */
+ @Test
+ public void shouldRespectDynamicFetchGraphForGetOneWithAttributeNamesById() {
+
+ Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em));
+
+ User user = repository.getOneWithAttributeNamesById(tom.getId());
+
+ assertThat(user, is(notNullValue()));
+ assertThat("colleages should be fetched with 'user.detail' fetchgraph",
+ Persistence.getPersistenceUtil().isLoaded(user.getColleagues()), is(true));
+ }
}
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 7bd7ebfe3..29672d482 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
@@ -324,6 +324,9 @@ 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);
@@ -338,6 +341,9 @@ public class JpaQueryMethodUnitTests {
@Test
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);
assertThat(method.getEntityGraph(), is(notNullValue()));
@@ -351,12 +357,31 @@ public class JpaQueryMethodUnitTests {
@Test
public void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethodFindOne() throws Exception {
- JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne"), metadata, extractor);
+ 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);
assertThat(method.getEntityGraph(), is(notNullValue()));
assertThat(method.getEntityGraph().getName(), is("User.detail"));
assertThat(method.getEntityGraph().getType(), is(EntityGraphType.FETCH));
}
+
+ /**
+ * DATAJPA-696
+ */
+ @Test
+ 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);
+
+ assertThat(method.getEntityGraph(), is(notNullValue()));
+ assertThat(method.getEntityGraph().getName(), is("User.getOneById"));
+ assertThat(method.getEntityGraph().getType(), is(EntityGraphType.FETCH));
+ }
/**
* Interface to define invalid repository methods for testing.
@@ -433,7 +458,13 @@ public class JpaQueryMethodUnitTests {
* DATAJPA-689
*/
@EntityGraph("User.detail")
- User findOne();
+ User findOne(Long id);
+
+ /**
+ * DATAJPA-696
+ */
+ @EntityGraph
+ User getOneById(Long id);
}
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
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
index a10c65a8e..6b24249e9 100644
--- a/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigJpaRepository.java
+++ b/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigJpaRepository.java
@@ -41,4 +41,16 @@ public interface RepositoryMethodsWithEntityGraphConfigJpaRepository extends Jpa
*/
@EntityGraph(type = EntityGraphType.FETCH, value = "User.detail")
User findOne(Integer id);
+
+ /**
+ * @see DATAJPA-696
+ */
+ @EntityGraph
+ User getOneWithDefinedEntityGraphById(Integer id);
+
+ /**
+ * @see DATAJPA-696
+ */
+ @EntityGraph(attributePaths = { "roles", "colleagues.roles" })
+ User getOneWithAttributeNamesById(Integer id);
}
diff --git a/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java
index 452b6a2c3..fbcc8d605 100644
--- a/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java
@@ -29,11 +29,11 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.jpa.repository.Lock;
-import org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor.CrudMethodMetadataPopulatingMethodIntercceptor;
+import org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor.CrudMethodMetadataPopulatingMethodInterceptor;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
- * Unit tests for {@link CrudMethodMetadataPopulatingMethodIntercceptor}.
+ * Unit tests for {@link CrudMethodMetadataPopulatingMethodInterceptor}.
*
* @author Oliver Gierke
*/
@@ -51,7 +51,7 @@ public class CrudMethodMetadataPopulatingMethodInterceptorUnitTests {
Method method = Sample.class.getMethod("someMethod");
when(invocation.getMethod()).thenReturn(method);
- CrudMethodMetadataPopulatingMethodIntercceptor interceptor = CrudMethodMetadataPopulatingMethodIntercceptor.INSTANCE;
+ CrudMethodMetadataPopulatingMethodInterceptor interceptor = CrudMethodMetadataPopulatingMethodInterceptor.INSTANCE;
interceptor.invoke(invocation);
assertThat(TransactionSynchronizationManager.getResource(method), is(nullValue()));
diff --git a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java
index 070f5a1c1..2536fb363 100644
--- a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java
@@ -15,8 +15,12 @@
*/
package org.springframework.data.jpa.repository.support;
-import static java.util.Collections.*;
-import static org.mockito.Mockito.*;
+import static java.util.Collections.singletonMap;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.Serializable;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
@@ -33,7 +37,7 @@ import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
-import org.springframework.data.jpa.repository.query.JpaEntityGraph;
+import org.springframework.data.repository.CrudRepository;
/**
* Unit tests for {@link SimpleJpaRepository}.
@@ -55,6 +59,7 @@ public class SimpleJpaRepositoryUnitTests {
@Mock JpaEntityInformation information;
@Mock CrudMethodMetadata metadata;
@Mock EntityGraph entityGraph;
+ @Mock org.springframework.data.jpa.repository.EntityGraph entityGraphAnnotation;
@Before
public void setUp() {
@@ -97,15 +102,20 @@ public class SimpleJpaRepositoryUnitTests {
/**
* @see DATAJPA-689
+ * @see DATAJPA-696
*/
@Test
@SuppressWarnings({ "rawtypes", "unchecked" })
- public void shouldPropagateConfiguredEntityGraphToFindOne() {
+ public void shouldPropagateConfiguredEntityGraphToFindOne() throws Exception{
String entityGraphName = "User.detail";
- when(metadata.getEntityGraph()).thenReturn(new JpaEntityGraph(entityGraphName, EntityGraphType.LOAD));
+ when(entityGraphAnnotation.value()).thenReturn(entityGraphName);
+ when(entityGraphAnnotation.type()).thenReturn(EntityGraphType.LOAD);
+ when(metadata.getEntityGraph()).thenReturn(entityGraphAnnotation);
when(em.getEntityGraph(entityGraphName)).thenReturn((EntityGraph) entityGraph);
-
+ when(information.getEntityName()).thenReturn("User");
+ when(metadata.getMethod()).thenReturn(CrudRepository.class.getMethod("findOne", Serializable.class));
+
Integer id = 0;
repo.findOne(id);