Consider property path root in QuerydslBindings.

We now consider a qualified property path when registering and querying `QuerydslBindings` to ensure that paths matching various domain types do not accidentally get applied for a different type than they were registered for.
Previously, a binding for `Address.description` would be also applied to `User.description` as only the property path `description` was considered when looking up bindings.

Closes: #2418
Original Pull Request: #2422
This commit is contained in:
Mark Paluch
2021-07-29 11:46:31 +02:00
committed by Christoph Strobl
parent 0505fc4975
commit bb3898add3
10 changed files with 211 additions and 26 deletions

View File

@@ -25,6 +25,7 @@ public class Address {
public String street, city;
public Double[] lonLat;
public String description;
public Address(String street, String city) {
this.street = street;

View File

@@ -37,6 +37,7 @@ public class User {
public List<Address> addresses;
public List<String> nickNames;
public Long inceptionYear;
public String description;
public User(String firstname, String lastname, Address address) {
@@ -56,7 +57,3 @@ class SpecialUser extends User {
}
}
@QueryEntity
class UserWrapper {
public User user;
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2021 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
*
* https://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.querydsl.core.annotations.QueryEntity;
@QueryEntity
public final class UserWrapper {
public User user;
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2021 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
*
* https://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 static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.data.querydsl.QUser;
import org.springframework.data.querydsl.User;
/**
* Unit tests for {@link PropertyPathInformation}.
*
* @author Mark Paluch
*/
class PropertyPathInformationUnitTests {
@Test // GH-2418
void shouldEqualsCorrectly() {
PropertyPathInformation information = PropertyPathInformation.of("address.description", User.class);
QuerydslPathInformation querydslPathInformation = QuerydslPathInformation.of(QUser.user.address.description);
assertThat(information).isEqualTo(querydslPathInformation);
assertThat(querydslPathInformation).isEqualTo(information);
}
@Test // GH-2418
void shouldHashCodeCorrectly() {
PropertyPathInformation information = PropertyPathInformation.of("address.description", User.class);
QuerydslPathInformation querydslPathInformation = QuerydslPathInformation.of(QUser.user.address.description);
assertThat(information).hasSameHashCodeAs(querydslPathInformation);
assertThat(querydslPathInformation).hasSameHashCodeAs(information);
}
}

View File

@@ -23,6 +23,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.querydsl.Address;
import org.springframework.data.querydsl.QAddress;
import org.springframework.data.querydsl.QSpecialUser;
import org.springframework.data.querydsl.QUser;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
@@ -67,6 +69,27 @@ class QuerydslBindingsUnitTests {
assertThat(bindings.getBindingForPath(path)).isEmpty();
}
@Test // GH-2418
void shouldConsiderOwningTypeWhenObtainingBindings() {
bindings.bind(QUser.user.description).first(CONTAINS_BINDING);
assertAdapterWithTargetBinding(bindings.getBindingForPath(PropertyPathInformation.of("description", User.class)),
CONTAINS_BINDING);
assertThat(bindings.getBindingForPath(PropertyPathInformation.of("address.description", User.class))).isEmpty();
assertThat(bindings.getBindingForPath(PropertyPathInformation.of("description", Address.class))).isEmpty();
}
@Test // GH-2418
void shouldConsiderOwningTypeWhenObtainingBindingsWithQuerydslPathInformation() {
bindings.bind(QUser.user.description).first(CONTAINS_BINDING);
assertAdapterWithTargetBinding(bindings.getBindingForPath(QuerydslPathInformation.of(QUser.user.description)),
CONTAINS_BINDING);
assertThat(bindings.getBindingForPath(QuerydslPathInformation.of(QAddress.address.description))).isEmpty();
}
@Test // DATACMNS-669
void returnsRegisteredBindingForSimplePath() {
@@ -78,7 +101,7 @@ class QuerydslBindingsUnitTests {
}
@Test // DATACMNS-669
void getBindingForPathShouldReturnSpeficicBindingForNestedElementsWhenAvailable() {
void getBindingForPathShouldReturnSpecificBindingForNestedElementsWhenAvailable() {
bindings.bind(QUser.user.address.street).first(CONTAINS_BINDING);

View File

@@ -27,11 +27,14 @@ import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.querydsl.Address;
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.UserWrapper;
import org.springframework.data.querydsl.Users;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Version;
@@ -42,6 +45,7 @@ import org.springframework.util.MultiValueMap;
import com.querydsl.collections.CollQueryFactory;
import com.querydsl.core.types.Constant;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.StringPath;
/**
* Unit tests for {@link QuerydslPredicateBuilder}.
@@ -54,6 +58,7 @@ class QuerydslPredicateBuilderUnitTests {
static final ClassTypeInformation<User> USER_TYPE = ClassTypeInformation.from(User.class);
static final QuerydslBindings DEFAULT_BINDINGS = new QuerydslBindings();
static final SingleValueBinding<StringPath, String> CONTAINS_BINDING = (path, value) -> path.contains(value);
QuerydslPredicateBuilder builder;
MultiValueMap<String, String> values;
@@ -83,6 +88,21 @@ class QuerydslPredicateBuilderUnitTests {
.isTrue();
}
@Test // GH-2418
void shouldLookupCorrectPath() {
DEFAULT_BINDINGS.bind(QUser.user.description).first(CONTAINS_BINDING);
values.add("description", "Linz");
Predicate predicate = this.builder.getPredicate(ClassTypeInformation.from(User.class), values, DEFAULT_BINDINGS);
assertThat(predicate).hasToString("contains(user.description,Linz)");
predicate = this.builder.getPredicate(ClassTypeInformation.from(Address.class), values, DEFAULT_BINDINGS);
assertThat(predicate).hasToString("address.description = Linz");
}
@Test // DATACMNS-669
void resolveArgumentShouldCreateSingleStringParameterPredicateCorrectly() throws Exception {
@@ -206,14 +226,14 @@ class QuerydslPredicateBuilderUnitTests {
values.add("user.specialProperty", "VALUE");
QUserWrapper $ = QUserWrapper.userWrapper;
QUserWrapper wrapper = QUserWrapper.userWrapper;
QuerydslBindings bindings = new QuerydslBindings();
bindings.bind($.user.as(QSpecialUser.class).specialProperty)//
bindings.bind(wrapper.user.as(QSpecialUser.class).specialProperty)//
.first(QuerydslBindingsUnitTests.ContainsBinding.INSTANCE);
assertThat(builder.getPredicate(USER_TYPE, values, bindings))//
.isEqualTo($.user.as(QSpecialUser.class).specialProperty.contains("VALUE"));
assertThat(builder.getPredicate(ClassTypeInformation.from(UserWrapper.class), values, bindings))//
.isEqualTo(wrapper.user.as(QSpecialUser.class).specialProperty.contains("VALUE"));
}
@Test // DATACMNS-1443