From fdede8593179e59d4d659ed22d42947dd3bc8506 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 3 Jun 2016 19:33:07 +0200 Subject: [PATCH] DATAJPA-911 - Assert compatibility with Hibernate 5.2. We now use a completely reflective lookup of the Hibernate query string as well as for the Hibernate specific PersistenceProvider lookup in tests. Added build profile for Hibernate 5.2 but it's currently not working due to Hibernate complaining about an invalid identifier mapping on CustomAbstractPersistable which is overriding the parent types property on accessor working fine on 5.1. The build profile for 5.1 is still broken due to the fixed but yet unreleased HHH-10514 [0] and the not yet fixed HHH-10515 [1] (apparently fixed but still open and unreleased) which has been broken since 5.0.8. [0] https://hibernate.atlassian.net/browse/HHH-10514 [1] https://hibernate.atlassian.net/browse/HHH-10515 --- pom.xml | 10 ++- .../data/jpa/provider/HibernateUtils.java | 66 +++++++++++++++++++ .../jpa/provider/PersistenceProvider.java | 3 +- .../infrastructure/HibernateTestUtils.java | 60 +++++++++++++++++ .../CrudMethodMetadataUnitTests.java | 5 +- .../PartTreeJpaQueryIntegrationTests.java | 12 ++-- .../query/QueryUtilsIntegrationTests.java | 10 +-- .../DefaultJpaContextIntegrationTests.java | 6 +- 8 files changed, 151 insertions(+), 21 deletions(-) create mode 100644 src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java create mode 100644 src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java diff --git a/pom.xml b/pom.xml index 0830594ce..f0ff6b522 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ 2.6.2 3.6.10.Final + hibernate-entitymanager 1.8.0.10 2.0.0 2.4.1 @@ -111,6 +112,13 @@ + + hibernate-52 + + 5.2.0.Final + hibernate-core + + eclipselink-next @@ -250,7 +258,7 @@ org.hibernate - hibernate-entitymanager + ${hibernate.artifact} ${hibernate} true diff --git a/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java new file mode 100644 index 000000000..84ce421b7 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016 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.provider; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +import org.hibernate.Query; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; + +/** + * Utility functions to work with Hibernate. Mostly using reflection to make sure common functionality can be executed + * against all the Hibernate version we support. + * + * @since 1.10.2 + * @soundtrack Benny Greb - Soulfood (Live, https://www.youtube.com/watch?v=9_ErMa_CtSw) + */ +@SuppressWarnings({ "deprecation", "rawtypes" }) +public abstract class HibernateUtils { + + private static final List TYPES = Arrays.asList("org.hibernate.jpa.HibernateQuery", + "org.hibernate.ejb.HibernateQuery"); + private static final Method GET_HIBERNATE_QUERY; + + private HibernateUtils() {} + + static { + + Class type = null; + Method method = null; + + for (String typeName : TYPES) { + try { + type = ClassUtils.forName(typeName, HibernateUtils.class.getClassLoader()); + method = type.getMethod("getHibernateQuery"); + } catch (Exception o_O) {} + } + + GET_HIBERNATE_QUERY = method; + } + + /** + * Return the query string of the underlying native Hibernate query. + * + * @param query + * @return + */ + public static String getHibernateQuery(Object query) { + return ((Query) ReflectionUtils.invokeMethod(GET_HIBERNATE_QUERY, query)).getQueryString(); + } +} 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 777011cb4..1c9eb844b 100644 --- a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -39,7 +39,6 @@ import org.eclipse.persistence.jpa.JpaQuery; import org.eclipse.persistence.queries.ScrollableCursor; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; -import org.hibernate.ejb.HibernateQuery; import org.hibernate.proxy.HibernateProxy; import org.springframework.data.util.CloseableIterator; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -67,7 +66,7 @@ public enum PersistenceProvider implements QueryExtractor,ProxyIdAccessor { Arrays.asList(HIBERNATE43_JPA_METAMODEL_TYPE, HIBERNATE_JPA_METAMODEL_TYPE)) { public String extractQueryString(Query query) { - return ((HibernateQuery) query).getHibernateQuery().getQueryString(); + return HibernateUtils.getHibernateQuery(query); } /** diff --git a/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java b/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java new file mode 100644 index 000000000..34329c932 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 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.infrastructure; + +import java.util.Arrays; +import java.util.List; + +import javax.persistence.spi.PersistenceProvider; + +import org.springframework.util.ClassUtils; + +/** + * Testing utilities for Hibernate. + * + * @author Oliver Gierke + * @soundtrack Ron Spielman - Africa's Napoleon (Swimming In The Dark) + * @since 1.10.2 + */ +public class HibernateTestUtils { + + private static final List PROVIDER_TYPES = Arrays.asList("org.hibernate.jpa.HibernatePersistenceProvider", + "org.hibernate.ejb.HibernatePersistence"); + + /** + * Returns the Hibernate {@link PersistenceProvider}. + * + * @return + */ + public static PersistenceProvider getPersistenceProvider() { + + ClassLoader classLoader = HibernateTestUtils.class.getClassLoader(); + + for (String provider : PROVIDER_TYPES) { + + if (ClassUtils.isPresent(provider, classLoader)) { + + try { + return (PersistenceProvider) ClassUtils.forName(provider, classLoader).newInstance(); + } catch (Exception o_O) { + throw new RuntimeException(o_O); + } + } + } + + throw new IllegalStateException("Could not obtain Hibernate PersistenceProvider!"); + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java index 5e3e2e09c..7f34b6b4d 100644 --- a/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2011-2016 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. @@ -29,7 +29,6 @@ import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; -import org.hibernate.ejb.HibernateEntityManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -117,7 +116,7 @@ public class CrudMethodMetadataUnitTests { @Test public void appliesLockModeAndQueryHintsToQuerydslQuery() { - when(em.getDelegate()).thenReturn(mock(HibernateEntityManager.class)); + when(em.getDelegate()).thenReturn(mock(EntityManager.class)); when(em.createQuery(anyString())).thenReturn(query); repository.findOne(QRole.role.name.eq("role")); diff --git a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 4bec37c30..0ea336ef3 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -33,17 +33,16 @@ import javax.persistence.Query; import javax.persistence.TemporalType; import org.hibernate.Version; -import org.hibernate.ejb.HibernateQuery; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; -import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.HibernateUtils; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.Temporal; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -114,22 +113,21 @@ public class PartTreeJpaQueryIntegrationTests { Query query = jpaQuery.createQuery(new Object[] { "Matthews", new PageRequest(0, 1) }); - HibernateQuery hibernateQuery = getValue(query, PROPERTY); - assertThat(hibernateQuery.getHibernateQuery().getQueryString(), endsWith("firstname=:param0")); + assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY)), endsWith("firstname=:param0")); query = jpaQuery.createQuery(new Object[] { null, new PageRequest(0, 1) }); - hibernateQuery = getValue(query, PROPERTY); - assertThat(hibernateQuery.getHibernateQuery().getQueryString(), endsWith("firstname is null")); + assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY)), endsWith("firstname is null")); } private void testIgnoreCase(String methodName, Object... values) throws Exception { Class[] parameterTypes = new Class[values.length]; + for (int i = 0; i < values.length; i++) { parameterTypes[i] = values[i].getClass(); } - Method method = UserRepository.class.getMethod(methodName, parameterTypes); + JpaQueryMethod queryMethod = getQueryMethod(methodName, parameterTypes); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, PersistenceProvider.fromEntityManager(entityManager)); 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 8a87e6274..d62543c92 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2016 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. @@ -39,13 +39,13 @@ 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.mockito.Mockito; import org.springframework.data.jpa.domain.sample.Category; import org.springframework.data.jpa.domain.sample.Order; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.infrastructure.HibernateTestUtils; import org.springframework.data.mapping.PropertyPath; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -191,16 +191,16 @@ public class QueryUtilsIntegrationTests { } /** - * A {@link PersistenceProviderResolver} that returns only {@link HibernatePersistence} and ignores other - * {@link PersistenceProvider}s. + * A {@link PersistenceProviderResolver} that returns only a Hibernate {@link PersistenceProvider} and ignores others. * * @author Thomas Darimont + * @author Oliver Gierke */ static class HibernateOnlyPersistenceProviderResolver implements PersistenceProviderResolver { @Override public List getPersistenceProviders() { - return Arrays. asList(new HibernatePersistence()); + return Arrays.asList(HibernateTestUtils.getPersistenceProvider()); } @Override diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java index 8098145bd..e5b9eb1e4 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2016 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. @@ -24,7 +24,6 @@ import java.util.HashSet; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; -import org.hibernate.ejb.HibernatePersistence; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; @@ -40,6 +39,7 @@ import org.springframework.context.annotation.FilterType; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.jpa.domain.sample.Category; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.infrastructure.HibernateTestUtils; import org.springframework.data.jpa.repository.JpaContext; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; @@ -147,7 +147,7 @@ public class DefaultJpaContextIntegrationTests { String persistenceUnitName) { LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); - factoryBean.setPersistenceProvider(new HibernatePersistence()); + factoryBean.setPersistenceProvider(HibernateTestUtils.getPersistenceProvider()); factoryBean.setDataSource(new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build()); factoryBean.setPersistenceUnitName(persistenceUnitName);