DATAJPA-1534 - Escape wildcards in QBE Like-queries.
The `StringMatcher` values `STARTING`, `ENDING`, and `CONTAINING` now escape wildcards in their parameters. See also: DATAJPA-1519, DATAJPA-1522.
This commit is contained in:
@@ -18,7 +18,6 @@ package org.springframework.data.jpa.convert;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -36,6 +35,7 @@ import javax.persistence.metamodel.SingularAttribute;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.ExampleMatcher;
|
||||
import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.data.repository.core.support.ExampleMatcherAccessor;
|
||||
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
@@ -54,6 +54,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
* @since 1.10
|
||||
*/
|
||||
public class QueryByExamplePredicateBuilder {
|
||||
@@ -71,9 +72,11 @@ public class QueryByExamplePredicateBuilder {
|
||||
* @param root must not be {@literal null}.
|
||||
* @param cb must not be {@literal null}.
|
||||
* @param example must not be {@literal null}.
|
||||
* @param escapeCharacter
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Example<T> example) {
|
||||
public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Example<T> example,
|
||||
EscapeCharacter escapeCharacter) {
|
||||
|
||||
Assert.notNull(root, "Root must not be null!");
|
||||
Assert.notNull(cb, "CriteriaBuilder must not be null!");
|
||||
@@ -82,7 +85,8 @@ public class QueryByExamplePredicateBuilder {
|
||||
ExampleMatcher matcher = example.getMatcher();
|
||||
|
||||
List<Predicate> predicates = getPredicates("", cb, root, root.getModel(), example.getProbe(),
|
||||
example.getProbeType(), new ExampleMatcherAccessor(matcher), new PathNode("root", null, example.getProbe()));
|
||||
example.getProbeType(), new ExampleMatcherAccessor(matcher), new PathNode("root", null, example.getProbe()),
|
||||
escapeCharacter);
|
||||
|
||||
if (predicates.isEmpty()) {
|
||||
return cb.isTrue(cb.literal(true));
|
||||
@@ -99,7 +103,8 @@ public class QueryByExamplePredicateBuilder {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> from, ManagedType<?> type, Object value,
|
||||
Class<?> probeType, ExampleMatcherAccessor exampleAccessor, PathNode currentNode) {
|
||||
Class<?> probeType, ExampleMatcherAccessor exampleAccessor, PathNode currentNode,
|
||||
EscapeCharacter escapeCharacter) {
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
DirectFieldAccessFallbackBeanWrapper beanWrapper = new DirectFieldAccessFallbackBeanWrapper(value);
|
||||
@@ -125,16 +130,17 @@ public class QueryByExamplePredicateBuilder {
|
||||
|
||||
if (attribute.getPersistentAttributeType().equals(PersistentAttributeType.EMBEDDED)) {
|
||||
|
||||
predicates.addAll(getPredicates(currentPath, cb, from.get(attribute.getName()),
|
||||
(ManagedType<?>) attribute.getType(), attributeValue, probeType, exampleAccessor, currentNode));
|
||||
predicates
|
||||
.addAll(getPredicates(currentPath, cb, from.get(attribute.getName()), (ManagedType<?>) attribute.getType(),
|
||||
attributeValue, probeType, exampleAccessor, currentNode, escapeCharacter));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isAssociation(attribute)) {
|
||||
|
||||
if (!(from instanceof From)) {
|
||||
throw new JpaSystemException(new IllegalArgumentException(
|
||||
String.format("Unexpected path type for %s. Found %s where From.class was expected.", currentPath, from)));
|
||||
throw new JpaSystemException(new IllegalArgumentException(String
|
||||
.format("Unexpected path type for %s. Found %s where From.class was expected.", currentPath, from)));
|
||||
}
|
||||
|
||||
PathNode node = currentNode.add(attribute.getName(), attributeValue);
|
||||
@@ -145,7 +151,7 @@ public class QueryByExamplePredicateBuilder {
|
||||
}
|
||||
|
||||
predicates.addAll(getPredicates(currentPath, cb, ((From<?, ?>) from).join(attribute.getName()),
|
||||
(ManagedType<?>) attribute.getType(), attributeValue, probeType, exampleAccessor, node));
|
||||
(ManagedType<?>) attribute.getType(), attributeValue, probeType, exampleAccessor, node, escapeCharacter));
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -165,13 +171,25 @@ public class QueryByExamplePredicateBuilder {
|
||||
predicates.add(cb.equal(expression, attributeValue));
|
||||
break;
|
||||
case CONTAINING:
|
||||
predicates.add(cb.like(expression, "%" + attributeValue + "%"));
|
||||
predicates.add(cb.like( //
|
||||
expression, //
|
||||
"%" + escapeCharacter.escape(attributeValue.toString()) + "%", //
|
||||
escapeCharacter.getEscapeCharacter() //
|
||||
));
|
||||
break;
|
||||
case STARTING:
|
||||
predicates.add(cb.like(expression, attributeValue + "%"));
|
||||
predicates.add(cb.like(//
|
||||
expression, //
|
||||
escapeCharacter.escape(attributeValue.toString()) + "%", //
|
||||
escapeCharacter.getEscapeCharacter()) //
|
||||
);
|
||||
break;
|
||||
case ENDING:
|
||||
predicates.add(cb.like(expression, "%" + attributeValue));
|
||||
predicates.add(cb.like( //
|
||||
expression, //
|
||||
"%" + escapeCharacter.escape(attributeValue.toString()), //
|
||||
escapeCharacter.getEscapeCharacter()) //
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
|
||||
@@ -110,6 +110,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
SimpleJpaRepository<?, ?> repository = getTargetRepository(information, entityManager);
|
||||
repository.setRepositoryMethodMetadata(crudMethodMetadataPostProcessor.getCrudMethodMetadata());
|
||||
repository.setEscapeCharacter(escapeCharacter);
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.data.jpa.repository.query.Jpa21Utils;
|
||||
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
|
||||
import org.springframework.data.jpa.repository.query.QueryUtils;
|
||||
@@ -63,7 +64,7 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Default implementation of the {@link org.springframework.data.repository.CrudRepository} interface. This will offer
|
||||
* you a more sophisticated interface than the plain {@link EntityManager} .
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Eberhard Wolff
|
||||
* @author Thomas Darimont
|
||||
@@ -83,10 +84,11 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
private final PersistenceProvider provider;
|
||||
|
||||
private CrudMethodMetadata metadata;
|
||||
private EscapeCharacter escapeCharacter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleJpaRepository} to manage objects of the given {@link JpaEntityInformation}.
|
||||
*
|
||||
*
|
||||
* @param entityInformation must not be {@literal null}.
|
||||
* @param entityManager must not be {@literal null}.
|
||||
*/
|
||||
@@ -102,7 +104,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleJpaRepository} to manage objects of the given domain type.
|
||||
*
|
||||
*
|
||||
* @param domainClass must not be {@literal null}.
|
||||
* @param em must not be {@literal null}.
|
||||
*/
|
||||
@@ -113,13 +115,17 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
/**
|
||||
* Configures a custom {@link CrudMethodMetadata} to be used to detect {@link LockModeType}s and query hints to be
|
||||
* applied to queries.
|
||||
*
|
||||
*
|
||||
* @param crudMethodMetadata
|
||||
*/
|
||||
public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) {
|
||||
this.metadata = crudMethodMetadata;
|
||||
}
|
||||
|
||||
public void setEscapeCharacter(EscapeCharacter escapeCharacter) {
|
||||
this.escapeCharacter = escapeCharacter;
|
||||
}
|
||||
|
||||
protected CrudMethodMetadata getRepositoryMethodMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
@@ -211,7 +217,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.JpaRepository#deleteAllInBatch()
|
||||
*/
|
||||
@@ -244,7 +250,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
/**
|
||||
* Returns a {@link Map} with the query hints based on the current {@link CrudMethodMetadata} and potential
|
||||
* {@link EntityGraph} information.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected Map<String, Object> getQueryHints() {
|
||||
@@ -267,7 +273,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
return new JpaEntityGraph(metadata.getEntityGraph(), fallbackName);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.JpaRepository#getOne(java.io.Serializable)
|
||||
*/
|
||||
@@ -417,35 +423,35 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
return getQuery(spec, sort).getResultList();
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findOne(org.springframework.data.domain.Example)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> S findOne(Example<S> example) {
|
||||
try {
|
||||
return getQuery(new ExampleSpecification<S>(example), example.getProbeType(), (Sort) null).getSingleResult();
|
||||
return getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), (Sort) null).getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryByExampleExecutor#count(org.springframework.data.domain.Example)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> long count(Example<S> example) {
|
||||
return executeCountQuery(getCountQuery(new ExampleSpecification<S>(example), example.getProbeType()));
|
||||
return executeCountQuery(getCountQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType()));
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryByExampleExecutor#exists(org.springframework.data.domain.Example)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> boolean exists(Example<S> example) {
|
||||
return !getQuery(new ExampleSpecification<S>(example), example.getProbeType(), (Sort) null).getResultList()
|
||||
return !getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), (Sort) null).getResultList()
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@@ -455,7 +461,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> List<S> findAll(Example<S> example) {
|
||||
return getQuery(new ExampleSpecification<S>(example), example.getProbeType(), (Sort) null).getResultList();
|
||||
return getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), (Sort) null).getResultList();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -464,7 +470,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
|
||||
return getQuery(new ExampleSpecification<S>(example), example.getProbeType(), sort).getResultList();
|
||||
return getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), sort).getResultList();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -474,9 +480,9 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
@Override
|
||||
public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
|
||||
|
||||
ExampleSpecification<S> spec = new ExampleSpecification<S>(example);
|
||||
ExampleSpecification<S> spec = new ExampleSpecification<S>(example, escapeCharacter);
|
||||
Class<S> probeType = example.getProbeType();
|
||||
TypedQuery<S> query = getQuery(new ExampleSpecification<S>(example), probeType, pageable);
|
||||
TypedQuery<S> query = getQuery(new ExampleSpecification<S>(example, escapeCharacter), probeType, pageable);
|
||||
|
||||
return pageable == null ? new PageImpl<S>(query.getResultList()) : readPage(query, probeType, pageable, spec);
|
||||
}
|
||||
@@ -624,7 +630,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
|
||||
/**
|
||||
* Creates a {@link TypedQuery} for the given {@link Specification} and {@link Sort}.
|
||||
*
|
||||
*
|
||||
* @param spec can be {@literal null}.
|
||||
* @param sort can be {@literal null}.
|
||||
* @return
|
||||
@@ -658,7 +664,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
|
||||
/**
|
||||
* Creates a new count query for the given {@link Specification}.
|
||||
*
|
||||
*
|
||||
* @param spec can be {@literal null}.
|
||||
* @return
|
||||
* @deprecated override {@link #getCountQuery(Specification, Class)} instead
|
||||
@@ -747,7 +753,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
|
||||
/**
|
||||
* Executes a count query and transparently sums up all values returned.
|
||||
*
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -769,7 +775,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
* Specification that gives access to the {@link Parameter} instance used to bind the ids for
|
||||
* {@link SimpleJpaRepository#findAll(Iterable)}. Workaround for OpenJPA not binding collections to in-clauses
|
||||
* correctly when using by-name binding.
|
||||
*
|
||||
*
|
||||
* @see <a href="https://issues.apache.org/jira/browse/OPENJPA-2018?focusedCommentId=13924055">OPENJPA-2018</a>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -807,16 +813,21 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
private static class ExampleSpecification<T> implements Specification<T> {
|
||||
|
||||
private final Example<T> example;
|
||||
private final EscapeCharacter escapeCharacter;
|
||||
|
||||
/**
|
||||
* Creates new {@link ExampleSpecification}.
|
||||
*
|
||||
* @param example
|
||||
* @param escapeCharacter
|
||||
*/
|
||||
public ExampleSpecification(Example<T> example) {
|
||||
public ExampleSpecification(Example<T> example, EscapeCharacter escapeCharacter) {
|
||||
|
||||
Assert.notNull(example, "Example must not be null!");
|
||||
Assert.notNull(escapeCharacter, "EscapeCharacter must not be null!");
|
||||
|
||||
this.example = example;
|
||||
this.escapeCharacter = escapeCharacter;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -825,7 +836,7 @@ public class SimpleJpaRepository<T, ID extends Serializable>
|
||||
*/
|
||||
@Override
|
||||
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
|
||||
return QueryByExamplePredicateBuilder.getPredicate(root, cb, example);
|
||||
return QueryByExamplePredicateBuilder.getPredicate(root, cb, example, escapeCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.ExampleMatcher;
|
||||
import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -56,6 +57,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@@ -118,22 +120,23 @@ public class QueryByExamplePredicateBuilderUnitTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAJPA-218
|
||||
public void getPredicateShouldThrowExceptionOnNullRoot() {
|
||||
QueryByExamplePredicateBuilder.getPredicate(null, cb, of(new Person()));
|
||||
QueryByExamplePredicateBuilder.getPredicate(null, cb, of(new Person()), EscapeCharacter.of('\\'));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAJPA-218
|
||||
public void getPredicateShouldThrowExceptionOnNullCriteriaBuilder() {
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, null, of(new Person()));
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, null, of(new Person()), EscapeCharacter.of('\\'));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAJPA-218
|
||||
public void getPredicateShouldThrowExceptionOnNullExample() {
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, null, null);
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, null, null, EscapeCharacter.of('\\'));
|
||||
}
|
||||
|
||||
@Test // DATAJPA-218
|
||||
public void emptyCriteriaListShouldResultTruePredicate() {
|
||||
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(new Person())), equalTo(truePredicate));
|
||||
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(new Person()), EscapeCharacter.of('\\')),
|
||||
equalTo(truePredicate));
|
||||
}
|
||||
|
||||
@Test // DATAJPA-218
|
||||
@@ -142,7 +145,8 @@ public class QueryByExamplePredicateBuilderUnitTests {
|
||||
Person p = new Person();
|
||||
p.firstname = "foo";
|
||||
|
||||
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p)), equalTo(dummyPredicate));
|
||||
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p), EscapeCharacter.of('\\')),
|
||||
equalTo(dummyPredicate));
|
||||
verify(cb, times(1)).equal(any(Expression.class), eq("foo"));
|
||||
}
|
||||
|
||||
@@ -157,7 +161,7 @@ public class QueryByExamplePredicateBuilderUnitTests {
|
||||
exception.expectCause(IsInstanceOf.<Throwable> instanceOf(IllegalArgumentException.class));
|
||||
exception.expectMessage("Unexpected path type");
|
||||
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p));
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p), EscapeCharacter.of('\\'));
|
||||
}
|
||||
|
||||
@Test // DATAJPA-218
|
||||
@@ -167,7 +171,8 @@ public class QueryByExamplePredicateBuilderUnitTests {
|
||||
p.firstname = "foo";
|
||||
p.age = 2L;
|
||||
|
||||
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p)), equalTo(andPredicate));
|
||||
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p), EscapeCharacter.of('\\')),
|
||||
equalTo(andPredicate));
|
||||
|
||||
verify(cb, times(1)).equal(any(Expression.class), eq("foo"));
|
||||
verify(cb, times(1)).equal(any(Expression.class), eq(2L));
|
||||
@@ -182,11 +187,67 @@ public class QueryByExamplePredicateBuilderUnitTests {
|
||||
|
||||
Example<Person> example = of(person, ExampleMatcher.matchingAny());
|
||||
|
||||
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, example), equalTo(orPredicate));
|
||||
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, example, EscapeCharacter.of('\\')),
|
||||
equalTo(orPredicate));
|
||||
|
||||
verify(cb, times(1)).or(Matchers.<Predicate>anyVararg());
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1534
|
||||
public void likePatternsGetEscapedContaining() {
|
||||
|
||||
Person person = new Person();
|
||||
person.firstname = "f\\o_o";
|
||||
|
||||
Example<Person> example = of( //
|
||||
person, //
|
||||
ExampleMatcher //
|
||||
.matchingAny() //
|
||||
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //
|
||||
);
|
||||
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, cb, example, EscapeCharacter.of('\\'));
|
||||
|
||||
verify(cb, times(1)).like(any(Expression.class), eq("%f\\\\o\\_o%"), eq('\\'));
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1534
|
||||
|
||||
public void likePatternsGetEscapedStarting() {
|
||||
|
||||
Person person = new Person();
|
||||
person.firstname = "f\\o_o";
|
||||
|
||||
Example<Person> example = of( //
|
||||
person, //
|
||||
ExampleMatcher //
|
||||
.matchingAny() //
|
||||
.withStringMatcher(ExampleMatcher.StringMatcher.STARTING) //
|
||||
);
|
||||
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, cb, example, EscapeCharacter.of('\\'));
|
||||
|
||||
verify(cb, times(1)).like(any(Expression.class), eq("f\\\\o\\_o%"), eq('\\'));
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1534
|
||||
public void likePatternsGetEscapedEnding() {
|
||||
|
||||
Person person = new Person();
|
||||
person.firstname = "f\\o_o";
|
||||
|
||||
Example<Person> example = of( //
|
||||
person, //
|
||||
ExampleMatcher //
|
||||
.matchingAny() //
|
||||
.withStringMatcher(ExampleMatcher.StringMatcher.ENDING) //
|
||||
);
|
||||
|
||||
QueryByExamplePredicateBuilder.getPredicate(root, cb, example, EscapeCharacter.of('\\'));
|
||||
|
||||
verify(cb, times(1)).like(any(Expression.class), eq("%f\\\\o\\_o"), eq('\\'));
|
||||
}
|
||||
|
||||
static class Person {
|
||||
|
||||
@Id Long id;
|
||||
|
||||
Reference in New Issue
Block a user