diff --git a/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java index 39f8e0258..d8baf0d48 100644 --- a/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java +++ b/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -63,7 +63,7 @@ public abstract class HibernateUtils { queryInterface = ClassUtils.forName("org.hibernate.query.Query", classLoader); } catch (Exception o_O) {} - HIBERNATE_QUERY_INTERFACE = queryInterface; + HIBERNATE_QUERY_INTERFACE = queryInterface == null ? type : queryInterface; QUERY_STRING_METHOD = HIBERNATE_QUERY_INTERFACE == null ? null : ReflectionUtils.findMethod(HIBERNATE_QUERY_INTERFACE, "getQueryString"); } @@ -76,10 +76,15 @@ public abstract class HibernateUtils { */ public static String getHibernateQuery(Object query) { - if (HIBERNATE_QUERY_INTERFACE != null && HIBERNATE_QUERY_INTERFACE.isInstance(query)) { + if (HIBERNATE_QUERY_INTERFACE != null && QUERY_STRING_METHOD != null + && HIBERNATE_QUERY_INTERFACE.isInstance(query)) { return String.class.cast(ReflectionUtils.invokeMethod(QUERY_STRING_METHOD, query)); } + if (HIBERNATE_QUERY_INTERFACE != null && !HIBERNATE_QUERY_INTERFACE.isInstance(query)) { + query = ((javax.persistence.Query) query).unwrap(HIBERNATE_QUERY_INTERFACE); + } + return ((Query) ReflectionUtils.invokeMethod(GET_HIBERNATE_QUERY, query)).getQueryString(); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index 6eeb3eac3..bbbddb90a 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2013 the original author or authors. + * Copyright 2008-2017 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. @@ -29,6 +29,7 @@ import org.springframework.data.repository.query.parser.PartTree; * Special {@link JpaQueryCreator} that creates a count projecting query. * * @author Oliver Gierke + * @author Marc Lefrançois */ public class JpaCountQueryCreator extends JpaQueryCreator { @@ -59,11 +60,16 @@ public class JpaCountQueryCreator extends JpaQueryCreator { * @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 - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings("unchecked") protected CriteriaQuery complete(Predicate predicate, Sort sort, CriteriaQuery query, CriteriaBuilder builder, Root root) { - CriteriaQuery select = query.select((Expression) builder.count(root)); + CriteriaQuery select = query.select(getCountQuery(query, builder, root)); return predicate == null ? select : select.where(predicate); } + + @SuppressWarnings("rawtypes") + private static Expression getCountQuery(CriteriaQuery query, CriteriaBuilder builder, Root root) { + return query.isDistinct() ? builder.countDistinct(root) : builder.count(root); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java new file mode 100644 index 000000000..52ebce262 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2017 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.query; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.lang.reflect.Method; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.data.jpa.domain.sample.Role; +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.projection.SpelAwareProxyProjectionFactory; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.core.support.AbstractRepositoryMetadata; +import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration tests for {@link JpaCountQueryCreator}. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:infrastructure.xml") +public class JpaCountQueryCreatorIntegrationTests { + + @PersistenceContext EntityManager entityManager; + + @Test // DATAJPA-1044 + public void distinctFlagOnCountQueryIssuesCountDistinct() throws Exception { + + Method method = SomeRepository.class.getMethod("findDistinctByRolesIn", List.class); + + PersistenceProvider provider = PersistenceProvider.fromEntityManager(entityManager); + JpaQueryMethod queryMethod = new JpaQueryMethod(method, + AbstractRepositoryMetadata.getMetadata(SomeRepository.class), new SpelAwareProxyProjectionFactory(), provider); + + PartTree tree = new PartTree("findDistinctByRolesIn", User.class); + ParameterMetadataProvider metadataProvider = new ParameterMetadataProvider(entityManager.getCriteriaBuilder(), + queryMethod.getParameters(), provider); + + JpaCountQueryCreator creator = new JpaCountQueryCreator(tree, queryMethod.getResultProcessor().getReturnedType(), + entityManager.getCriteriaBuilder(), metadataProvider); + + TypedQuery query = entityManager.createQuery(creator.createQuery()); + + assertThat(HibernateUtils.getHibernateQuery(query), startsWith("select distinct count(distinct")); + } + + interface SomeRepository extends Repository { + void findDistinctByRolesIn(List roles); + } +}