DATACMNS-941 - Support for downcasts in Querydsl web bindings.
We now support downcasts (e.g. user.as(QSpecialUser.class).…) expressions in Querydsl web binding. We do so by skipping the introduced intermediate step in the path when calculating the dot path. Refactored the internals of the binding to now work based on a PathInformation implementation for which either a Querydsl Path or a Spring Data PropertyPath can be used. This allows us to reuse the paths of predefined bindings. A bit of Lombok cleanup in internal implementations.
This commit is contained in:
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Path<Object>, 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<Path<Object>, 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")));
|
||||
|
||||
@@ -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<Path<? extends Object>, 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 <P extends Path<? extends S>, S> void assertAdapterWithTargetBinding(MultiValueBinding<P, S> binding,
|
||||
SingleValueBinding<? extends Path<?>, ?> expected) {
|
||||
|
||||
assertThat(binding, is(instanceOf(QuerydslBindings.MultiValueBindingAdapter.class)));
|
||||
assertThat(ReflectionTestUtils.getField(binding, "delegate"), is((Object) expected));
|
||||
}
|
||||
|
||||
enum ContainsBinding implements SingleValueBinding<StringPath, String> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user