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.
This commit is contained in:
135
README.md
Normal file
135
README.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# 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`](docs/00-versions.md) for how these were verified and a trap worth
|
||||
knowing about if you bump the Spring Boot version.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```bash
|
||||
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`](src/test/java/com/ankurm/hibernatedemo/GetVsGetReferenceTest.java) | 4859 (get vs getReference) | The 4-calls-4-outcomes table, the same-session matrix, proxy identity vs `equals()` |
|
||||
| [`MergeRefreshTest`](src/test/java/com/ankurm/hibernatedemo/MergeRefreshTest.java) | 4860 (merge vs refresh) | `merge()` returns the pre-existing managed instance; `merge()` initializes a cascaded LAZY collection; `refresh()` silently discards an unflushed edit |
|
||||
| [`OptimisticLockTest`](src/test/java/com/ankurm/hibernatedemo/OptimisticLockTest.java) | 4860 (merge vs refresh) | Exactly when `OptimisticLockException` surfaces relative to `merge()`/commit |
|
||||
| [`IdentityBatchTest`](src/test/java/com/ankurm/hibernatedemo/IdentityBatchTest.java) | 4861 (batch inserts) | `GenerationType.IDENTITY` disables batching entirely |
|
||||
| [`SequenceBatchTest`](src/test/java/com/ankurm/hibernatedemo/SequenceBatchTest.java) | 4861 (batch inserts) | `GenerationType.SEQUENCE` allows real batching |
|
||||
| [`AllocationSizeSweepTest`](src/test/java/com/ankurm/hibernatedemo/AllocationSizeSweepTest.java) | 4861 (batch inserts) | `allocationSize` sweep (1, 10, 25, 50) at fixed `batch_size=25` |
|
||||
| [`BatchSizeSweepTest`](src/test/java/com/ankurm/hibernatedemo/BatchSizeSweepTest.java) | 4861 (batch inserts) | `batch_size` sweep (1, 10, 25, 50) at fixed `allocationSize=50` |
|
||||
|
||||
```bash
|
||||
./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](docs/01-get-vs-load.md) |
|
||||
| `mergerefresh` | `merge()` vs `refresh()` against a `@Version`-ed entity | [docs/02-merge-vs-refresh.md](docs/02-merge-vs-refresh.md) |
|
||||
| `insert-identity` | Batch insert attempt with `GenerationType.IDENTITY` | [docs/03-inserting-objects.md](docs/03-inserting-objects.md) |
|
||||
| `insert-sequence` | The same insert, with `GenerationType.SEQUENCE` | [docs/03-inserting-objects.md](docs/03-inserting-objects.md) |
|
||||
|
||||
```bash
|
||||
./scripts/run.sh getvsload
|
||||
./scripts/run.sh mergerefresh
|
||||
./scripts/run.sh insert-identity
|
||||
./scripts/run.sh insert-sequence
|
||||
```
|
||||
|
||||
## Regenerating captured output
|
||||
|
||||
```bash
|
||||
./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](docs/00-versions.md) | Verified version pins, and the Spring Boot patch that silently changes which Hibernate patch you get |
|
||||
| [01 — get() vs getReference()](docs/01-get-vs-load.md) | The 4-calls-4-outcomes table, the same-session matrix, proxy identity vs `equals()` |
|
||||
| [02 — merge() vs refresh()](docs/02-merge-vs-refresh.md) | 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](docs/03-inserting-objects.md) | `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](docs/output/get-vs-load.txt) | `./scripts/run.sh getvsload` (CommandLineRunner) |
|
||||
| [docs/output/get-vs-getreference-tests.txt](docs/output/get-vs-getreference-tests.txt) | `./mvnw -Dtest=GetVsGetReferenceTest test` |
|
||||
| [docs/output/merge-vs-refresh.txt](docs/output/merge-vs-refresh.txt) | `./scripts/run.sh mergerefresh` (CommandLineRunner) |
|
||||
| [docs/output/merge-vs-refresh-tests.txt](docs/output/merge-vs-refresh-tests.txt) | `./mvnw -Dtest=MergeRefreshTest,OptimisticLockTest test` |
|
||||
| [docs/output/insert-identity.txt](docs/output/insert-identity.txt) | `./scripts/run.sh insert-identity` (also matches `IdentityBatchTest`) |
|
||||
| [docs/output/insert-sequence.txt](docs/output/insert-sequence.txt) | `./scripts/run.sh insert-sequence` (also matches `SequenceBatchTest`) |
|
||||
| [docs/output/allocation-and-batch-size-sweeps.txt](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](LICENSE).
|
||||
Reference in New Issue
Block a user