Skip to content

ANALYSIS - Force usage of FetchType LAZY for collections on JPA entities - #487

Merged
dedece35 merged 7 commits into
mainfrom
FetchType
Aug 7, 2026
Merged

ANALYSIS - Force usage of FetchType LAZY for collections on JPA entities#487
dedece35 merged 7 commits into
mainfrom
FetchType

Conversation

@dedece35

@dedece35 dedece35 commented Aug 4, 2026

Copy link
Copy Markdown
Member

Supersedes PRs #122, #125, #155, #325 and resolves #98, #116, #118

Context

Four PRs have proposed this rule since April 2023 (#122, #125, #155, #325). None could be merged:
three of them target the old java-plugin/ layout that no longer exists in this repository, and
they use four different rule keys (EC80, CRJVM205, EC_CRJVM205, EC205). On review, the rule
already exists inside SonarQube as the built-in rule java:S6904. This PR does not add a new
specification: it keeps the CRJVM205 entry in RULES.md, updates it to point at the built-in
rule, and preserves the work already done as supporting analysis rather than letting it go to
waste — see Measured impact below.

What this PR contains

  • RULES.md — updates the CRJVM205 row: instead of a new rule entry, it now states the rule
    already exists inside SonarQube as the built-in rule java:S6904, with the Reference/Validation
    link pointing at this PR.
  • CHANGELOG.md — entry under [Unreleased] / Added: "Add complete analysis for FetchType issue
    for Java (sonarqube built-in rule S6904)".
  • src/main/rules/S6904/java/S6904.asciidoc — documentation explaining why this isn't a new
    GCIxxx rule, plus the standard "why is this an issue," compliant/noncompliant examples,
    exceptions and resources sections, and a pointer to the full measurement analysis.
  • src/main/rules/S6904/java/analysis/ — the two-level measurement harness (JPA/Hibernate
    functional cost, then EnergyTracer hardware energy measurement) and two independent measurement
    campaigns, added to answer the impact question the four superseded PRs left open. See
    Measured impact below.

Scope decision

The rule targets collection associations only (@OneToMany, @ManyToMany) and only when
fetch = FetchType.EAGER is set explicitly
. Omitting fetch on those annotations is compliant,
since LAZY is already the JPA default — PR #155 reported those cases, which would have produced
false positives on the most common mapping in the domain.

Single-valued associations (@ManyToOne, @OneToOne) are explicitly out of scope: their cost
profile is different and their real impact belongs to the N+1 selects problem (CRJVM206).

Measured impact

None of the four superseded PRs provided any measurement of the actual cost — that gap is what
stalled the core-team discussion since April 2023. Two independent campaigns close
it, using a real Hibernate stack for functional cost and EnergyTracer's hardware counters
(Apple Silicon CPU/GPU/DRAM) for energy, 900 runs per variant per size across 30 measurement
phases.

Functional cost (Hibernate 6.4.4 + H2, single run per size):

Children collection size Extra rows hydrated Time Heap allocation SQL statements
10 +10 x1.81 x1.38 unchanged
100 +100 x3.73 x4.37 unchanged
1000 +1000 x12.65 x32.09 unchanged

The statement count never changes — Hibernate resolves the eager collection with a join, so this
is a payload/hydration cost, confirming the N+1 exclusion in the scope decision above rather than
overlapping with it.

Energy cost (two campaigns, six distinct collection sizes from 10 to 1000; three of them,
10/100/1000, measured independently in both):

Children collection size Relative energy overhead (with smell) Measured in Sign test across 30 phases
10 81.0–81.3% campaigns 1 and 2 p ≈ 1.9×10⁻⁹
30 83.9% campaign 2 p ≈ 1.9×10⁻⁹
50 85.8% campaign 2 p ≈ 1.9×10⁻⁹
100 86.3–86.7% campaigns 1 and 2 p ≈ 1.9×10⁻⁹
500 87.1% campaign 2 p ≈ 1.9×10⁻⁹
1000 90.1–90.7% campaigns 1 and 2 p ≈ 1.9×10⁻⁹

Every one of the 30 measurement phases at every size points the same way, at both hardware
components (CPU, DRAM) and wall time, with strictly disjoint EAGER/LAZY distributions. The
relative overhead is already large at the smallest size tested (10 elements) and climbs to 90%+
at 1000. The second campaign also located a genuine, honestly-disclosed anomaly in the
fine-grained scaling curve around 50–100 elements, which does not change this conclusion (see
campaign 2's document, §4–§5).

Reproducibility across the two independent measurement sessions (different days) is strong: the
relative overhead never moved by more than 0.6 percentage points at the three sizes tested in
both campaigns, even though absolute joule readings drifted by up to 16% between sessions —
which is why every figure above is a relative, paired comparison rather than a raw energy total.

Full detail, including the honest caveats (bimodal phases, the CONTEXT_SIZE confound, why the
tool's own p-values are not cited, the modest absolute yearly saving): campaign1-interpretation.md
and campaign2-interpretation.md under src/main/rules/S6904/java/analysis/level2-energytracer/.

Credits

@dedece35 dedece35 changed the title GCI116 - Force usage of FetchType LAZY for collections on JPA entities ANALYSIS - Force usage of FetchType LAZY for collections on JPA entities Aug 4, 2026
@sonarqubecloud

sonarqubecloud Bot commented Aug 4, 2026

Copy link
Copy Markdown


public static void main(String[] args) {
int[] sizes = args.length > 0 ? parseSizes(args[0]) : new int[] {10, 100, 1000};
int parentRows = args.length > 1 ? Integer.parseInt(args[1]) : DEFAULT_PARENT_ROWS;
String[] parts = arg.split(",");
int[] sizes = new int[parts.length];
for (int i = 0; i < parts.length; i++) {
sizes[i] = Integer.parseInt(parts[i].trim());
return reference;
}

public Set<OrderItem> getItems() {
return reference;
}

public Set<OrderItem> getItems() {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🗃️ rule rule improvment or rule development or bug java 🚀 enhancement New feature or request

Projects

Development

Successfully merging this pull request may close these issues.

[CRJVM205] [Java] Forcer l'utilisation du FetchType LAZY sur les collections dans les Entity JPA - TER

1 participant