1
0
Ankur Mhatre 7b06d653e4 Add hibernate-demo: get() vs load(), merge() vs refresh(), inserting objects (Hibernate 7.4.1.Final + Spring Boot 4.1.0)
Adds a JUnit test suite (GetVsGetReferenceTest, MergeRefreshTest, OptimisticLockTest,
IdentityBatchTest, SequenceBatchTest, AllocationSizeSweepTest, BatchSizeSweepTest) so every
surprising behavior described in the three companion posts has a reproducible test, alongside
the original CommandLineRunner scenarios. Rewrites all three doc chapters and the README around
the new experiments: the get()/getReference() same-session matrix, the merge()/refresh()
experiments (including exactly when OptimisticLockException surfaces and a corrected LAZY-plus-
cascade merge() result), and two new sweeps (allocationSize, batch_size) for batch inserts.
2026-08-26 18:05:00 +00:00

hibernate-demo

Companion repository for three ankurm.com posts on Hibernate 7's persistence-context APIs: get() vs getReference(), merge() vs refresh(), and batch inserts. Every claim in those posts that comes from this repo traces to a named JUnit test here and a captured transcript in docs/output/ — nothing is asserted that wasn't actually run. Every surprising behavior described in the three posts has a reproducible test backing it; the table below maps each one.

Versions

Component Version
Hibernate ORM 7.4.1.Final (GA 2026-06-09)
Spring Boot 4.1.0 (GA 2026-06-10)
Java 25 (LTS)
H2 in-memory, managed by Spring Boot

See docs/00-versions.md for how these were verified and a trap worth knowing about if you bump the Spring Boot version.

Quickstart

git clone https://ankurm.com/git.app/asmhatre/hibernate-demo.git
cd hibernate-demo
./mvnw test

Requires JDK 25 and a network connection the first time (Maven needs to fetch plugins online before -o offline mode works for subsequent runs).

Every surprising claim, mapped to a test

Each row is one ./mvnw -Dtest=ClassName test away from reproducing itself. This is the reproduction path referenced throughout all three posts and all three doc chapters below.

Test class Backs post Proves
GetVsGetReferenceTest 4859 (get vs getReference) The 4-calls-4-outcomes table, the same-session matrix, proxy identity vs equals()
MergeRefreshTest 4860 (merge vs refresh) merge() returns the pre-existing managed instance; merge() initializes a cascaded LAZY collection; refresh() silently discards an unflushed edit
OptimisticLockTest 4860 (merge vs refresh) Exactly when OptimisticLockException surfaces relative to merge()/commit
IdentityBatchTest 4861 (batch inserts) GenerationType.IDENTITY disables batching entirely
SequenceBatchTest 4861 (batch inserts) GenerationType.SEQUENCE allows real batching
AllocationSizeSweepTest 4861 (batch inserts) allocationSize sweep (1, 10, 25, 50) at fixed batch_size=25
BatchSizeSweepTest 4861 (batch inserts) batch_size sweep (1, 10, 25, 50) at fixed allocationSize=50
./mvnw -Dtest=GetVsGetReferenceTest test
./mvnw -Dtest=MergeRefreshTest,OptimisticLockTest test
./mvnw -Dtest=IdentityBatchTest,SequenceBatchTest test
./mvnw -Dtest=AllocationSizeSweepTest test
./mvnw -Dtest=BatchSizeSweepTest test
./mvnw test   # the whole suite, 24 tests, 0 failures as of the last commit

CommandLineRunner scenarios

The original narrative scenarios are still here, unchanged, for anyone who wants to read a straight-line script instead of a test class:

Profile Runs Chapter
getvsload session.get() vs session.getReference(), proxies, LazyInitializationException docs/01-get-vs-load.md
mergerefresh merge() vs refresh() against a @Version-ed entity docs/02-merge-vs-refresh.md
insert-identity Batch insert attempt with GenerationType.IDENTITY docs/03-inserting-objects.md
insert-sequence The same insert, with GenerationType.SEQUENCE docs/03-inserting-objects.md
./scripts/run.sh getvsload
./scripts/run.sh mergerefresh
./scripts/run.sh insert-identity
./scripts/run.sh insert-sequence

Regenerating captured output

./scripts/run-all.sh

Regenerates the CommandLineRunner-scenario files in docs/output/. scripts/clean_output.py strips JVM noise and a harmless duplicate SQL echo line so the committed transcripts stay readable — nothing else is edited by hand. The test-suite transcripts in docs/output/ (the session matrix, the sweeps) were captured the same way, from ./mvnw -Dtest=... test piped through the same script.

Documentation index

Chapter Covers
00 — Versions Verified version pins, and the Spring Boot patch that silently changes which Hibernate patch you get
01 — get() vs getReference() The 4-calls-4-outcomes table, the same-session matrix, proxy identity vs equals()
02 — merge() vs refresh() Three named experiments: which method fails loudly vs silently, exactly when the optimistic-lock check fires, and what a cascaded LAZY collection does under merge()
03 — Hibernate 7 batch inserts IDENTITY vs SEQUENCE, the allocationSize and batch_size sweeps, Session vs StatelessSession, with hibernate.generate_statistics as evidence throughout

Captured output index

File Source
docs/output/get-vs-load.txt ./scripts/run.sh getvsload (CommandLineRunner)
docs/output/get-vs-getreference-tests.txt ./mvnw -Dtest=GetVsGetReferenceTest test
docs/output/merge-vs-refresh.txt ./scripts/run.sh mergerefresh (CommandLineRunner)
docs/output/merge-vs-refresh-tests.txt ./mvnw -Dtest=MergeRefreshTest,OptimisticLockTest test
docs/output/insert-identity.txt ./scripts/run.sh insert-identity (also matches IdentityBatchTest)
docs/output/insert-sequence.txt ./scripts/run.sh insert-sequence (also matches SequenceBatchTest)
docs/output/allocation-and-batch-size-sweeps.txt ./mvnw -Dtest=AllocationSizeSweepTest,BatchSizeSweepTest test

Layout

hibernate-demo/
├── pom.xml
├── LICENSE
├── scripts/
│   ├── run.sh              start one CommandLineRunner profile, run it, exit
│   ├── run-all.sh          regenerate the CommandLineRunner docs/output/ files
│   └── clean_output.py     strip JVM noise + a duplicate SQL echo line from a raw capture
├── src/main/java/com/ankurm/hibernatedemo/
│   ├── HibernateDemoApplication.java
│   ├── model/               Book, Note, WidgetIdentity, WidgetSequence,
│   │                        WidgetAlloc1/10/25/50, WidgetBatchSweep1/10/25/50
│   └── scenario/            one CommandLineRunner per profile
├── src/test/java/com/ankurm/hibernatedemo/
│   ├── GetVsGetReferenceTest.java
│   ├── MergeRefreshTest.java, OptimisticLockTest.java
│   ├── IdentityBatchTest.java, SequenceBatchTest.java
│   └── AllocationSizeSweepTest.java, BatchSizeSweepTest.java
└── docs/
    ├── 00-versions.md .. 03-inserting-objects.md
    └── output/*.txt         captured, unedited console transcripts (both runner and test output)

License

MIT — see LICENSE.

Description
Companion repository for the ankurm.com Hibernate 7 batch: get() vs load(), merge() vs refresh(), and inserting objects efficiently. Hibernate 7.4.1.Final + Spring Boot 4.1.0.
Readme 92 KiB
Languages
Java 95.2%
Shell 2.5%
Python 2.3%