DATAJPA-1303 - Support ignoring case for finding in collections.
Original pull request: #261.
This commit is contained in:
committed by
Jens Schauder
parent
6765db0e4e
commit
6401f91c83
@@ -54,6 +54,7 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Paluch
|
||||
* @author Reda.Housni-Alaoui
|
||||
* @author Moritz Becker
|
||||
* @author Andrey Kovalev
|
||||
*/
|
||||
public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<? extends Object>, Predicate> {
|
||||
|
||||
@@ -270,10 +271,10 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<? extend
|
||||
return getTypedPath(root, part).isNotNull();
|
||||
case NOT_IN:
|
||||
// cast required for eclipselink workaround, see DATAJPA-433
|
||||
return getTypedPath(root, part).in((Expression<Collection<?>>) provider.next(part, Collection.class).getExpression()).not();
|
||||
return upperIfIgnoreCase(getTypedPath(root, part)).in((Expression<Collection<?>>) provider.next(part, Collection.class).getExpression()).not();
|
||||
case IN:
|
||||
// cast required for eclipselink workaround, see DATAJPA-433
|
||||
return getTypedPath(root, part).in((Expression<Collection<?>>) provider.next(part, Collection.class).getExpression());
|
||||
return upperIfIgnoreCase(getTypedPath(root, part)).in((Expression<Collection<?>>) provider.next(part, Collection.class).getExpression());
|
||||
case STARTING_WITH:
|
||||
case ENDING_WITH:
|
||||
case CONTAINING:
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.ParameterExpression;
|
||||
@@ -31,11 +32,13 @@ import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
|
||||
import org.springframework.data.repository.query.parser.Part.Type;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -46,6 +49,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @author Jens Schauder
|
||||
* @author Andrey Kovalev
|
||||
*/
|
||||
class ParameterMetadataProvider {
|
||||
|
||||
@@ -173,7 +177,7 @@ class ParameterMetadataProvider {
|
||||
|
||||
Object value = bindableParameterValues == null ? ParameterMetadata.PLACEHOLDER : bindableParameterValues.next();
|
||||
|
||||
ParameterMetadata<T> metadata = new ParameterMetadata<>(expression, part.getType(), value, persistenceProvider, escape);
|
||||
ParameterMetadata<T> metadata = new ParameterMetadata<>(expression, part, value, persistenceProvider, escape);
|
||||
expressions.add(metadata);
|
||||
|
||||
return metadata;
|
||||
@@ -186,6 +190,7 @@ class ParameterMetadataProvider {
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Andrey Kovalev
|
||||
* @param <T>
|
||||
*/
|
||||
static class ParameterMetadata<T> {
|
||||
@@ -196,16 +201,18 @@ class ParameterMetadataProvider {
|
||||
private final ParameterExpression<T> expression;
|
||||
private final PersistenceProvider persistenceProvider;
|
||||
private final EscapeCharacter escape;
|
||||
private final boolean ignoreCase;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ParameterMetadata}.
|
||||
*/
|
||||
public ParameterMetadata(ParameterExpression<T> expression, Type type, @Nullable Object value,
|
||||
public ParameterMetadata(ParameterExpression<T> expression, Part part, @Nullable Object value,
|
||||
PersistenceProvider provider, EscapeCharacter escape) {
|
||||
|
||||
this.expression = expression;
|
||||
this.persistenceProvider = provider;
|
||||
this.type = value == null && Type.SIMPLE_PROPERTY.equals(type) ? Type.IS_NULL : type;
|
||||
this.type = value == null && Type.SIMPLE_PROPERTY.equals(part.getType()) ? Type.IS_NULL : part.getType();
|
||||
this.ignoreCase = IgnoreCaseType.ALWAYS.equals(part.shouldIgnoreCase());
|
||||
this.escape = escape;
|
||||
}
|
||||
|
||||
@@ -253,7 +260,7 @@ class ParameterMetadataProvider {
|
||||
}
|
||||
|
||||
return Collection.class.isAssignableFrom(expressionType) //
|
||||
? persistenceProvider.potentiallyConvertEmptyCollection(toCollection(value)) //
|
||||
? persistenceProvider.potentiallyConvertEmptyCollection(upperIfIgnoreCase(ignoreCase, toCollection(value))) //
|
||||
: value;
|
||||
}
|
||||
|
||||
@@ -282,5 +289,24 @@ class ParameterMetadataProvider {
|
||||
|
||||
return Collections.singleton(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Collection<?> upperIfIgnoreCase(boolean ignoreCase, @Nullable Collection<?>
|
||||
collection) {
|
||||
if (ignoreCase
|
||||
&& !CollectionUtils.isEmpty(collection)) {
|
||||
return ((Collection<String>) collection).stream()
|
||||
.map(it -> {
|
||||
if (it == null) {
|
||||
return null;
|
||||
} else {
|
||||
return it.toUpperCase();
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
* @author Thomas Darimont
|
||||
* @author Jens Schauder
|
||||
* @author Moritz Becker
|
||||
* @author Andrey Kovalev
|
||||
*/
|
||||
@ContextConfiguration(value = "classpath:eclipselink.xml")
|
||||
public class EclipseLinkNamespaceUserRepositoryTests extends NamespaceUserRepositoryTests {
|
||||
@@ -134,4 +135,36 @@ public class EclipseLinkNamespaceUserRepositoryTests extends NamespaceUserReposi
|
||||
@Test
|
||||
@Ignore
|
||||
public void savingUserThrowsAnException() {}
|
||||
|
||||
/**
|
||||
* Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 is resolved.
|
||||
*/
|
||||
@Ignore
|
||||
@Override
|
||||
@Test // DATAJPA-1303
|
||||
public void findByElementCollectionInAttributeIgnoreCase() {}
|
||||
|
||||
/**
|
||||
* Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 is resolved.
|
||||
*/
|
||||
@Ignore
|
||||
@Override
|
||||
@Test // DATAJPA-1303
|
||||
public void findByElementCollectionNotInAttributeIgnoreCase() {}
|
||||
|
||||
/**
|
||||
* Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 is resolved.
|
||||
*/
|
||||
@Ignore
|
||||
@Override
|
||||
@Test // DATAJPA-1303
|
||||
public void findByElementVarargInAttributeIgnoreCase() {}
|
||||
|
||||
/**
|
||||
* Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 is resolved.
|
||||
*/
|
||||
@Ignore
|
||||
@Override
|
||||
@Test // DATAJPA-1303
|
||||
public void findByElementCollectionInAttributeIgnoreCaseWithNulls() {}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ import com.google.common.base.Optional;
|
||||
* @author Mark Paluch
|
||||
* @author Kevin Peters
|
||||
* @author Jens Schauder
|
||||
* @author Andrey Kovalev
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:application-context.xml")
|
||||
@@ -2223,6 +2224,62 @@ public class UserRepositoryTests {
|
||||
repository.delete(new User());
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1303
|
||||
public void findByElementCollectionInAttributeIgnoreCase() {
|
||||
|
||||
firstUser.getAttributes().add("cOOl");
|
||||
secondUser.getAttributes().add("hIp");
|
||||
thirdUser.getAttributes().add("roCKsTar");
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
List<User> result = repository.findByAttributesIgnoreCaseIn(new HashSet<>(Arrays.asList("cOOl", "hIP")));
|
||||
|
||||
assertThat(result).containsOnly(firstUser, secondUser);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1303
|
||||
public void findByElementCollectionNotInAttributeIgnoreCase() {
|
||||
|
||||
firstUser.getAttributes().add("cOOl");
|
||||
secondUser.getAttributes().add("hIp");
|
||||
thirdUser.getAttributes().add("rOckStAr");
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
List<User> result = repository.findByAttributesIgnoreCaseNotIn(Arrays.asList("CooL", "HIp"));
|
||||
|
||||
assertThat(result).containsOnly(thirdUser);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1303
|
||||
public void findByElementVarargInAttributeIgnoreCase() {
|
||||
|
||||
firstUser.getAttributes().add("cOOl");
|
||||
secondUser.getAttributes().add("hIp");
|
||||
thirdUser.getAttributes().add("rOckStAr");
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
Page<User> result = repository.findByAttributesIgnoreCaseIn(PageRequest.of(0, 20), "CooL", "HIp");
|
||||
|
||||
assertThat(result).containsOnly(firstUser, secondUser);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-1303
|
||||
public void findByElementCollectionInAttributeIgnoreCaseWithNulls() {
|
||||
|
||||
firstUser.getAttributes().add("cOOl");
|
||||
secondUser.getAttributes().add("hIp");
|
||||
thirdUser.getAttributes().add("roCKsTar");
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
List<User> result = repository.findByAttributesIgnoreCaseIn(Arrays.asList("cOOl", null));
|
||||
|
||||
assertThat(result).containsOnly(firstUser);
|
||||
}
|
||||
|
||||
private Page<User> executeSpecWithSort(Sort sort) {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.google.common.base.Optional;
|
||||
* @author Thomas Darimont
|
||||
* @author Kevin Peters
|
||||
* @author Jeff Sheets
|
||||
* @author Andrey Kovalev
|
||||
*/
|
||||
public interface UserRepository
|
||||
extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User>, UserRepositoryCustom {
|
||||
@@ -591,6 +592,15 @@ public interface UserRepository
|
||||
@Query("select u from User u where u.lastname like %?#{escape([0])}% escape ?#{escapeCharacter()}")
|
||||
List<User> findContainingEscaped(String namePart);
|
||||
|
||||
// DATAJPA-1303
|
||||
List<User> findByAttributesIgnoreCaseIn(Collection<String> attributes);
|
||||
|
||||
// DATAJPA-1303
|
||||
List<User> findByAttributesIgnoreCaseNotIn(Collection<String> attributes);
|
||||
|
||||
// DATAJPA-1303
|
||||
Page<User> findByAttributesIgnoreCaseIn(Pageable pageable, String... attributes);
|
||||
|
||||
interface RolesAndFirstname {
|
||||
|
||||
String getFirstname();
|
||||
|
||||
Reference in New Issue
Block a user