diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index d05d4de51..9ed7ef39c 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -160,6 +160,7 @@ The JPA module supports defining a query manually as a String or having it being Derived queries with the predicates `IsStartingWith`, `StartingWith`, `StartsWith`, IsEndingWith", `EndingWith`, `EndsWith`, `IsNotContaining`, `NotContaining`, `NotContains`, `IsContaining`, `Containing`, `Contains` the respective arguments for these queries will get sanitized. This means if the arguments actually contain characters recognized by `LIKE` as wildcards these will get escaped so they match only as literals. +The escape character used can be configured by setting the `escapeCharacter` of the `@EnableJpaRepositories` annotation. Compare with <>. ==== Declared Queries @@ -493,7 +494,7 @@ List findByLastnameWithSpelExpression(@Param("lastname") String lastname); ==== When using `like`-conditions with values that are coming from a not secure source the values should be sanitized so they can't contain any wildcards and thereby allow attackers to select more data than they should be able to. -For this purpose the the `escape(String, String)` method is made available in the SpEL context. +For this purpose the the `escape(String)` method is made available in the SpEL context. It prefixes all instances of `_` and `%` in the first argument with the single character from the second argument. In combination with the `escape` clause of the `like` expression available in JPQL and standard SQL this allows easy cleaning of bind parameters. @@ -502,13 +503,14 @@ In combination with the `escape` clause of the `like` expression available in JP ==== [source, java] ---- -@Query("select u from User u where u.firstname like %?#{#escape([0],'#')}% escape '#'") +@Query("select u from User u where u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}") List findContainingEscaped(String namePart); ---- ==== Given this method declaration in an repository interface `findContainingEscaped("Peter_")" will find `Peter_Parker` but not `Peter Parker`. -Note that the method `escape(String, String)` available in the SpEL context will only escape the SQL and JPQL standard wildcards `_` and `%`. +The escape character used can be configured by setting the `escapeCharacter` of the `@EnableJpaRepositories` annotation. +Note that the method `escape(String)` available in the SpEL context will only escape the SQL and JPQL standard wildcards `_` and `%`. If the underlying database or the JPA implementation supports additional wildcards these will not get escaped. [[jpa.modifying-queries]] diff --git a/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java b/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java index 269fa44ae..7d382a4d0 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java @@ -20,7 +20,7 @@ import lombok.Value; import java.util.Arrays; import java.util.List; -import org.springframework.util.Assert; +import org.springframework.lang.Nullable; /** * A value type encapsulating an escape character for LIKE queries and the actually usage of it in escaping @@ -39,14 +39,25 @@ public class EscapeCharacter { /** * Escapes all special like characters ({@code _}, {@code %}) using the configured escape character. * - * @param value must not be {@literal null}. + * @param value May be {@literal null}. * @return */ + @Nullable public String escape(String value) { - Assert.notNull(value, "Value must be not null."); - + if (value == null) { + return null; + } return TO_REPLACE.stream() // .reduce(value, (it, character) -> it.replace(character, this.value + character)); } + + /** + * Makes the underlying character available. + * + * @return the value + */ + public char escapeCharacter() { + return value; + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 4eaea14aa..09027bfc4 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -559,7 +559,7 @@ public interface UserRepository List findByNamedQueryWithConstructorExpression(); // DATAJPA-1519 - @Query("select u from User u where u.firstname like %?#{escape([0])}% escape '\\'") + @Query("select u from User u where u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}") List findContainingEscaped(String namePart); interface RolesAndFirstname {