DATACMNS-669 - Polishing.
Javadoc on MultiValueBinding. Hide QuerydslBindings.bind(…), ….include(…) and ….exclude(…) methods that take String arguments to reduce the probability of no query class being present. Refined the value handling for empty request values which are represented as String[] containing an empty String. We now massage those into an empty Collection for MultiValueBindings and a null value for SingleValueInvocations. We currently skip property values that match this pattern entirely but are prepared to allow a configuration flip switch to handle those scenarios in the future. We currently stick to skipping those values to prevent the bindings from having to deal with null values. We now also allow the bindings to return null to indicate they don't want to get applied in the resulting predicate at all. Switched to interface based customization to make it easier to use customizations with Spring Data REST. Added Querydsl-specific adapter of RepositoryInvoker to allow Spring Data REST to hook in the execution of the predicate obtained from request parameters. Original pull request: #132.
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.querydsl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.support.RepositoryInvoker;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
* {@link RepositoryInvoker} that is aware of a {@link QueryDslPredicateExecutor} and {@link Predicate} to be executed
|
||||
* for all flavors of {@code findAll(…)}. All other calls are forwarded to the configured delegate.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class QuerydslRepositoryInvokerAdapter implements RepositoryInvoker {
|
||||
|
||||
private final RepositoryInvoker delegate;
|
||||
private final QueryDslPredicateExecutor<Object> executor;
|
||||
private final Predicate predicate;
|
||||
|
||||
/**
|
||||
* Creates a new {@link QuerydslRepositoryInvokerAdapter} for the given delegate {@link RepositoryInvoker},
|
||||
* {@link QueryDslPredicateExecutor} and Querydsl {@link Predicate}.
|
||||
*
|
||||
* @param delegate must not be {@literal null}.
|
||||
* @param executor must not be {@literal null}.
|
||||
* @param predicate can be {@literal null}.
|
||||
*/
|
||||
public QuerydslRepositoryInvokerAdapter(RepositoryInvoker delegate, QueryDslPredicateExecutor<Object> executor,
|
||||
Predicate predicate) {
|
||||
|
||||
Assert.notNull(delegate, "Delegate RepositoryInvoker must not be null!");
|
||||
Assert.notNull(executor, "QuerydslPredicateExecutor must not be null!");
|
||||
|
||||
this.delegate = delegate;
|
||||
this.executor = executor;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeFindAll(org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
@Override
|
||||
public Iterable<Object> invokeFindAll(Pageable pageable) {
|
||||
return executor.findAll(predicate, pageable);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeFindAll(org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@Override
|
||||
public Iterable<Object> invokeFindAll(Sort sort) {
|
||||
return executor.findAll(predicate, sort);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvocationInformation#hasDeleteMethod()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasDeleteMethod() {
|
||||
return delegate.hasDeleteMethod();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvocationInformation#hasFindAllMethod()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasFindAllMethod() {
|
||||
return delegate.hasFindAllMethod();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvocationInformation#hasFindOneMethod()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasFindOneMethod() {
|
||||
return delegate.hasFindOneMethod();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvocationInformation#hasSaveMethod()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasSaveMethod() {
|
||||
return delegate.hasSaveMethod();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeDelete(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public void invokeDelete(Serializable id) {
|
||||
delegate.invokeDelete(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeFindOne(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public <T> T invokeFindOne(Serializable id) {
|
||||
return delegate.invokeFindOne(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeQueryMethod(java.lang.reflect.Method, java.util.Map, org.springframework.data.domain.Pageable, org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@Override
|
||||
public Object invokeQueryMethod(Method method, Map<String, String[]> parameters, Pageable pageable, Sort sort) {
|
||||
return delegate.invokeQueryMethod(method, parameters, pageable, sort);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeQueryMethod(java.lang.reflect.Method, org.springframework.util.MultiValueMap, org.springframework.data.domain.Pageable, org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@Override
|
||||
public Object invokeQueryMethod(Method method, MultiValueMap<String, ? extends Object> parameters, Pageable pageable,
|
||||
Sort sort) {
|
||||
return delegate.invokeQueryMethod(method, parameters, pageable, sort);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryInvoker#invokeSave(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public <T> T invokeSave(T object) {
|
||||
return delegate.invokeSave(object);
|
||||
}
|
||||
}
|
||||
@@ -92,13 +92,18 @@ public class DefaultRepositoryInvokerFactory implements RepositoryInvokerFactory
|
||||
* @param domainType
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private RepositoryInvoker prepareInvokers(Class<?> domainType) {
|
||||
|
||||
Object repository = repositories.getRepositoryFor(domainType);
|
||||
Assert.notNull(repository, String.format("No repository found for domain type: %s", domainType));
|
||||
RepositoryInformation information = repositories.getRepositoryInformationFor(domainType);
|
||||
|
||||
return createInvoker(information, repository);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected RepositoryInvoker createInvoker(RepositoryInformation information, Object repository) {
|
||||
|
||||
if (repository instanceof PagingAndSortingRepository) {
|
||||
return new PagingAndSortingRepositoryInvoker((PagingAndSortingRepository<Object, Serializable>) repository,
|
||||
information, conversionService);
|
||||
|
||||
@@ -21,9 +21,21 @@ import com.mysema.query.types.Path;
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
* {@link MultiValueBinding} creates a {@link Predicate} out of given {@link Path} and collection value. Used for
|
||||
* specific parameter treatment in {@link QuerydslBindings}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.11
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MultiValueBinding<T extends Path<? extends S>, S> {
|
||||
|
||||
/**
|
||||
* Returns the predicate to be applied to the given {@link Path} for the given value.
|
||||
*
|
||||
* @param path {@link Path} to the property. Will not be {@literal null}.
|
||||
* @param value the value that should be bound. Will not be {@literal null} or empty.
|
||||
* @return can be {@literal null}, in which case the binding will not be incorporated in the overall {@link Predicate}.
|
||||
*/
|
||||
Predicate bind(T path, Collection<? extends S> value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.web.querydsl;
|
||||
|
||||
import com.mysema.query.types.EntityPath;
|
||||
|
||||
/**
|
||||
* A component that will customize {@link QuerydslBindings} for the given entity path.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface QuerydslBinderCustomizer<T extends EntityPath<?>> {
|
||||
|
||||
void customize(QuerydslBindings bindings, T root);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.web.querydsl;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -39,7 +40,7 @@ import com.mysema.query.types.Predicate;
|
||||
* new QuerydslBindings() {
|
||||
* {
|
||||
* bind(QUser.user.address.city).using((path, value) -> path.like(value.toString()));
|
||||
* bind("lastname").using((path, value) -> path.like(value.toString()));
|
||||
* bind(String.class).using((path, value) -> path.like(value.toString()));
|
||||
* }
|
||||
* }
|
||||
* </code>
|
||||
@@ -72,27 +73,27 @@ public class QuerydslBindings {
|
||||
/**
|
||||
* Returns a new {@link PathBinder} for the given {@link Path}s to define bindings for them.
|
||||
*
|
||||
* @param paths must not be {@literal null} or emtpy.
|
||||
* @param paths must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public final <T extends Path<S>, S> PathBinder<T, S> bind(T... paths) {
|
||||
return new PathBinder<T, S>(paths);
|
||||
}
|
||||
|
||||
public final <T> TypeBinder<T> bind(Class<T> type) {
|
||||
return new TypeBinder<T>(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a binding for the given
|
||||
*
|
||||
* @param paths
|
||||
* @return
|
||||
*/
|
||||
public final PropertyBinder bind(String... paths) {
|
||||
final PropertyBinder bind(String... paths) {
|
||||
return new PropertyBinder(Arrays.asList(paths));
|
||||
}
|
||||
|
||||
public final <T> TypeBinder<T> bind(Class<T> type) {
|
||||
return new TypeBinder<T>(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude properties from binding. Exclusion of all properties of a nested type can be done by exclusion on a higher
|
||||
* level. E.g. {@code address} would exclude both {@code address.city} and {@code address.street}.
|
||||
@@ -114,7 +115,7 @@ public class QuerydslBindings {
|
||||
*
|
||||
* @param properties
|
||||
*/
|
||||
public final void excluding(String... properties) {
|
||||
final void excluding(String... properties) {
|
||||
this.blackList.addAll(Arrays.asList(properties));
|
||||
}
|
||||
|
||||
@@ -137,7 +138,7 @@ public class QuerydslBindings {
|
||||
*
|
||||
* @param properties
|
||||
*/
|
||||
public final void including(String... properties) {
|
||||
final void including(String... properties) {
|
||||
|
||||
Assert.notEmpty(properties, "At least one property has to be provided!");
|
||||
|
||||
@@ -264,7 +265,7 @@ public class QuerydslBindings {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
protected final class PathBinder<P extends Path<? extends T>, T> {
|
||||
public final class PathBinder<P extends Path<? extends T>, T> {
|
||||
|
||||
private final List<P> paths;
|
||||
|
||||
@@ -302,7 +303,7 @@ public class QuerydslBindings {
|
||||
}
|
||||
}
|
||||
|
||||
protected final class TypeBinder<T> {
|
||||
public final class TypeBinder<T> {
|
||||
|
||||
private final Class<T> type;
|
||||
|
||||
@@ -323,7 +324,7 @@ public class QuerydslBindings {
|
||||
}
|
||||
}
|
||||
|
||||
protected final class PropertyBinder {
|
||||
public final class PropertyBinder {
|
||||
|
||||
private final List<String> paths;
|
||||
|
||||
@@ -393,7 +394,8 @@ public class QuerydslBindings {
|
||||
*/
|
||||
@Override
|
||||
public Predicate bind(T path, Collection<? extends S> value) {
|
||||
return delegate.bind(path, value.iterator().next());
|
||||
Iterator<? extends S> iterator = value.iterator();
|
||||
return delegate.bind(path, iterator.hasNext() ? iterator.next() : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ package org.springframework.data.web.querydsl;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mysema.query.BooleanBuilder;
|
||||
import com.mysema.query.types.Path;
|
||||
import com.mysema.query.types.Predicate;
|
||||
import com.mysema.query.types.expr.SimpleExpression;
|
||||
@@ -26,7 +29,6 @@ import com.mysema.query.types.path.CollectionPathBase;
|
||||
* Default implementation of {@link MultiValueBinding} creating {@link Predicate} based on the {@link Path}s type.
|
||||
* Binds:
|
||||
* <ul>
|
||||
* <li><i>{@literal null}</i> as {@link SimpleExpression#isNull()}.</li>
|
||||
* <li><i>{@link java.lang.Object}</i> as {@link SimpleExpression#eq()} on simple properties.</li>
|
||||
* <li><i>{@link java.lang.Object}</i> as {@link SimpleExpression#contains()} on collection properties.</li>
|
||||
* <li><i>{@link java.util.Collection}</i> as {@link SimpleExpression#in()} on simple properties.</li>
|
||||
@@ -44,25 +46,33 @@ class QuerydslDefaultBinding implements MultiValueBinding<Path<? extends Object>
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Predicate bind(Path<?> path, Collection<? extends Object> source) {
|
||||
public Predicate bind(Path<?> path, Collection<? extends Object> value) {
|
||||
|
||||
if ((source == null || source.isEmpty())) {
|
||||
return ((SimpleExpression) path).isNull();
|
||||
Assert.notNull(path, "Path must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
if (value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object firstValue = source.iterator().next();
|
||||
|
||||
if (path instanceof CollectionPathBase) {
|
||||
return ((CollectionPathBase) path).contains(firstValue);
|
||||
|
||||
BooleanBuilder builder = new BooleanBuilder();
|
||||
|
||||
for (Object element : value) {
|
||||
builder.and(((CollectionPathBase) path).contains(element));
|
||||
}
|
||||
|
||||
return builder.getValue();
|
||||
}
|
||||
|
||||
if (path instanceof SimpleExpression) {
|
||||
|
||||
if (source.size() > 1) {
|
||||
return ((SimpleExpression) path).in(source);
|
||||
if (value.size() > 1) {
|
||||
return ((SimpleExpression) path).in(value);
|
||||
}
|
||||
|
||||
return ((SimpleExpression) path).eq(firstValue);
|
||||
return ((SimpleExpression) path).eq(value.iterator().next());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.lang.annotation.Target;
|
||||
* @author Christoph Strobl
|
||||
* @since 1.11
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Target({ ElementType.PARAMETER, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface QuerydslPredicate {
|
||||
|
||||
@@ -36,12 +36,13 @@ public @interface QuerydslPredicate {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> root() default Object.class;
|
||||
Class<?>root() default Object.class;
|
||||
|
||||
/**
|
||||
* Configuration class providing options on a per field base.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<? extends QuerydslBindings> bindings() default QuerydslBindings.class;
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class<? extends QuerydslBinderCustomizer>bindings() default QuerydslBinderCustomizer.class;
|
||||
}
|
||||
|
||||
@@ -15,22 +15,28 @@
|
||||
*/
|
||||
package org.springframework.data.web.querydsl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.querydsl.SimpleEntityPathResolver;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import com.mysema.query.types.EntityPath;
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
@@ -43,8 +49,6 @@ import com.mysema.query.types.Predicate;
|
||||
*/
|
||||
public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentResolver, ApplicationContextAware {
|
||||
|
||||
private static final QuerydslBindings DEFAULT_BINDINGS = new QuerydslBindings();
|
||||
|
||||
private final QuerydslPredicateBuilder predicateBuilder;
|
||||
|
||||
private AutowireCapableBeanFactory beanFactory;
|
||||
@@ -95,8 +99,16 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
|
||||
public Predicate resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
return predicateBuilder.getPredicate(new MutablePropertyValues(webRequest.getParameterMap()),
|
||||
createBindings(parameter), extractTypeInfo(parameter).getActualType());
|
||||
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
for (Entry<String, String[]> entry : webRequest.getParameterMap().entrySet()) {
|
||||
parameters.put(entry.getKey(), Arrays.asList(entry.getValue()));
|
||||
}
|
||||
|
||||
QuerydslPredicate annotation = parameter.getParameterAnnotation(QuerydslPredicate.class);
|
||||
TypeInformation<?> domainType = extractTypeInfo(parameter).getActualType();
|
||||
|
||||
return predicateBuilder.getPredicate(parameters, createBindings(annotation, domainType.getType()), domainType);
|
||||
}
|
||||
|
||||
private TypeInformation<?> extractTypeInfo(MethodParameter parameter) {
|
||||
@@ -110,16 +122,22 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
|
||||
return ClassTypeInformation.from(annotation.root());
|
||||
}
|
||||
|
||||
private QuerydslBindings createBindings(MethodParameter parameter)
|
||||
throws InstantiationException, IllegalAccessException {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public QuerydslBindings createBindings(QuerydslPredicate annotation, Class<?> domainType) {
|
||||
|
||||
QuerydslPredicate annotation = parameter.getParameterAnnotation(QuerydslPredicate.class);
|
||||
EntityPath<?> path = SimpleEntityPathResolver.INSTANCE.createPath(domainType);
|
||||
|
||||
if (annotation == null) {
|
||||
return DEFAULT_BINDINGS;
|
||||
QuerydslBindings bindings = new QuerydslBindings();
|
||||
|
||||
if (annotation == null || annotation.bindings().equals(QuerydslBinderCustomizer.class)) {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
Class<? extends QuerydslBindings> type = annotation.bindings();
|
||||
return beanFactory != null ? beanFactory.createBean(type) : BeanUtils.instantiateClass(type);
|
||||
Class<? extends QuerydslBinderCustomizer> type = annotation.bindings();
|
||||
QuerydslBinderCustomizer<EntityPath<?>> customizer = beanFactory != null ? beanFactory.createBean(type)
|
||||
: BeanUtils.instantiateClass(type);
|
||||
customizer.customize(bindings, path);
|
||||
|
||||
return bindings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
@@ -30,12 +31,11 @@ import org.springframework.data.mapping.PropertyReferenceException;
|
||||
import org.springframework.data.querydsl.SimpleEntityPathResolver;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mysema.query.BooleanBuilder;
|
||||
import com.mysema.query.types.EntityPath;
|
||||
import com.mysema.query.types.Path;
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
@@ -46,7 +46,7 @@ import com.mysema.query.types.Predicate;
|
||||
* @author Oliver Gierke
|
||||
* @since 1.11
|
||||
*/
|
||||
class QuerydslPredicateBuilder {
|
||||
public class QuerydslPredicateBuilder {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
private final MultiValueBinding<?, ?> defaultBinding;
|
||||
@@ -67,26 +67,36 @@ class QuerydslPredicateBuilder {
|
||||
* @return
|
||||
*/
|
||||
|
||||
public Predicate getPredicate(PropertyValues values, QuerydslBindings bindings, TypeInformation<?> type) {
|
||||
public Predicate getPredicate(MultiValueMap<String, String> values, QuerydslBindings bindings,
|
||||
TypeInformation<?> type) {
|
||||
|
||||
Assert.notNull(bindings, "Context must not be null!");
|
||||
|
||||
if (values.isEmpty()) {
|
||||
return new BooleanBuilder();
|
||||
}
|
||||
|
||||
BooleanBuilder builder = new BooleanBuilder();
|
||||
|
||||
for (PropertyValue propertyValue : values.getPropertyValues()) {
|
||||
if (values.isEmpty()) {
|
||||
return builder.getValue();
|
||||
}
|
||||
|
||||
for (Entry<String, List<String>> entry : values.entrySet()) {
|
||||
|
||||
if (isSingleElementCollectionWithoutText(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
PropertyPath propertyPath = PropertyPath.from(propertyValue.getName(), type);
|
||||
PropertyPath propertyPath = PropertyPath.from(entry.getKey(), type);
|
||||
|
||||
if (bindings.isPathVisible(propertyPath)) {
|
||||
|
||||
Collection<Object> value = convertToPropertyPathSpecificType(propertyValue.getValue(), propertyPath);
|
||||
builder.and(invokeBinding(propertyPath, bindings, value));
|
||||
Collection<Object> value = convertToPropertyPathSpecificType(entry.getValue(), propertyPath);
|
||||
|
||||
Predicate binding = invokeBinding(propertyPath, bindings, value);
|
||||
|
||||
if (binding != null) {
|
||||
builder.and(binding);
|
||||
}
|
||||
}
|
||||
} catch (PropertyReferenceException o_O) {
|
||||
// not a property of the domain object, continue
|
||||
@@ -127,48 +137,33 @@ class QuerydslPredicateBuilder {
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
private static Path<?> reifyPath(PropertyPath path, EntityPath<?> base) {
|
||||
private static Path<?> reifyPath(PropertyPath path, Path<?> base) {
|
||||
|
||||
EntityPath<?> entityPath = base != null ? base
|
||||
Path<?> entityPath = base != null ? base
|
||||
: SimpleEntityPathResolver.INSTANCE.createPath(path.getOwningType().getType());
|
||||
|
||||
Field field = ReflectionUtils.findField(entityPath.getClass(), path.getSegment());
|
||||
Object value = ReflectionUtils.getField(field, entityPath);
|
||||
|
||||
if (path.hasNext() && value instanceof EntityPath) {
|
||||
return reifyPath(path.next(), (EntityPath<?>) value);
|
||||
if (path.hasNext()) {
|
||||
return reifyPath(path.next(), (Path<?>) value);
|
||||
}
|
||||
|
||||
return (Path<?>) value;
|
||||
}
|
||||
|
||||
private Collection<Object> convertToPropertyPathSpecificType(Object source, PropertyPath path) {
|
||||
private Collection<Object> convertToPropertyPathSpecificType(List<String> source, PropertyPath path) {
|
||||
|
||||
Class<?> targetType = path.getLeafProperty().getType();
|
||||
|
||||
if (targetType.isInstance(source)) {
|
||||
return Collections.singleton(source);
|
||||
if (isSingleElementCollectionWithoutText(source)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Object value = source;
|
||||
|
||||
if (ObjectUtils.isArray(value)) {
|
||||
value = CollectionUtils.arrayToList(value);
|
||||
}
|
||||
|
||||
if (value instanceof Collection) {
|
||||
return potentiallyConvertCollectionValues((Collection<?>) value, targetType);
|
||||
}
|
||||
|
||||
return Collections.singleton(potentiallyConvertValue(value, targetType));
|
||||
}
|
||||
|
||||
private Collection<Object> potentiallyConvertCollectionValues(Collection<?> source, Class<?> elementType) {
|
||||
|
||||
Collection<Object> target = new ArrayList<Object>(source.size());
|
||||
|
||||
for (Object value : source) {
|
||||
target.add(potentiallyConvertValue(value, elementType));
|
||||
for (String value : source) {
|
||||
target.add(potentiallyConvertValue(value, targetType));
|
||||
}
|
||||
|
||||
return target;
|
||||
@@ -178,4 +173,15 @@ class QuerydslPredicateBuilder {
|
||||
return conversionService.canConvert(source.getClass(), targetType) ? conversionService.convert(source, targetType)
|
||||
: source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given collection has exactly one element that doesn't contain any text. This is basically an
|
||||
* indicator that a request parameter has been submitted but no value for it.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static boolean isSingleElementCollectionWithoutText(List<String> source) {
|
||||
return source.size() == 1 && !StringUtils.hasText(source.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ import com.mysema.query.types.Path;
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
* {@link SingleValueBinding} creates a {@link Predicate} out of given {@link Path} and value. Used for specific parameter
|
||||
* treatment in {@link QuerydslBindings}.
|
||||
* {@link SingleValueBinding} creates a {@link Predicate} out of given {@link Path} and value. Used for specific
|
||||
* parameter treatment in {@link QuerydslBindings}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
@@ -32,9 +32,9 @@ public interface SingleValueBinding<T extends Path<? extends S>, S> {
|
||||
/**
|
||||
* Returns the predicate to be applied to the given {@link Path} for the given value.
|
||||
*
|
||||
* @param path {@link Path} to the property. Must not be {@literal null}.
|
||||
* @param value the value that should be bound. Can be {@literal null}.
|
||||
* @return must not be {@literal null}.
|
||||
* @param path {@link Path} to the property. Will not be {@literal null}.
|
||||
* @param value the value that should be bound. Will not be {@literal null}.
|
||||
* @return can be {@literal null}, in which case the binding will not be incorporated in the overall {@link Predicate}.
|
||||
*/
|
||||
Predicate bind(T path, S value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.querydsl;
|
||||
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.support.RepositoryInvoker;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link QuerydslRepositoryInvokerAdapter}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @soundtrack Emilie Nicolas - Grown Up
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class QuerydslRepositoryInvokerAdapterUnitTests {
|
||||
|
||||
@Mock RepositoryInvoker delegate;
|
||||
@Mock QueryDslPredicateExecutor<Object> executor;
|
||||
@Mock Predicate predicate;
|
||||
|
||||
QuerydslRepositoryInvokerAdapter adapter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.adapter = new QuerydslRepositoryInvokerAdapter(delegate, executor, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void forwardsFindAllToExecutorWithPredicate() {
|
||||
|
||||
Sort sort = new Sort("firstname");
|
||||
adapter.invokeFindAll(sort);
|
||||
|
||||
verify(executor, times(1)).findAll(predicate, sort);
|
||||
verify(delegate, times(0)).invokeFindAll(sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void forwardsFindAllWithPageableToExecutorWithPredicate() {
|
||||
|
||||
PageRequest pageable = new PageRequest(0, 10);
|
||||
adapter.invokeFindAll(pageable);
|
||||
|
||||
verify(executor, times(1)).findAll(predicate, pageable);
|
||||
verify(delegate, times(0)).invokeFindAll(pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings({ "deprecation", "unchecked" })
|
||||
public void forwardsMethodsToDelegate() {
|
||||
|
||||
adapter.hasDeleteMethod();
|
||||
verify(delegate, times(1)).hasDeleteMethod();
|
||||
|
||||
adapter.hasFindAllMethod();
|
||||
verify(delegate, times(1)).hasFindAllMethod();
|
||||
|
||||
adapter.hasFindOneMethod();
|
||||
verify(delegate, times(1)).hasFindOneMethod();
|
||||
|
||||
adapter.hasSaveMethod();
|
||||
verify(delegate, times(1)).hasSaveMethod();
|
||||
|
||||
adapter.invokeDelete(any(Serializable.class));
|
||||
verify(delegate, times(1)).invokeDelete(any(Serializable.class));
|
||||
|
||||
adapter.invokeFindOne(any(Serializable.class));
|
||||
verify(delegate, times(1)).invokeFindOne(any(Serializable.class));
|
||||
|
||||
adapter.invokeQueryMethod(any(Method.class), any(Map.class), any(Pageable.class), any(Sort.class));
|
||||
verify(delegate, times(1)).invokeQueryMethod(any(Method.class), any(Map.class), any(Pageable.class),
|
||||
any(Sort.class));
|
||||
|
||||
adapter.invokeQueryMethod(any(Method.class), (MultiValueMap<String, String>) any(MultiValueMap.class),
|
||||
any(Pageable.class), any(Sort.class));
|
||||
verify(delegate, times(1)).invokeQueryMethod(any(Method.class),
|
||||
(MultiValueMap<String, String>) any(MultiValueMap.class), any(Pageable.class), any(Sort.class));
|
||||
|
||||
adapter.invokeSave(any());
|
||||
verify(delegate, times(1)).invokeSave(any());
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.web.querydsl;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -32,14 +33,15 @@ import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class QuerydslDefaultBindingUnitTests {
|
||||
|
||||
QuerydslDefaultBinding builder;
|
||||
QuerydslDefaultBinding binding;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
builder = new QuerydslDefaultBinding();
|
||||
binding = new QuerydslDefaultBinding();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,9 +50,9 @@ public class QuerydslDefaultBindingUnitTests {
|
||||
@Test
|
||||
public void shouldCreatePredicateCorrectlyWhenPropertyIsInRoot() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.firstname, Collections.singleton("tam"));
|
||||
Predicate predicate = binding.bind(QUser.user.firstname, Collections.singleton("tam"));
|
||||
|
||||
assertThat(predicate, is(QUser.user.firstname.eq("tam")));
|
||||
assertPredicate(predicate, is(QUser.user.firstname.eq("tam")));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,31 +61,20 @@ public class QuerydslDefaultBindingUnitTests {
|
||||
@Test
|
||||
public void shouldCreatePredicateCorrectlyWhenPropertyIsInNestedElement() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.address.city, Collections.singleton("two rivers"));
|
||||
Predicate predicate = binding.bind(QUser.user.address.city, Collections.singleton("two rivers"));
|
||||
|
||||
Assert.assertThat(predicate.toString(), is(QUser.user.address.city.eq("two rivers").toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreatePredicateCorrectlyWhenValueIsNull() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.firstname, Collections.emptySet());
|
||||
|
||||
assertThat(predicate, is(QUser.user.firstname.isNull()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreatePredicateWithContainingWhenPropertyIsCollectionLikeAndValueIsObject() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.nickNames, Collections.singleton("dragon reborn"));
|
||||
Predicate predicate = binding.bind(QUser.user.nickNames, Collections.singleton("dragon reborn"));
|
||||
|
||||
assertThat(predicate, is(QUser.user.nickNames.contains("dragon reborn")));
|
||||
assertPredicate(predicate, is(QUser.user.nickNames.contains("dragon reborn")));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,17 +83,22 @@ public class QuerydslDefaultBindingUnitTests {
|
||||
@Test
|
||||
public void shouldCreatePredicateWithInWhenPropertyIsAnObjectAndValueIsACollection() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.firstname, Arrays.asList("dragon reborn", "shadowkiller"));
|
||||
Predicate predicate = binding.bind(QUser.user.firstname, Arrays.asList("dragon reborn", "shadowkiller"));
|
||||
|
||||
assertThat(predicate, is(QUser.user.firstname.in(Arrays.asList("dragon reborn", "shadowkiller"))));
|
||||
assertPredicate(predicate, is(QUser.user.firstname.in(Arrays.asList("dragon reborn", "shadowkiller"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testname() {
|
||||
|
||||
assertThat(binding.bind(QUser.user.lastname, Collections.emptySet()), is(nullValue()));
|
||||
}
|
||||
|
||||
/*
|
||||
* just to satisfy generic type boundaries o_O
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void assertThat(Predicate predicate, Matcher<? extends Expression> matcher) {
|
||||
private void assertPredicate(Predicate predicate, Matcher<? extends Expression> matcher) {
|
||||
Assert.assertThat((Expression) predicate, Is.<Expression> is((Matcher<Expression>) matcher));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -205,18 +205,6 @@ public class QuerydslPredicateArgumentResolverUnitTests {
|
||||
assertThat(predicate.toString(), is(QUser.user.inceptionYear.eq(973L).toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void createBindingContextShouldUseQuerydslPredicationAnntotationDefaultBindingIfNotAnnotated() {
|
||||
|
||||
Object bindings = ReflectionTestUtils.invokeMethod(resolver, "createBindings",
|
||||
getMethodParameterFor("predicateWithoutAnnotation", Predicate.class));
|
||||
|
||||
assertThat(bindings, is(instanceOf(QuerydslBindings.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@@ -239,11 +227,11 @@ public class QuerydslPredicateArgumentResolverUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class SpecificBinding extends QuerydslBindings {
|
||||
static class SpecificBinding implements QuerydslBinderCustomizer<QUser> {
|
||||
|
||||
public SpecificBinding() {
|
||||
public void customize(QuerydslBindings bindings, QUser user) {
|
||||
|
||||
bind("firstname").using(new SingleValueBinding<StringPath, String>() {
|
||||
bindings.bind("firstname").using(new SingleValueBinding<StringPath, String>() {
|
||||
|
||||
@Override
|
||||
public Predicate bind(StringPath path, String value) {
|
||||
@@ -251,7 +239,7 @@ public class QuerydslPredicateArgumentResolverUnitTests {
|
||||
}
|
||||
});
|
||||
|
||||
bind(QUser.user.lastname).single(new SingleValueBinding<StringPath, String>() {
|
||||
bindings.bind(user.lastname).single(new SingleValueBinding<StringPath, String>() {
|
||||
|
||||
@Override
|
||||
public Predicate bind(StringPath path, String value) {
|
||||
@@ -259,7 +247,7 @@ public class QuerydslPredicateArgumentResolverUnitTests {
|
||||
}
|
||||
});
|
||||
|
||||
excluding("address");
|
||||
bindings.excluding("address");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.web.querydsl;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -24,7 +23,6 @@ import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.querydsl.QUser;
|
||||
import org.springframework.data.querydsl.User;
|
||||
@@ -33,9 +31,9 @@ import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.mysema.query.BooleanBuilder;
|
||||
import com.mysema.query.collections.CollQueryFactory;
|
||||
import com.mysema.query.types.Predicate;
|
||||
import com.mysema.query.types.path.StringPath;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link QuerydslPredicateBuilder}.
|
||||
@@ -45,13 +43,16 @@ import com.mysema.query.types.Predicate;
|
||||
*/
|
||||
public class QuerydslPredicateBuilderUnitTests {
|
||||
|
||||
static final ClassTypeInformation<User> USER_TYPE = ClassTypeInformation.from(User.class);
|
||||
static final QuerydslBindings DEFAULT_BINDINGS = new QuerydslBindings();
|
||||
|
||||
QuerydslPredicateBuilder builder;
|
||||
MultiValueMap<String, String> values;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
builder = new QuerydslPredicateBuilder(new DefaultConversionService());
|
||||
this.builder = new QuerydslPredicateBuilder(new DefaultConversionService());
|
||||
this.values = new LinkedMultiValueMap<String, String>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +68,7 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void getPredicateShouldThrowErrorWhenBindingContextIsNull() {
|
||||
builder.getPredicate(new MutablePropertyValues(), null, null);
|
||||
builder.getPredicate(values, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,8 +77,7 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
@Test
|
||||
public void getPredicateShouldReturnEmptyPredicateWhenPropertiesAreEmpty() {
|
||||
|
||||
assertThat(builder.getPredicate(new MutablePropertyValues(), DEFAULT_BINDINGS, ClassTypeInformation.OBJECT),
|
||||
is((Predicate) new BooleanBuilder()));
|
||||
assertThat(builder.getPredicate(values, DEFAULT_BINDINGS, ClassTypeInformation.OBJECT), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,8 +86,9 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
@Test
|
||||
public void resolveArgumentShouldCreateSingleStringParameterPredicateCorrectly() throws Exception {
|
||||
|
||||
Predicate predicate = builder.getPredicate(new MutablePropertyValues(singletonMap("firstname", "Oliver")),
|
||||
DEFAULT_BINDINGS, ClassTypeInformation.from(User.class));
|
||||
values.add("firstname", "Oliver");
|
||||
|
||||
Predicate predicate = builder.getPredicate(values, DEFAULT_BINDINGS, USER_TYPE);
|
||||
|
||||
assertThat(predicate, is((Predicate) QUser.user.firstname.eq("Oliver")));
|
||||
|
||||
@@ -103,8 +104,9 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
@Test
|
||||
public void resolveArgumentShouldCreateNestedStringParameterPredicateCorrectly() throws Exception {
|
||||
|
||||
Predicate predicate = builder.getPredicate(new MutablePropertyValues(singletonMap("address.city", "Linz")),
|
||||
DEFAULT_BINDINGS, ClassTypeInformation.from(User.class));
|
||||
values.add("address.city", "Linz");
|
||||
|
||||
Predicate predicate = builder.getPredicate(values, DEFAULT_BINDINGS, USER_TYPE);
|
||||
|
||||
assertThat(predicate, is((Predicate) QUser.user.address.city.eq("Linz")));
|
||||
|
||||
@@ -114,16 +116,37 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
assertThat(result, hasItem(Users.CHRISTOPH));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void ignoresNonDomainTypeProperties() {
|
||||
|
||||
MultiValueMap<String, String> values = new LinkedMultiValueMap<String, String>();
|
||||
values.add("firstname", "rand");
|
||||
values.add("lastname".toUpperCase(), "al'thor");
|
||||
|
||||
Predicate predicate = builder.getPredicate(new MutablePropertyValues(values), new QuerydslBindings(),
|
||||
ClassTypeInformation.from(User.class));
|
||||
Predicate predicate = builder.getPredicate(values, new QuerydslBindings(), USER_TYPE);
|
||||
|
||||
assertThat(predicate, is((Predicate) QUser.user.firstname.eq("rand")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void forwardsNullForEmptyParameterToSingleValueBinder() {
|
||||
|
||||
values.add("lastname", null);
|
||||
|
||||
QuerydslBindings bindings = new QuerydslBindings();
|
||||
bindings.bind(QUser.user.lastname).single(new SingleValueBinding<StringPath, String>() {
|
||||
|
||||
@Override
|
||||
public Predicate bind(StringPath path, String value) {
|
||||
return value == null ? null : path.contains(value);
|
||||
}
|
||||
});
|
||||
|
||||
builder.getPredicate(values, bindings, USER_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user