diff --git a/pom.xml b/pom.xml index 56c829ab7f..265d866726 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 1.6.0.BUILD-SNAPSHOT + 1.6.0.DATAJPA-466-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -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/domain/JpaEntityGraph.java b/src/main/java/org/springframework/data/jpa/domain/JpaEntityGraph.java new file mode 100644 index 0000000000..175c5a0560 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/domain/JpaEntityGraph.java @@ -0,0 +1,118 @@ +/* + * 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.domain; + +import javax.persistence.EntityGraph; + +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.notNull(name, "Name must not be null!"); + Assert.notNull(type, "FetchGraphType must not be null!"); + Assert.hasText(name, "The name of an EntityGraph must not be empty!"); + + 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 + "]"; + } + + /** + * Enum for JPA 2.1 {@link javax.persistence.EntityGraph} types. + * + * @author Thomas Darimont + * @since 1.6 + */ + public enum EntityGraphType { + + /** + * Fetch Graph Semantics + *

+ * 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.1 Fetch Graph Semantics + */ + LOAD("javax.persistence.loadgraph"), + + /** + * Fetch Graph Semantics: + *

+ * 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.2 Load 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/EntityGraph.java b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java new file mode 100644 index 0000000000..c7c08dd067 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java @@ -0,0 +1,52 @@ +/* + * 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; +import org.springframework.data.jpa.domain.JpaEntityGraph.EntityGraphType; + +/** + * 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; +} 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 5426f47bca..5d054ab65a 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 @@ -21,6 +21,8 @@ import javax.persistence.QueryHint; import javax.persistence.TypedQuery; +import org.springframework.data.jpa.domain.JpaEntityGraph; +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 +123,26 @@ protected JpaQueryExecution getExecution() { 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 +161,28 @@ protected ParameterBinder createBinder(Object[] values) { } 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 0000000000..f569451d7d --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21QueryCustomizer.java @@ -0,0 +1,78 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.lang.reflect.Method; + +import javax.persistence.EntityGraph; +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.springframework.data.jpa.domain.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. + * + * @author Thomas Darimont + * @since 1.6 + */ +enum Jpa21QueryCustomizer { + + INSTANCE; + + private static final boolean JPA21_AVAILABLE = ClassUtils.isPresent("javax.persistence.NamedEntityGraph", + Jpa21QueryCustomizer.class.getClassLoader()); + + private static final Method GET_ENTITY_GRAPH_METHOD; + + 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/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index bc2ae187fc..ebc2bc5185 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 @@ -26,6 +26,8 @@ import javax.persistence.QueryHint; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.jpa.domain.JpaEntityGraph; +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 +145,23 @@ LockModeType getLockModeType() { 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); + + if (annotation == null) { + return null; + } + + return 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 c2031f50a7..f0e74b0560 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.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 @@ * @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 7228c3a09c..8e5774b006 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.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.JpaEntityGraph.EntityGraphType; import org.springframework.data.jpa.domain.sample.User; +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.support.PersistenceProvider; @@ -39,6 +42,8 @@ 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 @@ @ContextConfiguration("classpath:infrastructure.xml") public class AbstractJpaQueryTests { - @PersistenceContext - EntityManager em; + @PersistenceContext EntityManager em; Query query; TypedQuery countQuery; @@ -122,6 +126,55 @@ public void addsLockingModeToQueryObject() throws Exception { 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 @@ interface SampleRepository extends Repository { @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 12f61413eb..5930f61094 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 @@ -35,7 +35,9 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.JpaEntityGraph.EntityGraphType; import org.springframework.data.jpa.domain.sample.User; +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; @@ -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 void setUp() throws Exception { findsProjection = ValidRepository.class.getMethod("findsProjection"); withMetaAnnotation = ValidRepository.class.getMethod("withMetaAnnotation"); + + queryMethodWithCustomEntityFetchGraph = ValidRepository.class.getMethod("queryMethodWithCustomEntityFetchGraph", + Integer.class); } @Test @@ -313,6 +316,19 @@ public void detectsLockAndQueryHintsOnIfUsedAsMetaAnnotation() { 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 @@ static interface ValidRepository { @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 65a0f0b7c4..4f8deaddcf 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 @@ 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.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 void createsJoingToTraverseCollectionPath() { @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 @@ static class Credential { @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() {} + } }