diff --git a/pom.xml b/pom.xml
index 56c829ab7..27cb5f784 100644
--- a/pom.xml
+++ b/pom.xml
@@ -138,12 +138,6 @@
-
- org.hibernate
- hibernate-entitymanager
- ${hibernate}
- true
-
org.eclipse.persistence
@@ -152,6 +146,13 @@
true
+
+ org.hibernate
+ hibernate-entitymanager
+ ${hibernate}
+ true
+
+
org.apache.openjpa
openjpa-persistence-jdbc
diff --git a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java
new file mode 100644
index 000000000..f316c6d1d
--- /dev/null
+++ b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java
@@ -0,0 +1,88 @@
+/*
+ * 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 java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+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.
+ *
+ * @author Thomas Darimont
+ * @since 1.6
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@QueryAnnotation
+@Documented
+public @interface EntityGraph {
+
+ /**
+ * The name of the EntityGraph to use.
+ *
+ * @return
+ */
+ String value();
+
+ /**
+ * The {@link Type} of the EntityGraph to use, defaults to {@link Type#FETCH}.
+ *
+ * @return
+ */
+ EntityGraphType type() default EntityGraphType.FETCH;
+
+ /**
+ * Enum for JPA 2.1 {@link javax.persistence.EntityGraph} types.
+ *
+ * @author Thomas Darimont
+ * @since 1.6
+ */
+ public enum EntityGraphType {
+
+ /**
+ * When the javax.persistence.loadgraph property is used to specify an entity graph, attributes that are specified
+ * by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are
+ * treated according to their specified or default FetchType.
+ *
+ * @see JPA 2.1 Specification: 3.7.4.2 Load Graph Semantics
+ */
+ LOAD("javax.persistence.loadgraph"),
+
+ /**
+ * When the javax.persistence.fetchgraph property is used to specify an entity graph, attributes that are specified
+ * by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are
+ * treated as FetchType.LAZY
+ *
+ * @see JPA 2.1 Specification: 3.7.4.1 Fetch Graph Semantics
+ */
+ FETCH("javax.persistence.fetchgraph");
+
+ private final String key;
+
+ private EntityGraphType(String value) {
+ this.key = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+ }
+}
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 5426f47bc..2c2c63551 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2013 the original author or authors.
+ * Copyright 2008-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.
@@ -21,6 +21,7 @@ import javax.persistence.Query;
import javax.persistence.QueryHint;
import javax.persistence.TypedQuery;
+import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.CollectionExecution;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution;
@@ -121,12 +122,26 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
private T applyHints(T query, JpaQueryMethod method) {
for (QueryHint hint : method.getHints()) {
- query.setHint(hint.name(), hint.value());
+ applyQueryHint(query, hint);
}
return query;
}
+ /**
+ * Protected to be able to customize in sub-classes.
+ *
+ * @param query must not be {@literal null}.
+ * @param hint must not be {@literal null}.
+ */
+ protected void applyQueryHint(T query, QueryHint hint) {
+
+ Assert.notNull(query, "Query must not be null!");
+ Assert.notNull(hint, "QueryHint must not be null!");
+
+ query.setHint(hint.name(), hint.value());
+ }
+
/**
* Applies the {@link LockModeType} provided by the {@link JpaQueryMethod} to the given {@link Query}.
*
@@ -145,7 +160,29 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
}
protected Query createQuery(Object[] values) {
- return applyLockMode(applyHints(doCreateQuery(values), method), method);
+ return applyLockMode(applyEntityGraphConfiguration(applyHints(doCreateQuery(values), method), method), method);
+ }
+
+ /**
+ * Configures the {@link javax.persistence.EntityGraph} to use for the given {@link JpaQueryMethod} if the
+ * {@link EntityGraph} annotation is present.
+ *
+ * @param query must not be {@literal null}.
+ * @param method must not be {@literal null}.
+ * @return
+ */
+ 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();
+
+ if (entityGraph != null) {
+ Jpa21QueryCustomizer.INSTANCE.tryConfigureFetchGraph(em, query, entityGraph);
+ }
+
+ return query;
}
protected TypedQuery createCountQuery(Object[] values) {
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21QueryCustomizer.java b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21QueryCustomizer.java
new file mode 100644
index 000000000..d33de0edb
--- /dev/null
+++ b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21QueryCustomizer.java
@@ -0,0 +1,77 @@
+/*
+ * 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.query;
+
+import java.lang.reflect.Method;
+
+import javax.persistence.EntityGraph;
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+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.
+ *
+ * @author Thomas Darimont
+ * @since 1.6
+ */
+enum Jpa21QueryCustomizer {
+
+ INSTANCE;
+
+ private static final Method GET_ENTITY_GRAPH_METHOD;
+ private static final boolean JPA21_AVAILABLE = ClassUtils.isPresent("javax.persistence.NamedEntityGraph",
+ Jpa21QueryCustomizer.class.getClassLoader());
+
+ static {
+
+ if (JPA21_AVAILABLE) {
+ GET_ENTITY_GRAPH_METHOD = ReflectionUtils.findMethod(EntityManager.class, "getEntityGraph", String.class);
+ } else {
+ GET_ENTITY_GRAPH_METHOD = null;
+ }
+ }
+
+ /**
+ * 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}
+ */
+ public void tryConfigureFetchGraph(EntityManager em, Query query, JpaEntityGraph entityGraph) {
+
+ 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.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);
+ }
+}
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
new file mode 100644
index 000000000..0368d4de3
--- /dev/null
+++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java
@@ -0,0 +1,75 @@
+/*
+ * 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.query;
+
+import javax.persistence.EntityGraph;
+
+import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
+import org.springframework.util.Assert;
+
+/**
+ * EntityGraph configuration for JPA 2.1 {@link EntityGraph}s.
+ *
+ * @author Thomas Darimont
+ * @since 1.6
+ */
+public class JpaEntityGraph {
+
+ private final String name;
+ private final EntityGraphType type;
+
+ /**
+ * Creates an {@link JpaEntityGraph}.
+ *
+ * @param name must not be {@null}.
+ * @param type must not be {@null}.
+ */
+ public JpaEntityGraph(String name, EntityGraphType type) {
+
+ 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;
+ }
+
+ /**
+ * Returns the name of the {@link EntityGraph} configuration to use.
+ *
+ * @return
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the {@link EntityGraphType} of the {@link EntityGraph} to use.
+ *
+ * @return
+ */
+ public EntityGraphType getType() {
+ return type;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "JpaEntityGraph [name=" + name + ", type=" + type + "]";
+ }
+}
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 bc2ae187f..0342e786e 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2013 the original author or authors.
+ * Copyright 2008-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.
@@ -26,6 +26,7 @@ import javax.persistence.LockModeType;
import javax.persistence.QueryHint;
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.Modifying;
import org.springframework.data.jpa.repository.Query;
@@ -143,6 +144,18 @@ public class JpaQueryMethod extends QueryMethod {
return (LockModeType) AnnotationUtils.getValue(annotation);
}
+ /**
+ * Returns the {@link EntityGraph} to be used for the query.
+ *
+ * @return
+ * @since 1.6
+ */
+ JpaEntityGraph getEntityGraph() {
+
+ EntityGraph annotation = findAnnotation(method, EntityGraph.class);
+ return annotation == null ? null : new JpaEntityGraph(annotation.value(), annotation.type());
+ }
+
/**
* Returns whether the potentially configured {@link QueryHint}s shall be applied when triggering the count query for
* pagination.
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 c2031f50a..f0e74b056 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
@@ -30,6 +30,9 @@ import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
+import javax.persistence.NamedAttributeNode;
+import javax.persistence.NamedEntityGraph;
+import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedQuery;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@@ -42,6 +45,10 @@ import javax.persistence.TemporalType;
* @author Thomas Darimont
*/
@Entity
+@NamedEntityGraphs({
+ @NamedEntityGraph(name = "User.overview", attributeNodes = { @NamedAttributeNode("roles") }),
+ @NamedEntityGraph(name = "User.detail", attributeNodes = { @NamedAttributeNode("roles"),
+ @NamedAttributeNode("manager"), @NamedAttributeNode("colleagues") }) })
@NamedQuery(name = "User.findByEmailAddress", query = "SELECT u FROM User u WHERE u.emailAddress = ?1")
public class User {
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 7228c3a09..ba142dc53 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
@@ -28,10 +28,13 @@ import javax.persistence.Query;
import javax.persistence.QueryHint;
import javax.persistence.TypedQuery;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.Lock;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.data.jpa.repository.support.PersistenceProvider;
@@ -39,6 +42,8 @@ import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
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}.
@@ -49,8 +54,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration("classpath:infrastructure.xml")
public class AbstractJpaQueryTests {
- @PersistenceContext
- EntityManager em;
+ @PersistenceContext EntityManager em;
Query query;
TypedQuery countQuery;
@@ -122,6 +126,55 @@ public class AbstractJpaQueryTests {
verify(result).setLockMode(LockModeType.PESSIMISTIC_WRITE);
}
+ /**
+ * @see DATAJPA-466
+ */
+ @Test
+ @Transactional
+ public void shouldAddEntityGraphHintForFetch() throws Exception {
+
+ Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager());
+
+ Method findAllMethod = SampleRepository.class.getMethod("findAll");
+ QueryExtractor provider = PersistenceProvider.fromEntityManager(em);
+ JpaQueryMethod queryMethod = new JpaQueryMethod(findAllMethod,
+ new DefaultRepositoryMetadata(SampleRepository.class), provider);
+
+ javax.persistence.EntityGraph> entityGraph = em.getEntityGraph("User.overview");
+
+ AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em);
+ Query result = jpaQuery.createQuery(new Object[0]);
+
+ verify(result).setHint("javax.persistence.fetchgraph", entityGraph);
+ }
+
+ /**
+ * @see DATAJPA-466
+ */
+ @Test
+ @Transactional
+ public void shouldAddEntityGraphHintForLoad() throws Exception {
+
+ Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager());
+
+ Method getByIdMethod = SampleRepository.class.getMethod("getById", Integer.class);
+ QueryExtractor provider = PersistenceProvider.fromEntityManager(em);
+ JpaQueryMethod queryMethod = new JpaQueryMethod(getByIdMethod,
+ new DefaultRepositoryMetadata(SampleRepository.class), provider);
+
+ javax.persistence.EntityGraph> entityGraph = em.getEntityGraph("User.detail");
+
+ AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em);
+ Query result = jpaQuery.createQuery(new Object[] { 1 });
+
+ 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") })
@@ -133,6 +186,18 @@ public class AbstractJpaQueryTests {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@org.springframework.data.jpa.repository.Query("select u from User u where u.id = ?1")
List findOneLocked(Integer primaryKey);
+
+ /**
+ * @see DATAJPA-466
+ */
+ @EntityGraph(value = "User.detail", type = EntityGraphType.LOAD)
+ User getById(Integer id);
+
+ /**
+ * @see DATAJPA-466
+ */
+ @EntityGraph("User.overview")
+ List findAll();
}
class DummyJpaQuery extends AbstractJpaQuery {
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 12f61413e..00026864b 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
@@ -36,6 +36,8 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
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.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
@@ -57,14 +59,12 @@ public class JpaQueryMethodUnitTests {
static final Class> DOMAIN_CLASS = User.class;
static final String METHOD_NAME = "findByFirstname";
- @Mock
- QueryExtractor extractor;
- @Mock
- RepositoryMetadata metadata;
+ @Mock QueryExtractor extractor;
+ @Mock RepositoryMetadata metadata;
Method repositoryMethod, invalidReturnType, pageableAndSort, pageableTwice, sortableTwice, modifyingMethod,
nativeQuery, namedQuery, findWithLockMethod, invalidNamedParameter, findsProjections, findsProjection,
- withMetaAnnotation;
+ withMetaAnnotation, queryMethodWithCustomEntityFetchGraph;
/**
* @throws Exception
@@ -91,6 +91,9 @@ public class JpaQueryMethodUnitTests {
findsProjection = ValidRepository.class.getMethod("findsProjection");
withMetaAnnotation = ValidRepository.class.getMethod("withMetaAnnotation");
+
+ queryMethodWithCustomEntityFetchGraph = ValidRepository.class.getMethod("queryMethodWithCustomEntityFetchGraph",
+ Integer.class);
}
@Test
@@ -313,6 +316,19 @@ public class JpaQueryMethodUnitTests {
assertThat(method.getHints().get(0).value(), is("bar"));
}
+ /**
+ * @see DATAJPA-466
+ */
+ @Test
+ public void shouldStoreJpa21FetchGraphInformationAsHint() {
+
+ JpaQueryMethod method = new JpaQueryMethod(queryMethodWithCustomEntityFetchGraph, metadata, extractor);
+
+ assertThat(method.getEntityGraph(), is(notNullValue()));
+ assertThat(method.getEntityGraph().getName(), is("User.propertyLoadPath"));
+ assertThat(method.getEntityGraph().getType(), is(EntityGraphType.LOAD));
+ }
+
/**
* Interface to define invalid repository methods for testing.
*
@@ -367,6 +383,12 @@ public class JpaQueryMethodUnitTests {
@CustomAnnotation
void withMetaAnnotation();
+
+ /**
+ * @see DATAJPA-466
+ */
+ @EntityGraph(value = "User.propertyLoadPath", type = EntityGraphType.LOAD)
+ User queryMethodWithCustomEntityFetchGraph(Integer id);
}
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
diff --git a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java
index 65a0f0b7c..4f8deaddc 100644
--- a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java
@@ -18,6 +18,8 @@ package org.springframework.data.jpa.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
+import java.util.Arrays;
+import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
@@ -30,7 +32,11 @@ import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
+import javax.persistence.spi.PersistenceProvider;
+import javax.persistence.spi.PersistenceProviderResolver;
+import javax.persistence.spi.PersistenceProviderResolverHolder;
+import org.hibernate.ejb.HibernatePersistence;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.Order;
@@ -118,12 +124,22 @@ public class QueryUtilsIntegrationTests {
@Test
public void traversesPluralAttributeCorrectly() {
- EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("merchant");
- CriteriaBuilder builder = entityManagerFactory.createEntityManager().getCriteriaBuilder();
- CriteriaQuery query = builder.createQuery(Merchant.class);
- Root root = query.from(Merchant.class);
+ PersistenceProviderResolver originalPersistenceProviderResolver = PersistenceProviderResolverHolder
+ .getPersistenceProviderResolver();
- QueryUtils.toExpressionRecursively(root, PropertyPath.from("employeesCredentialsUid", Merchant.class));
+ try {
+
+ PersistenceProviderResolverHolder.setPersistenceProviderResolver(new HibernateOnlyPersistenceProviderResolver());
+ EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("merchant");
+ CriteriaBuilder builder = entityManagerFactory.createEntityManager().getCriteriaBuilder();
+ CriteriaQuery query = builder.createQuery(Merchant.class);
+ Root root = query.from(Merchant.class);
+
+ QueryUtils.toExpressionRecursively(root, PropertyPath.from("employeesCredentialsUid", Merchant.class));
+
+ } finally {
+ PersistenceProviderResolverHolder.setPersistenceProviderResolver(originalPersistenceProviderResolver);
+ }
}
protected void assertNoJoinRequestedForOptionalAssociation(Root root) {
@@ -150,4 +166,21 @@ public class QueryUtilsIntegrationTests {
@Id String id;
String uid;
}
+
+ /**
+ * A {@link PersistenceProviderResolver} that returns only {@link HibernatePersistence} and ignores other
+ * {@link PersistenceProvider}s.
+ *
+ * @author Thomas Darimont
+ */
+ static class HibernateOnlyPersistenceProviderResolver implements PersistenceProviderResolver {
+
+ @Override
+ public List getPersistenceProviders() {
+ return Arrays. asList(new HibernatePersistence());
+ }
+
+ @Override
+ public void clearCachedProviders() {}
+ }
}