diff --git a/src/main/java/org/springframework/data/querydsl/QueryDslUtils.java b/src/main/java/org/springframework/data/querydsl/QueryDslUtils.java index abc87f0fb..a6091e828 100644 --- a/src/main/java/org/springframework/data/querydsl/QueryDslUtils.java +++ b/src/main/java/org/springframework/data/querydsl/QueryDslUtils.java @@ -21,6 +21,7 @@ import org.springframework.util.StringUtils; import com.querydsl.core.types.Path; import com.querydsl.core.types.PathMetadata; +import com.querydsl.core.types.PathType; /** * Utility class for Querydsl. @@ -64,6 +65,10 @@ public class QueryDslUtils { return tail; } + if (metadata.getPathType().equals(PathType.DELEGATE)) { + return toDotPath(parent, tail); + } + Object element = metadata.getElement(); if (element == null || !StringUtils.hasText(element.toString())) { diff --git a/src/main/java/org/springframework/data/querydsl/binding/PathInformation.java b/src/main/java/org/springframework/data/querydsl/binding/PathInformation.java new file mode 100644 index 000000000..071bf97dd --- /dev/null +++ b/src/main/java/org/springframework/data/querydsl/binding/PathInformation.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016 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.binding; + +import java.beans.PropertyDescriptor; + +import org.springframework.data.mapping.PropertyPath; +import org.springframework.data.querydsl.EntityPathResolver; + +import com.querydsl.core.types.Path; + +/** + * Internal abstraction of mapped paths. + * + * @author Oliver Gierke + * @since 1.13 + */ +interface PathInformation { + + /** + * The type of the leaf property. + * + * @return + */ + Class getLeafType(); + + /** + * The type of the leaf property's parent type. + * + * @return + */ + Class getLeafParentType(); + + /** + * The name of the leaf property. + * + * @return + */ + String getLeafProperty(); + + /** + * The {@link PropertyDescriptor} for the leaf property. + * + * @return + */ + PropertyDescriptor getLeafPropertyDescriptor(); + + /** + * The dot separated representation of the current path. + * + * @return + */ + String toDotPath(); + + /** + * Tries to reify a Querydsl {@link Path} from the given {@link PropertyPath} and base. + * + * @param path must not be {@literal null}. + * @param base can be {@literal null}. + * @return + */ + Path reifyPath(EntityPathResolver resolver); +} diff --git a/src/main/java/org/springframework/data/querydsl/binding/PropertyPathInformation.java b/src/main/java/org/springframework/data/querydsl/binding/PropertyPathInformation.java new file mode 100644 index 000000000..463402809 --- /dev/null +++ b/src/main/java/org/springframework/data/querydsl/binding/PropertyPathInformation.java @@ -0,0 +1,141 @@ +/* + * Copyright 2016 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.binding; + +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; + +import org.springframework.beans.BeanUtils; +import org.springframework.data.mapping.PropertyPath; +import org.springframework.data.querydsl.EntityPathResolver; +import org.springframework.data.util.TypeInformation; +import org.springframework.util.ReflectionUtils; + +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.CollectionPathBase; + +/** + * {@link PropertyPath} based implementation of {@link PathInformation}. + * + * @author Oliver Gierke + * @since 1.13 + */ +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor(staticName = "of", access = AccessLevel.PRIVATE) +class PropertyPathInformation implements PathInformation { + + private final PropertyPath path; + + /** + * Creates a new {@link PropertyPathInformation} for the given path and type. + * + * @param path must not be {@literal null} or empty. + * @param type must not be {@literal null}. + * @return + */ + public static PropertyPathInformation of(String path, Class type) { + return PropertyPathInformation.of(PropertyPath.from(path, type)); + } + + /** + * Creates a new {@link PropertyPathInformation} for the given path and {@link TypeInformation}. + * + * @param path must not be {@literal null} or empty. + * @param type must not be {@literal null}. + * @return + */ + public static PropertyPathInformation of(String path, TypeInformation type) { + return PropertyPathInformation.of(PropertyPath.from(path, type)); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#getLeafType() + */ + @Override + public Class getLeafType() { + return path.getLeafProperty().getType(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#getLeafParentType() + */ + @Override + public Class getLeafParentType() { + return path.getLeafProperty().getOwningType().getType(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#getLeafProperty() + */ + @Override + public String getLeafProperty() { + return path.getLeafProperty().getSegment(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#getLeafPropertyDescriptor() + */ + @Override + public PropertyDescriptor getLeafPropertyDescriptor() { + return BeanUtils.getPropertyDescriptor(getLeafParentType(), getLeafProperty()); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#toDotPath() + */ + @Override + public String toDotPath() { + return path.toDotPath(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.PathInformation#reifyPath(org.springframework.data.querydsl.EntityPathResolver) + */ + @Override + public Path reifyPath(EntityPathResolver resolver) { + return reifyPath(resolver, path, null); + } + + private static Path reifyPath(EntityPathResolver resolver, PropertyPath path, Path base) { + + if (base instanceof CollectionPathBase) { + return reifyPath(resolver, path, (Path) ((CollectionPathBase) base).any()); + } + + Path entityPath = base != null ? base : resolver.createPath(path.getOwningType().getType()); + + Field field = ReflectionUtils.findField(entityPath.getClass(), path.getSegment()); + Object value = ReflectionUtils.getField(field, entityPath); + + if (path.hasNext()) { + return reifyPath(resolver, path.next(), (Path) value); + } + + return (Path) value; + } +} diff --git a/src/main/java/org/springframework/data/querydsl/binding/QuerydslBindings.java b/src/main/java/org/springframework/data/querydsl/binding/QuerydslBindings.java index 69a0f45f0..14c4847e7 100644 --- a/src/main/java/org/springframework/data/querydsl/binding/QuerydslBindings.java +++ b/src/main/java/org/springframework/data/querydsl/binding/QuerydslBindings.java @@ -17,6 +17,10 @@ package org.springframework.data.querydsl.binding; import static org.springframework.data.querydsl.QueryDslUtils.*; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Value; + import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -197,7 +201,7 @@ public class QuerydslBindings { * @return can be {@literal null}. */ @SuppressWarnings("unchecked") - public , T> MultiValueBinding getBindingForPath(PropertyPath path) { + public , T> MultiValueBinding getBindingForPath(PathInformation path) { Assert.notNull(path, "PropertyPath must not be null!"); @@ -212,7 +216,7 @@ public class QuerydslBindings { } } - pathAndBinding = (PathAndBinding) typeSpecs.get(path.getLeafProperty().getType()); + pathAndBinding = (PathAndBinding) typeSpecs.get(path.getLeafType()); return pathAndBinding == null ? null : pathAndBinding.getBinding(); } @@ -223,7 +227,7 @@ public class QuerydslBindings { * @param path must not be {@literal null}. * @return */ - Path getExistingPath(PropertyPath path) { + Path getExistingPath(PathInformation path) { Assert.notNull(path, "PropertyPath must not be null!"); @@ -232,24 +236,27 @@ public class QuerydslBindings { } /** - * @param path - * @param type + * Returns the {@link PathInformation} for the given path and {@link TypeInformation}. + * + * @param path must not be {@literal null}. + * @param type must not be {@literal null}. * @return */ - PropertyPath getPropertyPath(String path, TypeInformation type) { + PathInformation getPropertyPath(String path, TypeInformation type) { Assert.notNull(path, "Path must not be null!"); + Assert.notNull(type, "Type information must not be null!"); if (!isPathVisible(path)) { return null; } if (pathSpecs.containsKey(path)) { - return PropertyPath.from(toDotPath(pathSpecs.get(path).getPath()), type); + return QuerydslPathInformation.of(pathSpecs.get(path).getPath()); } try { - PropertyPath propertyPath = PropertyPath.from(path, type); + PathInformation propertyPath = PropertyPathInformation.of(path, type); return isPathVisible(propertyPath) ? propertyPath : null; } catch (PropertyReferenceException o_O) { return null; @@ -262,7 +269,7 @@ public class QuerydslBindings { * @param path * @return */ - private boolean isPathVisible(PropertyPath path) { + private boolean isPathVisible(PathInformation path) { List segments = Arrays.asList(path.toDotPath().split("\\.")); @@ -436,21 +443,10 @@ public class QuerydslBindings { * * @author Oliver Gierke */ + @RequiredArgsConstructor public final class TypeBinder { - private final Class type; - - /** - * Creates a new {@link TypeBinder} for the given type. - * - * @param type must not be {@literal null}. - */ - private TypeBinder(Class type) { - - Assert.notNull(type, "Type must not be null!"); - - this.type = type; - } + private final @NonNull Class type; /** * Configures the given {@link SingleValueBinding} to be used for the current type. @@ -479,32 +475,14 @@ public class QuerydslBindings { * A pair of a {@link Path} and the registered {@link MultiValueBinding}. * * @author Christoph Strobl + * @author Oliver Gierke * @since 1.11 */ + @Value private static class PathAndBinding, T> { - private final Path path; - private final MultiValueBinding binding; - - /** - * Creates a new {@link PathAndBinding} for the given {@link Path} and {@link MultiValueBinding}. - * - * @param path must not be {@literal null}. - * @param binding must not be {@literal null}. - */ - public PathAndBinding(S path, MultiValueBinding binding) { - - this.path = path; - this.binding = binding; - } - - public Path getPath() { - return path; - } - - public MultiValueBinding getBinding() { - return binding; - } + Path path; + MultiValueBinding binding; } /** @@ -513,26 +491,18 @@ public class QuerydslBindings { * * @author Oliver Gierke */ - static class MultiValueBindingAdapter, S> implements MultiValueBinding { + @RequiredArgsConstructor + static class MultiValueBindingAdapter

, T> implements MultiValueBinding { - private final SingleValueBinding delegate; - - /** - * Creates a new {@link MultiValueBindingAdapter} for the given {@link SingleValueBinding}. - * - * @param delegate must not be {@literal null}. - */ - public MultiValueBindingAdapter(SingleValueBinding delegate) { - this.delegate = delegate; - } + private final @NonNull SingleValueBinding delegate; /* * (non-Javadoc) * @see org.springframework.data.web.querydsl.MultiValueBinding#bind(com.mysema.query.types.Path, java.util.Collection) */ @Override - public Predicate bind(T path, Collection value) { - Iterator iterator = value.iterator(); + public Predicate bind(P path, Collection value) { + Iterator iterator = value.iterator(); return delegate.bind(path, iterator.hasNext() ? iterator.next() : null); } } diff --git a/src/main/java/org/springframework/data/querydsl/binding/QuerydslPathInformation.java b/src/main/java/org/springframework/data/querydsl/binding/QuerydslPathInformation.java new file mode 100644 index 000000000..1f78dc3c2 --- /dev/null +++ b/src/main/java/org/springframework/data/querydsl/binding/QuerydslPathInformation.java @@ -0,0 +1,95 @@ +/* + * Copyright 2016 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.binding; + +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +import java.beans.PropertyDescriptor; + +import org.springframework.beans.BeanUtils; +import org.springframework.data.querydsl.EntityPathResolver; +import org.springframework.data.querydsl.QueryDslUtils; + +import com.querydsl.core.types.Path; + +/** + * {@link PathInformation} based on a Querydsl {@link Path}. + * + * @author Oliver Gierke + * @since 1.13 + */ +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor(staticName = "of") +class QuerydslPathInformation implements PathInformation { + + private final Path path; + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#getLeafType() + */ + @Override + public Class getLeafType() { + return path.getType(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#getLeafParentType() + */ + @Override + public Class getLeafParentType() { + return path.getMetadata().getParent().getType(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#getLeafProperty() + */ + @Override + public String getLeafProperty() { + return path.getMetadata().getElement().toString(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#getLeafPropertyDescriptor() + */ + @Override + public PropertyDescriptor getLeafPropertyDescriptor() { + return BeanUtils.getPropertyDescriptor(getLeafParentType(), getLeafProperty()); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.MappedPath#toDotPath() + */ + @Override + public String toDotPath() { + return QueryDslUtils.toDotPath(path); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.PathInformation#reifyPath(org.springframework.data.querydsl.EntityPathResolver) + */ + public Path reifyPath(EntityPathResolver resolver) { + return path; + } +} diff --git a/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java b/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java index bc53abdcd..bd230fab1 100644 --- a/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java +++ b/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java @@ -16,7 +16,6 @@ package org.springframework.data.querydsl.binding; import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -25,7 +24,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import org.springframework.beans.BeanUtils; import org.springframework.beans.PropertyValues; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.Property; @@ -41,7 +39,6 @@ import org.springframework.util.StringUtils; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Path; import com.querydsl.core.types.Predicate; -import com.querydsl.core.types.dsl.CollectionPathBase; /** * Builder assembling {@link Predicate} out of {@link PropertyValues}. @@ -54,7 +51,7 @@ public class QuerydslPredicateBuilder { private final ConversionService conversionService; private final MultiValueBinding defaultBinding; - private final Map> paths; + private final Map> paths; private final EntityPathResolver resolver; /** @@ -70,7 +67,7 @@ public class QuerydslPredicateBuilder { this.defaultBinding = new QuerydslDefaultBinding(); this.conversionService = conversionService; - this.paths = new HashMap>(); + this.paths = new HashMap>(); this.resolver = resolver; } @@ -106,7 +103,7 @@ public class QuerydslPredicateBuilder { continue; } - PropertyPath propertyPath = bindings.getPropertyPath(path, type); + PathInformation propertyPath = bindings.getPropertyPath(path, type); if (propertyPath == null) { continue; @@ -132,7 +129,7 @@ public class QuerydslPredicateBuilder { * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) - private Predicate invokeBinding(PropertyPath dotPath, QuerydslBindings bindings, Collection values) { + private Predicate invokeBinding(PathInformation dotPath, QuerydslBindings bindings, Collection values) { Path path = getPath(dotPath, bindings); @@ -151,7 +148,7 @@ public class QuerydslPredicateBuilder { * @param bindings must not be {@literal null}. * @return */ - private Path getPath(PropertyPath path, QuerydslBindings bindings) { + private Path getPath(PathInformation path, QuerydslBindings bindings) { Path resolvedPath = bindings.getExistingPath(path); @@ -165,37 +162,12 @@ public class QuerydslPredicateBuilder { return resolvedPath; } - resolvedPath = reifyPath(path, null); + resolvedPath = path.reifyPath(resolver); paths.put(path, resolvedPath); return resolvedPath; } - /** - * Tries to reify a Querydsl {@link Path} from the given {@link PropertyPath} and base. - * - * @param path must not be {@literal null}. - * @param base can be {@literal null}. - * @return - */ - private Path reifyPath(PropertyPath path, Path base) { - - if (base instanceof CollectionPathBase) { - return reifyPath(path, (Path) ((CollectionPathBase) base).any()); - } - - Path entityPath = base != null ? base : resolver.createPath(path.getOwningType().getType()); - - Field field = ReflectionUtils.findField(entityPath.getClass(), path.getSegment()); - Object value = ReflectionUtils.getField(field, entityPath); - - if (path.hasNext()) { - return reifyPath(path.next(), (Path) value); - } - - return (Path) value; - } - /** * Converts the given source values into a collection of elements that are of the given {@link PropertyPath}'s type. * Considers a single element list with an empty {@link String} an empty collection because this basically indicates @@ -205,10 +177,9 @@ public class QuerydslPredicateBuilder { * @param path must not be {@literal null}. * @return */ - private Collection convertToPropertyPathSpecificType(List source, PropertyPath path) { + private Collection convertToPropertyPathSpecificType(List source, PathInformation path) { - PropertyPath leafProperty = path.getLeafProperty(); - Class targetType = leafProperty.getOwningType().getProperty(leafProperty.getSegment()).getType(); + Class targetType = path.getLeafType(); if (source.isEmpty() || isSingleElementCollectionWithoutText(source)) { return Collections.emptyList(); @@ -226,26 +197,25 @@ public class QuerydslPredicateBuilder { } /** - * Returns the target {@link TypeDescriptor} for the given {@link PropertyPath} by either inspecting the field or + * Returns the target {@link TypeDescriptor} for the given {@link PathInformation} by either inspecting the field or * property (the latter preferred) to pick up annotations potentially defined for formatting purposes. * * @param path must not be {@literal null}. * @return */ - private static TypeDescriptor getTargetTypeDescriptor(PropertyPath path) { + private static TypeDescriptor getTargetTypeDescriptor(PathInformation path) { - PropertyPath leafProperty = path.getLeafProperty(); - Class owningType = leafProperty.getOwningType().getType(); + PropertyDescriptor descriptor = path.getLeafPropertyDescriptor(); - PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(owningType, leafProperty.getSegment()); + Class owningType = path.getLeafParentType(); + String leafProperty = path.getLeafProperty(); if (descriptor == null) { - return TypeDescriptor.nested(ReflectionUtils.findField(owningType, leafProperty.getSegment()), 0); + return TypeDescriptor.nested(ReflectionUtils.findField(owningType, leafProperty), 0); } - return TypeDescriptor.nested( - new Property(owningType, descriptor.getReadMethod(), descriptor.getWriteMethod(), leafProperty.getSegment()), - 0); + return TypeDescriptor + .nested(new Property(owningType, descriptor.getReadMethod(), descriptor.getWriteMethod(), leafProperty), 0); } /** diff --git a/src/test/java/org/springframework/data/querydsl/QueryDslUtilsUnitTests.java b/src/test/java/org/springframework/data/querydsl/QueryDslUtilsUnitTests.java index da33c453e..88e57fd93 100644 --- a/src/test/java/org/springframework/data/querydsl/QueryDslUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/querydsl/QueryDslUtilsUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.querydsl; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.springframework.data.querydsl.QueryDslUtils.*; import org.junit.Test; @@ -32,6 +33,17 @@ public class QueryDslUtilsUnitTests { */ @Test public void rendersDotPathForPathTraversalContainingAnyExpression() { - assertThat(QueryDslUtils.toDotPath(QUser.user.addresses.any().street), is("addresses.street")); + assertThat(toDotPath(QUser.user.addresses.any().street), is("addresses.street")); + } + + /** + * @see DATACMNS-941 + */ + @Test + public void skipsIntermediateDelegates() { + + assertThat(toDotPath(QUser.user.as(QSpecialUser.class).as(QSpecialUser.class).specialProperty), + is("specialProperty")); + assertThat(toDotPath(QUser.user.as(QSpecialUser.class).specialProperty), is("specialProperty")); } } diff --git a/src/test/java/org/springframework/data/querydsl/User.java b/src/test/java/org/springframework/data/querydsl/User.java index 0e523f53d..3a4d63bcf 100644 --- a/src/test/java/org/springframework/data/querydsl/User.java +++ b/src/test/java/org/springframework/data/querydsl/User.java @@ -45,3 +45,18 @@ public class User { this.address = address; } } + +@QueryEntity +class SpecialUser extends User { + + public String specialProperty; + + public SpecialUser(String firstname, String lastname, Address address) { + super(firstname, lastname, address); + } +} + +@QueryEntity +class UserWrapper { + public User user; +} diff --git a/src/test/java/org/springframework/data/querydsl/binding/QuerydslBindingsFactoryUnitTests.java b/src/test/java/org/springframework/data/querydsl/binding/QuerydslBindingsFactoryUnitTests.java index 4e2d3228d..105ff77e9 100644 --- a/src/test/java/org/springframework/data/querydsl/binding/QuerydslBindingsFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/querydsl/binding/QuerydslBindingsFactoryUnitTests.java @@ -26,7 +26,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.data.mapping.PropertyPath; import org.springframework.data.querydsl.QUser; import org.springframework.data.querydsl.SimpleEntityPathResolver; import org.springframework.data.querydsl.User; @@ -76,7 +75,7 @@ public class QuerydslBindingsFactoryUnitTests { QuerydslBindings bindings = factory.createBindingsFor(null, USER_TYPE); MultiValueBinding, Object> binding = bindings - .getBindingForPath(PropertyPath.from("firstname", User.class)); + .getBindingForPath(PropertyPathInformation.of("firstname", User.class)); assertThat(binding.bind((Path) QUser.user.firstname, Collections.singleton("rand")), is((Predicate) QUser.user.firstname.contains("rand"))); @@ -97,7 +96,7 @@ public class QuerydslBindingsFactoryUnitTests { QuerydslBindings bindings = factory.createBindingsFor(SpecificBinding.class, USER_TYPE); MultiValueBinding, Object> binding = bindings - .getBindingForPath(PropertyPath.from("firstname", User.class)); + .getBindingForPath(PropertyPathInformation.of("firstname", User.class)); assertThat(binding.bind((Path) QUser.user.firstname, Collections.singleton("rand")), is((Predicate) QUser.user.firstname.eq("RAND"))); diff --git a/src/test/java/org/springframework/data/querydsl/binding/QuerydslBindingsUnitTests.java b/src/test/java/org/springframework/data/querydsl/binding/QuerydslBindingsUnitTests.java index 6a9bd23c0..808f2224b 100644 --- a/src/test/java/org/springframework/data/querydsl/binding/QuerydslBindingsUnitTests.java +++ b/src/test/java/org/springframework/data/querydsl/binding/QuerydslBindingsUnitTests.java @@ -21,7 +21,7 @@ import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.data.mapping.PropertyPath; +import org.springframework.data.querydsl.QSpecialUser; import org.springframework.data.querydsl.QUser; import org.springframework.data.querydsl.SimpleEntityPathResolver; import org.springframework.data.querydsl.User; @@ -71,7 +71,10 @@ public class QuerydslBindingsUnitTests { */ @Test public void returnsNullIfNoBindingRegisteredForPath() { - assertThat(bindings.getBindingForPath(PropertyPath.from("lastname", User.class)), nullValue()); + + PathInformation path = PropertyPathInformation.of("lastname", User.class); + + assertThat(bindings.getBindingForPath(path), nullValue()); } /** @@ -82,8 +85,9 @@ public class QuerydslBindingsUnitTests { bindings.bind(QUser.user.firstname).first(CONTAINS_BINDING); - assertAdapterWithTargetBinding(bindings.getBindingForPath(PropertyPath.from("firstname", User.class)), - CONTAINS_BINDING); + PathInformation path = PropertyPathInformation.of("firstname", User.class); + + assertAdapterWithTargetBinding(bindings.getBindingForPath(path), CONTAINS_BINDING); } /** @@ -94,8 +98,9 @@ public class QuerydslBindingsUnitTests { bindings.bind(QUser.user.address.street).first(CONTAINS_BINDING); - assertAdapterWithTargetBinding(bindings.getBindingForPath(PropertyPath.from("address.street", User.class)), - CONTAINS_BINDING); + PathInformation path = PropertyPathInformation.of("address.street", User.class); + + assertAdapterWithTargetBinding(bindings.getBindingForPath(path), CONTAINS_BINDING); } /** @@ -106,8 +111,9 @@ public class QuerydslBindingsUnitTests { bindings.bind(String.class).first(CONTAINS_BINDING); - assertAdapterWithTargetBinding(bindings.getBindingForPath(PropertyPath.from("address.street", User.class)), - CONTAINS_BINDING); + PathInformation path = PropertyPathInformation.of("address.street", User.class); + + assertAdapterWithTargetBinding(bindings.getBindingForPath(path), CONTAINS_BINDING); } /** @@ -118,7 +124,9 @@ public class QuerydslBindingsUnitTests { bindings.bind(String.class).first(CONTAINS_BINDING); - assertThat(bindings.getBindingForPath(PropertyPath.from("inceptionYear", User.class)), nullValue()); + PathInformation path = PropertyPathInformation.of("inceptionYear", User.class); + + assertThat(bindings.getBindingForPath(path), nullValue()); } /** @@ -261,7 +269,7 @@ public class QuerydslBindingsUnitTests { bindings.bind(QUser.user.address.city).as("city").first(CONTAINS_BINDING); - PropertyPath path = bindings.getPropertyPath("city", ClassTypeInformation.from(User.class)); + PathInformation path = bindings.getPropertyPath("city", ClassTypeInformation.from(User.class)); assertThat(path, is(notNullValue())); assertThat(bindings.isPathAvailable("city", User.class), is(true)); @@ -279,14 +287,14 @@ public class QuerydslBindingsUnitTests { bindings.including(QUser.user.address.city); bindings.bind(QUser.user.address.city).as("city").first(CONTAINS_BINDING); - PropertyPath path = bindings.getPropertyPath("city", ClassTypeInformation.from(User.class)); + PathInformation path = bindings.getPropertyPath("city", ClassTypeInformation.from(User.class)); assertThat(path, is(notNullValue())); assertThat(bindings.isPathAvailable("city", User.class), is(true)); assertThat(bindings.isPathAvailable("address.city", User.class), is(true)); - PropertyPath propertyPath = bindings.getPropertyPath("address.city", ClassTypeInformation.from(User.class)); + PathInformation propertyPath = bindings.getPropertyPath("address.city", ClassTypeInformation.from(User.class)); assertThat(propertyPath, is(notNullValue())); assertAdapterWithTargetBinding(bindings.getBindingForPath(propertyPath), CONTAINS_BINDING); @@ -300,17 +308,42 @@ public class QuerydslBindingsUnitTests { bindings.bind(QUser.user.address.city).as("city").withDefaultBinding(); - PropertyPath path = bindings.getPropertyPath("city", ClassTypeInformation.from(User.class)); + PathInformation path = bindings.getPropertyPath("city", ClassTypeInformation.from(User.class)); assertThat(path, is(notNullValue())); MultiValueBinding, Object> binding = bindings.getBindingForPath(path); assertThat(binding, is(nullValue())); } + /** + * @see DATACMNS-941 + */ + @Test + public void registersBindingForPropertyOfSubtype() { + + bindings.bind(QUser.user.as(QSpecialUser.class).specialProperty).first(ContainsBinding.INSTANCE); + + assertThat(bindings.isPathAvailable("specialProperty", User.class), is(true)); + } + private static

, S> void assertAdapterWithTargetBinding(MultiValueBinding binding, SingleValueBinding, ?> expected) { assertThat(binding, is(instanceOf(QuerydslBindings.MultiValueBindingAdapter.class))); assertThat(ReflectionTestUtils.getField(binding, "delegate"), is((Object) expected)); } + + enum ContainsBinding implements SingleValueBinding { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.binding.SingleValueBinding#bind(com.querydsl.core.types.Path, java.lang.Object) + */ + @Override + public Predicate bind(StringPath path, String value) { + return path.contains(value); + } + } } diff --git a/src/test/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilderUnitTests.java b/src/test/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilderUnitTests.java index 5b75d8b38..46098be98 100644 --- a/src/test/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilderUnitTests.java +++ b/src/test/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilderUnitTests.java @@ -27,7 +27,9 @@ import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.junit.Before; import org.junit.Test; +import org.springframework.data.querydsl.QSpecialUser; import org.springframework.data.querydsl.QUser; +import org.springframework.data.querydsl.QUserWrapper; import org.springframework.data.querydsl.SimpleEntityPathResolver; import org.springframework.data.querydsl.User; import org.springframework.data.querydsl.Users; @@ -219,4 +221,40 @@ public class QuerydslPredicateBuilderUnitTests { assertThat(predicate, is((Predicate) QUser.user.addresses.any().street.eq("VALUE"))); } + + /** + * @see DATACMNS-941 + */ + @Test + public void buildsPredicateForBindingUsingDowncast() { + + values.add("specialProperty", "VALUE"); + + QuerydslBindings bindings = new QuerydslBindings(); + bindings.bind(QUser.user.as(QSpecialUser.class).specialProperty)// + .first(QuerydslBindingsUnitTests.ContainsBinding.INSTANCE); + + Predicate predicate = builder.getPredicate(USER_TYPE, values, bindings); + + assertThat(predicate, is((Predicate) QUser.user.as(QSpecialUser.class).specialProperty.contains("VALUE"))); + } + + /** + * @see DATACMNS-941 + */ + @Test + public void buildsPredicateForBindingUsingNestedDowncast() { + + values.add("user.specialProperty", "VALUE"); + + QUserWrapper $ = QUserWrapper.userWrapper; + + QuerydslBindings bindings = new QuerydslBindings(); + bindings.bind($.user.as(QSpecialUser.class).specialProperty)// + .first(QuerydslBindingsUnitTests.ContainsBinding.INSTANCE); + + Predicate predicate = builder.getPredicate(USER_TYPE, values, bindings); + + assertThat(predicate, is((Predicate) $.user.as(QSpecialUser.class).specialProperty.contains("VALUE"))); + } }