Skip to content

Commit

Permalink
Introduce flag to skip JSqlParser for a given custom query.
Browse files Browse the repository at this point in the history
See #2989
  • Loading branch information
gregturn committed Sep 8, 2023
1 parent 729acf6 commit 4a3842b
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,12 @@
* @since 3.0
*/
Class<? extends QueryRewriter> queryRewriter() default QueryRewriter.IdentityQueryRewriter.class;

/**
* For native queries, indicate whether or not to skip the JSqlParser.
*
* @return
* @since 3.2
*/
boolean skipJSql() default false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri

this.evaluationContextProvider = evaluationContextProvider;
this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser,
method.isNativeQuery());
method.isNativeQuery(), method.skipJSql());

this.countQuery = Lazy.of(() -> {
DeclaredQuery countQuery = query.deriveCountQuery(countQueryString, method.getCountQueryProjection());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,8 @@ default boolean usesPaging() {
default boolean isNativeQuery() {
return false;
}

default boolean skipJSql() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,15 @@ class ExpressionBasedStringQuery extends StringQuery {
* @param parser must not be {@literal null}.
* @param nativeQuery is a given query is native or not
*/
public ExpressionBasedStringQuery(String query, JpaEntityMetadata<?> metadata, SpelExpressionParser parser,
boolean nativeQuery, boolean skipJSql) {
super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser), nativeQuery && !containsExpression(query),
skipJSql);
}

public ExpressionBasedStringQuery(String query, JpaEntityMetadata<?> metadata, SpelExpressionParser parser,
boolean nativeQuery) {
super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser), nativeQuery && !containsExpression(query));
this(query, metadata, parser, nativeQuery, false);
}

/**
Expand All @@ -75,7 +81,7 @@ public ExpressionBasedStringQuery(String query, JpaEntityMetadata<?> metadata, S
*/
static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata<?> metadata,
SpelExpressionParser parser, boolean nativeQuery) {
return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser, nativeQuery);
return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser, nativeQuery, query.skipJSql());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public class JpaQueryMethod extends QueryMethod {
private final Lazy<JpaEntityGraph> jpaEntityGraph;
private final Lazy<Modifying> modifying;
private final Lazy<Boolean> isNativeQuery;
private final Lazy<Boolean> skipJSql;
private final Lazy<Boolean> isCollectionQuery;
private final Lazy<Boolean> isProcedureQuery;
private final Lazy<JpaEntityMetadata<?>> entityMetadata;
Expand Down Expand Up @@ -136,6 +137,7 @@ protected JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionF
return new JpaEntityGraph(entityGraph, getNamedQueryName());
});
this.isNativeQuery = Lazy.of(() -> getAnnotationValue("nativeQuery", Boolean.class));
this.skipJSql = Lazy.of(() -> getAnnotationValue("skipJSql", Boolean.class));
this.isCollectionQuery = Lazy.of(() -> super.isCollectionQuery() && !NATIVE_ARRAY_TYPES.contains(this.returnType));
this.isProcedureQuery = Lazy.of(() -> AnnotationUtils.findAnnotation(method, Procedure.class) != null);
this.entityMetadata = Lazy.of(() -> new DefaultJpaEntityMetadata<>(getDomainClass()));
Expand Down Expand Up @@ -387,6 +389,10 @@ boolean isNativeQuery() {
return this.isNativeQuery.get();
}

boolean skipJSql() {
return this.skipJSql.get();
}

@Override
public String getNamedQueryName() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static QueryEnhancer forQuery(DeclaredQuery query) {

if (query.isNativeQuery()) {

if (jSqlParserPresent) {
if (jSqlParserPresent && !query.skipJSql()) {
/*
* If JSqlParser fails, throw some alert signaling that people should write a custom Impl.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class StringQuery implements DeclaredQuery {
private final boolean containsPageableInSpel;
private final boolean usesJdbcStyleParameters;
private final boolean isNative;
private final boolean skipJSql;
private final QueryEnhancer queryEnhancer;

/**
Expand All @@ -73,11 +74,12 @@ class StringQuery implements DeclaredQuery {
* @param query must not be {@literal null} or empty.
*/
@SuppressWarnings("deprecation")
StringQuery(String query, boolean isNative) {
StringQuery(String query, boolean isNative, boolean skipJSql) {

Assert.hasText(query, "Query must not be null or empty");

this.isNative = isNative;
this.skipJSql = skipJSql;
this.bindings = new ArrayList<>();
this.containsPageableInSpel = query.contains("#pageable");

Expand All @@ -92,6 +94,10 @@ class StringQuery implements DeclaredQuery {
this.hasConstructorExpression = this.queryEnhancer.hasConstructorExpression();
}

StringQuery(String query, boolean isNative) {
this(query, isNative, false);
}

/**
* Returns whether we have found some like bindings.
*/
Expand Down Expand Up @@ -157,6 +163,11 @@ public boolean isNativeQuery() {
return isNative;
}

@Override
public boolean skipJSql() {
return skipJSql;
}

/**
* A parser that extracts the parameter bindings from a given query string.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2914,6 +2914,19 @@ void supportsProjectionsWithNativeQueries() {
assertThat(result.getLastname()).isEqualTo(user.getLastname());
}

@Test // GH-2989
void supportsProjectionsWithNativeQueriesSkippingJSql() {

flushTestUsers();

User user = repository.findAll().get(0);

NameOnly result = repository.findByNativeQueryWithNoJSql(user.getId());

assertThat(result.getFirstname()).isEqualTo(user.getFirstname());
assertThat(result.getLastname()).isEqualTo(user.getLastname());
}

@Test // DATAJPA-1248
void supportsProjectionsWithNativeQueriesAndCamelCaseProperty() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,13 @@ void modifyingQueriesAreDetectedCorrectly() {
assertThat(QueryEnhancerFactory.forQuery(modiQuery).createCountQueryFor()).isEqualToIgnoringCase(modifyingQuery);
}

@Test // GH-2989
void skippingJSqlShouldRevertToDefaultQueryEnhancer() {

assertThat(getEnhancer(new StringQuery(QUERY, true, false))).isInstanceOf(JSqlParserQueryEnhancer.class);
assertThat(getEnhancer(new StringQuery(QUERY, true, true))).isInstanceOf(DefaultQueryEnhancer.class);
}

@ParameterizedTest // GH-2593
@MethodSource("insertStatementIsProcessedSameAsDefaultSource")
void insertStatementIsProcessedSameAsDefault(String insertQuery) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,11 @@ List<User> findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity
@Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);

// GH-2989
@Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true, skipJSql = true)
NameOnly findByNativeQueryWithNoJSql(Integer id);


// DATAJPA-1248
@Query(value = "SELECT emailaddress FROM SD_User WHERE id = ?1", nativeQuery = true)
EmailOnly findEmailOnlyByNativeQuery(Integer id);
Expand Down

0 comments on commit 4a3842b

Please sign in to comment.