-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Optimize derived queries to avoid unnecessary JOINs for association ID access #3970
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize derived queries to avoid unnecessary JOINs for association ID access #3970
Conversation
4543e07
to
6e1d918
Compare
This is similar to #3922. Now with the fixes in Hibernate in place we can continue with both pull requests. |
@mp911de Thank you for your review! I see that #3922 addresses the same issue. Would it be valuable to
Happy to collaborate or close this in favor of #3922 if that's preferred. |
Derived queries use a slightly different API that is however derived from Query utils basically duplicating code. We tried to use less JPA API to determine what we need to know for joining, especially that we use similar code for AOT processing where we have a limited metamodel.
Feel free to consolidate both variants into a single PR and introduce a better abstraction or design if you like.
Am 11. Aug. 2025, 16:34 +0200 schrieb Hyunjoon Park ***@***.***>:
… academey left a comment (spring-projects/spring-data-jpa#3970)
@mp911de Thank you for your review! I see that #3922 addresses the same issue.
I've reviewed their approach and noticed they modify QueryUtils directly, while my implementation focuses on JpqlUtils.
Would it be valuable to
1. Contribute my test cases to strengthen the validation?
2. Compare both approaches to see if there are complementary insights?
Happy to collaborate or close this in favor of #3922 if that's preferred.
Thanks
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Thank you for the clarification and the opportunity to improve the design! I understand that Derived queries and regular queries use different but duplicated code paths. There's a need to minimize JPA API usage for AOT processing. A unified abstraction would be beneficial I'll review #3922's approach and work on consolidating both solutions.
I'll update this PR with the consolidated solution. Thank you |
85e8de9
to
a93b17a
Compare
…D access This commit introduces an optimization that eliminates unnecessary JOINs when accessing association IDs in derived query methods. Changes: - Add PathOptimizationStrategy interface for query path optimization - Implement DefaultPathOptimizationStrategy using SingularAttribute.isId() - Create JpaMetamodelContext for AOT-compatible metamodel access - Update QueryUtils and JpqlUtils to use the unified optimization - Add comprehensive test coverage for the optimization The optimization detects patterns like findByAuthorId() and generates SQL that directly uses the foreign key column instead of creating a JOIN. This improves query performance with Hibernate 6.4+ where entity path traversal generates JOINs by default. Fixes spring-projects#3349 Signed-off-by: academey <[email protected]>
b522dd6
to
894a06c
Compare
- Add blank lines for better readability - Improve code formatting consistency Signed-off-by: academey <[email protected]>
Signed-off-by: Hyunjoon Park <[email protected]>
dc3b2de
to
14a380f
Compare
@mp911de I've completed the unified implementation as discussed. The key changes include
The implementation now provides a clean abstraction layer that can be extended for different optimization strategies while maintaining compatibility with AOT compilation. |
I'll review this later. For now, I see you skipped all my tests. |
@bukajsytlos Thank you for looking into this PR About the two test classes, I can see how they might look similar at first glance. ForeignKeyOptimizationIntegrationTests goes deeper and actually looks at the SQL being generated. It uses Hibernate's utilities to check that we're really avoiding the JOINs. So it's more like "does it work the way we intended?" I'm not sure which tests you're referring to as skipped, could you point me to specific ones? I tried to cover all the main scenarios, but I might have missed something from your previous work. Happy to add anything that's missing! The core idea from #3922 is definitely in here, just wrapped in a more extensible way with the strategy pattern. Let me know what you think needs more coverage. |
@academey all the composite id tests (EmbeddedId, IdClass) Regarding those differences, they test it from the same level of abstraction and are kind of misleading. |
@bukajsytlos You're absolutely right on both points. I think the right approach would be to like that
Would it make sense to consolidate everything into one test class like DerivedQueryOptimizationTests that covers all cases and properly verifies the SQL generation? I can refactor this based on your feedback. Thanks. |
I liked the approach to test result correctness on repository level and query correctness on QueryUtils level. edit: I have just noticed, that in "comprehensive" tests, you first invoke repository and then call EntityManager directly and doing asserts just for hibernate. So in the end you are just testing hibernate implementation and not really changes of this PR |
Thanks @academey and @bukajsytlos for your collaboration on optimizing queries. The tests from #3922 are much closer to what is actually happening and run as integration test validating repository usage. I went ahead and took inspiration from both pull requests (and took #3922 as base) to refine expression creation and relationship Id detection. Given the scope of these changes, I would like to ship the change with the upcoming milestone first and ask you to give it a test. If those changes prove useful and do not break existing applications, we would consider backporting these into the Also, as general feedback:
That being said, I'm closing this PR as being superseded by #3922. |
We now no longer create a join for query property paths that point to an identifier of referenced entities to optimize query creation. Closes #3349 Original pull request: #3922 See also: #3970 Signed-off-by: Jakub Soltys <[email protected]>
@mp911de Thanks for the feedback and for handling this optimization through #3922. I understand the decision Also I appreciate the guidance on PR practices. I'll keep descriptions more concise and avoid mass mentions in future contributions. The complexity of unifying different abstraction levels was indeed a challenging aspect of this work. I nealry don't have experiences of making contributions to open source, so I was not good at that. I'm sorry about that. Looking forward to contributing to the project again with these learnings in mind. Thanks for maintaining such a great project |
No worries, we're happily here to help and provide guidance. Everyone started at zero experience and zero contributions. |
Summary
This PR optimizes derived query methods to avoid unnecessary JOINs when accessing association IDs. The implementation consolidates approaches from both this PR and PR #3922 into a unified solution with improved abstraction.
Background
Starting from Hibernate 6.4.1, entity path traversal (e.g.,
author.id
) generates JOINs even when accessing only the ID field. This affects query performance and generates suboptimal SQL for common patterns likefindByAuthorId
.Solution
Unified Architecture
SingularAttribute.isId()
for reliable ID detectionImplementation
Examples
Before optimization:
After optimization:
Features
findByAuthorId
,countByProfileId
, etc.Testing
Performance Impact
This optimization eliminates unnecessary JOINs in common query patterns:
Compatibility
Fixes #3349