DATAJPA-342 - Corrected generated count projection from string queries.

The variable put into the count(…) clause of the generated query is now the original value except we find a DTO project or a count projection in the first place.
This commit is contained in:
Oliver Gierke
2013-05-07 14:01:25 +02:00
parent 31006e1866
commit d655862251
3 changed files with 41 additions and 13 deletions

View File

@@ -62,7 +62,9 @@ public abstract class QueryUtils {
public static final String DELETE_ALL_QUERY_STRING = "delete from %s x";
private static final String DEFAULT_ALIAS = "x";
private static final String COUNT_REPLACEMENT = "select count($3$5) $4$5$6";
private static final String COUNT_REPLACEMENT_TEMPLATE = "select count(%s) $5$6$7";
private static final String SIMPLE_COUNT_VALUE = "$2";
private static final String COMPLEX_COUNT_VALUE = "$3$6";
private static final Pattern ALIAS_MATCH;
private static final Pattern COUNT_MATCH;
@@ -90,7 +92,7 @@ public abstract class QueryUtils {
ALIAS_MATCH = compile(builder.toString(), CASE_INSENSITIVE);
builder = new StringBuilder();
builder.append("(select\\s+((distinct )?.+?)\\s+)?(from\\s+");
builder.append("(select\\s+((distinct )?(.+)?)\\s+)?(from\\s+");
builder.append(IDENTIFIER);
builder.append("(?:\\s+as)?\\s+)");
builder.append(IDENTIFIER_GROUP);
@@ -326,7 +328,12 @@ public abstract class QueryUtils {
Assert.hasText(originalQuery);
Matcher matcher = COUNT_MATCH.matcher(originalQuery);
return matcher.replaceFirst(COUNT_REPLACEMENT);
String variable = matcher.matches() ? matcher.group(4) : null;
boolean useVariable = StringUtils.hasText(variable) && !variable.startsWith("new")
&& !variable.startsWith("count(");
return matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, useVariable ? SIMPLE_COUNT_VALUE
: COMPLEX_COUNT_VALUE));
}
/**

View File

@@ -51,8 +51,6 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
this.method = method;
this.query = new StringQuery(queryString);
this.countQuery = new StringQuery(method.getCountQuery() == null ? QueryUtils.createCountQueryFor(queryString)
: method.getCountQuery());
Parameters parameters = method.getParameters();
boolean hasPagingOrSortingParameter = parameters.hasPageableParameter() || parameters.hasSortParameter();
@@ -61,15 +59,28 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
throw new IllegalStateException("Cannot use native queries with dynamic sorting and/or pagination!");
}
// Try to create a Query object already to fail fast
String preparedQueryString = this.query.getQuery();
if (!method.isNativeQuery()) {
try {
em.createQuery(query.getQuery());
} catch (RuntimeException e) {
// Needed as there's ambiguities in how an invalid query string shall be expressed by the persistence provider
// http://java.net/projects/jpa-spec/lists/jsr338-experts/archive/2012-07/message/17
throw e instanceof IllegalArgumentException ? e : new IllegalArgumentException(e);
}
validateQuery(preparedQueryString, em);
}
this.countQuery = new StringQuery(method.getCountQuery() != null ? method.getCountQuery()
: QueryUtils.createCountQueryFor(preparedQueryString));
if (!method.isNativeQuery()) {
validateQuery(this.countQuery.getQuery(), em);
}
}
private final void validateQuery(String query, EntityManager em) {
try {
em.createQuery(query);
} catch (RuntimeException e) {
// Needed as there's ambiguities in how an invalid query string shall be expressed by the persistence provider
// http://java.net/projects/jpa-spec/lists/jsr338-experts/archive/2012-07/message/17
throw e instanceof IllegalArgumentException ? e : new IllegalArgumentException(e);
}
}

View File

@@ -203,6 +203,16 @@ public class QueryUtilsUnitTests {
assertThat(applySorting(query, sort, "p"), endsWith("order by p.lastname asc, lower(p.firstname) asc"));
}
/**
* @see DATAJPA-342
*/
@Test
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 order by m.genre asc");
}
private void assertCountQuery(String originalQuery, String countQuery) {
assertThat(createCountQueryFor(originalQuery), is(countQuery));
}