DATAJPA-252 - Applying sort to manually defined queries considers joins.

If a manually defined query uses outer joins, prefixing the sort criteria handed to the execution of the method causes an additional join being added as the JPA considers path expressions to be inner joins always (see Sect. 4.4.4, JPA 2.0 spec).

We now parse the outer join aliases potentially used in a manually defined JPQL query and do not prefix the query with the root entity's alias.
This commit is contained in:
Oliver Gierke
2012-08-29 17:16:33 +02:00
parent 0e6c4fd36a
commit a9cfe73511
4 changed files with 130 additions and 12 deletions

View File

@@ -921,6 +921,19 @@ public class UserRepositoryTests {
assertThat(all.getContent().isEmpty(), is(false));
}
/**
* @see DATAJPA-252
*/
@Test
public void bindsSortingToOuterJoinCorrectly() {
flushTestUsers();
// Managers not set, make sure adding the sort does not rule out those Users
Page<User> result = repository.findAllPaged(new PageRequest(0, 10, new Sort("manager.lastname")));
assertThat(result.getContent(), hasSize((int) repository.count()));
}
private Page<User> executeSpecWithSort(Sort sort) {
flushTestUsers();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2011 the original author or authors.
* Copyright 2008-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.
@@ -15,12 +15,15 @@
*/
package org.springframework.data.jpa.repository.query;
import static org.hamcrest.CoreMatchers.*;
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.data.domain.Sort;
/**
* Unit test for {@link QueryUtils}.
@@ -131,8 +134,52 @@ public class QueryUtilsUnitTests {
assertCountQuery(FQ_QUERY, "select count(u) from org.acme.domain.User$Foo_Bar u");
}
private void assertCountQuery(String originalQuery, String countQuery) {
/**
* @see DATAJPA-252
*/
@Test
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"));
}
/**
* @see DATAJPA-252
*/
@Test
public void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() {
String query = "select p from Person p left join p.address address";
assertThat(applySorting(query, new Sort("address.city")), endsWith("order by address.city asc"));
assertThat(applySorting(query, new Sort("address.city", "lastname"), "p"),
endsWith("order by address.city asc, p.lastname asc"));
}
/**
* @see DATAJPA-252
*/
@Test
public void extendsExistingOrderByClausesCorrectly() {
String query = "select p from Person p order by p.lastname asc";
assertThat(applySorting(query, new Sort("firstname"), "p"), endsWith("order by p.lastname asc, p.firstname asc"));
}
private void assertCountQuery(String originalQuery, String countQuery) {
assertThat(createCountQueryFor(originalQuery), is(countQuery));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2011 the original author or authors.
* Copyright 2008-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.
@@ -68,7 +68,7 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
*/
User findByEmailAddress(String emailAddress);
@Query("select u from User u ")
@Query("select u from User u left outer join u.manager as manager")
Page<User> findAllPaged(Pageable pageable);
/**