jakartaee / jakartaee/persistence
Call-site fetch control: type-safe, composable fetch paths on EntityManager and TypedQuery
- Dominant language
- Java
- Stars
- 268
- Forks
- 78
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 13
Description
## Problem
JPA's `EntityGraph` API is the right mechanism for controlling association fetch depth at the call site, but its API is less developer-friendly than it could be.
The JPA static metamodel (`Person_`, `Organization_`) provides type-safe attribute references, but each metamodel attribute is an isolated constant with no way to express traversal as a single composable expression. Building an `EntityGraph` still requires verbose, manual tree construction:
```java
EntityGraph graph = em.createEntityGraph(Person.class);
graph.addSubgraph(Person_.organization)
.addAttributeNodes(Organization_.country);
em.find(Person.class, id, Map.of("jakarta.persistence.fetchgraph", graph));
```
Every additional hop requires another `addSubgraph` call. Combining independent paths (e.g. `organization.country` and `role`) means building the tree manually. There is no way to express a multi-segment path as a single composable unit.
The cumulative effect is that `EntityGraph` is harder to use than it should be. Rather than expressing fetch intent directly as path chains, developers must translate that intent into a manually constructed subgraph tree — a mismatch that makes the API less intuitive than the underlying concept.
---
## Proposal
Two complementary additions to the spec.
### 1. Composable metamodel path expressions
Extend the static metamodel so that association attributes expose further traversal, producing a single expression that describes a full path:
```java
// Single-segment
Person_.organization // → Organization
// Multi-segment — organization → country
Person_.organization.country() // → Country
// Independent paths
Person_.organization.country()
Person_.role
```
The exact mechanism — whether via generated methods on metamodel types, a new `Path` wrapper type, or another approach — is left open for discussion. The key requirement is that multi-segment paths are expressible as a single composable unit, validated at compile time.
### 2. `setFetchPaths` on `TypedQuery` and `EntityManager.find`
Add a first-class method for specifying fetch paths at the call site, replacing the current hint-based approach:
```java
// EntityManager.find
Person person = em.find(Person.class, id,
Person_.organization.country(),
Person_.role);
// TypedQuery
List persons = em.createQuery("select p from Person p where p.name = ?1", Person.class)
.setParameter(1, "Smith")
.setFetchPaths(Person_.organization.country(), Person_.role)
.getResultList();
```
The implementation would translate path expressions into `EntityGraph` subgraphs internally, merging shared prefixes automatically. Callers would never need to construct an `EntityGraph` directly.
---
## Prior art
This pattern has been validated in production since 2015 and is available as an open-source reference implementation: [jpa-fetch](https://github.com/alterioncorp/jpa-fetch). It implements both a JPA metamodel style (`FetchPaths.of(Person_.organization, Organization_.country)`) and a QueryDSL style (`QPerson.person.organization().country()`), built entirely on existing JPA primitives. The library demonstrates that the `EntityGraph` construction and prefix-merging logic is straightforward to implement once callers have a composable path API to work with.
---
## Notes
- The composable metamodel path (proposal 1) is the more foundational change and would be valuable independent of proposal 2.
- `setFetchPaths` on `TypedQuery` is a natural extension — fetch control belongs at the query execution site, not scattered across `@NamedEntityGraph` annotations on entity classes.
- Both proposals are backward compatible — no existing API is changed.
Contributor guide
Research direction
Start by reviewing the EntityGraph API, the static metamodel, EntityManager.find, and TypedQuery, then compare the jpa-fetch reference implementation linked in the issue. The open design question is how to represent composable paths; done means the specification defines that mechanism and the fetch-path methods, including EntityGraph translation, prefix merging, and backward compatibility.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100