diff --git a/pom.xml b/pom.xml
index a230dab8b..069cad460 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,11 +24,11 @@
3.0.2
5.6.0.Final
+ 4.3
8.0.23
42.2.19
3.0.0-SNAPSHOT
0.10.3
- 4.3
org.hibernate
diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml
index e4f17e4ac..ba9220cc2 100644
--- a/spring-data-jpa/pom.xml
+++ b/spring-data-jpa/pom.xml
@@ -229,7 +229,7 @@
com.github.jsqlparser
jsqlparser
- ${jsqlparser.version}
+ ${jsqlparser}
provided
true
diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java
index 7504d8135..7dd4f9ce2 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java
@@ -15,14 +15,16 @@
*/
package org.springframework.data.jpa.repository.query;
-import org.springframework.data.domain.Sort;
-
import java.util.Set;
+import org.springframework.data.domain.Sort;
+import org.springframework.lang.Nullable;
+
/**
* The implementation of {@link QueryEnhancer} using {@link QueryUtils}.
*
* @author Diego Krupitza
+ * @since 2.7.0
*/
public class DefaultQueryEnhancer implements QueryEnhancer {
@@ -33,17 +35,7 @@ public class DefaultQueryEnhancer implements QueryEnhancer {
}
@Override
- public String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable idAttributes) {
- return QueryUtils.getExistsQueryString(entityName, countQueryPlaceHolder, idAttributes);
- }
-
- @Override
- public String getQueryString(String template, String entityName) {
- return QueryUtils.getQueryString(template, entityName);
- }
-
- @Override
- public String applySorting(Sort sort, String alias) {
+ public String applySorting(Sort sort, @Nullable String alias) {
return QueryUtils.applySorting(this.query.getQueryString(), sort, alias);
}
@@ -53,7 +45,7 @@ public class DefaultQueryEnhancer implements QueryEnhancer {
}
@Override
- public String createCountQueryFor(String countProjection) {
+ public String createCountQueryFor(@Nullable String countProjection) {
return QueryUtils.createCountQueryFor(this.query.getQueryString(), countProjection);
}
diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java
index 74fffaa86..ce399d321 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java
@@ -69,19 +69,18 @@ class ExpressionBasedStringQuery extends StringQuery {
* @param query the original query. Must not be {@literal null}.
* @param metadata the {@link JpaEntityMetadata} for the given entity. Must not be {@literal null}.
* @param parser Parser for resolving SpEL expressions. Must not be {@literal null}.
- * @param nativeQuery
+ * @param nativeQuery is a given query native or not
* @return A query supporting SpEL expressions.
*/
- static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata metadata, SpelExpressionParser parser,
- boolean nativeQuery) {
+ static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata> metadata,
+ SpelExpressionParser parser, boolean nativeQuery) {
return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser, nativeQuery);
}
/**
- * @param query, the query expression potentially containing a SpEL expression. Must not be {@literal null}.}
+ * @param query, the query expression potentially containing a SpEL expression. Must not be {@literal null}.
* @param metadata the {@link JpaEntityMetadata} for the given entity. Must not be {@literal null}.
* @param parser Must not be {@literal null}.
- * @return
*/
private static String renderQueryIfExpressionOrReturnQuery(String query, JpaEntityMetadata> metadata,
SpelExpressionParser parser) {
diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
index 220ddc868..3c4d038c0 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
@@ -15,38 +15,44 @@
*/
package org.springframework.data.jpa.repository.query;
+import static org.springframework.data.jpa.repository.query.JSqlParserUtils.*;
+import static org.springframework.data.jpa.repository.query.QueryUtils.*;
+
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
-import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
-import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
-import net.sf.jsqlparser.schema.Table;
-import net.sf.jsqlparser.statement.select.*;
-import net.sf.jsqlparser.util.SelectUtils;
+import net.sf.jsqlparser.statement.select.OrderByElement;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.Select;
+import net.sf.jsqlparser.statement.select.SelectExpressionItem;
+import net.sf.jsqlparser.statement.select.SelectItem;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
import org.springframework.data.domain.Sort;
-import org.springframework.data.util.Streamable;
+import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static org.springframework.data.jpa.repository.query.JSqlParserUtils.*;
-import static org.springframework.data.jpa.repository.query.QueryUtils.checkSortExpression;
-
/**
* The implementation of {@link QueryEnhancer} using JSqlParser.
*
* @author Diego Krupitza
+ * @author Greg Turnquist
+ * @since 2.7.0
*/
public class JSqlParserQueryEnhancer implements QueryEnhancer {
- private static final String DEFAULT_TABLE_ALIAS = "x";
-
private final DeclaredQuery query;
/**
@@ -57,40 +63,8 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
}
@Override
- public String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable idAttributes) {
- final Table tableNameWithAlias = getTableWithAlias(entityName, DEFAULT_TABLE_ALIAS);
- Function jSqlCount = getJSqlCount(Collections.singletonList(countQueryPlaceHolder), false);
+ public String applySorting(Sort sort, @Nullable String alias) {
- Select select = SelectUtils.buildSelectFromTableAndSelectItems(tableNameWithAlias,
- new SelectExpressionItem(jSqlCount));
-
- PlainSelect selectBody = (PlainSelect) select.getSelectBody();
-
- List equalityExpressions = Streamable.of(idAttributes).stream() //
- .map(field -> {
- Expression tableNameField = new Column().withTable(tableNameWithAlias).withColumnName(field);
- Expression inputField = new Column(":".concat(field));
- return new EqualsTo(tableNameField, inputField);
- }).collect(Collectors.toList());
-
- if (equalityExpressions.size() > 1) {
- AndExpression rootOfWhereClause = concatenateWithAndExpression(equalityExpressions);
- selectBody.setWhere(rootOfWhereClause);
- } else if (equalityExpressions.size() == 1) {
- selectBody.setWhere(equalityExpressions.get(0));
- }
-
- return selectBody.toString();
- }
-
- @Override
- public String getQueryString(String template, String entityName) {
- Assert.hasText(entityName, "Entity name must not be null or empty!");
- return String.format(template, entityName);
- }
-
- @Override
- public String applySorting(Sort sort, String alias) {
String queryString = query.getQueryString();
Assert.hasText(queryString, "Query must not be null or empty!");
@@ -145,6 +119,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
* @return a {@literal Set} containing all found aliases. Guaranteed to be not {@literal null}.
*/
Set getSelectionAliases() {
+
Select selectStatement = parseSelectStatement(this.query.getQueryString());
PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody();
return this.getSelectionAliases(selectBody);
@@ -189,7 +164,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
* @return a {@link OrderByElement} containing an order clause. Guaranteed to be not {@literal null}.
*/
private OrderByElement getOrderClause(final Set joinAliases, final Set selectionAliases,
- final String alias, final Sort.Order order) {
+ @Nullable final String alias, final Sort.Order order) {
final OrderByElement orderByElement = new OrderByElement();
orderByElement.setAsc(order.getDirection().isAscending());
@@ -233,7 +208,9 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
* @param query must not be {@literal null}.
* @return Might return {@literal null}.
*/
+ @Nullable
private String detectAlias(String query) {
+
Select selectStatement = parseSelectStatement(query);
PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody();
return detectAlias(selectBody);
@@ -246,13 +223,15 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
* @param selectBody must not be {@literal null}.
* @return Might return {@literal null}.
*/
+ @Nullable
private static String detectAlias(PlainSelect selectBody) {
+
Alias alias = selectBody.getFromItem().getAlias();
return alias == null ? null : alias.getName();
}
@Override
- public String createCountQueryFor(String countProjection) {
+ public String createCountQueryFor(@Nullable String countProjection) {
Assert.hasText(this.query.getQueryString(), "OriginalQuery must not be null or empty!");
@@ -298,6 +277,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
@Override
public String getProjection() {
+
Assert.hasText(query.getQueryString(), "Query must not be null or empty!");
Select selectStatement = parseSelectStatement(query.getQueryString());
@@ -321,6 +301,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
* @return the parsed query
*/
private static Select parseSelectStatement(String query) {
+
try {
return (Select) CCJSqlParserUtil.parse(query);
} catch (JSQLParserException e) {
@@ -329,12 +310,13 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
}
/**
- * Checks whether a given projection only contains a single column definition (aka without functions, etc)
+ * Checks whether a given projection only contains a single column definition (aka without functions, etc.)
*
* @param projection the projection to analyse
* @return true when the projection only contains a single column definition otherwise false
*/
private boolean onlyASingleColumnProjection(List projection) {
+
// this is unfortunately the only way to check without any hacky & hard string regex magic
return projection.size() == 1 && projection.get(0) instanceof SelectExpressionItem
&& (((SelectExpressionItem) projection.get(0)).getExpression()) instanceof Column;
diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java
index 1cc4eb1d6..4fbf0bf9c 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java
@@ -15,15 +15,10 @@
*/
package org.springframework.data.jpa.repository.query;
-import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
-import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.schema.Column;
-import net.sf.jsqlparser.schema.Table;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.StringUtils;
import java.util.Collections;
import java.util.List;
@@ -33,58 +28,12 @@ import java.util.stream.Collectors;
* A utility class for JSqlParser.
*
* @author Diego Krupitza
+ * @author Greg Turnquist
+ * @since 2.7.0
*/
public final class JSqlParserUtils {
- private JSqlParserUtils() {
- }
-
- /**
- * Generates a JSqlParser table from an entity name and an optional alias name
- *
- * @param entityName the name of the table
- * @param alias the optional alias. Might be {@literal null} or empty
- * @return the newly generated table
- */
- public static Table getTableWithAlias(String entityName, String alias) {
- Table table = new Table(entityName);
- return StringUtils.hasText(alias) ? table.withAlias(new Alias(alias)) : table;
- }
-
- /**
- * Concatenates a list of expression with AND.
- *
- * @param expressions the list of expressions to concatenate. Has to be non empty and with size >= 2
- * @return the root of the concatenated expression
- */
- public static AndExpression concatenateWithAndExpression(List expressions) {
-
- if (CollectionUtils.isEmpty(expressions) || expressions.size() == 1) {
- throw new IllegalArgumentException(
- "The list of expression has to be at least of length 2! Otherwise it is not possible to concatinate with an");
- }
-
- AndExpression rootAndExpression = new AndExpression();
- AndExpression currentLocation = rootAndExpression;
-
- // traverse the list with looking 1 element ahead
- for (int i = 0; i < expressions.size(); i++) {
- Expression currentExpression = expressions.get(i);
- if (currentLocation.getLeftExpression() == null) {
- currentLocation.setLeftExpression(currentExpression);
- } else if (currentLocation.getRightExpression() == null && i == expressions.size() - 1) {
- currentLocation.setRightExpression(currentExpression);
- } else {
- AndExpression nextAndExpression = new AndExpression();
- nextAndExpression.setLeftExpression(currentExpression);
-
- currentLocation.setRightExpression(nextAndExpression);
- currentLocation = (AndExpression) currentLocation.getRightExpression();
- }
- }
-
- return rootAndExpression;
- }
+ private JSqlParserUtils() {}
/**
* Generates a count function call, based on the {@code countFields}.
@@ -94,12 +43,14 @@ public final class JSqlParserUtils {
* @return the generated count function call
*/
public static Function getJSqlCount(final List countFields, final boolean distinct) {
+
List countColumns = countFields //
.stream() //
.map(Column::new) //
.collect(Collectors.toList());
ExpressionList countExpression = new ExpressionList(countColumns);
+
return new Function() //
.withName("count") //
.withParameters(countExpression) //
@@ -113,11 +64,12 @@ public final class JSqlParserUtils {
* @return the generated lower function call
*/
public static Function getJSqlLower(String column) {
+
List expressions = Collections.singletonList(new Column(column));
ExpressionList lowerParamExpression = new ExpressionList(expressions);
+
return new Function() //
.withName("lower") //
.withParameters(lowerParamExpression);
}
-
}
diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java
index 4afd0a79a..c23eef900 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java
@@ -55,6 +55,7 @@ import org.springframework.util.Assert;
* @author Reda.Housni-Alaoui
* @author Moritz Becker
* @author Andrey Kovalev
+ * @author Greg Turnquist
*/
public class JpaQueryCreator extends AbstractQueryCreator, Predicate> {
@@ -166,8 +167,10 @@ public class JpaQueryCreator extends AbstractQueryCreator typeToRead = returnedType.getReturnedType();
- query = typeToRead.isInterface() ? query.multiselect(selections)
- : query.select((Selection) builder.construct(typeToRead, selections.toArray(new Selection[0])));
+ query = typeToRead.isInterface() //
+ ? query.multiselect(selections) //
+ : query.select((Selection) builder.construct(typeToRead, //
+ selections.toArray(new Selection[0])));
} else if (tree.isExistsProjection()) {
@@ -239,83 +242,84 @@ public class JpaQueryCreator extends AbstractQueryCreator first = provider.next(part);
- ParameterMetadata second = provider.next(part);
- return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression());
- case AFTER:
- case GREATER_THAN:
- return builder.greaterThan(getComparablePath(root, part),
- provider.next(part, Comparable.class).getExpression());
- case GREATER_THAN_EQUAL:
- return builder.greaterThanOrEqualTo(getComparablePath(root, part),
- provider.next(part, Comparable.class).getExpression());
- case BEFORE:
- case LESS_THAN:
- return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression());
- case LESS_THAN_EQUAL:
- return builder.lessThanOrEqualTo(getComparablePath(root, part),
- provider.next(part, Comparable.class).getExpression());
- case IS_NULL:
- return getTypedPath(root, part).isNull();
- case IS_NOT_NULL:
- return getTypedPath(root, part).isNotNull();
- case NOT_IN:
- // cast required for eclipselink workaround, see DATAJPA-433
- return upperIfIgnoreCase(getTypedPath(root, part))
- .in((Expression>) provider.next(part, Collection.class).getExpression()).not();
- case IN:
- // cast required for eclipselink workaround, see DATAJPA-433
- return upperIfIgnoreCase(getTypedPath(root, part))
- .in((Expression>) provider.next(part, Collection.class).getExpression());
- case STARTING_WITH:
- case ENDING_WITH:
- case CONTAINING:
- case NOT_CONTAINING:
+ case BETWEEN:
+ ParameterMetadata first = provider.next(part);
+ ParameterMetadata second = provider.next(part);
+ return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression());
+ case AFTER:
+ case GREATER_THAN:
+ return builder.greaterThan(getComparablePath(root, part),
+ provider.next(part, Comparable.class).getExpression());
+ case GREATER_THAN_EQUAL:
+ return builder.greaterThanOrEqualTo(getComparablePath(root, part),
+ provider.next(part, Comparable.class).getExpression());
+ case BEFORE:
+ case LESS_THAN:
+ return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression());
+ case LESS_THAN_EQUAL:
+ return builder.lessThanOrEqualTo(getComparablePath(root, part),
+ provider.next(part, Comparable.class).getExpression());
+ case IS_NULL:
+ return getTypedPath(root, part).isNull();
+ case IS_NOT_NULL:
+ return getTypedPath(root, part).isNotNull();
+ case NOT_IN:
+ // cast required for eclipselink workaround, see DATAJPA-433
+ return upperIfIgnoreCase(getTypedPath(root, part))
+ .in((Expression>) provider.next(part, Collection.class).getExpression()).not();
+ case IN:
+ // cast required for eclipselink workaround, see DATAJPA-433
+ return upperIfIgnoreCase(getTypedPath(root, part))
+ .in((Expression>) provider.next(part, Collection.class).getExpression());
+ case STARTING_WITH:
+ case ENDING_WITH:
+ case CONTAINING:
+ case NOT_CONTAINING:
- if (property.getLeafProperty().isCollection()) {
+ if (property.getLeafProperty().isCollection()) {
- Expression> propertyExpression = traversePath(root, property);
- ParameterExpression