Skip to content

Commit

Permalink
Properly handle native queries with SpEL expressions.
Browse files Browse the repository at this point in the history
Native queries with SpEL expressions should be properly parsed instead of converted to non-native queries.

See #3096
  • Loading branch information
gregturn committed Aug 11, 2023
1 parent 7b9baa5 commit a34f7fa
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*
* @author Jens Schauder
* @author Diego Krupitza
* @author Greg Turnquist
* @since 2.0.3
*/
interface DeclaredQuery {
Expand All @@ -37,7 +38,28 @@ interface DeclaredQuery {
* @return a {@literal DeclaredQuery} instance even for a {@literal null} or empty argument.
*/
static DeclaredQuery of(@Nullable String query, boolean nativeQuery) {
return ObjectUtils.isEmpty(query) ? EmptyDeclaredQuery.EMPTY_QUERY : new StringQuery(query, nativeQuery);
return of(query, nativeQuery, null);
}

/**
* Creates a {@literal DeclaredQuery} from a query {@literal String} and the relative {@link JpaQueryMethod}.
*
* @param query might be {@literal null} or empty.
* @param nativeQuery is a given query is native or not
* @param method is a {@link JpaQueryMethod} that has the related metadata
* @return a {@literal DeclaredQuery} instance even for a {@literal null} or empty argument.
*/
static DeclaredQuery of(@Nullable String query, Boolean nativeQuery, @Nullable JpaQueryMethod method) {

if (ObjectUtils.isEmpty(query)) {
return EmptyDeclaredQuery.EMPTY_QUERY;
}

if (ExpressionBasedStringQuery.containsExpression(query) && method != null) {
return new ExpressionBasedStringQuery(query, method.getEntityInformation(), JpaQueryFactory.PARSER, nativeQuery);
}

return new StringQuery(query, nativeQuery);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class ExpressionBasedStringQuery extends StringQuery {
*/
public ExpressionBasedStringQuery(String query, JpaEntityMetadata<?> metadata, SpelExpressionParser parser,
boolean nativeQuery) {
super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser), nativeQuery && !containsExpression(query));
super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser), nativeQuery);
}

/**
Expand Down Expand Up @@ -118,7 +118,7 @@ private static String potentiallyQuoteExpressionsParameter(String query) {
return EXPRESSION_PARAMETER_QUOTING.matcher(query).replaceAll(QUOTED_EXPRESSION_PARAMETER);
}

private static boolean containsExpression(String query) {
static boolean containsExpression(String query) {
return query.contains(ENTITY_NAME_VARIABLE_EXPRESSION);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@
*
* @author Thomas Darimont
* @author Mark Paluch
* @author Greg Turnquist
*/
enum JpaQueryFactory {

INSTANCE;

private static final SpelExpressionParser PARSER = new SpelExpressionParser();
static final SpelExpressionParser PARSER = new SpelExpressionParser();

/**
* Creates a {@link RepositoryQuery} from the given {@link String} query.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ private void assertParameterNamesInAnnotatedQuery() {

String annotatedQuery = getAnnotatedQuery();

if (!DeclaredQuery.of(annotatedQuery, this.isNativeQuery.get()).hasNamedParameter()) {
if (!DeclaredQuery.of(annotatedQuery, this.isNativeQuery.get(), this).hasNamedParameter()) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,14 @@ void shouldSupportModifyingQueryWithVarArgs() {
assertThat(repository.findByActiveTrue()).isEmpty();
}

@Test // GH-3096
void shouldSupportNativeQueriesWithSpEL() {

flushTestUsers();

assertThat(repository.nativeQueryWithSpEL()).containsExactly(firstUser, secondUser, thirdUser, fourthUser);
}

@Test // DATAJPA-405
void executesFinderWithOrderClauseOnly() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ void shouldDetectBindParameterCountCorrectlyWithJDBCStyleParameters() {
}

@Test
void shouldDetectComplexNativeQueriesWithSpelAsNonNative() {
void shouldDetectComplexNativeQueriesWithSpelAsRetainingNativeQueryStatus() {

StringQuery query = new ExpressionBasedStringQuery(
"select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )"
Expand All @@ -106,15 +106,15 @@ void shouldDetectComplexNativeQueriesWithSpelAsNonNative() {
+ "AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})",
metadata, SPEL_PARSER, true);

assertThat(query.isNativeQuery()).isFalse();
assertThat(query.isNativeQuery()).isTrue();
}

@Test
void shouldDetectSimpleNativeQueriesWithSpelAsNonNative() {
void shouldDetectSimpleNativeQueriesWithSpelAsRetainingNativeQueryStatus() {

StringQuery query = new ExpressionBasedStringQuery("select n from #{#entityName} n", metadata, SPEL_PARSER, true);

assertThat(query.isNativeQuery()).isFalse();
assertThat(query.isNativeQuery()).isTrue();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,10 @@ List<String> findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter
@Query("select u from User u where u.firstname >= (select Min(u0.firstname) from User u0)")
List<NameOnly> findProjectionBySubselect();

// GH-3096
@Query(value = "select * from SD_User as #{#entityName}", nativeQuery = true)
List<User> nativeQueryWithSpEL();

Window<User> findBy(OffsetScrollPosition position);

interface RolesAndFirstname {
Expand Down

0 comments on commit a34f7fa

Please sign in to comment.