DATAJPA-207 - Fixed query creation for projections on native queries.
When executing a projecting query through a native query we must not use em.createNativeQuery(String, Class<?>) as this expects an entity type as type parameter. We now use the newly introduced isQueryMethodForEntity() on JpaQueryMethod to determine whether the query is actually projecting and rather use the plain em.createNativeQuery(String). Fixed dependency configuration for Hamcrest and JUnit (upgrade to JUnit 4.10, depending on junit-dep).
This commit is contained in:
20
pom.xml
20
pom.xml
@@ -57,11 +57,11 @@
|
||||
<spring.version.range>[${spring.version.30}, ${spring.version.40})</spring.version.range>
|
||||
<spring.data.commons.version>1.3.0.BUILD-SNAPSHOT</spring.data.commons.version>
|
||||
<hibernate.version>3.6.9.Final</hibernate.version>
|
||||
<openjpa.version>2.1.1</openjpa.version>
|
||||
<openjpa.version>2.2.0</openjpa.version>
|
||||
<eclipselink.version>2.3.2</eclipselink.version>
|
||||
<aspectj.version>1.6.12</aspectj.version>
|
||||
<querydsl.version>2.5.0</querydsl.version>
|
||||
<junit.version>4.8.1</junit.version>
|
||||
<junit.version>4.10</junit.version>
|
||||
<jpa.version>2.0.0</jpa.version>
|
||||
<slf4j.version>1.6.1</slf4j.version>
|
||||
<cdi.version>1.0</cdi.version>
|
||||
@@ -338,9 +338,16 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-library</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
@@ -352,13 +359,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
|
||||
@@ -227,6 +227,15 @@ public class JpaQueryMethod extends QueryMethod {
|
||||
return (Boolean) AnnotationUtils.getValue(method.getAnnotation(Modifying.class), "clearAutomatically");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the query method will actually return entities.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isQueryMethodForEntity() {
|
||||
return getDomainClass().isAssignableFrom(getReturnedObjectType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Query} annotation's attribute casted to the given type or default value if no annotation
|
||||
* available.
|
||||
|
||||
@@ -85,15 +85,15 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
|
||||
|
||||
ParameterAccessor accessor = new ParametersParameterAccessor(method.getParameters(), values);
|
||||
String sortedQueryString = QueryUtils.applySorting(queryString, accessor.getSort(), alias);
|
||||
EntityManager em = getEntityManager();
|
||||
|
||||
Query query = null;
|
||||
|
||||
if (method.isNativeQuery()) {
|
||||
query = method.isModifyingQuery() ? getEntityManager().createNativeQuery(sortedQueryString) : getEntityManager()
|
||||
.createNativeQuery(sortedQueryString, method.getReturnedObjectType());
|
||||
query = method.isQueryMethodForEntity() ? em.createNativeQuery(sortedQueryString, method.getReturnedObjectType())
|
||||
: em.createNativeQuery(sortedQueryString);
|
||||
} else {
|
||||
query = method.isModifyingQuery() ? getEntityManager().createQuery(sortedQueryString) : getEntityManager()
|
||||
.createQuery(sortedQueryString);
|
||||
query = em.createQuery(sortedQueryString);
|
||||
}
|
||||
|
||||
return createBinder(values).bindAndPrepare(query);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012 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.sql.Types;
|
||||
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
|
||||
/**
|
||||
* Fix for missing type declarations for HSQL.
|
||||
*
|
||||
* @see http://www.codesmell.org/blog/2008/12/hibernate-hsql-native-queries-and-booleans/
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class CustomHsqlHibernateJpaVendorAdaptor extends HibernateJpaVendorAdapter {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter#determineDatabaseDialectClass(org.springframework.orm.jpa.vendor.Database)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> determineDatabaseDialectClass(Database database) {
|
||||
|
||||
if (Database.HSQL.equals(database)) {
|
||||
return CustomHsqlDialect.class;
|
||||
}
|
||||
|
||||
return super.determineDatabaseDialectClass(database);
|
||||
}
|
||||
|
||||
public static class CustomHsqlDialect extends HSQLDialect {
|
||||
|
||||
public CustomHsqlDialect() {
|
||||
registerColumnType(Types.BOOLEAN, "boolean");
|
||||
registerHibernateType(Types.BOOLEAN, "boolean");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -836,6 +836,20 @@ public class UserRepositoryTests {
|
||||
assertThat(page.getContent(), hasItems(firstUser, secondUser, thirdUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-207
|
||||
*/
|
||||
@Test
|
||||
public void executesNativeQueryForNonEntitiesCorrectly() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
List<Integer> result = repository.findOnesByNativeQuery();
|
||||
|
||||
assertThat(result.size(), is(3));
|
||||
assertThat(result, hasItem(1));
|
||||
}
|
||||
|
||||
protected void flushTestUsers() {
|
||||
|
||||
firstUser = repository.save(firstUser);
|
||||
|
||||
@@ -57,7 +57,7 @@ public class AuditingBeanDefinitionParserTests {
|
||||
PropertyValue value = definition.getPropertyValues().getPropertyValue("dateTimeProvider");
|
||||
|
||||
assertThat(value, is(notNullValue()));
|
||||
assertThat(value.getValue(), is(RuntimeBeanReference.class));
|
||||
assertThat(value.getValue(), is(instanceOf(RuntimeBeanReference.class)));
|
||||
assertThat(((RuntimeBeanReference) value.getValue()).getBeanName(), is("dateTimeProvider"));
|
||||
|
||||
BeanFactory factory = loadFactoryFrom(location);
|
||||
|
||||
@@ -60,7 +60,7 @@ public class JpaQueryMethodUnitTests {
|
||||
RepositoryMetadata metadata;
|
||||
|
||||
Method repositoryMethod, invalidReturnType, pageableAndSort, pageableTwice, sortableTwice, modifyingMethod,
|
||||
nativeQuery, namedQuery, findWithLockMethod, invalidNamedParameter;
|
||||
nativeQuery, namedQuery, findWithLockMethod, invalidNamedParameter, findsProjections, findsProjection;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
@@ -82,6 +82,9 @@ public class JpaQueryMethodUnitTests {
|
||||
|
||||
findWithLockMethod = ValidRepository.class.getMethod("findOneLocked", Integer.class);
|
||||
invalidNamedParameter = InvalidRepository.class.getMethod("findByAnnotatedQuery", String.class);
|
||||
|
||||
findsProjections = ValidRepository.class.getMethod("findsProjections");
|
||||
findsProjection = ValidRepository.class.getMethod("findsProjection");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -275,6 +278,21 @@ public class JpaQueryMethodUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-207
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void returnsTrueIfReturnTypeIsEntity() {
|
||||
|
||||
when(metadata.getDomainType()).thenReturn((Class) User.class);
|
||||
when(metadata.getReturnedDomainClass(findsProjections)).thenReturn((Class) Integer.class);
|
||||
when(metadata.getReturnedDomainClass(findsProjection)).thenReturn((Class) Integer.class);
|
||||
|
||||
assertThat(new JpaQueryMethod(findsProjections, metadata, extractor).isQueryMethodForEntity(), is(false));
|
||||
assertThat(new JpaQueryMethod(findsProjection, metadata, extractor).isQueryMethodForEntity(), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to define invalid repository methods for testing.
|
||||
*
|
||||
@@ -322,5 +340,9 @@ public class JpaQueryMethodUnitTests {
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select u from User u where u.id = ?1")
|
||||
List<User> findOneLocked(Integer primaryKey);
|
||||
|
||||
List<Integer> findsProjections();
|
||||
|
||||
Integer findsProjection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,4 +240,6 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
|
||||
*/
|
||||
List<User> findByFirstnameContaining(String firstname);
|
||||
|
||||
@Query(value = "SELECT 1 FROM User", nativeQuery = true)
|
||||
List<Integer> findOnesByNativeQuery();
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
|
||||
SampleWithIdClass.class, em);
|
||||
Object id = information.getId(entity);
|
||||
|
||||
assertThat(id, is(SampleWithIdClassPK.class));
|
||||
assertThat(id, is(instanceOf(SampleWithIdClassPK.class)));
|
||||
assertThat(id, is((Object) new SampleWithIdClassPK(2L, 4L)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
|
||||
|
||||
<bean id="vendorAdaptor"
|
||||
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" parent="abstractVendorAdaptor" />
|
||||
<bean id="vendorAdaptor" class="org.springframework.data.jpa.repository.CustomHsqlHibernateJpaVendorAdaptor" parent="abstractVendorAdaptor" />
|
||||
|
||||
<util:properties id="jpaProperties" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user