Browse Source

DATACMNS-669 - Added support to bind Querydsl Predicates to Spring MVC controllers.

Adding @QuerydslPredicate to a parameter within SpringMVC Controller when having registered the QueryDslPredicateArgumentResolver allows to generate a Querydsl Predicate based on the request parameters.

Parameters will be converted into their corresponding property types before using the as predicate value. Given more than one attribute will connect those using 'and'. Collections of values will force usage of 'in' while single values on collection like properties result in 'contains'.

The base type for building the Querydsl Path is extracted from the methods return type. In case of non domain type return values it can optionally be specified via @QueydslPredicate(root = …). Specific Predicate conversions can be registered via '@QuerydslSpecification'.

This allows to sneak in specific property path handlings more easily.

new QueryDslPredicateSpecification() {
  {
    define(new StringPath("address.city"), (path, value) -> path.like(value.toString()));
  }
};

Renamed resolver, accessor etc. to express operation of binding properties to a resolved path and introduced explicit binding context which allows reuse of predicate builder within the ArgumentResolver.

Added registration of QuerydslPredicateArgumentResolver directly in SpringDataWebConfiguration reusing the provided ConversionService.

Added Javadoc and changed visibility of non public API and types.

Original pull request: #132.
pull/133/head
Christoph Strobl 11 years ago committed by Oliver Gierke
parent
commit
e645501174
  1. 6
      src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java
  2. 36
      src/main/java/org/springframework/data/web/querydsl/QuerydslBinding.java
  3. 124
      src/main/java/org/springframework/data/web/querydsl/QuerydslBindingContext.java
  4. 248
      src/main/java/org/springframework/data/web/querydsl/QuerydslBindings.java
  5. 68
      src/main/java/org/springframework/data/web/querydsl/QuerydslDefaultBinding.java
  6. 47
      src/main/java/org/springframework/data/web/querydsl/QuerydslPredicate.java
  7. 103
      src/main/java/org/springframework/data/web/querydsl/QuerydslPredicateArgumentResolver.java
  8. 179
      src/main/java/org/springframework/data/web/querydsl/QuerydslPredicateBuilder.java
  9. 27
      src/test/java/org/springframework/data/querydsl/Address.java
  10. 9
      src/test/java/org/springframework/data/querydsl/User.java
  11. 242
      src/test/java/org/springframework/data/web/querydsl/QuerydslBindingsUnitTests.java
  12. 107
      src/test/java/org/springframework/data/web/querydsl/QuerydslDefaultBindingUnitTests.java
  13. 250
      src/test/java/org/springframework/data/web/querydsl/QuerydslPredicateArgumentResolverUnitTests.java
  14. 76
      src/test/java/org/springframework/data/web/querydsl/QuerydslPredicateBuilderUnitTests.java

6
src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java

@ -26,10 +26,12 @@ import org.springframework.context.annotation.Configuration; @@ -26,10 +26,12 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.geo.format.DistanceFormatter;
import org.springframework.data.geo.format.PointFormatter;
import org.springframework.data.querydsl.QueryDslUtils;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.data.web.ProxyingHandlerMethodArgumentResolver;
import org.springframework.data.web.SortHandlerMethodArgumentResolver;
import org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@ -97,6 +99,10 @@ public class SpringDataWebConfiguration extends WebMvcConfigurerAdapter { @@ -97,6 +99,10 @@ public class SpringDataWebConfiguration extends WebMvcConfigurerAdapter {
argumentResolvers.add(sortResolver());
argumentResolvers.add(pageableResolver());
if (QueryDslUtils.QUERY_DSL_PRESENT) {
argumentResolvers.add(new QuerydslPredicateArgumentResolver(conversionService.getObject()));
}
ProxyingHandlerMethodArgumentResolver resolver = new ProxyingHandlerMethodArgumentResolver(
conversionService.getObject());
resolver.setBeanFactory(context);

36
src/main/java/org/springframework/data/web/querydsl/QuerydslBinding.java

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
/*
* 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.Path;
import com.mysema.query.types.Predicate;
/**
* {@link QuerydslBinding} creates a {@link Predicate} out of given {@link Path} and value. Used for specific parameter
* treatment in {@link QuerydslBindings}. <br />
*
* @author Christoph Strobl
* @since 1.11
*/
public interface QuerydslBinding<T extends Path<?>> {
/**
* @param path {@link Path} to the property. Must not be {@literal null}.
* @param value
* @return
*/
Predicate bind(T path, Object value);
}

124
src/main/java/org/springframework/data/web/querydsl/QuerydslBindingContext.java

@ -0,0 +1,124 @@ @@ -0,0 +1,124 @@
/*
* 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 org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.mysema.query.types.Path;
/**
* {@link QuerydslBindingContext} holds parameters required for executing {@link QuerydslBinding} on a specific property
* and {@link Path}.
*
* @author Christoph Strobl
* @since 1.11
*/
class QuerydslBindingContext {
private final TypeInformation<?> rootTypeInformation;
private final ConversionService conversionService;
private final QuerydslBindings bindings;
/**
* @param rootTypeInformation Root type information. Must not be {@literal null}.
* @param bindings Path specific bindings. Defaulted to {@link QuerydslBindings} if {@literal null}.
* @param conversionService {@link ConversionService} to be used for converting request parameters into target type.
* Defaulted to {@link DefaultConversionService} if {@literal null}.
*/
public QuerydslBindingContext(TypeInformation<?> rootTypeInformation, QuerydslBindings bindings,
ConversionService conversionService) {
Assert.notNull(rootTypeInformation, "TypeInformation must not be null!");
this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService;
this.rootTypeInformation = rootTypeInformation;
this.bindings = bindings == null ? new QuerydslBindings() : bindings;
}
/**
* {@link ConversionService} to be used for converting parameter values.
*
* @return never {@literal null}.
*/
public ConversionService getConversionService() {
return this.conversionService;
}
/**
* Root type information
*
* @return never {@literal null}.
*/
public TypeInformation<?> getTypeInformation() {
return rootTypeInformation;
}
/**
* {@link QuerydslBindings} to be considered.
*
* @return never {@literal null}.
*/
public QuerydslBindings getBindings() {
return bindings;
}
/**
* Specific {@link QuerydslBinding} for {@link PropertyPath}.
*
* @param path must not be {@literal null}.
* @return {@literal null} when no specific {@link QuerydslBinding} set for path.
* @see QuerydslBindings#getBindingForPath(PropertyPath)
*/
public QuerydslBinding<? extends Path<?>> getBindingForPath(PropertyPath path) {
return bindings.getBindingForPath(path);
}
/**
* Specific {@link Path} for {@link PropertyPath}.
*
* @param path
* @return
* @see QuerydslBindings#getPath(PropertyPath)
*/
public Path<?> getPathForPropertyPath(PropertyPath path) {
return bindings.getPath(path);
}
/**
* Checks visibility of given path against {@link QuerydslBindings}.
*
* @param path must not be {@literal null}.
* @return true when given path is visible by declaration.
* @see QuerydslBindings#isPathVisible(PropertyPath)
*/
public boolean isPathVisible(PropertyPath path) {
return bindings.isPathVisible(path);
}
/**
* Get the actual raw type of the used root.
*
* @return
* @see TypeInformation#getActualType()
*/
public Class<?> getTargetType() {
return rootTypeInformation.getActualType().getType();
}
}

248
src/main/java/org/springframework/data/web/querydsl/QuerydslBindings.java

@ -0,0 +1,248 @@ @@ -0,0 +1,248 @@
/*
* 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 java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mysema.query.types.Path;
/**
* {@link QuerydslBindings} allows definition of path specific {@link QuerydslBinding}. *
*
* <pre>
* <code>
* new QuerydslBindings() {
* {
* bind(QUser.user.address.city, (path, value) -> path.like(value.toString()));
* bind(new StringPath("address.city"), (path, value) -> path.like(value.toString()));
* }
* }
* </code>
* </pre>
*
* @author Christoph Strobl
* @since 1.11
*/
public class QuerydslBindings {
private final Map<String, PathAndBinding> pathSpecs;
private final Map<Class<?>, PathAndBinding> typeSpecs;
private Set<String> whiteList;
private final Set<String> blackList;
public QuerydslBindings() {
this.pathSpecs = new LinkedHashMap<String, PathAndBinding>();
this.typeSpecs = new LinkedHashMap<Class<?>, PathAndBinding>();
this.whiteList = new HashSet<String>();
this.blackList = new HashSet<String>();
}
/**
* @param path
* @param builder
*/
protected QuerydslBindings bind(String path, QuerydslBinding<? extends Path<?>> builder) {
Assert.hasText(path, "Cannot bind to null/empty path");
this.pathSpecs.put(path, new PathAndBinding(null, builder));
return this;
}
/**
* @param type
* @param builder
*/
protected <T> QuerydslBindings bind(Class<T> type, QuerydslBinding<? extends Path<T>> builder) {
Assert.notNull(type, "Cannot bind to null type!");
this.typeSpecs.put(type, new PathAndBinding(null, builder));
return this;
}
/**
* @param path
* @param builder
*/
protected <T extends Path<?>> QuerydslBindings bind(T path, QuerydslBinding<T> builder) {
Assert.notNull(path, "Cannot bind to null path!");
this.pathSpecs.put(extractPropertyPath(path), new PathAndBinding(path, builder));
return this;
}
/**
* Exclude properties from binding. <br />
* Exclusion of all properties of a nested type can be done by exclusion on a higher level. Eg. {@code address} would
* exclude both {@code address.city} and {@code address.street}.
*
* @param properties
*/
protected QuerydslBindings excluding(String... properties) {
this.blackList.addAll(Arrays.asList(properties));
return this;
}
/**
* Include properties for binding. <br />
* Include the property considered a binding candidate.
*
* @param properties
*/
protected QuerydslBindings including(String... properties) {
this.whiteList.addAll(Arrays.asList(properties));
return this;
}
/**
* Checks if a given {@link PropertyPath} should be visible for binding values.
*
* @param path
* @return
*/
public boolean isPathVisible(PropertyPath path) {
List<String> segments = Arrays.asList(path.toDotPath().split("\\."));
for (int i = 1; i <= segments.size(); i++) {
if (!isPathVisible(StringUtils.collectionToDelimitedString(segments.subList(0, i), "."))) {
// check if full path is on whitelist if though partial one is not
if (!whiteList.isEmpty()) {
return whiteList.contains(path.toDotPath());
}
return false;
}
}
return true;
}
private boolean isPathVisible(String path) {
if (whiteList.isEmpty() && blackList.isEmpty()) {
return true;
}
if (!blackList.isEmpty()) {
if (blackList.contains(path)) {
if (!whiteList.isEmpty() && whiteList.contains(path)) {
return true;
}
return false;
}
return true;
}
if (!whiteList.isEmpty()) {
if (whiteList.contains(path)) {
return true;
}
return false;
}
return true;
}
/**
* Returns the {@link QuerydslBinding} for the given {@link PropertyPath}. Prefers a path configured for the specific
* path but falls back to the builder registered for a given type.
*
* @param path must not be {@literal null}.
* @return
*/
public QuerydslBinding<? extends Path<?>> getBindingForPath(PropertyPath path) {
Assert.notNull(path, "PropertyPath must not be null!");
PathAndBinding pathAndBinding = pathSpecs.get(path.toDotPath());
if (pathAndBinding != null) {
return pathAndBinding.getBinding();
}
pathAndBinding = typeSpecs.get(path.getLeafProperty().getType());
return pathAndBinding == null ? null : pathAndBinding.getBinding();
}
Path<?> getPath(PropertyPath path) {
return getPathForStringPath(path.toDotPath());
}
private Path<?> getPathForStringPath(String path) {
PathAndBinding pathAndBuilder = pathSpecs.get(path);
if (pathAndBuilder == null) {
return null;
}
return pathAndBuilder.getPath();
}
private String extractPropertyPath(Path<?> path) {
if (path == null) {
return "";
}
if (path.getMetadata().getParent() != null && !path.getMetadata().getParent().getMetadata().isRoot()) {
return extractPropertyPath(path.getMetadata().getParent()) + "." + path.getMetadata().getName();
}
return path.getMetadata().getName();
}
/**
* @author Christoph Strobl
* @since 1.11
*/
private static class PathAndBinding {
private final Path<?> path;
private final QuerydslBinding<? extends Path<?>> binding;
public PathAndBinding(Path<?> path, QuerydslBinding<? extends Path<?>> binding) {
this.path = path;
this.binding = binding;
}
public Path<?> getPath() {
return path;
}
public QuerydslBinding<? extends Path<?>> getBinding() {
return binding;
}
}
}

68
src/main/java/org/springframework/data/web/querydsl/QuerydslDefaultBinding.java

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
/*
* 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 java.util.Collection;
import com.mysema.query.types.Path;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.expr.SimpleExpression;
import com.mysema.query.types.path.CollectionPathBase;
/**
* Default implementation of {@link QuerydslBinding} 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>
* </ul>
*
* @author Christoph Strobl
* @since 1.11
*/
class QuerydslDefaultBinding implements QuerydslBinding<Path<?>> {
/*
* (non-Javadoc)
* @see org.springframework.data.web.querydsl.QueryDslPredicateBuilder#buildPredicate(org.springframework.data.mapping.PropertyPath, java.lang.Object)
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Predicate bind(Path<?> path, Object source) {
if (source == null && path instanceof SimpleExpression) {
return ((SimpleExpression) path).isNull();
}
if (path instanceof CollectionPathBase) {
return ((CollectionPathBase) path).contains(source);
}
if (path instanceof SimpleExpression) {
if (source instanceof Collection) {
return ((SimpleExpression) path).in((Collection) source);
}
return ((SimpleExpression) path).eq(source);
}
throw new IllegalArgumentException(String.format("Cannot create predicate for path '%s' with type '%s'.", path,
path.getMetadata().getPathType()));
}
}

47
src/main/java/org/springframework/data/web/querydsl/QuerydslPredicate.java

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
/*
* 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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* {@link java.lang.annotation.Annotation} to specify aspects of {@link com.mysema.query.types.Predicate} used in Spring
* MVC {@link org.springframework.web.servlet.mvc.Controller}.
*
* @author Christoph Strobl
* @since 1.11
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface QuerydslPredicate {
/**
* Root type to be used for {@link com.mysema.query.types.Path}
*
* @return
*/
Class<?> root() default Object.class;
/**
* Configuration class providing options on a per field base.
*
* @return
*/
Class<? extends QuerydslBindings> bindings() default QuerydslBindings.class;
}

103
src/main/java/org/springframework/data/web/querydsl/QuerydslPredicateArgumentResolver.java

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
/*
* 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 org.springframework.beans.BeanUtils;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
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.Predicate;
/**
* {@link HandlerMethodArgumentResolver} to allow injection of {@link com.mysema.query.types.Predicate} into Spring MVC
* controller methods.
*
* @author Christoph Strobl
* @since 1.11
*/
public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentResolver {
private final QuerydslPredicateBuilder predicateBuilder;
private final ConversionService conversionService;
/**
* @param conversionService Defaulted to {@link DefaultConversionService} if {@literal null}.
*/
public QuerydslPredicateArgumentResolver(ConversionService conversionService) {
this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService;
this.predicateBuilder = new QuerydslPredicateBuilder();
}
/*
* (non-Javadoc)
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (Predicate.class.equals(parameter.getParameterType())) {
return true;
}
if (parameter.hasParameterAnnotation(QuerydslPredicate.class)) {
throw new IllegalArgumentException(String.format(
"Parameter at position %s must be of type Predicate but was %s.", parameter.getParameterIndex(),
parameter.getParameterType()));
}
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return predicateBuilder.getPredicate(new MutablePropertyValues(webRequest.getParameterMap()),
createBindingContext(parameter));
}
private QuerydslBindingContext createBindingContext(MethodParameter parameter) throws InstantiationException,
IllegalAccessException {
return new QuerydslBindingContext(extractTypeInfo(parameter), extractBindings(parameter), conversionService);
}
private TypeInformation<?> extractTypeInfo(MethodParameter parameter) {
Class<?> type = parameter.getParameterAnnotation(QuerydslPredicate.class).root();
return type == Object.class ? ClassTypeInformation.fromReturnTypeOf(parameter.getMethod()) : ClassTypeInformation
.from(type);
}
private QuerydslBindings extractBindings(MethodParameter parameter) throws InstantiationException,
IllegalAccessException {
return BeanUtils.instantiateClass(parameter.getParameterAnnotation(QuerydslPredicate.class).bindings());
}
}

179
src/main/java/org/springframework/data/web/querydsl/QuerydslPredicateBuilder.java

@ -0,0 +1,179 @@ @@ -0,0 +1,179 @@
/*
* 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 java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import com.mysema.query.BooleanBuilder;
import com.mysema.query.types.Path;
import com.mysema.query.types.PathMetadata;
import com.mysema.query.types.PathMetadataFactory;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.path.CollectionPath;
import com.mysema.query.types.path.NumberPath;
import com.mysema.query.types.path.PathBuilder;
import com.mysema.query.types.path.PathBuilderFactory;
import com.mysema.query.types.path.SimplePath;
import com.mysema.query.types.path.StringPath;
/**
* Builder assembling {@link Predicate} out of {@link PropertyValues}.
*
* @author Christoph Strobl
* @since 1.11
*/
class QuerydslPredicateBuilder {
private final QuerydslBinding<?> defaultBinding;
public QuerydslPredicateBuilder() {
this.defaultBinding = new QuerydslDefaultBinding();
}
/**
* @param values
* @param context
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Predicate getPredicate(PropertyValues values, QuerydslBindingContext context) {
Assert.notNull(context, "Context must not be null!");
if (values.isEmpty()) {
return new BooleanBuilder();
}
BooleanBuilder builder = new BooleanBuilder();
for (PropertyValue propertyValue : values.getPropertyValues()) {
PropertyPath propertyPath = PropertyPath.from(propertyValue.getName(), context.getTargetType());
if (context.isPathVisible(propertyPath)) {
Object value = convertToPropertyPathSpecificType(propertyValue.getValue(), propertyPath, context);
QuerydslBinding binding = getPredicateBuilderForPath(propertyPath, context);
builder.and(binding.bind(getPath(propertyPath, context), value));
}
}
return builder.getValue();
}
private QuerydslBinding<? extends Path<?>> getPredicateBuilderForPath(PropertyPath dotPath,
QuerydslBindingContext context) {
QuerydslBinding<? extends Path<?>> binding = context.getBindingForPath(dotPath);
return binding == null ? defaultBinding : binding;
}
private Path<?> getPath(PropertyPath propertyPath, QuerydslBindingContext context) {
Path<?> path = context.getPathForPropertyPath(propertyPath);
if (path != null) {
return path;
}
return new PropertyPathPathBuilder(context.getTargetType()).forProperty(propertyPath);
}
private Object convertToPropertyPathSpecificType(Object source, PropertyPath path, QuerydslBindingContext context) {
Object value = source;
if (ObjectUtils.isArray(value)) {
List<?> list = CollectionUtils.arrayToList(value);
if (!list.isEmpty() && list.size() == 1) {
value = list.get(0);
} else {
value = list;
}
}
if (value instanceof Collection) {
return potentiallyConvertCollectionValues((Collection<?>) value, path.getType(), context);
}
return potentiallyConvertValue(value, path.getType(), context);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private Collection<?> potentiallyConvertCollectionValues(Collection<?> source, Class<?> targetType,
QuerydslBindingContext context) {
Collection target = new ArrayList(source.size());
for (Object value : source) {
target.add(potentiallyConvertValue(value, targetType, context));
}
return target;
}
private Object potentiallyConvertValue(Object source, Class<?> targetType, QuerydslBindingContext context) {
return context.getConversionService().canConvert(source.getClass(), targetType)
? context.getConversionService().convert(source, targetType) : source;
}
/**
* {@link PropertyPathPathBuilder} creates a typed {@link Path} based on type information contained in
* {@link PropertyPath}.
*
* @author Christoph Strobl
*/
@SuppressWarnings("rawtypes")
static class PropertyPathPathBuilder {
final PathBuilder<?> pathBuilder;
public PropertyPathPathBuilder(Class<?> type) {
pathBuilder = new PathBuilderFactory().create(type);
}
@SuppressWarnings({ "unchecked" })
public Path<?> forProperty(PropertyPath source) {
PathMetadata<?> metadata = PathMetadataFactory.forVariable(source.toDotPath());
Path<?> path = null;
if (source.isCollection()) {
path = pathBuilder.get(new CollectionPath(Collection.class, source.getType(), metadata));
} else if (ClassUtils.isAssignable(String.class, source.getType())) {
path = pathBuilder.get(new StringPath(metadata));
} else if (ClassUtils.isAssignable(Number.class, source.getType())) {
path = pathBuilder.get(new NumberPath(source.getType(), metadata));
} else {
path = pathBuilder.get(new SimplePath(source.getType(), metadata));
}
return path;
}
}
}

27
src/test/java/org/springframework/data/querydsl/Address.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
/*
* 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 com.mysema.query.annotations.QueryEntity;
/**
* @author Christoph Strobl
*/
@QueryEntity
public class Address {
String street;
String city;
}

9
src/test/java/org/springframework/data/querydsl/User.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-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.
@ -16,16 +16,23 @@ @@ -16,16 +16,23 @@
package org.springframework.data.querydsl;
import java.util.Date;
import java.util.List;
import com.mysema.query.annotations.QueryEntity;
/**
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
*/
@QueryEntity
public class User {
String firstname;
String lastname;
Date dateOfBirth;
Address address;
List<String> nickNames;
Long inceptionYear;
}

242
src/test/java/org/springframework/data/web/querydsl/QuerydslBindingsUnitTests.java

@ -0,0 +1,242 @@ @@ -0,0 +1,242 @@
/*
* 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 static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.querydsl.Address;
import org.springframework.data.querydsl.QAddress;
import org.springframework.data.querydsl.User;
import org.springframework.data.util.ClassTypeInformation;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.path.StringPath;
/**
* Unit tests for {@link QuerydslBindings}.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class QuerydslBindingsUnitTests {
private QuerydslPredicateBuilder builder;
private QuerydslBindings typeBasedBindings;
private QuerydslBindings pathBasedBindings;
@Before
public void setUp() {
builder = new QuerydslPredicateBuilder();
typeBasedBindings = new QuerydslBindings() {
{
bind(String.class, new QuerydslBinding<StringPath>() {
@Override
public Predicate bind(StringPath path, Object value) {
return path.contains(value.toString());
}
});
}
};
pathBasedBindings = new QuerydslBindings() {
{
bind("address.street", new QuerydslBinding<StringPath>() {
@Override
public Predicate bind(StringPath path, Object value) {
return path.contains(value.toString());
}
});
bind("firstname", new QuerydslBinding<StringPath>() {
@Override
public Predicate bind(StringPath path, Object value) {
return path.contains(value.toString());
}
});
}
};
}
/**
* @see DATACMNS-669
*/
@Test(expected = IllegalArgumentException.class)
public void getBindingForPathShouldThrowErrorWhenPathIsNull() {
pathBasedBindings.getBindingForPath(null);
}
/**
* @see DATACMNS-669
*/
@Test
public void getBindingForPathShouldReturnNullWhenNoSpecifcBindingAvailable() {
assertThat(pathBasedBindings.getBindingForPath(PropertyPath.from("lastname", User.class)), nullValue());
}
/**
* @see DATACMNS-669
*/
@Test
public void getBindingForPathShouldReturnSpeficicBindingWhenAvailable() {
assertThat(pathBasedBindings.getBindingForPath(PropertyPath.from("firstname", User.class)), notNullValue());
}
/**
* @see DATACMNS-669
*/
@Test
public void getBindingForPathShouldReturnSpeficicBindingForNestedElementsWhenAvailable() {
assertThat(pathBasedBindings.getBindingForPath(PropertyPath.from("address.street", User.class)), notNullValue());
}
/**
* @see DATACMNS-669
*/
@Test
public void getBindingForPathShouldReturnSpeficicBindingForTypes() {
assertThat(typeBasedBindings.getBindingForPath(PropertyPath.from("address.street", User.class)), notNullValue());
}
/**
* @see DATACMNS-669
*/
@Test
public void getBindingForPathShouldIgnoreSpeficicBindingForTypesWhenTypesDoNotMatch() {
assertThat(typeBasedBindings.getBindingForPath(PropertyPath.from("inceptionYear", User.class)), nullValue());
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnTrueWhenNoRestrictionDefined() {
assertThat(typeBasedBindings.isPathVisible(PropertyPath.from("inceptionYear", User.class)), is(true));
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnTrueWhenPathContainedInIncluding() {
assertThat(
new QuerydslBindings().including("inceptionYear").isPathVisible(PropertyPath.from("inceptionYear", User.class)),
is(true));
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnFalseWhenPathNotContainedInIncluding() {
assertThat(
new QuerydslBindings().including("inceptionYear").isPathVisible(PropertyPath.from("firstname", User.class)),
is(false));
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnFalseWhenPathContainedInExcluding() {
assertThat(
new QuerydslBindings().excluding("inceptionYear").isPathVisible(PropertyPath.from("inceptionYear", User.class)),
is(false));
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnTrueWhenPathNotContainedInExcluding() {
assertThat(
new QuerydslBindings().excluding("inceptionYear").isPathVisible(PropertyPath.from("firstname", User.class)),
is(true));
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnTrueWhenPathContainedInExcludingAndIncluding() {
assertThat(
new QuerydslBindings().excluding("inceptionYear").isPathVisible(PropertyPath.from("firstname", User.class)),
is(true));
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnFalseWhenPartialPathContainedInExcluding() {
assertThat(
new QuerydslBindings().excluding("address").isPathVisible(PropertyPath.from("address.city", User.class)),
is(false));
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnTrueWhenPartialPathContainedInExcludingButConcretePathIsIncluded() {
assertThat(
new QuerydslBindings().excluding("address").including("address.city")
.isPathVisible(PropertyPath.from("address.city", User.class)), is(true));
}
/**
* @see DATACMNS-669
*/
@Test
public void isPathVisibleShouldReturnFalseWhenPartialPathContainedInExcludingAndConcretePathToDifferentPropertyIsIncluded() {
assertThat(
new QuerydslBindings().excluding("address").including("address.city")
.isPathVisible(PropertyPath.from("address.street", User.class)), is(false));
}
/**
* @see DATACMNS-669
*/
@Test
public void usesTypeBasedBindingIfConfigured() {
MutablePropertyValues values = new MutablePropertyValues(Collections.singletonMap("city", "Dresden"));
QuerydslBindingContext context = new QuerydslBindingContext(ClassTypeInformation.from(Address.class),
this.typeBasedBindings, null);
assertThat(builder.getPredicate(values, context), is((Predicate) QAddress.address.city.contains("Dresden")));
}
}

107
src/test/java/org/springframework/data/web/querydsl/QuerydslDefaultBindingUnitTests.java

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
/*
* 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 static org.hamcrest.core.Is.*;
import java.util.Arrays;
import org.hamcrest.Matcher;
import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.querydsl.QUser;
import com.mysema.query.types.Expression;
import com.mysema.query.types.Predicate;
/**
* @author Christoph Strobl
*/
public class QuerydslDefaultBindingUnitTests {
QuerydslDefaultBinding builder;
@Before
public void setUp() {
builder = new QuerydslDefaultBinding();
}
/**
* @see DATACMNS-669
*/
@Test
public void shouldCreatePredicateCorrectlyWhenPropertyIsInRoot() {
Predicate predicate = builder.bind(QUser.user.firstname, "tam");
assertThat(predicate, is(QUser.user.firstname.eq("tam")));
}
/**
* @see DATACMNS-669
*/
@Test
public void shouldCreatePredicateCorrectlyWhenPropertyIsInNestedElement() {
Predicate predicate = builder.bind(QUser.user.address.city, "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, null);
assertThat(predicate, is(QUser.user.firstname.isNull()));
}
/**
* @see DATACMNS-669
*/
@Test
public void shouldCreatePredicateWithContainingWhenPropertyIsCollectionLikeAndValueIsObject() {
Predicate predicate = builder.bind(QUser.user.nickNames, "dragon reborn");
assertThat(predicate, is(QUser.user.nickNames.contains("dragon reborn")));
}
/**
* @see DATACMNS-669
*/
@Test
public void shouldCreatePredicateWithInWhenPropertyIsAnObjectAndValueIsACollection() {
Predicate predicate = builder.bind(QUser.user.firstname, Arrays.asList("dragon reborn", "shadowkiller"));
assertThat(predicate, is(QUser.user.firstname.in(Arrays.asList("dragon reborn", "shadowkiller"))));
}
/*
* just to satisfy generic type boundaries o_O
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void assertThat(Predicate predicate, Matcher<? extends Expression> matcher) {
Assert.assertThat((Expression) predicate, Is.<Expression> is((Matcher<Expression>) matcher));
}
}

250
src/test/java/org/springframework/data/web/querydsl/QuerydslPredicateArgumentResolverUnitTests.java

@ -0,0 +1,250 @@ @@ -0,0 +1,250 @@
/*
* 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 static org.hamcrest.core.Is.*;
import static org.junit.Assert.*;
import org.hamcrest.core.Is;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QUser;
import org.springframework.data.querydsl.User;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.ServletWebRequest;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.expr.BooleanExpression;
import com.mysema.query.types.path.StringPath;
/**
* @author Christoph Strobl
*/
public class QuerydslPredicateArgumentResolverUnitTests {
QuerydslPredicateArgumentResolver resolver;
MockHttpServletRequest request;
@Before
public void setUp() {
resolver = new QuerydslPredicateArgumentResolver(null);
request = new MockHttpServletRequest();
}
/**
* @see DATACMNS-669
*/
@Test
public void supportsParameterReturnsTrueWhenMethodParameterIsPredicateAndAnnotatedCorrectly() {
assertThat(resolver.supportsParameter(getMethodParameterFor("simpleFind", Predicate.class)), is(true));
}
/**
* @see DATACMNS-669
*/
@Test
public void supportsParameterReturnsTrueWhenMethodParameterIsPredicateButNotAnnotatedAsSuch() {
assertThat(resolver.supportsParameter(getMethodParameterFor("predicateWithoutAnnotation", Predicate.class)),
is(true));
}
/**
* @see DATACMNS-669
*/
@Test(expected = IllegalArgumentException.class)
public void supportsParameterShouldThrowExceptionWhenMethodParameterIsNoPredicateButAnnotatedAsSuch() {
resolver.supportsParameter(getMethodParameterFor("nonPredicateWithAnnotation", String.class));
}
/**
* @see DATACMNS-669
*/
@Test
public void supportsParameterReturnsFalseWhenMethodParameterIsNoPredicate() {
assertThat(resolver.supportsParameter(getMethodParameterFor("nonPredicateWithoutAnnotation", String.class)),
is(false));
}
/**
* @see DATACMNS-669
*/
@Test
public void resolveArgumentShouldCreateSingleStringParameterPredicateCorrectly() throws Exception {
request.addParameter("firstname", "rand");
Object predicate = (BooleanExpression) resolver.resolveArgument(
getMethodParameterFor("simpleFind", Predicate.class), null, new ServletWebRequest(request), null);
assertThat(predicate, Is.<Object> is(QUser.user.firstname.eq("rand")));
}
/**
* @see DATACMNS-669
*/
@Test
public void resolveArgumentShouldCreateMultipleParametersPredicateCorrectly() throws Exception {
request.addParameter("firstname", "rand");
request.addParameter("lastname", "al'thor");
Object predicate = resolver.resolveArgument(getMethodParameterFor("simpleFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate, Is.<Object> is(QUser.user.firstname.eq("rand").and(QUser.user.lastname.eq("al'thor"))));
}
/**
* @see DATACMNS-669
*/
@Test
public void resolveArgumentShouldCreateNestedObjectPredicateCorrectly() throws Exception {
request.addParameter("address.city", "two rivers");
Object predicate = resolver.resolveArgument(getMethodParameterFor("simpleFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate.toString(), is(QUser.user.address.city.eq("two rivers").toString()));
}
/**
* @see DATACMNS-669
*/
@Test
public void resolveArgumentShouldResolveTypePropertyFromPageCorrectly() throws Exception {
request.addParameter("address.city", "tar valon");
Object predicate = resolver.resolveArgument(getMethodParameterFor("pagedFind", Predicate.class, Pageable.class),
null, new ServletWebRequest(request), null);
assertThat(predicate.toString(), is(QUser.user.address.city.eq("tar valon").toString()));
}
/**
* @see DATACMNS-669
*/
@Test
public void resolveArgumentShouldHonorCustomSpeficifcation() throws Exception {
request.addParameter("firstname", "egwene");
request.addParameter("lastname", "al'vere");
Object predicate = resolver.resolveArgument(getMethodParameterFor("specificFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate.toString(),
is(QUser.user.firstname.eq("egwene".toUpperCase()).and(QUser.user.lastname.toLowerCase().eq("al'vere"))
.toString()));
}
/**
* @see DATACMNS-669
*/
@Test
public void shouldCreatePredicateForNonStringPropertyCorrectly() throws Exception {
request.addParameter("inceptionYear", "978");
Object predicate = (BooleanExpression) resolver.resolveArgument(
getMethodParameterFor("specificFind", Predicate.class), null, new ServletWebRequest(request), null);
assertThat(predicate, Is.<Object> is(QUser.user.inceptionYear.eq(978L)));
}
/**
* @see DATACMNS-669
*/
@Test
public void shouldCreatePredicateForNonStringListPropertyCorrectly() throws Exception {
request.addParameter("inceptionYear", new String[] { "978", "998" });
Object predicate = (BooleanExpression) resolver.resolveArgument(
getMethodParameterFor("specificFind", Predicate.class), null, new ServletWebRequest(request), null);
assertThat(predicate, Is.<Object> is(QUser.user.inceptionYear.in(978L, 998L)));
}
/**
* @see DATACMNS-669
*/
@Test
public void shouldExcludePorpertiesCorrectly() throws Exception {
request.addParameter("address.street", "downhill");
request.addParameter("inceptionYear", "973");
Object predicate = resolver.resolveArgument(getMethodParameterFor("specificFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate.toString(), is(QUser.user.inceptionYear.eq(973L).toString()));
}
private MethodParameter getMethodParameterFor(String methodName, Class<?>... args) throws RuntimeException {
try {
return new MethodParameter(Sample.class.getMethod(methodName, args), 0);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
static class SpecificBinding extends QuerydslBindings {
public SpecificBinding() {
bind("firstname", new QuerydslBinding<StringPath>() {
@Override
public Predicate bind(StringPath path, Object value) {
return path.eq(value.toString().toUpperCase());
}
});
bind(QUser.user.lastname, new QuerydslBinding<StringPath>() {
@Override
public Predicate bind(StringPath path, Object value) {
return path.toLowerCase().eq(value.toString());
}
});
excluding("address");
}
}
static interface Sample {
User predicateWithoutAnnotation(Predicate predicate);
User nonPredicateWithAnnotation(@QuerydslPredicate String predicate);
User nonPredicateWithoutAnnotation(String predicate);
User simpleFind(@QuerydslPredicate Predicate predicate);
Page<User> pagedFind(@QuerydslPredicate Predicate predicate, Pageable page);
User specificFind(@QuerydslPredicate(bindings = SpecificBinding.class) Predicate predicate);
}
}

76
src/test/java/org/springframework/data/web/querydsl/QuerydslPredicateBuilderUnitTests.java

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
/*
* 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 static org.hamcrest.core.Is.*;
import static org.junit.Assert.*;
import java.util.Collections;
import org.hamcrest.core.Is;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.data.querydsl.QUser;
import org.springframework.data.querydsl.User;
import org.springframework.data.util.ClassTypeInformation;
import com.mysema.query.BooleanBuilder;
import com.mysema.query.types.Predicate;
/**
* @author Christoph Strobl
*/
public class QuerydslPredicateBuilderUnitTests {
QuerydslPredicateBuilder builder;
@Before
public void setUp() {
builder = new QuerydslPredicateBuilder();
}
/**
* @see DATACMNS-669
*/
@Test(expected = IllegalArgumentException.class)
public void getPredicateShouldThrowErrorWhenBindingContextIsNull() {
builder.getPredicate(new MutablePropertyValues(), null);
}
/**
* @see DATACMNS-669
*/
@Test
public void getPredicateShouldReturnEmptyPredicateWhenPropertiesAreEmpty() {
assertThat(builder.getPredicate(new MutablePropertyValues(), new QuerydslBindingContext(
ClassTypeInformation.OBJECT, null, null)), is((Predicate) new BooleanBuilder()));
}
/**
* @see DATACMNS-669
*/
@Test
public void resolveArgumentShouldCreateSingleStringParameterPredicateCorrectly() throws Exception {
Predicate predicate = builder.getPredicate(
new MutablePropertyValues(Collections.singletonMap("firstname", new String[] { "rand" })),
new QuerydslBindingContext(ClassTypeInformation.from(User.class), null, null));
assertThat(predicate, Is.<Object> is(QUser.user.firstname.eq("rand")));
}
}
Loading…
Cancel
Save