Skip to content

Conversation

@mp911de
Copy link
Member

@mp911de mp911de commented Nov 17, 2025

We now support type-safe property paths and property references:

Java variants:

PropertyPath.from("name", Person.class) // existing String-based API
PropertyPath.of(Person::getName) // type-safe property reference expression

PropertyPath.from("address.country", Person.class) // existing nested path API
PropertyPath.of(Person::getAddress).then(Address::getCountry) // type-safe composed path expression


PropertyReference.of(Secret::getSecret)

Kotlin variants:

PropertyReference.of(Secret::secret)

PropertyPath.of(Person::address / Address::city)

allowing type-safe usage through e.g.:

Sort.by(Person::getFirstName, Person::getLastName)

Module adoption:

@mp911de mp911de added the type: enhancement A general enhancement label Nov 17, 2025
Refine type naming.
Remove lambda cache in favor of resolved property references/PropertyPaths.
Inline KProperty references in TypedPropertyPaths.
@mp911de mp911de self-assigned this Jan 6, 2026
Copy link
Contributor

@schauder schauder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. I left a few nitpicks.

I didn't look into actually using it, since it seems Christoph already did that.
Let me know if I should take it for a spin as well.

[[property-path-internals]]
=== Property Path Internals

The `org.springframework.data.core` package is the basis for Spring Data's navigation across domain classes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make org.springframework.data.core a link into the package overview in the JavaDoc?

[[property-path-internals]]
=== Property Path Internals

The `org.springframework.data.core` package is the basis for Spring Data's navigation across domain classes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make org.springframework.data.core a link into the package overview in the JavaDoc?

This chapter covers the concept of property paths.
Property paths are a form of navigation through domain classes to apply certain aspects in the context of interacting with the model.
Application code provides property paths to data access components to express intents such as selection of properties within a query, forming predicates, or applying sorting.
A property path originates from its owning type and can consist of one to many segments.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"one to many segments"

I know, I lost the fight before, but I still find it really weird, that we don't have zero length paths, referring to the a aggregate root itself.

While a property path is a simple representation of object navigation, String-based property paths are inherently fragile during refactoring as they can be easily missed with an increasing distance between the property definition and its usage.
Type-safe alternatives derive property paths from method references, enabling the compiler to validate property names and IDEs to support refactoring operations.

Consider the practical difference: The following examples express the same intent - sorting by `address.city` - but only the type-safe version benefits from compiler validation and IDE support:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The following examples express the same intent" hints at a comparison with a text base definition of the same property path. We should either explicitly reference the example used above, or include the text based version here again in the example.

While a property path is a simple representation of object navigation, String-based property paths are inherently fragile during refactoring as they can be easily missed with an increasing distance between the property definition and its usage.
Type-safe alternatives derive property paths from method references, enabling the compiler to validate property names and IDEs to support refactoring operations.

Consider the practical difference: The following examples express the same intent - sorting by `address.city` - but only the type-safe version benefits from compiler validation and IDE support:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The following examples express the same intent" hints at a comparison with a text base definition of the same property path. We should either explicitly reference the example used above, or include the text based version here again in the example.

Type-safe property paths compose method references to construct navigation expressions.
The following examples demonstrate inline usage and composition:

.Type-safe Property Path Construction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this title a little confusing, because it seems to match the previous example much better than this one.

In general we should probably decide if we want titles with our examples or not and make all examples match that style.

* c
* address.city.name (this object)
* city.name (next() object)
* city.name (next().next() object)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last one should be just name, instead of city.name

* This method returns a resolved {@link PropertyReference} by introspecting the given method reference.
*
* @param property the method reference to a Java beans property.
* @param <T> owning type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we improve readability a little by using something more mnemonic than T? Or am I missing something?

O for Orgin or Owner.
R for Root
S for Source

Applies also for TypedPropertyPath

@Contract("_ -> new")
@CheckReturnValue
default <T> ExampleMatcher withIgnorePaths(TypedPropertyPath<T, ?>... ignoredPaths) {
return withIgnorePaths(Arrays.stream(ignoredPaths).map(TypedPropertyPath::of).map(TypedPropertyPath::toDotPath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we mapping it explicitly to TypedPropertyPath? This happens already in the getSegment called inside toDotPath.

If this is for performance reasons (just guessing) we should add a comment to that effect .

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern appears a couple of times.

*
* @author Mark Paluch
*/
class PropertyPathTck {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused by the name TCK. This seems more like a PropertyPathTestSupport.

From a TCK I would expect that it actually forces you to provide certain propertyPaths.
Maybe as a Map from a String representation to the representation under test and then run a bunch of tests against these, and failing when expected paths aren't in the provided Map.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: enhancement A general enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce first-class type-safe property path references at the core level

3 participants