From 14642a58696e7b4a5724bfb0f37576d0ff122212 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 15 May 2023 14:12:19 -0500 Subject: [PATCH] Resolve handling of ESCAPE clause with LIKE queries on Hibernate. The HQL parser has to handle parameters in addition to character values in order to support SpEL. Closes #2954 Original Pull Request: #2956. --- .../data/jpa/repository/query/Hql.g4 | 2 +- .../jpa/repository/query/HqlQueryRenderer.java | 2 +- .../repository/query/JpqlQueryRenderer.java | 6 ++++++ .../EclipseLinkUserRepositoryFinderTests.java | 18 ++++++++++++++++++ .../repository/UserRepositoryFinderTests.java | 7 +------ .../jpa/repository/sample/UserRepository.java | 2 +- 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 488031b42c..731bb16c7d 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -579,7 +579,7 @@ dealingWithNullExpression // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-like-predicate stringPatternMatching - : expression NOT? (LIKE | ILIKE) expression (ESCAPE character)? + : expression NOT? (LIKE | ILIKE) expression (ESCAPE expression)? ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-elements-indices diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 39d6271fe3..5446173ca2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -2124,7 +2124,7 @@ public List visitStringPatternMatching(HqlParser.StringPat if (ctx.ESCAPE() != null) { tokens.add(new JpaQueryParsingToken(ctx.ESCAPE())); - tokens.addAll(visit(ctx.character())); + tokens.addAll(visit(ctx.expression(2))); } return tokens; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java index 0a148f145f..b5a41fe998 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -1206,6 +1206,12 @@ public List visitLike_expression(JpqlParser.Like_expressio tokens.add(new JpaQueryParsingToken(ctx.LIKE())); tokens.addAll(visit(ctx.pattern_value())); + if (ctx.ESCAPE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ESCAPE())); + tokens.addAll(visit(ctx.escape_character())); + } + return tokens; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java index 0caf735d4e..1b6bc2a7bf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java @@ -34,4 +34,22 @@ void executesNotInQueryCorrectly() {} @Disabled @Override void executesInKeywordForPageCorrectly() {} + + @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") + @Override + void escapingInLikeSpels() { + super.escapingInLikeSpels(); + } + + @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") + @Override + void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { + super.escapingInLikeSpelsInThePresenceOfEscapeCharacters(); + } + + @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") + @Override + void escapingInLikeSpelsInThePresenceOfEscapedWildcards() { + super.escapingInLikeSpelsInThePresenceOfEscapedWildcards(); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 6fa3e2b00f..bccc417ea0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -25,7 +25,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -235,7 +234,6 @@ void parametersForContainsGetProperlyEscaped() { .isEmpty(); } - @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1519 void escapingInLikeSpels() { @@ -246,7 +244,6 @@ void escapingInLikeSpels() { assertThat(userRepository.findContainingEscaped("att_")).containsExactly(extra); } - @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { @@ -256,7 +253,6 @@ void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { assertThat(userRepository.findContainingEscaped("att\\x")).containsExactly(withEscapeCharacter); } - @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapedWildcards() { @@ -288,8 +284,7 @@ void executesQueryWithProjectionContainingReferenceToPluralAttribute() { List rolesAndFirstnameBy = userRepository.findRolesAndFirstnameBy(); - assertThat(rolesAndFirstnameBy) - .isNotNull(); + assertThat(rolesAndFirstnameBy).isNotNull(); for (RolesAndFirstname rolesAndFirstname : rolesAndFirstnameBy) { assertThat(rolesAndFirstname.getFirstname()).isNotNull(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 5eb7490398..821b119a73 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -625,7 +625,7 @@ List findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity List findByNamedQueryWithConstructorExpression(); // DATAJPA-1519 - @Query("select u from User u where u.lastname like '%?#{escape([0])}%' escape ?#{escapeCharacter()}") + @Query("select u from User u where u.lastname like %?#{escape([0])}% escape ?#{escapeCharacter()}") List findContainingEscaped(String namePart); // DATAJPA-1303