DATAJPA-231 - Added support for derived countBy queries.
PartTreeJpaQuery evaluates the newly introduced PartTree.isCountProjection() to rather trigger a derived count query instead of the actual query. Extended Query execution to potentially massage the returned value into the return type of the query method to allow long and int as return types for count queries.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2011 the original author or authors.
|
||||
* Copyright 2008-2013 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.
|
||||
@@ -40,25 +40,16 @@ public class JpaCountQueryCreator extends JpaQueryCreator {
|
||||
*/
|
||||
public JpaCountQueryCreator(PartTree tree, Class<?> domainClass, CriteriaBuilder builder,
|
||||
ParameterMetadataProvider provider) {
|
||||
|
||||
super(tree, domainClass, builder, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.JpaQueryCreator#complete
|
||||
* (javax.persistence.criteria.Predicate,
|
||||
* org.springframework.data.domain.Sort,
|
||||
* javax.persistence.criteria.CriteriaQuery,
|
||||
* javax.persistence.criteria.CriteriaBuilder,
|
||||
* javax.persistence.criteria.Root)
|
||||
* @see org.springframework.data.jpa.repository.query.JpaQueryCreator#complete(javax.persistence.criteria.Predicate, org.springframework.data.domain.Sort, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder, javax.persistence.criteria.Root)
|
||||
*/
|
||||
@Override
|
||||
protected CriteriaQuery<Object> complete(Predicate predicate, Sort sort, CriteriaQuery<Object> query,
|
||||
CriteriaBuilder builder, Root<?> root) {
|
||||
|
||||
return query.select(builder.count(root)).where(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2011 the original author or authors.
|
||||
* Copyright 2008-2013 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.
|
||||
@@ -23,6 +23,8 @@ import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
@@ -39,11 +41,13 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public abstract class JpaQueryExecution {
|
||||
|
||||
private static final ConversionService conversionService = new DefaultConversionService();
|
||||
|
||||
/**
|
||||
* Executes the given {@link AbstractStringBasedJpaQuery} with the given {@link ParameterBinder}.
|
||||
*
|
||||
* @param query
|
||||
* @param binder
|
||||
* @param query must not be {@literal null}.
|
||||
* @param binder must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Object execute(AbstractJpaQuery query, Object[] values) {
|
||||
@@ -51,11 +55,26 @@ public abstract class JpaQueryExecution {
|
||||
Assert.notNull(query);
|
||||
Assert.notNull(values);
|
||||
|
||||
Object result;
|
||||
|
||||
try {
|
||||
return doExecute(query, values);
|
||||
result = doExecute(query, values);
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
JpaQueryMethod queryMethod = query.getQueryMethod();
|
||||
Class<?> requiredType = queryMethod.getReturnType();
|
||||
|
||||
if (void.class.equals(requiredType) || requiredType.isAssignableFrom(result.getClass())) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return conversionService.convert(result, requiredType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2011 the original author or authors.
|
||||
* Copyright 2008-2013 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.
|
||||
@@ -46,8 +46,8 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
/**
|
||||
* Creates a new {@link PartTreeJpaQuery}.
|
||||
*
|
||||
* @param method
|
||||
* @param em
|
||||
* @param method must not be {@literal null}.
|
||||
* @param em must not be {@literal null}.
|
||||
*/
|
||||
public PartTreeJpaQuery(JpaQueryMethod method, EntityManager em) {
|
||||
|
||||
@@ -57,8 +57,8 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
this.tree = new PartTree(method.getName(), domainClass);
|
||||
this.parameters = method.getParameters();
|
||||
|
||||
this.query = new QueryPreparer(parameters.potentiallySortsDynamically());
|
||||
this.countQuery = new CountQueryPreparer(parameters.potentiallySortsDynamically());
|
||||
this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(parameters.potentiallySortsDynamically());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -163,15 +163,12 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
private class CountQueryPreparer extends QueryPreparer {
|
||||
|
||||
public CountQueryPreparer(boolean recreateQueries) {
|
||||
|
||||
super(recreateQueries);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.query.PartTreeJpaQuery.
|
||||
* QueryPreparer#createCreator()
|
||||
* @see org.springframework.data.jpa.repository.query.PartTreeJpaQuery.QueryPreparer#createCreator(org.springframework.data.repository.query.ParametersParameterAccessor)
|
||||
*/
|
||||
@Override
|
||||
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor) {
|
||||
@@ -191,8 +188,8 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
* @see org.springframework.data.jpa.repository.query.PartTreeJpaQuery.QueryPreparer#invokeBinding(org.springframework.data.jpa.repository.query.ParameterBinder,
|
||||
* javax.persistence.TypedQuery)
|
||||
*/
|
||||
@Override
|
||||
protected Query invokeBinding(ParameterBinder binder, javax.persistence.TypedQuery<?> query) {
|
||||
|
||||
return binder.bind(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1027,6 +1027,28 @@ public class UserRepositoryTests {
|
||||
assertThat(result, hasItem(thirdUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-231
|
||||
*/
|
||||
@Test
|
||||
public void executesDerivedCountQueryToLong() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
assertThat(repository.countByLastname("Matthews"), is(1L));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-231
|
||||
*/
|
||||
@Test
|
||||
public void executesDerivedCountQueryToInt() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
assertThat(repository.countUsersByFirstname("Dave"), is(1));
|
||||
}
|
||||
|
||||
private Page<User> executeSpecWithSort(Sort sort) {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2011 the original author or authors.
|
||||
* Copyright 2008-2013 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.
|
||||
@@ -88,6 +88,7 @@ public class JpaQueryExecutionUnitTests {
|
||||
when(query.executeUpdate()).thenReturn(0);
|
||||
when(method.getReturnType()).thenReturn((Class) void.class);
|
||||
when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query);
|
||||
when(jpaQuery.getQueryMethod()).thenReturn(method);
|
||||
|
||||
ModifyingExecution execution = new ModifyingExecution(method, em);
|
||||
execution.execute(jpaQuery, new Object[] {});
|
||||
|
||||
@@ -254,4 +254,14 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
|
||||
|
||||
@Query(value = "SELECT 1 FROM User", nativeQuery = true)
|
||||
List<Integer> findOnesByNativeQuery();
|
||||
|
||||
/**
|
||||
* @see DATAJPA-231
|
||||
*/
|
||||
long countByLastname(String lastname);
|
||||
|
||||
/**
|
||||
* @see DATAJPA-231
|
||||
*/
|
||||
int countUsersByFirstname(String firstname);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user