DATAJPA-1562 - Made projections work with late Hibernate 4 versions.

For Projections we check the Hibernate version to make sure it supports Tuples.
This was only done based on Hibernate 5 versions although some Hibernate 4 versions do so as well.
This is now taken in consideration for the version check.

The versions we consider functional for tuples are determined by trial and error because no clear documentation when this was introduced to Hibernate could be found.

Original pull request: #387.
This commit is contained in:
Jens Schauder
2019-06-20 13:39:10 +02:00
committed by Mark Paluch
parent 7ba82b218c
commit f0855026cb
8 changed files with 89 additions and 28 deletions

View File

@@ -27,7 +27,9 @@ 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.
*
*
* @author Oliver Gierke
* @author Jens Schauder
* @since 1.10.2
* @soundtrack Benny Greb - Soulfood (Live, https://www.youtube.com/watch?v=9_ErMa_CtSw)
*/
@@ -36,6 +38,11 @@ public abstract class HibernateUtils {
private static final List<String> TYPES = Arrays.asList("org.hibernate.jpa.HibernateQuery",
"org.hibernate.ejb.HibernateQuery");
private static final Version HIBERNATE4_VERSION_SUPPORTING_TUPLES = new Version(4, 2, 21);
private static final Version HIBERNATE5_VERSION = new Version(5, 0, 0);
private static final Version HIBERNATE5_VERSION_SUPPORTING_TUPLES = new Version(5, 2, 11);
private static final Method GET_HIBERNATE_QUERY;
private static final Class<?> HIBERNATE_QUERY_INTERFACE;
@@ -103,4 +110,36 @@ public abstract class HibernateUtils {
public static boolean isVersionOrBetter(Version version) {
return HIBERNATE_VERSION.isGreaterThanOrEqualTo(version);
}
/**
* Returns whether the currently used version of Hibernate is in the given interval of versions.
*
* @param lowerIncluding lower version bound to compare to. The lower version bound is inclusive. Must not be
* {@literal null}.
* @param upperExcluding upper version bound to compare to. The upper version bound is exclusive. Must not be
* {@literal null}.
* @return whence lowerIncluding <= Hibernate version < upperExcluding.
*/
private static boolean isVersionInInterval(Version lowerIncluding, Version upperExcluding) {
return HIBERNATE_VERSION.isGreaterThanOrEqualTo(lowerIncluding) && HIBERNATE_VERSION.isLessThan(upperExcluding);
}
/**
* Returns wether the current version of Hibernate supports {@link javax.persistence.Tuple} as a return type for
* native queries.
*/
public static boolean supportsTuples() {
return HibernateUtils.isVersionInInterval(HIBERNATE4_VERSION_SUPPORTING_TUPLES, HIBERNATE5_VERSION)
|| HibernateUtils.isVersionOrBetter(HIBERNATE5_VERSION_SUPPORTING_TUPLES);
}
/**
* Returns wether the current version of Hibernate supports {@link javax.persistence.Tuple} as a return type for
* native queries.
*/
public static boolean supportsTuplesForNativeQueries() {
return HibernateUtils.isVersionOrBetter(HIBERNATE5_VERSION_SUPPORTING_TUPLES);
}
}

View File

@@ -47,7 +47,6 @@ import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.util.Version;
import org.springframework.util.Assert;
/**
@@ -57,11 +56,10 @@ import org.springframework.util.Assert;
* @author Thomas Darimont
* @author Mark Paluch
* @author Nicolas Cirigliano
* @author Jens Schauder
*/
public abstract class AbstractJpaQuery implements RepositoryQuery {
protected static final Version HIBERNATE_VERSION_SUPPORTING_TUPLES = new Version(5, 2, 11);
private final JpaQueryMethod method;
private final EntityManager em;
private final JpaMetamodel metamodel;
@@ -242,9 +240,11 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
return null;
}
return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) //
? HibernateUtils.isVersionOrBetter(HIBERNATE_VERSION_SUPPORTING_TUPLES) ? Tuple.class : null //
: null;
return returnedType.isProjecting() //
&& !getMetamodel().isJpaManaged(returnedType.getReturnedType()) //
&& HibernateUtils.supportsTuples() //
? Tuple.class //
: null;
}
/**

View File

@@ -81,8 +81,10 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery {
return result;
}
return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) //
? HibernateUtils.isVersionOrBetter(HIBERNATE_VERSION_SUPPORTING_TUPLES) ? Tuple.class : null //
: result;
return returnedType.isProjecting() //
&& !getMetamodel().isJpaManaged(returnedType.getReturnedType()) //
&& HibernateUtils.supportsTuplesForNativeQueries() //
? Tuple.class //
: result;
}
}

View File

@@ -102,7 +102,7 @@ public abstract class MetamodelIntegrationTests {
assertThat(elements.get(0).getAlias(), is(nullValue()));
}
@Test
@Test // DATAJPA-1273
@Transactional
public void returnsAliasesInTuple() {

View File

@@ -24,7 +24,6 @@ import javax.persistence.Query;
import org.junit.Assume;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.jpa.repository.sample.UserRepository;
import org.springframework.data.util.Version;
import org.springframework.test.context.ContextConfiguration;
@@ -124,6 +123,12 @@ public class EclipseLinkNamespaceUserRepositoryTests extends NamespaceUserReposi
@Test
public void bindsNativeQueryResultsToProjectionByName() {}
/**
* TODO: Remove, once https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 is fixed.
*/
@Override
public void findListOfMap() {}
/**
* Ignores the test for EclipseLink 2.7.2. Reconsider once https://bugs.eclipse.org/bugs/show_bug.cgi?id=533240 is
* fixed.

View File

@@ -128,6 +128,12 @@ public class OpenJpaNamespaceUserRepositoryTests extends NamespaceUserRepository
@Override
public void bindsNativeQueryResultsToProjectionByName() {}
/**
* ignored since OpenJPA doesn't support tuples
*/
@Override
public void findListOfMap() {}
/**
* ignored since OpenJPA doesn't support tuples
*/

View File

@@ -72,6 +72,7 @@ import org.springframework.data.jpa.domain.sample.Address;
import org.springframework.data.jpa.domain.sample.Role;
import org.springframework.data.jpa.domain.sample.SpecialUser;
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.sample.SampleEvaluationContextExtension.SampleSecurityContextHolder;
import org.springframework.data.jpa.repository.sample.UserRepository;
@@ -101,8 +102,6 @@ import com.google.common.base.Optional;
@Transactional
public class UserRepositoryTests {
private static final Version HIBERNATE_VERSION_SUPPORTING_TUPLE_ON_NATIVE_QUERIES = new Version(5, 2, 11);
@PersistenceContext EntityManager em;
// CUT
@@ -2186,8 +2185,7 @@ public class UserRepositoryTests {
@Test // DATAJPA-980
public void supportsProjectionsWithNativeQueries() {
Assume
.assumeTrue(getHibernateVersion().isGreaterThanOrEqualTo(HIBERNATE_VERSION_SUPPORTING_TUPLE_ON_NATIVE_QUERIES));
Assume.assumeTrue(HibernateUtils.supportsTuplesForNativeQueries());
flushTestUsers();
@@ -2202,8 +2200,7 @@ public class UserRepositoryTests {
@Test // DATAJPA-1248
public void supportsProjectionsWithNativeQueriesAndCamelCaseProperty() throws Exception {
Assume
.assumeTrue(getHibernateVersion().isGreaterThanOrEqualTo(HIBERNATE_VERSION_SUPPORTING_TUPLE_ON_NATIVE_QUERIES));
Assume.assumeTrue(HibernateUtils.supportsTuplesForNativeQueries());
flushTestUsers();
User user = repository.findAll().get(0);
@@ -2221,8 +2218,7 @@ public class UserRepositoryTests {
@Test // DATAJPA-1273
public void bindsNativeQueryResultsToProjectionByName() {
Assume
.assumeTrue(getHibernateVersion().isGreaterThanOrEqualTo(HIBERNATE_VERSION_SUPPORTING_TUPLE_ON_NATIVE_QUERIES));
Assume.assumeTrue(HibernateUtils.supportsTuples());
flushTestUsers();
@@ -2239,8 +2235,7 @@ public class UserRepositoryTests {
@Test // DATAJPA-1301
public void returnsNullValueInMap() {
Assume
.assumeTrue(getHibernateVersion().isGreaterThanOrEqualTo(HIBERNATE_VERSION_SUPPORTING_TUPLE_ON_NATIVE_QUERIES));
Assume.assumeTrue(HibernateUtils.supportsTuples());
firstUser.setLastname(null);
flushTestUsers();
@@ -2257,6 +2252,22 @@ public class UserRepositoryTests {
assertThat(map.get(new Object()), is(nullValue()));
}
@Test // DATAJPA-1562
public void findListOfMap() {
Assume.assumeTrue(HibernateUtils.supportsTuples());
flushTestUsers();
List<Map<String, Object>> listOfMaps = repository.findListOfMaps();
assertThat(listOfMaps, Matchers.<Map<String, Object>> hasSize(4));
for (Map<String, Object> map : listOfMaps) {
assertThat(map.entrySet(), Matchers.hasSize(2));
}
}
@Test(expected = DataIntegrityViolationException.class) // DATAJPA-1535
public void savingUserThrowsAnException() {
// if this test fails this means deleteNewInstanceSucceedsByDoingNothing() might actually save the user without the
@@ -2279,10 +2290,4 @@ public class UserRepositoryTests {
assertThat(result.getTotalElements(), is(2L));
return result;
}
private static Version getHibernateVersion() {
String hibernateVersion = org.hibernate.Version.getVersionString();
return Version.parse(hibernateVersion.substring(0, hibernateVersion.lastIndexOf(".")));
}
}

View File

@@ -493,6 +493,10 @@ public interface UserRepository
@Query("select firstname as firstname, lastname as lastname from User u where u.firstname = 'Oliver'")
Map<String, Object> findMapWithNullValues();
// DATAJPA-1562
@Query("select firstname as firstname, lastname as lastname from User u")
List<Map<String, Object>> findListOfMaps();
// DATAJPA-1334
List<NameOnlyDto> findByNamedQueryWithConstructorExpression();