diff --git a/pom.xml b/pom.xml
index 36cfec88a2..e2d9798319 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-jpa
- 1.5.0.BUILD-SNAPSHOT
+ 1.5.0.DATAJPA-444.SNAPSHOT
Spring Data JPA
Spring Data module for JPA repositories.
diff --git a/src/main/java/org/springframework/data/jpa/repository/support/PersistenceProvider.java b/src/main/java/org/springframework/data/jpa/repository/support/PersistenceProvider.java
index 89a5596990..5433eb417f 100644
--- a/src/main/java/org/springframework/data/jpa/repository/support/PersistenceProvider.java
+++ b/src/main/java/org/springframework/data/jpa/repository/support/PersistenceProvider.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.
@@ -17,6 +17,9 @@
import static org.springframework.data.jpa.repository.utils.JpaClassUtils.*;
+import java.util.Arrays;
+import java.util.List;
+
import javax.persistence.EntityManager;
import javax.persistence.Query;
@@ -27,16 +30,22 @@
import org.springframework.util.Assert;
/**
- * Enumeration representing peristence providers to be used.
+ * Enumeration representing persistence providers to be used.
*
* @author Oliver Gierke
+ * @author Thomas Darimont
*/
public enum PersistenceProvider implements QueryExtractor {
/**
* Hibernate persistence provider.
+ *
+ * Since Hibernate 4.3 the location of the HibernateEntityManager moved to the org.hibernate.jpa package. In order to
+ * support both locations we interpret both classnames as a Hibernate {@code PersistenceProvider}.
+ *
+ * @see DATAJPA-444
*/
- HIBERNATE("org.hibernate.ejb.HibernateEntityManager") {
+ HIBERNATE(Constants.HIBERNATE_ENTITY_MANAGER_INTERFACE, Constants.HIBERNATE43_ENTITY_MANAGER_INTERFACE) {
public String extractQueryString(Query query) {
@@ -60,7 +69,7 @@ protected String getCountQueryPlaceholder() {
/**
* EclipseLink persistence provider.
*/
- ECLIPSELINK("org.eclipse.persistence.jpa.JpaEntityManager") {
+ ECLIPSELINK(Constants.ECLIPSELINK_ENTITY_MANAGER_INTERFACE) {
public String extractQueryString(Query query) {
@@ -72,7 +81,7 @@ public String extractQueryString(Query query) {
/**
* OpenJpa persistence provider.
*/
- OPEN_JPA("org.apache.openjpa.persistence.OpenJPAEntityManager") {
+ OPEN_JPA(Constants.OPENJPA_ENTITY_MANAGER_INTERFACE) {
public String extractQueryString(Query query) {
@@ -83,7 +92,7 @@ public String extractQueryString(Query query) {
/**
* Unknown special provider. Use standard JPA.
*/
- GENERIC_JPA("javax.persistence.EntityManager") {
+ GENERIC_JPA(Constants.GENERIC_JPA_ENTITY_MANAGER_INTERFACE) {
public String extractQueryString(Query query) {
@@ -97,16 +106,33 @@ public boolean canExtractQuery() {
}
};
- private String entityManagerClassName;
+ /**
+ * Holds the PersistenceProvider specific interface names.
+ *
+ * @author Thomas Darimont
+ */
+ static interface Constants {
+
+ String GENERIC_JPA_ENTITY_MANAGER_INTERFACE = "javax.persistence.EntityManager";
+ String OPENJPA_ENTITY_MANAGER_INTERFACE = "org.apache.openjpa.persistence.OpenJPAEntityManager";
+ String ECLIPSELINK_ENTITY_MANAGER_INTERFACE = "org.eclipse.persistence.jpa.JpaEntityManager";
+ String HIBERNATE_ENTITY_MANAGER_INTERFACE = "org.hibernate.ejb.HibernateEntityManager";
+ String HIBERNATE43_ENTITY_MANAGER_INTERFACE = "org.hibernate.jpa.HibernateEntityManager";
+ }
+
+ private List entityManagerClassNames;
/**
* Creates a new {@link PersistenceProvider}.
*
- * @param entityManagerClassName the name of the provider specific {@link EntityManager} implementation
+ * @param entityManagerClassNames the names of the provider specific {@link EntityManager} implementations. Must not
+ * be {@literal null} or empty.
*/
- private PersistenceProvider(String entityManagerClassName) {
+ private PersistenceProvider(String... entityManagerClassNames) {
- this.entityManagerClassName = entityManagerClassName;
+ Assert.notEmpty(entityManagerClassNames, "EntityManagerClassNames must not be empty!");
+
+ this.entityManagerClassNames = Arrays.asList(entityManagerClassNames);
}
/**
@@ -121,8 +147,11 @@ public static PersistenceProvider fromEntityManager(EntityManager em) {
Assert.notNull(em);
for (PersistenceProvider provider : values()) {
- if (isEntityManagerOfType(em, provider.entityManagerClassName)) {
- return provider;
+ for (String entityManagerClassName : provider.entityManagerClassNames) {
+
+ if (isEntityManagerOfType(em, entityManagerClassName)) {
+ return provider;
+ }
}
}
diff --git a/src/test/java/org/springframework/data/jpa/repository/support/PersistenceProviderTests.java b/src/test/java/org/springframework/data/jpa/repository/support/PersistenceProviderTests.java
new file mode 100644
index 0000000000..594eb2c884
--- /dev/null
+++ b/src/test/java/org/springframework/data/jpa/repository/support/PersistenceProviderTests.java
@@ -0,0 +1,165 @@
+/*
+ * 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.support;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
+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.instrument.classloading.ShadowingClassLoader;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Tests for PersistenceProvider detection logic in {@link PersistenceProvider}.
+ *
+ * @author Thomas Darimont
+ */
+public class PersistenceProviderTests {
+
+ private ShadowingClassLoader shadowingClassLoader;
+
+ @Before
+ public void setup() {
+ shadowingClassLoader = new ShadowingClassLoader(getClass().getClassLoader());
+ }
+
+ /**
+ * @see DATAJPA-444
+ */
+ @Test
+ public void detectsHibernatePersistenceProviderForHibernateVersionLessThan4dot3() throws Exception {
+
+ shadowingClassLoader.excludePackage("org.hibernate");
+
+ EntityManager em = mockProviderSpecificEntityManagerInterface(PersistenceProvider.Constants.HIBERNATE_ENTITY_MANAGER_INTERFACE);
+
+ assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.HIBERNATE));
+ }
+
+ /**
+ * @see DATAJPA-444
+ */
+ @Test
+ public void detectsHibernatePersistenceProviderForHibernateVersionGreaterEqual4dot3() throws Exception {
+
+ shadowingClassLoader.excludePackage("org.hibernate");
+
+ EntityManager em = mockProviderSpecificEntityManagerInterface(PersistenceProvider.Constants.HIBERNATE43_ENTITY_MANAGER_INTERFACE);
+
+ assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.HIBERNATE));
+ }
+
+ @Test
+ public void detectsOpenJpaPersistenceProvider() throws Exception {
+
+ shadowingClassLoader.excludePackage("org.apache.openjpa.persistence");
+
+ EntityManager em = mockProviderSpecificEntityManagerInterface(PersistenceProvider.Constants.OPENJPA_ENTITY_MANAGER_INTERFACE);
+
+ assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.OPEN_JPA));
+ }
+
+ @Test
+ public void detectsEclipseLinkPersistenceProvider() throws Exception {
+
+ shadowingClassLoader.excludePackage("org.eclipse.persistence.jpa");
+
+ EntityManager em = mockProviderSpecificEntityManagerInterface(PersistenceProvider.Constants.ECLIPSELINK_ENTITY_MANAGER_INTERFACE);
+
+ assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.ECLIPSELINK));
+ }
+
+ @Test
+ public void fallbackToGenericJpaForUnknownPersistenceProvider() throws Exception {
+
+ EntityManager em = mockProviderSpecificEntityManagerInterface("foo.bar.unknown.jpa.JpaEntityManager");
+
+ assertThat(PersistenceProvider.fromEntityManager(em), is(PersistenceProvider.GENERIC_JPA));
+ }
+
+ private EntityManager mockProviderSpecificEntityManagerInterface(String interfaceName) throws ClassNotFoundException {
+
+ Class> providerSpecificEntityManagerInterface = InterfaceGenerator.generate(interfaceName, shadowingClassLoader,
+ EntityManager.class);
+
+ EntityManager em = EntityManager.class.cast(Mockito.mock(providerSpecificEntityManagerInterface));
+ Mockito.when(em.getDelegate()).thenReturn(em); // delegate is used to determine the classloader of the provider
+ // specific interface, therefore we return the proxied
+ // EntityManager.
+
+ return em;
+ }
+
+ static class InterfaceGenerator implements Opcodes {
+
+ public static Class> generate(final String interfaceName, ClassLoader parentClassLoader,
+ final Class>... interfaces) throws ClassNotFoundException {
+
+ class CustomClassLoader extends ClassLoader {
+
+ public CustomClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ @Override
+ protected Class> findClass(String name) throws ClassNotFoundException {
+
+ if (name.equals(interfaceName)) {
+
+ byte[] byteCode = generateByteCodeForInterface(interfaceName, interfaces);
+ return defineClass(name, byteCode, 0, byteCode.length);
+ }
+
+ return super.findClass(name);
+ }
+ }
+
+ return new CustomClassLoader(parentClassLoader).loadClass(interfaceName);
+ }
+
+ private static byte[] generateByteCodeForInterface(final String interfaceName, Class>... interfacesToImplement) {
+
+ String interfaceResourcePath = ClassUtils.convertClassNameToResourcePath(interfaceName);
+
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(V1_6, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, interfaceResourcePath, null, "java/lang/Object",
+ toResourcePaths(interfacesToImplement));
+ cw.visitSource(interfaceResourcePath + ".java", null);
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private static String[] toResourcePaths(Class>... interfacesToImplement) {
+
+ List interfaceResourcePaths = new ArrayList(interfacesToImplement.length);
+ for (Class> iface : interfacesToImplement) {
+ interfaceResourcePaths.add(ClassUtils.convertClassNameToResourcePath(iface.getName()));
+ }
+
+ return interfaceResourcePaths.toArray(new String[interfaceResourcePaths.size()]);
+ }
+ }
+}