Repository navigation
[WP2] @JoinFetch on paged queries forces Hibernate in-memory pagination (+ delete fetch heuristic) #192
Copy link
Copy link
Open
Labels
status: pending-design-work動工前需要更多的設計或研究動工前需要更多的設計或研究type: bugSomething isn't workingSomething isn't working
Description
此內容由 AI 產生(specification-mapper 全庫審查 2026-07-16.執行圖)。
Work package WP2 · findings: PERF-01, COR-08 · worst severity: high · effort: M
From the 2026-07-16 whole-codebase review of branch jakarta (snapshot 7de4a33; modules mapper/ + starter/). Every finding below is CONFIRMED by independent adversarial verification. File:line coordinates are from the snapshot and may have drifted.
Findings
PERF-01 — Fetch join executes on paged content queries, forcing Hibernate in-memory pagination (performance, high)
- File:
mapper/src/main/java/tw/com/softleader/data/jpa/spec/domain/JoinFetch.java:91 - Evidence:
JoinFetchdelegates fetch → plain join only forNumberresult types (count/exists). The paged content query keeps the collection fetch, sofindBySpec(pojo, pageable)combinesfirstResult/maxResultswith a collection fetch — Hibernate warnsHHH90003004and loads the whole joined result set into heap before slicing. - Failure scenario: a paged
findBySpecover a large table (e.g. 1M rows) — latency and heap grow linearly with table size, potential OOM, on the primary public API.
COR-08 — Number-result-type delegation heuristic does not cover delete(Specification) (correctness, medium)
- File:
mapper/src/main/java/tw/com/softleader/data/jpa/spec/domain/JoinFetch.java:91(shouldDelegateToJoin, same method as PERF-01) - Evidence:
shouldDelegateToJoinreturnsNumber.class.isAssignableFrom(query.getResultType()), but its own comment says "delegate to join for number result type queries, e.g. count or delete". A criteriadeletehas noNumberresult type, so the fetch is not delegated and a fetch is applied where it is invalid — contradicting the comment. Bundled here (not in the join cluster) because it edits the exacttoPredicate/shouldDelegateToJoinregion PERF-01 reshapes.
Design decision needed
Every candidate fix changes public semantics, so the choice is maintainer-owned:
- (a) Downgrade fetch → plain join on paged content queries: fixes memory, but paged results silently stop eager-loading collections → possible
LazyInitializationExceptionfor consumers relying on the fetch. - (b) Two-phase id-page-then-fetch: page ids first, then fetch the page's entities — preserves both semantics and memory, but is an architecture-level change to query execution.
- (c) Warn + document only: emit a one-time WARN when fetch + pagination combine and document the hazard — no behavior change, but the OOM stays.
Once the direction is decided, add a Plan section here and swap status: pending-design-work for status: ready-for-agent — work authorization travels with spec completeness.
Acceptance criteria (target)
- A paged
findBySpecwith@JoinFetchno longer triggersHHH90003004in-memory pagination (or, under option c, emits the documented warning). totalElementsstays consistent with page content.- A criteria
deletewith a@JoinFetchPOJO no longer applies an invalid fetch (COR-08 resolved as part of the same delegation rework). - Regression:
make testgreen.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
status: pending-design-work動工前需要更多的設計或研究動工前需要更多的設計或研究type: bugSomething isn't workingSomething isn't working
Activity