diff --git a/src/main/java/org/springframework/data/mapping/TypedPropertyPath.java b/src/main/java/org/springframework/data/core/TypedPropertyPath.java
similarity index 97%
rename from src/main/java/org/springframework/data/mapping/TypedPropertyPath.java
rename to src/main/java/org/springframework/data/core/TypedPropertyPath.java
index 36df105ac..e0f5d2576 100644
--- a/src/main/java/org/springframework/data/mapping/TypedPropertyPath.java
+++ b/src/main/java/org/springframework/data/core/TypedPropertyPath.java
@@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.data.mapping;
+package org.springframework.data.core;
import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import org.jspecify.annotations.Nullable;
-import org.springframework.data.util.TypeInformation;
/**
* Type-safe representation of a property path expressed through method references.
@@ -32,13 +31,13 @@ import org.springframework.data.util.TypeInformation;
*
* Typed property paths can be created directly they are accepted used or conveniently using the static factory method
* {@link #of(TypedPropertyPath)} with method references:
- *
+ *
*
* PropertyPath.of(Person::getName);
*
- *
+ *
* Property paths can be composed to navigate nested properties using {@link #then(TypedPropertyPath)}:
- *
+ *
*
* PropertyPath.of(Person::getAddress).then(Address::getCountry).then(Country::getName);
*
@@ -56,7 +55,7 @@ import org.springframework.data.util.TypeInformation;
*
* Note that using lambda expressions requires bytecode analysis of the declaration site classes and therefore presence
* of their class files.
- *
+ *
* @param the owning type of the property path segment, but typically the root type for composed property paths.
* @param the property value type at this path segment.
* @author Mark Paluch
diff --git a/src/main/java/org/springframework/data/mapping/TypedPropertyPaths.java b/src/main/java/org/springframework/data/core/TypedPropertyPaths.java
similarity index 98%
rename from src/main/java/org/springframework/data/mapping/TypedPropertyPaths.java
rename to src/main/java/org/springframework/data/core/TypedPropertyPaths.java
index 55d2f3f2a..18c36769f 100644
--- a/src/main/java/org/springframework/data/mapping/TypedPropertyPaths.java
+++ b/src/main/java/org/springframework/data/core/TypedPropertyPaths.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.data.mapping;
+package org.springframework.data.core;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
@@ -46,8 +46,6 @@ import org.springframework.asm.Type;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.dao.InvalidDataAccessApiUsageException;
-import org.springframework.data.mapping.model.Property;
-import org.springframework.data.util.TypeInformation;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ObjectUtils;
@@ -59,7 +57,7 @@ import org.springframework.util.ReflectionUtils;
class TypedPropertyPaths {
private static final Map> lambdas = new WeakHashMap<>();
- private static final Map>> resolved = new WeakHashMap<>();
+ private static final Map, ResolvedTypedPropertyPath, ?>>> resolved = new WeakHashMap<>();
/**
* Retrieve {@link PropertyPathInformation} for a given {@link TypedPropertyPath}.
@@ -92,7 +90,7 @@ class TypedPropertyPaths {
return lambda;
}
- Map> cache;
+ Map, ResolvedTypedPropertyPath, ?>> cache;
synchronized (resolved) {
cache = resolved.computeIfAbsent(lambda.getClass().getClassLoader(), k -> new ConcurrentReferenceHashMap<>());
}
@@ -168,7 +166,7 @@ class TypedPropertyPaths {
Field field = ReflectionUtils.findField(owner, fieldName, fieldType);
if (field == null) {
- throw new IllegalArgumentException("Field %s.%s() not found".formatted(owner.getName(), field));
+ throw new IllegalArgumentException("Field %s.%s() not found".formatted(owner.getName(), fieldName));
}
return new PropertyPathInformation(TypeInformation.of(owner),
diff --git a/src/main/java/org/springframework/data/domain/ExampleMatcher.java b/src/main/java/org/springframework/data/domain/ExampleMatcher.java
index e59698a71..cc842b8f3 100644
--- a/src/main/java/org/springframework/data/domain/ExampleMatcher.java
+++ b/src/main/java/org/springframework/data/domain/ExampleMatcher.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.domain;
+import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -24,6 +25,7 @@ import java.util.function.Function;
import org.jspecify.annotations.Nullable;
+import org.springframework.data.core.TypedPropertyPath;
import org.springframework.lang.CheckReturnValue;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
@@ -77,6 +79,21 @@ public interface ExampleMatcher {
return new TypedExampleMatcher().withMode(MatchMode.ALL);
}
+ /**
+ * Returns a copy of this {@link ExampleMatcher} with the specified {@code propertyPaths}. This instance is immutable
+ * and unaffected by this method call.
+ *
+ * @param ignoredPaths must not be {@literal null} and not empty.
+ * @return new instance of {@link ExampleMatcher}.
+ * @since 4.1
+ */
+ @Contract("_ -> new")
+ @CheckReturnValue
+ default ExampleMatcher withIgnorePaths(TypedPropertyPath... ignoredPaths) {
+ return withIgnorePaths(Arrays.stream(ignoredPaths).map(TypedPropertyPath::of).map(TypedPropertyPath::toDotPath)
+ .toArray(String[]::new));
+ }
+
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code propertyPaths}. This instance is immutable
* and unaffected by this method call.
@@ -122,6 +139,22 @@ public interface ExampleMatcher {
@CheckReturnValue
ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase);
+ /**
+ * Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
+ * {@code propertyPath}. This instance is immutable and unaffected by this method call.
+ *
+ * @param propertyPath must not be {@literal null}.
+ * @param matcherConfigurer callback to configure a {@link GenericPropertyMatcher}, must not be {@literal null}.
+ * @return new instance of {@link ExampleMatcher}.
+ * @since 4.1
+ */
+ @Contract("_, _ -> new")
+ @CheckReturnValue
+ default ExampleMatcher withMatcher(TypedPropertyPath propertyPath,
+ MatcherConfigurer matcherConfigurer) {
+ return withMatcher(propertyPath.toDotPath(), matcherConfigurer);
+ }
+
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
* {@code propertyPath}. This instance is immutable and unaffected by this method call.
@@ -143,6 +176,21 @@ public interface ExampleMatcher {
return withMatcher(propertyPath, genericPropertyMatcher);
}
+ /**
+ * Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
+ * {@code propertyPath}. This instance is immutable and unaffected by this method call.
+ *
+ * @param propertyPath must not be {@literal null}.
+ * @param genericPropertyMatcher callback to configure a {@link GenericPropertyMatcher}, must not be {@literal null}.
+ * @return new instance of {@link ExampleMatcher}.
+ */
+ @Contract("_, _ -> new")
+ @CheckReturnValue
+ default ExampleMatcher withMatcher(TypedPropertyPath propertyPath,
+ GenericPropertyMatcher genericPropertyMatcher) {
+ return withMatcher(propertyPath.toDotPath(), genericPropertyMatcher);
+ }
+
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
* {@code propertyPath}. This instance is immutable and unaffected by this method call.
@@ -155,6 +203,22 @@ public interface ExampleMatcher {
@CheckReturnValue
ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
+ /**
+ * Returns a copy of this {@link ExampleMatcher} with the specified {@code PropertyValueTransformer} for the
+ * {@code propertyPath}.
+ *
+ * @param propertyPath must not be {@literal null}.
+ * @param propertyValueTransformer must not be {@literal null}.
+ * @return new instance of {@link ExampleMatcher}.
+ * @since 4.1
+ */
+ @Contract("_, _ -> new")
+ @CheckReturnValue
+ default ExampleMatcher withTransformer(TypedPropertyPath propertyPath,
+ PropertyValueTransformer propertyValueTransformer) {
+ return withTransformer(propertyPath.toDotPath(), propertyValueTransformer);
+ }
+
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code PropertyValueTransformer} for the
* {@code propertyPath}.
@@ -167,6 +231,20 @@ public interface ExampleMatcher {
@CheckReturnValue
ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer);
+ /**
+ * Returns a copy of this {@link ExampleMatcher} with ignore case sensitivity for the {@code propertyPaths}. This
+ * instance is immutable and unaffected by this method call.
+ *
+ * @param propertyPaths must not be {@literal null} and not empty.
+ * @return new instance of {@link ExampleMatcher}.
+ */
+ @Contract("_ -> new")
+ @CheckReturnValue
+ default ExampleMatcher withIgnoreCase(TypedPropertyPath... propertyPaths) {
+ return withIgnoreCase(Arrays.stream(propertyPaths).map(TypedPropertyPath::of).map(TypedPropertyPath::toDotPath)
+ .toArray(String[]::new));
+ }
+
/**
* Returns a copy of this {@link ExampleMatcher} with ignore case sensitivity for the {@code propertyPaths}. This
* instance is immutable and unaffected by this method call.
diff --git a/src/main/java/org/springframework/data/domain/Sort.java b/src/main/java/org/springframework/data/domain/Sort.java
index 4b3974933..44aad6693 100644
--- a/src/main/java/org/springframework/data/domain/Sort.java
+++ b/src/main/java/org/springframework/data/domain/Sort.java
@@ -29,11 +29,11 @@ import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
+import org.springframework.data.core.PropertyPath;
+import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.util.MethodInvocationRecorder;
import org.springframework.data.util.MethodInvocationRecorder.Recorded;
-import org.springframework.data.util.PropertyPath;
import org.springframework.data.util.Streamable;
-import org.springframework.data.util.TypedPropertyPath;
import org.springframework.lang.CheckReturnValue;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
@@ -797,7 +797,7 @@ public class Sort implements Streamable extends Sort {
diff --git a/src/test/java/org/springframework/data/mapping/TypedPropertyPathUnitTests.java b/src/test/java/org/springframework/data/core/TypedPropertyPathUnitTests.java
similarity index 99%
rename from src/test/java/org/springframework/data/mapping/TypedPropertyPathUnitTests.java
rename to src/test/java/org/springframework/data/core/TypedPropertyPathUnitTests.java
index 8e9a36562..b4dd02671 100644
--- a/src/test/java/org/springframework/data/mapping/TypedPropertyPathUnitTests.java
+++ b/src/test/java/org/springframework/data/core/TypedPropertyPathUnitTests.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.data.mapping;
+package org.springframework.data.core;
import static org.assertj.core.api.Assertions.*;
diff --git a/src/test/java/org/springframework/data/domain/ExampleMatcherUnitTests.java b/src/test/java/org/springframework/data/domain/ExampleMatcherUnitTests.java
index 20875f0b2..72209cd23 100755
--- a/src/test/java/org/springframework/data/domain/ExampleMatcherUnitTests.java
+++ b/src/test/java/org/springframework/data/domain/ExampleMatcherUnitTests.java
@@ -121,6 +121,15 @@ class ExampleMatcherUnitTests {
assertThat(matcher.getIgnoredPaths()).hasSize(2);
}
+ @Test //
+ void ignoredPropertyPathsShouldReturnUniqueProperties() {
+
+ matcher = matching().withIgnorePaths(Person::getFirstname, Person::getLastname, Person::getFirstname);
+
+ assertThat(matcher.getIgnoredPaths()).contains("firstname", "lastname");
+ assertThat(matcher.getIgnoredPaths()).hasSize(2);
+ }
+
@Test // DATACMNS-810
void withCreatesNewInstance() {
@@ -160,11 +169,11 @@ class ExampleMatcherUnitTests {
void shouldCompareUsingHashCodeAndEquals() {
matcher = matching() //
- .withIgnorePaths("foo", "bar", "baz") //
+ .withIgnorePaths(Random::getFoo, Random::getBar, Random::getBaz) //
.withNullHandler(NullHandler.IGNORE) //
.withIgnoreCase("ignored-case") //
- .withMatcher("hello", GenericPropertyMatchers.contains().caseSensitive()) //
- .withMatcher("world", GenericPropertyMatcher::endsWith);
+ .withMatcher(Random::getHello, GenericPropertyMatchers.contains().caseSensitive()) //
+ .withMatcher(Random::getWorld, GenericPropertyMatcher::endsWith);
var sameAsMatcher = matching() //
.withIgnorePaths("foo", "bar", "baz") //
@@ -182,8 +191,54 @@ class ExampleMatcherUnitTests {
assertThat(matcher).isEqualTo(sameAsMatcher).isNotEqualTo(different);
}
+ static class Random {
+
+ String foo;
+ String bar;
+ String baz;
+ String hello;
+ String world;
+
+ public String getFoo() {
+ return foo;
+ }
+
+ public String getBar() {
+ return bar;
+ }
+
+ public String getBaz() {
+ return baz;
+ }
+
+ public String getHello() {
+ return hello;
+ }
+
+ public String getWorld() {
+ return world;
+ }
+ }
+
static class Person {
String firstname;
+ String lastname;
+
+ public String getFirstname() {
+ return firstname;
+ }
+
+ public void setFirstname(String firstname) {
+ this.firstname = firstname;
+ }
+
+ public String getLastname() {
+ return lastname;
+ }
+
+ public void setLastname(String lastname) {
+ this.lastname = lastname;
+ }
}
}
diff --git a/src/test/java/org/springframework/data/domain/SortUnitTests.java b/src/test/java/org/springframework/data/domain/SortUnitTests.java
index 4e0b1c853..498a6ae96 100755
--- a/src/test/java/org/springframework/data/domain/SortUnitTests.java
+++ b/src/test/java/org/springframework/data/domain/SortUnitTests.java
@@ -21,11 +21,12 @@ import static org.springframework.data.domain.Sort.NullHandling.*;
import java.util.Collection;
import org.junit.jupiter.api.Test;
+
+import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.geo.Circle;
import org.springframework.data.mapping.Person;
-import org.springframework.data.util.TypedPropertyPath;
/**
* Unit test for {@link Sort}.