DATAJPA-1564 - Support for linebreaks in SQL and JPQL statements.
Annotated queries now my contain linebreaks. This seems especially relevant for JVM languages with multiline String literals. Original pull request: #386.
This commit is contained in:
committed by
Jens Schauder
parent
d931c0b20b
commit
6c8421b54a
@@ -73,6 +73,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Jens Schauder
|
* @author Jens Schauder
|
||||||
* @author Florian Lüdiger
|
* @author Florian Lüdiger
|
||||||
* @author Grégoire Druant
|
* @author Grégoire Druant
|
||||||
|
* @author Mohammad Hewedy
|
||||||
*/
|
*/
|
||||||
public abstract class QueryUtils {
|
public abstract class QueryUtils {
|
||||||
|
|
||||||
@@ -81,7 +82,8 @@ public abstract class QueryUtils {
|
|||||||
|
|
||||||
private static final String COUNT_REPLACEMENT_TEMPLATE = "select count(%s) $5$6$7";
|
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 SIMPLE_COUNT_VALUE = "$2";
|
||||||
private static final String COMPLEX_COUNT_VALUE = "$3$6";
|
private static final String COMPLEX_COUNT_VALUE = "$3 $6";
|
||||||
|
private static final String COMPLEX_COUNT_LAST_VALUE = "$6";
|
||||||
private static final String ORDER_BY_PART = "(?iu)\\s+order\\s+by\\s+.*";
|
private static final String ORDER_BY_PART = "(?iu)\\s+order\\s+by\\s+.*";
|
||||||
|
|
||||||
private static final Pattern ALIAS_MATCH;
|
private static final Pattern ALIAS_MATCH;
|
||||||
@@ -107,6 +109,7 @@ public abstract class QueryUtils {
|
|||||||
|
|
||||||
private static final int QUERY_JOIN_ALIAS_GROUP_INDEX = 3;
|
private static final int QUERY_JOIN_ALIAS_GROUP_INDEX = 3;
|
||||||
private static final int VARIABLE_NAME_GROUP_INDEX = 4;
|
private static final int VARIABLE_NAME_GROUP_INDEX = 4;
|
||||||
|
private static final int COMPLEX_COUNT_FIRST_INDEX = 3;
|
||||||
|
|
||||||
private static final Pattern PUNCTATION_PATTERN = Pattern.compile(".*((?![\\._])[\\p{Punct}|\\s])");
|
private static final Pattern PUNCTATION_PATTERN = Pattern.compile(".*((?![\\._])[\\p{Punct}|\\s])");
|
||||||
private static final Pattern FUNCTION_PATTERN;
|
private static final Pattern FUNCTION_PATTERN;
|
||||||
@@ -124,12 +127,12 @@ public abstract class QueryUtils {
|
|||||||
builder.append(IDENTIFIER_GROUP); // Entity name, can be qualified (any
|
builder.append(IDENTIFIER_GROUP); // Entity name, can be qualified (any
|
||||||
builder.append("(?:\\sas)*"); // exclude possible "as" keyword
|
builder.append("(?:\\sas)*"); // exclude possible "as" keyword
|
||||||
builder.append("(?:\\s)+"); // at least one space separating
|
builder.append("(?:\\s)+"); // at least one space separating
|
||||||
builder.append("(?!(?:where|group by|order by))(\\w+)"); // the actual alias
|
builder.append("(?!(?:where|group\\s*by|order\\s*by))(\\w+)"); // the actual alias
|
||||||
|
|
||||||
ALIAS_MATCH = compile(builder.toString(), CASE_INSENSITIVE);
|
ALIAS_MATCH = compile(builder.toString(), CASE_INSENSITIVE);
|
||||||
|
|
||||||
builder = new StringBuilder();
|
builder = new StringBuilder();
|
||||||
builder.append("(select\\s+((distinct )?(.+?)?)\\s+)?(from\\s+");
|
builder.append("(select\\s+((distinct)?((?s).+?)?)\\s+)?(from\\s+");
|
||||||
builder.append(IDENTIFIER);
|
builder.append(IDENTIFIER);
|
||||||
builder.append("(?:\\s+as)?\\s+)");
|
builder.append("(?:\\s+as)?\\s+)");
|
||||||
builder.append(IDENTIFIER_GROUP);
|
builder.append(IDENTIFIER_GROUP);
|
||||||
@@ -467,10 +470,14 @@ public abstract class QueryUtils {
|
|||||||
if (countProjection == null) {
|
if (countProjection == null) {
|
||||||
|
|
||||||
String variable = matcher.matches() ? matcher.group(VARIABLE_NAME_GROUP_INDEX) : null;
|
String variable = matcher.matches() ? matcher.group(VARIABLE_NAME_GROUP_INDEX) : null;
|
||||||
boolean useVariable = variable != null && StringUtils.hasText(variable) && !variable.startsWith("new")
|
boolean useVariable = StringUtils.hasText(variable) && !variable.startsWith(" new")
|
||||||
&& !variable.startsWith("count(") && !variable.contains(",");
|
&& !variable.startsWith("count(") && !variable.contains(",");
|
||||||
|
|
||||||
String replacement = useVariable ? SIMPLE_COUNT_VALUE : COMPLEX_COUNT_VALUE;
|
String complexCountValue = matcher.matches() &&
|
||||||
|
StringUtils.hasText(matcher.group(COMPLEX_COUNT_FIRST_INDEX)) ?
|
||||||
|
COMPLEX_COUNT_VALUE : COMPLEX_COUNT_LAST_VALUE;
|
||||||
|
|
||||||
|
String replacement = useVariable ? SIMPLE_COUNT_VALUE : complexCountValue;
|
||||||
countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, replacement));
|
countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, replacement));
|
||||||
} else {
|
} else {
|
||||||
countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, countProjection));
|
countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, countProjection));
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import org.springframework.data.jpa.domain.JpaSort;
|
|||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @author Florian Lüdiger
|
* @author Florian Lüdiger
|
||||||
* @author Grégoire Druant
|
* @author Grégoire Druant
|
||||||
|
* @author Mohammad Hewedy
|
||||||
*/
|
*/
|
||||||
public class QueryUtilsUnitTests {
|
public class QueryUtilsUnitTests {
|
||||||
|
|
||||||
@@ -416,8 +417,20 @@ public class QueryUtilsUnitTests {
|
|||||||
public void createCountQuerySupportsWhitespaceCharacters() {
|
public void createCountQuerySupportsWhitespaceCharacters() {
|
||||||
|
|
||||||
assertThat(createCountQueryFor("select * from User user\n" + //
|
assertThat(createCountQueryFor("select * from User user\n" + //
|
||||||
" where user.age = 18\n" + //
|
" where user.age = 18\n" + //
|
||||||
" order by user.name\n "), //
|
" order by user.name\n "), //
|
||||||
|
is("select count(user) from User user\n" + //
|
||||||
|
" where user.age = 18\n "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createCountQuerySupportsLineBreaksInSelectClause() {
|
||||||
|
|
||||||
|
assertThat(createCountQueryFor("select user.age,\n" + //
|
||||||
|
" user.name\n" + //
|
||||||
|
" from User user\n" + //
|
||||||
|
" where user.age = 18\n" + //
|
||||||
|
" order\nby\nuser.name\n "), //
|
||||||
is("select count(user) from User user\n" + //
|
is("select count(user) from User user\n" + //
|
||||||
" where user.age = 18\n "));
|
" where user.age = 18\n "));
|
||||||
}
|
}
|
||||||
@@ -452,6 +465,26 @@ public class QueryUtilsUnitTests {
|
|||||||
assertThat(fullQuery, endsWith("order by m.price asc"));
|
assertThat(fullQuery, endsWith("order by m.price asc"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createCountQuerySupportsLineBreakRightAfterDistinct() {
|
||||||
|
|
||||||
|
assertThat(createCountQueryFor("select\ndistinct\nuser.age,\n" + //
|
||||||
|
"user.name\n" + //
|
||||||
|
"from\nUser\nuser"), //
|
||||||
|
is(createCountQueryFor("select\ndistinct user.age,\n" + //
|
||||||
|
"user.name\n" + //
|
||||||
|
"from\nUser\nuser")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void detectsAliasWithGroupAndOrderByWithLineBreaks() {
|
||||||
|
|
||||||
|
assertThat(detectAlias("select * from User group\nby name"), is(nullValue()));
|
||||||
|
assertThat(detectAlias("select * from User order\nby name"), is(nullValue()));
|
||||||
|
assertThat(detectAlias("select * from User u group\nby name"), is("u"));
|
||||||
|
assertThat(detectAlias("select * from User u order\nby name"), is("u"));
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertCountQuery(String originalQuery, String countQuery) {
|
private static void assertCountQuery(String originalQuery, String countQuery) {
|
||||||
assertThat(createCountQueryFor(originalQuery), is(countQuery));
|
assertThat(createCountQueryFor(originalQuery), is(countQuery));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user