Skip to content

Handle LIKE with ESCAPE clause in Hibernate and EclipseLink #2956

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-gh-2954-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data JPA Parent</name>
Expand All @@ -31,6 +31,7 @@
<eclipselink>3.0.3</eclipselink>
<hibernate>6.2.1.Final</hibernate>
<hsqldb>2.7.1</hsqldb>
<h2>2.1.214</h2>
<jsqlparser>4.5</jsqlparser>
<mysql-connector-java>8.0.31</mysql-connector-java>
<postgresql>42.5.0</postgresql>
Expand Down
4 changes: 2 additions & 2 deletions spring-data-envers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-gh-2954-SNAPSHOT</version>

<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-gh-2954-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-jpa-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-gh-2954-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
11 changes: 9 additions & 2 deletions spring-data-jpa/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-gh-2954-SNAPSHOT</version>

<name>Spring Data JPA</name>
<description>Spring Data module for JPA repositories.</description>
Expand All @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-gh-2954-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -100,6 +100,13 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2}</version>
<scope>test</scope>
</dependency>

<!-- MySQL testing support -->
<dependency>
<groupId>com.mysql</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (character|parameter))?
;

// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-elements-indices
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2124,7 +2124,11 @@ public List<JpaQueryParsingToken> visitStringPatternMatching(HqlParser.StringPat
if (ctx.ESCAPE() != null) {

tokens.add(new JpaQueryParsingToken(ctx.ESCAPE()));
tokens.addAll(visit(ctx.character()));
if (ctx.character() != null) {
tokens.addAll(visit(ctx.character()));
} else if (ctx.parameter() != null) {
tokens.addAll(visit(ctx.parameter()));
}
}

return tokens;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,12 @@ public List<JpaQueryParsingToken> 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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@ContextConfiguration("classpath:eclipselink.xml")
@ContextConfiguration("classpath:eclipselink-h2.xml")
class EclipseLinkUserRepositoryFinderTests extends UserRepositoryFinderTests {

@Disabled
Expand All @@ -34,4 +35,5 @@ void executesNotInQueryCorrectly() {}
@Disabled
@Override
void executesInKeywordForPageCorrectly() {}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -53,10 +52,11 @@
*
* @author Oliver Gierke
* @author Krzysztof Krason
* @author Greg Turnquist
* @see QueryLookupStrategy
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:config/namespace-application-context.xml")
@ContextConfiguration(locations = "classpath:config/namespace-application-context-h2.xml")
@Transactional
class UserRepositoryFinderTests {

Expand Down Expand Up @@ -235,7 +235,6 @@ void parametersForContainsGetProperlyEscaped() {
.isEmpty();
}

@Disabled("Can't get ESCAPE clause working with Hibernate")
@Test // DATAJPA-1519
void escapingInLikeSpels() {

Expand All @@ -246,7 +245,6 @@ void escapingInLikeSpels() {
assertThat(userRepository.findContainingEscaped("att_")).containsExactly(extra);
}

@Disabled("Can't get ESCAPE clause working with Hibernate")
@Test // DATAJPA-1522
void escapingInLikeSpelsInThePresenceOfEscapeCharacters() {

Expand All @@ -256,7 +254,6 @@ void escapingInLikeSpelsInThePresenceOfEscapeCharacters() {
assertThat(userRepository.findContainingEscaped("att\\x")).containsExactly(withEscapeCharacter);
}

@Disabled("Can't get ESCAPE clause working with Hibernate")
@Test // DATAJPA-1522
void escapingInLikeSpelsInThePresenceOfEscapedWildcards() {

Expand Down Expand Up @@ -288,8 +285,7 @@ void executesQueryWithProjectionContainingReferenceToPluralAttribute() {

List<RolesAndFirstname> rolesAndFirstnameBy = userRepository.findRolesAndFirstnameBy();

assertThat(rolesAndFirstnameBy)
.isNotNull();
assertThat(rolesAndFirstnameBy).isNotNull();

for (RolesAndFirstname rolesAndFirstname : rolesAndFirstnameBy) {
assertThat(rolesAndFirstname.getFirstname()).isNotNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ List<User> findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity
List<NameOnlyDto> 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<User> findContainingEscaped(String namePart);

// DATAJPA-1303
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

<import resource="../infrastructure-h2.xml"/>

<!--

Simplified DAO configuration:

1. Looks for DAO interfaces in ${dao-config.dao-package-name} named ${dao.name}Dao
2. Uses entity classes from ${dao-config.entity-package-name}
3. Registers daos under ${dao.name}Dao in the applicationContext
4. Add postfix attributes to dao-config element as needed

You can register DAOs as needed by simply adding "dao" elements named after domain classes
and provide an DAO interface named as described above.

! Note, that PersistenceAnnotationBeanPostProcessor and PersistenceExceptionTranslationPostProcessor !
! do not have to be explicitly registered as they are included by namespace parser !

-->
<jpa:repositories base-package="org.springframework.data.jpa.repository.sample"/>

<!-- Register custom DAO implementation explicitly -->
<bean id="userRepositoryImpl" class="org.springframework.data.jpa.repository.sample.UserRepositoryImpl"/>

</beans>
21 changes: 21 additions & 0 deletions spring-data-jpa/src/test/resources/eclipselink-h2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

<!-- EclipseLink vendor adaptor with workaround platform class for HSQL usage -->
<bean id="vendorAdaptor" class="org.springframework.data.jpa.repository.CustomEclipseLinkJpaVendorAdapter" parent="abstractVendorAdaptor" />

<util:properties id="jpaProperties">
<prop key="jakarta.persistence.jdbc.driver">org.h2.Driver</prop>
<prop key="jakarta.persistence.jdbc.url">jdbc:h2:mem:hades</prop>
<prop key="jakarta.persistence.jdbc.user">sa</prop>
<prop key="jakarta.persistence.jdbc.password"></prop>
<prop key="jakarta.persistence.ddl-generation">create-tables</prop>
<prop key="eclipselink.weaving.internal">false</prop>
<prop key="eclipselink.logging.level">SEVERE</prop>
</util:properties>

</beans>
35 changes: 35 additions & 0 deletions spring-data-jpa/src/test/resources/infrastructure-h2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

<import resource="hibernate.xml"/>

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="spring-data-jpa"/>
<property name="jpaVendorAdapter" ref="vendorAdaptor"/>
<property name="jpaProperties" ref="jpaProperties"/>
</bean>

<bean id="abstractVendorAdaptor" abstract="true">
<property name="generateDdl" value="true"/>
<property name="database" value="H2"/>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean name="sampleEvaluationContextExtension"
class="org.springframework.data.jpa.repository.sample.SampleEvaluationContextExtension"/>

<jdbc:embedded-database id="dataSource" type="H2" generate-name="true">
<jdbc:script execution="INIT" separator="/;" location="classpath:scripts/h2-init.sql"/>
<jdbc:script execution="INIT" separator="/;" location="classpath:scripts/h2-stored-procedures.sql"/>
</jdbc:embedded-database>

</beans>
1 change: 1 addition & 0 deletions spring-data-jpa/src/test/resources/scripts/h2-init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/;
DROP alias IF EXISTS plus1inout
/;
CREATE alias plus1inout AS $$
Integer plus1inout(Integer arg) {
return arg + 1;
}
$$
/;