DATACMNS-669 - Polishing.
Javadoc on MultiValueBinding. Hide QuerydslBindings.bind(…), ….include(…) and ….exclude(…) methods that take String arguments to reduce the probability of no query class being present. Refined the value handling for empty request values which are represented as String[] containing an empty String. We now massage those into an empty Collection for MultiValueBindings and a null value for SingleValueInvocations. We currently skip property values that match this pattern entirely but are prepared to allow a configuration flip switch to handle those scenarios in the future. We currently stick to skipping those values to prevent the bindings from having to deal with null values. We now also allow the bindings to return null to indicate they don't want to get applied in the resulting predicate at all. Switched to interface based customization to make it easier to use customizations with Spring Data REST. Added Querydsl-specific adapter of RepositoryInvoker to allow Spring Data REST to hook in the execution of the predicate obtained from request parameters. Original pull request: #132.
This commit is contained in:
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.web.querydsl;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -32,14 +33,15 @@ import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class QuerydslDefaultBindingUnitTests {
|
||||
|
||||
QuerydslDefaultBinding builder;
|
||||
QuerydslDefaultBinding binding;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
builder = new QuerydslDefaultBinding();
|
||||
binding = new QuerydslDefaultBinding();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,9 +50,9 @@ public class QuerydslDefaultBindingUnitTests {
|
||||
@Test
|
||||
public void shouldCreatePredicateCorrectlyWhenPropertyIsInRoot() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.firstname, Collections.singleton("tam"));
|
||||
Predicate predicate = binding.bind(QUser.user.firstname, Collections.singleton("tam"));
|
||||
|
||||
assertThat(predicate, is(QUser.user.firstname.eq("tam")));
|
||||
assertPredicate(predicate, is(QUser.user.firstname.eq("tam")));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,31 +61,20 @@ public class QuerydslDefaultBindingUnitTests {
|
||||
@Test
|
||||
public void shouldCreatePredicateCorrectlyWhenPropertyIsInNestedElement() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.address.city, Collections.singleton("two rivers"));
|
||||
Predicate predicate = binding.bind(QUser.user.address.city, Collections.singleton("two rivers"));
|
||||
|
||||
Assert.assertThat(predicate.toString(), is(QUser.user.address.city.eq("two rivers").toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreatePredicateCorrectlyWhenValueIsNull() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.firstname, Collections.emptySet());
|
||||
|
||||
assertThat(predicate, is(QUser.user.firstname.isNull()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreatePredicateWithContainingWhenPropertyIsCollectionLikeAndValueIsObject() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.nickNames, Collections.singleton("dragon reborn"));
|
||||
Predicate predicate = binding.bind(QUser.user.nickNames, Collections.singleton("dragon reborn"));
|
||||
|
||||
assertThat(predicate, is(QUser.user.nickNames.contains("dragon reborn")));
|
||||
assertPredicate(predicate, is(QUser.user.nickNames.contains("dragon reborn")));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,17 +83,22 @@ public class QuerydslDefaultBindingUnitTests {
|
||||
@Test
|
||||
public void shouldCreatePredicateWithInWhenPropertyIsAnObjectAndValueIsACollection() {
|
||||
|
||||
Predicate predicate = builder.bind(QUser.user.firstname, Arrays.asList("dragon reborn", "shadowkiller"));
|
||||
Predicate predicate = binding.bind(QUser.user.firstname, Arrays.asList("dragon reborn", "shadowkiller"));
|
||||
|
||||
assertThat(predicate, is(QUser.user.firstname.in(Arrays.asList("dragon reborn", "shadowkiller"))));
|
||||
assertPredicate(predicate, is(QUser.user.firstname.in(Arrays.asList("dragon reborn", "shadowkiller"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testname() {
|
||||
|
||||
assertThat(binding.bind(QUser.user.lastname, Collections.emptySet()), is(nullValue()));
|
||||
}
|
||||
|
||||
/*
|
||||
* just to satisfy generic type boundaries o_O
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void assertThat(Predicate predicate, Matcher<? extends Expression> matcher) {
|
||||
private void assertPredicate(Predicate predicate, Matcher<? extends Expression> matcher) {
|
||||
Assert.assertThat((Expression) predicate, Is.<Expression> is((Matcher<Expression>) matcher));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -205,18 +205,6 @@ public class QuerydslPredicateArgumentResolverUnitTests {
|
||||
assertThat(predicate.toString(), is(QUser.user.inceptionYear.eq(973L).toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void createBindingContextShouldUseQuerydslPredicationAnntotationDefaultBindingIfNotAnnotated() {
|
||||
|
||||
Object bindings = ReflectionTestUtils.invokeMethod(resolver, "createBindings",
|
||||
getMethodParameterFor("predicateWithoutAnnotation", Predicate.class));
|
||||
|
||||
assertThat(bindings, is(instanceOf(QuerydslBindings.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@@ -239,11 +227,11 @@ public class QuerydslPredicateArgumentResolverUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class SpecificBinding extends QuerydslBindings {
|
||||
static class SpecificBinding implements QuerydslBinderCustomizer<QUser> {
|
||||
|
||||
public SpecificBinding() {
|
||||
public void customize(QuerydslBindings bindings, QUser user) {
|
||||
|
||||
bind("firstname").using(new SingleValueBinding<StringPath, String>() {
|
||||
bindings.bind("firstname").using(new SingleValueBinding<StringPath, String>() {
|
||||
|
||||
@Override
|
||||
public Predicate bind(StringPath path, String value) {
|
||||
@@ -251,7 +239,7 @@ public class QuerydslPredicateArgumentResolverUnitTests {
|
||||
}
|
||||
});
|
||||
|
||||
bind(QUser.user.lastname).single(new SingleValueBinding<StringPath, String>() {
|
||||
bindings.bind(user.lastname).single(new SingleValueBinding<StringPath, String>() {
|
||||
|
||||
@Override
|
||||
public Predicate bind(StringPath path, String value) {
|
||||
@@ -259,7 +247,7 @@ public class QuerydslPredicateArgumentResolverUnitTests {
|
||||
}
|
||||
});
|
||||
|
||||
excluding("address");
|
||||
bindings.excluding("address");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.web.querydsl;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -24,7 +23,6 @@ import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.querydsl.QUser;
|
||||
import org.springframework.data.querydsl.User;
|
||||
@@ -33,9 +31,9 @@ import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.mysema.query.BooleanBuilder;
|
||||
import com.mysema.query.collections.CollQueryFactory;
|
||||
import com.mysema.query.types.Predicate;
|
||||
import com.mysema.query.types.path.StringPath;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link QuerydslPredicateBuilder}.
|
||||
@@ -45,13 +43,16 @@ import com.mysema.query.types.Predicate;
|
||||
*/
|
||||
public class QuerydslPredicateBuilderUnitTests {
|
||||
|
||||
static final ClassTypeInformation<User> USER_TYPE = ClassTypeInformation.from(User.class);
|
||||
static final QuerydslBindings DEFAULT_BINDINGS = new QuerydslBindings();
|
||||
|
||||
QuerydslPredicateBuilder builder;
|
||||
MultiValueMap<String, String> values;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
builder = new QuerydslPredicateBuilder(new DefaultConversionService());
|
||||
this.builder = new QuerydslPredicateBuilder(new DefaultConversionService());
|
||||
this.values = new LinkedMultiValueMap<String, String>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +68,7 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void getPredicateShouldThrowErrorWhenBindingContextIsNull() {
|
||||
builder.getPredicate(new MutablePropertyValues(), null, null);
|
||||
builder.getPredicate(values, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,8 +77,7 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
@Test
|
||||
public void getPredicateShouldReturnEmptyPredicateWhenPropertiesAreEmpty() {
|
||||
|
||||
assertThat(builder.getPredicate(new MutablePropertyValues(), DEFAULT_BINDINGS, ClassTypeInformation.OBJECT),
|
||||
is((Predicate) new BooleanBuilder()));
|
||||
assertThat(builder.getPredicate(values, DEFAULT_BINDINGS, ClassTypeInformation.OBJECT), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,8 +86,9 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
@Test
|
||||
public void resolveArgumentShouldCreateSingleStringParameterPredicateCorrectly() throws Exception {
|
||||
|
||||
Predicate predicate = builder.getPredicate(new MutablePropertyValues(singletonMap("firstname", "Oliver")),
|
||||
DEFAULT_BINDINGS, ClassTypeInformation.from(User.class));
|
||||
values.add("firstname", "Oliver");
|
||||
|
||||
Predicate predicate = builder.getPredicate(values, DEFAULT_BINDINGS, USER_TYPE);
|
||||
|
||||
assertThat(predicate, is((Predicate) QUser.user.firstname.eq("Oliver")));
|
||||
|
||||
@@ -103,8 +104,9 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
@Test
|
||||
public void resolveArgumentShouldCreateNestedStringParameterPredicateCorrectly() throws Exception {
|
||||
|
||||
Predicate predicate = builder.getPredicate(new MutablePropertyValues(singletonMap("address.city", "Linz")),
|
||||
DEFAULT_BINDINGS, ClassTypeInformation.from(User.class));
|
||||
values.add("address.city", "Linz");
|
||||
|
||||
Predicate predicate = builder.getPredicate(values, DEFAULT_BINDINGS, USER_TYPE);
|
||||
|
||||
assertThat(predicate, is((Predicate) QUser.user.address.city.eq("Linz")));
|
||||
|
||||
@@ -114,16 +116,37 @@ public class QuerydslPredicateBuilderUnitTests {
|
||||
assertThat(result, hasItem(Users.CHRISTOPH));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void ignoresNonDomainTypeProperties() {
|
||||
|
||||
MultiValueMap<String, String> values = new LinkedMultiValueMap<String, String>();
|
||||
values.add("firstname", "rand");
|
||||
values.add("lastname".toUpperCase(), "al'thor");
|
||||
|
||||
Predicate predicate = builder.getPredicate(new MutablePropertyValues(values), new QuerydslBindings(),
|
||||
ClassTypeInformation.from(User.class));
|
||||
Predicate predicate = builder.getPredicate(values, new QuerydslBindings(), USER_TYPE);
|
||||
|
||||
assertThat(predicate, is((Predicate) QUser.user.firstname.eq("rand")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-669
|
||||
*/
|
||||
@Test
|
||||
public void forwardsNullForEmptyParameterToSingleValueBinder() {
|
||||
|
||||
values.add("lastname", null);
|
||||
|
||||
QuerydslBindings bindings = new QuerydslBindings();
|
||||
bindings.bind(QUser.user.lastname).single(new SingleValueBinding<StringPath, String>() {
|
||||
|
||||
@Override
|
||||
public Predicate bind(StringPath path, String value) {
|
||||
return value == null ? null : path.contains(value);
|
||||
}
|
||||
});
|
||||
|
||||
builder.getPredicate(values, bindings, USER_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user