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:
committed by
Mark Paluch
parent
7ba82b218c
commit
f0855026cb
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public abstract class MetamodelIntegrationTests {
|
||||
assertThat(elements.get(0).getAlias(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJPA-1273
|
||||
@Transactional
|
||||
public void returnsAliasesInTuple() {
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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(".")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user