/*
* 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.
* 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.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.jpa.repository.query.QueryUtils.*;
import java.util.Set;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.JpaSort;
/**
* Unit test for {@link QueryUtils}.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Komi Innocent
* @author Christoph Strobl
*/
public class QueryUtilsUnitTests {
static final String QUERY = "select u from User u";
static final String FQ_QUERY = "select u from org.acme.domain.User$Foo_Bar u";
static final String SIMPLE_QUERY = "from User u";
static final String COUNT_QUERY = "select count(u) from User u";
static final String QUERY_WITH_AS = "select u from User as u where u.username = ?";
static final Matcher<String> IS_U = is("u");
@Test
public void createsCountQueryCorrectly() throws Exception {
assertCountQuery(QUERY, COUNT_QUERY);
}
@Test
public void createsCountQueriesCorrectlyForCapitalLetterJPQL() {
assertCountQuery("FROM User u WHERE u.foo.bar = ?", "select count(u) FROM User u WHERE u.foo.bar = ?");
assertCountQuery("SELECT u FROM User u where u.foo.bar = ?", "select count(u) FROM User u where u.foo.bar = ?");
}
@Test
public void createsCountQueryForDistinctQueries() throws Exception {
assertCountQuery("select distinct u from User u where u.foo = ?",
"select count(distinct u) from User u where u.foo = ?");
}
@Test
public void createsCountQueryForConstructorQueries() throws Exception {
assertCountQuery("select distinct new User(u.name) from User u where u.foo = ?",
"select count(distinct u) from User u where u.foo = ?");
}
@Test
public void createsCountQueryForJoins() throws Exception {
assertCountQuery("select distinct new User(u.name) from User u left outer join u.roles r WHERE r = ?",
"select count(distinct u) from User u left outer join u.roles r WHERE r = ?");
}
@Test
public void createsCountQueryForQueriesWithSubSelects() throws Exception {
assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role)",
"select count(u) from User u left outer join u.roles r where r in (select r from Role)");
}
@Test
public void createsCountQueryForAliasesCorrectly() throws Exception {
assertCountQuery("select u from User as u", "select count(u) from User as u");
}
@Test
public void allowsShortJpaSyntax() throws Exception {
assertCountQuery(SIMPLE_QUERY, COUNT_QUERY);
}
@Test
public void detectsAliasCorrectly() throws Exception {
assertThat(detectAlias(QUERY), IS_U);
assertThat(detectAlias(SIMPLE_QUERY), IS_U);
assertThat(detectAlias(COUNT_QUERY), IS_U);
assertThat(detectAlias(QUERY_WITH_AS), IS_U);
assertThat(detectAlias("SELECT FROM USER U"), is("U"));
assertThat(detectAlias("select u from User u"), IS_U);
assertThat(detectAlias("select u from com.acme.User u"), IS_U);
assertThat(detectAlias("select u from T05User u"), IS_U);
}
@Test
public void allowsFullyQualifiedEntityNamesInQuery() {
assertThat(detectAlias(FQ_QUERY), IS_U);
assertCountQuery(FQ_QUERY, "select count(u) from org.acme.domain.User$Foo_Bar u");
}
@Test // DATAJPA-252
public void detectsJoinAliasesCorrectly() {
Set<String> aliases = getOuterJoinAliases("select p from Person p left outer join x.foo b2_$ar where …");
assertThat(aliases, hasSize(1));
assertThat(aliases, hasItems("b2_$ar"));
aliases = getOuterJoinAliases("select p from Person p left join x.foo b2_$ar where …");
assertThat(aliases, hasSize(1));
assertThat(aliases, hasItems("b2_$ar"));
aliases = getOuterJoinAliases(
"select p from Person p left outer join x.foo as b2_$ar, left join x.bar as foo where …");
assertThat(aliases, hasSize(2));
assertThat(aliases, hasItems("b2_$ar", "foo"));
aliases = getOuterJoinAliases(
"select p from Person p left join x.foo as b2_$ar, left outer join x.bar foo where …");
assertThat(aliases, hasSize(2));
assertThat(aliases, hasItems("b2_$ar", "foo"));
}
@Test // DATAJPA-252
public void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() {
String query = "select p from Person p left join p.address address";
assertThat(applySorting(query, Sort.by("address.city")), endsWith("order by address.city asc"));
assertThat(applySorting(query, Sort.by("address.city", "lastname"), "p"),
endsWith("order by address.city asc, p.lastname asc"));
}
@Test // DATAJPA-252
public void extendsExistingOrderByClausesCorrectly() {
String query = "select p from Person p order by p.lastname asc";
assertThat(applySorting(query, Sort.by("firstname"), "p"), endsWith("order by p.lastname asc, p.firstname asc"));
}
@Test // DATAJPA-296
public void appliesIgnoreCaseOrderingCorrectly() {
Sort sort = Sort.by(Order.by("firstname").ignoreCase());
String query = "select p from Person p";
assertThat(applySorting(query, sort, "p"), endsWith("order by lower(p.firstname) asc"));
}
@Test // DATAJPA-296
public void appendsIgnoreCaseOrderingCorrectly() {
Sort sort = Sort.by(Order.by("firstname").ignoreCase());
String query = "select p from Person p order by p.lastname asc";
assertThat(applySorting(query, sort, "p"), endsWith("order by p.lastname asc, lower(p.firstname) asc"));
}
@Test // DATAJPA-342
public void usesReturnedVariableInCOuntProjectionIfSet() {
assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc",
"select count(distinct m.genre) from Media m where m.user = ?1");
}
@Test // DATAJPA-343
public void projectsCOuntQueriesForQueriesWithSubselects() {
assertCountQuery("select o from Foo o where cb.id in (select b from Bar b)",
"select count(o) from Foo o where cb.id in (select b from Bar b)");
}
@Test(expected = InvalidDataAccessApiUsageException.class) // DATAJPA-148
public void doesNotPrefixSortsIfFunction() {
Sort sort = Sort.by("sum(foo)");
assertThat(applySorting("select p from Person p", sort, "p"), endsWith("order by sum(foo) asc"));
}
@Test // DATAJPA-377
public void removesOrderByInGeneratedCountQueryFromOriginalQueryIfPresent() {
assertCountQuery("select distinct m.genre from Media m where m.user = ?1 OrDer By m.genre ASC",
"select count(distinct m.genre) from Media m where m.user = ?1");
}
@Test // DATAJPA-375
public void findsExistingOrderByIndependentOfCase() {
Sort sort = Sort.by("lastname");
String query = applySorting("select p from Person p ORDER BY p.firstname", sort, "p");
assertThat(query, endsWith("ORDER BY p.firstname, p.lastname asc"));
}
@Test // DATAJPA-409
public void createsCountQueryForNestedReferenceCorrectly() {
assertCountQuery("select a.b from A a", "select count(a.b) from A a");
}
@Test // DATAJPA-420
public void createsCountQueryForScalarSelects() {
assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p");
}
@Test // DATAJPA-456
public void createCountQueryFromTheGivenCountProjection() {
assertThat(createCountQueryFor("select p.lastname,p.firstname from Person p", "p.lastname"),
is("select count(p.lastname) from Person p"));
}
@Test // DATAJPA-726
public void detectsAliassesInPlainJoins() {
String query = "select p from Customer c join c.productOrder p where p.delayed = true";
Sort sort = Sort.by("p.lineItems");
assertThat(applySorting(query, sort, "c"), endsWith("order by p.lineItems asc"));
}
@Test // DATAJPA-736
public void supportsNonAsciiCharactersInEntityNames() {
assertThat(createCountQueryFor("select u from Usèr u"), is("select count(u) from Usèr u"));
}
@Test // DATAJPA-798
public void detectsAliasInQueryContainingLineBreaks() {
assertThat(detectAlias("select \n u \n from \n User \nu"), is("u"));
}
@Test // DATAJPA-815
public void doesPrefixPropertyWith() {
String query = "from Cat c join Dog d";
Sort sort = Sort.by("dPropertyStartingWithJoinAlias");
assertThat(applySorting(query, sort, "c"), endsWith("order by c.dPropertyStartingWithJoinAlias asc"));
}
@Test // DATAJPA-938
public void detectsConstructorExpressionInDistinctQuery() {
assertThat(hasConstructorExpression("select distinct new Foo() from Bar b"), is(true));
}
@Test // DATAJPA-938
public void detectsComplexConstructorExpression() {
assertThat(hasConstructorExpression("select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) " //
+ "from Bar lp join lp.investmentProduct ip " //
+ "where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId " //
+ "group by ip.id, ip.name, lp.accountId " //
+ "order by ip.name ASC"), is(true));
}
@Test // DATAJPA-938
public void detectsConstructorExpressionWithLineBreaks() {
assertThat(hasConstructorExpression("select new foo.bar.FooBar(\na.id) from DtoA a "), is(true));
}
@Test // DATAJPA-960
public void doesNotQualifySortIfNoAliasDetected() {
assertThat(applySorting("from mytable where ?1 is null", Sort.by("firstname")), endsWith("order by firstname asc"));
}
@Test(expected = InvalidDataAccessApiUsageException.class) // DATAJPA-965, DATAJPA-970
public void doesNotAllowWhitespaceInSort() {
Sort sort = Sort.by("case when foo then bar");
applySorting("select p from Person p", sort, "p");
}
@Test // DATAJPA-965, DATAJPA-970
public void doesNotPrefixUnsageJpaSortFunctionCalls() {
JpaSort sort = JpaSort.unsafe("sum(foo)");
assertThat(applySorting("select p from Person p", sort, "p"), endsWith("order by sum(foo) asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void doesNotPrefixMultipleAliasedFunctionCalls() {
String query = "SELECT AVG(m.price) AS avgPrice, SUM(m.stocks) AS sumStocks FROM Magazine m";
Sort sort = Sort.by("avgPrice", "sumStocks");
assertThat(applySorting(query, sort, "m"), endsWith("order by avgPrice asc, sumStocks asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void doesNotPrefixSingleAliasedFunctionCalls() {
String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m";
Sort sort = Sort.by("avgPrice");
assertThat(applySorting(query, sort, "m"), endsWith("order by avgPrice asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() {
String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m";
Sort sort = Sort.by("someOtherProperty");
assertThat(applySorting(query, sort, "m"), endsWith("order by m.someOtherProperty asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainesAliasedFunctionForDifferentProperty() {
String query = "SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m";
Sort sort = Sort.by("name", "avgPrice");
assertThat(applySorting(query, sort, "m"), endsWith("order by m.name asc, avgPrice asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() {
String query = "SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m";
Sort sort = Sort.by("trimmedName");
assertThat(applySorting(query, sort, "m"), endsWith("order by trimmedName asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() {
String query = "SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m";
Sort sort = Sort.by("extendedName");
assertThat(applySorting(query, sort, "m"), endsWith("order by extendedName asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void doesNotPrefixAliasedFunctionCallNameWithUnderscores() {
String query = "SELECT AVG(m.price) AS avg_price FROM Magazine m";
Sort sort = Sort.by("avg_price");
assertThat(applySorting(query, sort, "m"), endsWith("order by avg_price asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void doesNotPrefixAliasedFunctionCallNameWithDots() {
String query = "SELECT AVG(m.price) AS m.avg FROM Magazine m";
Sort sort = Sort.by("m.avg");
assertThat(applySorting(query, sort, "m"), endsWith("order by m.avg asc"));
}
@Test // DATAJPA-965, DATAJPA-970
public void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpaces() {
String query = "SELECT AVG( m.price ) AS avgPrice FROM Magazine m";
Sort sort = Sort.by("avgPrice");
assertThat(applySorting(query, sort, "m"), endsWith("order by avgPrice asc"));
}
@Test // DATAJPA-1000
public void discoversCorrectAliasForJoinFetch() {
Set<String> aliases = QueryUtils
.getOuterJoinAliases("SELECT DISTINCT user FROM User user LEFT JOIN FETCH user.authorities AS authority");
assertThat(aliases, contains("authority"));
}
private static void assertCountQuery(String originalQuery, String countQuery) {
assertThat(createCountQueryFor(originalQuery), is(countQuery));
}
}