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:
Oliver Gierke
2012-05-15 17:38:24 +02:00
parent 831cf83658
commit f466744acd
10 changed files with 118 additions and 19 deletions

20
pom.xml
View File

@@ -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>

View File

@@ -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.

View File

@@ -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);

View File

@@ -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");
}
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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)));
}
}

View File

@@ -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" />