Skip to content

Commit c4d9bc6

Browse files
DiegoKrupitzagregturn
authored andcommitted
Introduce JSqlParser as option for parsing native queries.
Introduce a library that makes it possible to parse native SQL statements. See #2409.
1 parent 35a7644 commit c4d9bc6

28 files changed

+2383
-277
lines changed

Diff for: pom.xml

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<postgresql>42.2.19</postgresql>
2828
<springdata.commons>2.7.0-SNAPSHOT</springdata.commons>
2929
<vavr>0.10.3</vavr>
30+
<jsqlparser.version>4.3</jsqlparser.version>
3031

3132
<hibernate.groupId>org.hibernate</hibernate.groupId>
3233

@@ -372,6 +373,13 @@
372373
<scope>test</scope>
373374
</dependency>
374375

376+
<dependency>
377+
<groupId>com.github.jsqlparser</groupId>
378+
<artifactId>jsqlparser</artifactId>
379+
<version>${jsqlparser.version}</version>
380+
<scope>provided</scope>
381+
<optional>true</optional>
382+
</dependency>
375383
</dependencies>
376384

377385
<build>

Diff for: src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
6969
private final PersistenceProvider provider;
7070
private final Lazy<JpaQueryExecution> execution;
7171

72-
final Lazy<ParameterBinder> parameterBinder = Lazy.of(this::createBinder);
72+
final Lazy<ParameterBinder> parameterBinder = new Lazy<>(this::createBinder);
7373

7474
/**
7575
* Creates a new {@link AbstractJpaQuery} from the given {@link JpaQueryMethod}.

Diff for: src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java

+9-5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* @author Tom Hombergs
3535
* @author David Madden
3636
* @author Mark Paluch
37+
* @author Diego Krupitza
3738
*/
3839
abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
3940

@@ -55,8 +56,8 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
5556
* @param parser must not be {@literal null}.
5657
*/
5758
public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,
58-
@Nullable String countQueryString,
59-
QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {
59+
@Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider,
60+
SpelExpressionParser parser) {
6061

6162
super(method, em);
6263

@@ -65,10 +66,12 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri
6566
Assert.notNull(parser, "Parser must not be null!");
6667

6768
this.evaluationContextProvider = evaluationContextProvider;
68-
this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser);
69+
this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser,
70+
method.isNativeQuery());
6971

7072
DeclaredQuery countQuery = query.deriveCountQuery(countQueryString, method.getCountQueryProjection());
71-
this.countQuery = ExpressionBasedStringQuery.from(countQuery, method.getEntityInformation(), parser);
73+
this.countQuery = ExpressionBasedStringQuery.from(countQuery, method.getEntityInformation(), parser,
74+
method.isNativeQuery());
7275

7376
this.parser = parser;
7477

@@ -83,7 +86,8 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri
8386
@Override
8487
public Query doCreateQuery(JpaParametersParameterAccessor accessor) {
8588

86-
String sortedQueryString = QueryUtils.applySorting(query.getQueryString(), accessor.getSort(), query.getAlias());
89+
String sortedQueryString = QueryEnhancerFactory.forQuery(query) //
90+
.applySorting(accessor.getSort(), query.getAlias());
8791
ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor);
8892

8993
Query query = createJpaQuery(sortedQueryString, processor.getReturnedType());

Diff for: src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* A wrapper for a String representation of a query offering information about the query.
2525
*
2626
* @author Jens Schauder
27+
* @author Diego Krupitza
2728
* @since 2.0.3
2829
*/
2930
interface DeclaredQuery {
@@ -32,10 +33,11 @@ interface DeclaredQuery {
3233
* Creates a {@literal DeclaredQuery} from a query {@literal String}.
3334
*
3435
* @param query might be {@literal null} or empty.
36+
* @param nativeQuery is a given query is native or not
3537
* @return a {@literal DeclaredQuery} instance even for a {@literal null} or empty argument.
3638
*/
37-
static DeclaredQuery of(@Nullable String query) {
38-
return ObjectUtils.isEmpty(query) ? EmptyDeclaredQuery.EMPTY_QUERY : new StringQuery(query);
39+
static DeclaredQuery of(@Nullable String query, boolean nativeQuery) {
40+
return ObjectUtils.isEmpty(query) ? EmptyDeclaredQuery.EMPTY_QUERY : new StringQuery(query, nativeQuery);
3941
}
4042

4143
/**
@@ -100,4 +102,13 @@ default boolean usesPaging() {
100102
* @since 2.0.6
101103
*/
102104
boolean usesJdbcStyleParameters();
105+
106+
/**
107+
* Return whether the query is a native query of not.
108+
*
109+
* @return <code>true</code> if native query otherwise <code>false</code>
110+
*/
111+
default boolean isNativeQuery() {
112+
return false;
113+
}
103114
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.repository.query;
17+
18+
import org.springframework.data.domain.Sort;
19+
20+
import java.util.Set;
21+
22+
/**
23+
* The implementation of {@link QueryEnhancer} using {@link QueryUtils}.
24+
*
25+
* @author Diego Krupitza
26+
*/
27+
public class DefaultQueryEnhancer implements QueryEnhancer {
28+
29+
private final DeclaredQuery query;
30+
31+
public DefaultQueryEnhancer(DeclaredQuery query) {
32+
this.query = query;
33+
}
34+
35+
@Override
36+
public String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable<String> idAttributes) {
37+
return QueryUtils.getExistsQueryString(entityName, countQueryPlaceHolder, idAttributes);
38+
}
39+
40+
@Override
41+
public String getQueryString(String template, String entityName) {
42+
return QueryUtils.getQueryString(template, entityName);
43+
}
44+
45+
@Override
46+
public String applySorting(Sort sort, String alias) {
47+
return QueryUtils.applySorting(this.query.getQueryString(), sort, alias);
48+
}
49+
50+
@Override
51+
public String detectAlias() {
52+
return QueryUtils.detectAlias(this.query.getQueryString());
53+
}
54+
55+
@Override
56+
public String createCountQueryFor(String countProjection) {
57+
return QueryUtils.createCountQueryFor(this.query.getQueryString(), countProjection);
58+
}
59+
60+
@Override
61+
public String getProjection() {
62+
return QueryUtils.getProjection(this.query.getQueryString());
63+
}
64+
65+
@Override
66+
public Set<String> getJoinAliases() {
67+
return QueryUtils.getOuterJoinAliases(this.query.getQueryString());
68+
}
69+
70+
@Override
71+
public DeclaredQuery getQuery() {
72+
return this.query;
73+
}
74+
}

Diff for: src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable Str
9797

9898
Assert.hasText(countQuery, "CountQuery must not be empty!");
9999

100-
return DeclaredQuery.of(countQuery);
100+
return DeclaredQuery.of(countQuery, false);
101101
}
102102

103103
/*

Diff for: src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java

+9-5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* @author Oliver Gierke
3737
* @author Tom Hombergs
3838
* @author Michael J. Simons
39+
* @author Diego Krupitza
3940
*/
4041
class ExpressionBasedStringQuery extends StringQuery {
4142

@@ -55,9 +56,11 @@ class ExpressionBasedStringQuery extends StringQuery {
5556
* @param query must not be {@literal null} or empty.
5657
* @param metadata must not be {@literal null}.
5758
* @param parser must not be {@literal null}.
59+
* @param nativeQuery is a given query is native or not
5860
*/
59-
public ExpressionBasedStringQuery(String query, JpaEntityMetadata<?> metadata, SpelExpressionParser parser) {
60-
super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser));
61+
public ExpressionBasedStringQuery(String query, JpaEntityMetadata<?> metadata, SpelExpressionParser parser,
62+
boolean nativeQuery) {
63+
super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser), nativeQuery && !containsExpression(query));
6164
}
6265

6366
/**
@@ -66,11 +69,12 @@ public ExpressionBasedStringQuery(String query, JpaEntityMetadata<?> metadata, S
6669
* @param query the original query. Must not be {@literal null}.
6770
* @param metadata the {@link JpaEntityMetadata} for the given entity. Must not be {@literal null}.
6871
* @param parser Parser for resolving SpEL expressions. Must not be {@literal null}.
72+
* @param nativeQuery
6973
* @return A query supporting SpEL expressions.
7074
*/
71-
static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata metadata,
72-
SpelExpressionParser parser) {
73-
return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser);
75+
static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata metadata, SpelExpressionParser parser,
76+
boolean nativeQuery) {
77+
return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser, nativeQuery);
7478
}
7579

7680
/**

0 commit comments

Comments
 (0)