Skip to content

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

academey
Copy link

@academey academey commented Aug 10, 2025

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 like findByAuthorId.

Solution

Unified Architecture

  • PathOptimizationStrategy: Interface for optimizing property path traversal
  • JpaMetamodelContext: Metamodel abstraction compatible with AOT compilation
  • DefaultPathOptimizationStrategy: Implementation using SingularAttribute.isId() for reliable ID detection

Implementation

Examples

Before optimization:

-- findByAuthorId(1L) 
SELECT b FROM Book b JOIN b.author a WHERE a.id = 1

After optimization:

-- findByAuthorId(1L)
SELECT b FROM Book b WHERE b.author.id = 1

Features

  • Handles @manytoone relationships
  • Supports owning-side @OnetoOne relationships
  • Works with composite ID scenarios
  • Compatible with derived queries: findByAuthorId, countByProfileId, etc.
  • AOT compilation support
  • Graceful fallback when metamodel unavailable

Testing

  • Added comprehensive unit tests for optimization strategy
  • Integration tests verify SQL generation improvements
  • Tested with both Hibernate and EclipseLink providers
  • All existing tests continue to pass

Performance Impact

This optimization eliminates unnecessary JOINs in common query patterns:

  • Simpler SQL execution plans
  • Better utilization of foreign key indexes
  • Consistent behavior across query generation methods
  • No breaking changes to existing functionality

Compatibility

  • Works with Hibernate 6.4+ where the issue manifests
  • Maintains compatibility with other JPA providers
  • No changes required for existing application code
  • Backward compatible with all Spring Data JPA versions

Fixes #3349

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Aug 10, 2025
@academey academey force-pushed the fix-3349-unnecessary-join-optimization branch from 4543e07 to 6e1d918 Compare August 10, 2025 11:48
@mp911de mp911de self-assigned this Aug 11, 2025
@mp911de mp911de added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Aug 11, 2025
@mp911de
Copy link
Member

mp911de commented Aug 11, 2025

This is similar to #3922. Now with the fixes in Hibernate in place we can continue with both pull requests.

@academey
Copy link
Author

@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

@mp911de
Copy link
Member

mp911de commented Aug 11, 2025 via email

@academey academey closed this Aug 11, 2025
@academey academey reopened this Aug 11, 2025
@academey
Copy link
Author

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.
My plan is that.

  1. Study both QueryUtils and JpqlUtils implementations
  2. Design a common abstraction that works for both use cases
  3. Ensure minimal JPA API usage for AOT compatibility
  4. Combine the test cases from both PRs

I'll update this PR with the consolidated solution. Thank you

@academey academey force-pushed the fix-3349-unnecessary-join-optimization branch from 85e8de9 to a93b17a Compare August 11, 2025 22:47
…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]>
@academey academey force-pushed the fix-3349-unnecessary-join-optimization branch 3 times, most recently from b522dd6 to 894a06c Compare August 11, 2025 23:29
- Add blank lines for better readability
- Improve code formatting consistency

Signed-off-by: academey <[email protected]>
@academey academey force-pushed the fix-3349-unnecessary-join-optimization branch from dc3b2de to 14a380f Compare August 12, 2025 00:29
@academey
Copy link
Author

@mp911de I've completed the unified implementation as discussed. The key changes include

  • Introduced PathOptimizationStrategy interface for abstraction
  • Created DefaultPathOptimizationStrategy that incorporates the approach from PR Remove unnecessary join when filtering on relationship id #3922
  • Added JpaMetamodelContext to minimize JPA API exposure and support AOT scenarios
  • Integrated comprehensive test cases covering various optimization scenarios

The implementation now provides a clean abstraction layer that can be extended for different optimization strategies while maintaining compatibility with AOT compilation.

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.

Regression for method-based query where predicate compares primary key of @ManyToOne relationship
3 participants