Add hibernate-demo: get() vs load(), merge() vs refresh(), inserting objects (Hibernate 7.4.1.Final + Spring Boot 4.1.0)
This commit is contained in:
45
docs/00-versions.md
Normal file
45
docs/00-versions.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# 00 — Versions
|
||||
|
||||
[Next: 01 — get() vs load() →](01-get-vs-load.md)
|
||||
|
||||
This repository is pinned to:
|
||||
|
||||
| Component | Version | GA date | Source |
|
||||
|---|---|---|---|
|
||||
| Hibernate ORM | `7.4.1.Final` | 2026-06-09 | [hibernate.org/orm/releases/7.4](https://hibernate.org/orm/releases/7.4/) |
|
||||
| Spring Boot | `4.1.0` | 2026-06-10 | [spring.io/blog/2026/06/10/spring-boot-4](https://spring.io/blog/2026/06/10/spring-boot-4/) |
|
||||
| Java | `25` (LTS) | 2025-09 | latest LTS at the time this repo was built |
|
||||
| H2 | managed by Spring Boot 4.1.0 | — | in-memory, `DB_CLOSE_DELAY=-1` |
|
||||
|
||||
## The pin lines up, but check before you assume it always will
|
||||
|
||||
`hibernate-core`'s own `maven-metadata.xml` on Maven Central lists `7.4.6.Final` as the newest
|
||||
GA release at the time this was written — several patches ahead of `7.4.1.Final`. This repo
|
||||
pins to `7.4.1.Final` deliberately, because that is the version this batch of posts was written
|
||||
and run against, and because it is *exactly* the version Spring Boot 4.1.0 resolves on its own.
|
||||
|
||||
That last part is not a coincidence to take for granted, though. Checking
|
||||
`spring-boot-dependencies-4.1.0.pom` directly shows `<hibernate.version>7.4.1.Final</hibernate.version>` —
|
||||
so on Boot 4.1.0, `pom.xml` in this repo does not need to override anything to get 7.4.1.Final;
|
||||
the `<hibernate.version>` property declared here is redundant with what Boot already resolves,
|
||||
kept only so the pin is visible without cracking open Boot's own POM.
|
||||
|
||||
That stops being true one patch release later. `spring-boot-dependencies-4.1.1.pom` resolves
|
||||
`hibernate.version` to `7.4.5.Final` — a different Hibernate patch from the same Spring Boot
|
||||
minor version, four Hibernate patch releases apart. If you bump this repo's parent to `4.1.1`
|
||||
without touching the `<hibernate.version>` property, you get `7.4.1.Final` back (the explicit
|
||||
property now *does* override Boot's own management) rather than the `7.4.5.Final` Boot intended
|
||||
you to get — which is a more useful trap to know about than to fall into.
|
||||
|
||||
| Spring Boot version | Hibernate version Boot resolves |
|
||||
|---|---|
|
||||
| `4.0.8` | `7.2.24.Final` |
|
||||
| `4.1.0` | `7.4.1.Final` |
|
||||
| `4.1.1` | `7.4.5.Final` |
|
||||
|
||||
Verified against `maven-metadata.xml` on `repo1.maven.org`, not against Maven Central's
|
||||
`solrsearch` API — that index has been observed stale from this kind of sandboxed build
|
||||
environment (it reported an old Spring Boot release as newest well after a later one had
|
||||
shipped), so it should not be trusted for currency checks.
|
||||
|
||||
[Next: 01 — get() vs load() →](01-get-vs-load.md)
|
||||
98
docs/01-get-vs-load.md
Normal file
98
docs/01-get-vs-load.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 01 — get() vs load() / getReference()
|
||||
|
||||
[← Previous: 00 — Versions](00-versions.md) | [Next: 02 — merge() vs refresh() →](02-merge-vs-refresh.md)
|
||||
|
||||
Backs [ankurm.com: Hibernate 7 — get() vs load()](https://ankurm.com/hibernate-7-get-vs-load-which-one-should-you-actually-use/).
|
||||
|
||||
Source: [`GetVsLoadRunner`](../src/main/java/com/ankurm/hibernatedemo/scenario/GetVsLoadRunner.java).
|
||||
Run it yourself with `./scripts/run.sh getvsload`; the full transcript below is committed at
|
||||
[`docs/output/get-vs-load.txt`](output/get-vs-load.txt).
|
||||
|
||||
## What the six steps show
|
||||
|
||||
1. **`session.get()` on an existing id** — one `SELECT` immediately, returns the real entity.
|
||||
2. **`session.get()` on a missing id** — one `SELECT`, returns `null`. No exception, ever.
|
||||
3. **`session.getReference()` on an existing id** — no `SELECT` at the call site. The `SELECT`
|
||||
only happens the moment a non-identifier accessor (`getTitle()`) is called on the returned
|
||||
proxy. The log ordering in the transcript is the actual evidence for this, not a claim about
|
||||
it — the `about to call` / `now calling` markers in the code bracket exactly where each
|
||||
`SELECT` does or doesn't appear.
|
||||
4. **`session.getReference()` on a missing id** — same deferral: the proxy is handed back with
|
||||
no exception, and only accessing it throws
|
||||
`jakarta.persistence.EntityNotFoundException: No row with the given identifier exists`.
|
||||
5. **A proxy outlives its session** — `getReference()`, then commit and close, then access:
|
||||
`org.hibernate.LazyInitializationException: Could not initialize proxy [...] - no session`.
|
||||
6. **Proxy identity** — `Book.class.isInstance(proxy)` is `true`, but `real.getClass() ==
|
||||
proxy.getClass()` is `false` (the proxy's runtime class is `Book$HibernateProxy`), and
|
||||
`real.equals(proxy)` is `false` even though both represent the same row.
|
||||
|
||||
## What surprised me building this
|
||||
|
||||
Step 6 is the one worth sitting with. `Book` here does not override `equals()`, so
|
||||
`real.equals(proxy)` falling through to reference equality is not a Hibernate quirk — it's plain
|
||||
Java doing exactly what an un-overridden `equals()` always does. But it means the moment `get()`
|
||||
and `getReference()` are mixed for the *same row* in code that ever compares entities by
|
||||
`equals()` (a `Set`, a `List.contains()`, a manual reconciliation), the proxy boundary silently
|
||||
breaks that comparison. `instanceof` survives it. `equals()`, `hashCode()`-based collections, and
|
||||
naive `==` do not. The fix is the one every Effective-Java-style guide already recommends —
|
||||
implement `equals()`/`hashCode()` off the identifier, never off the object's identity — but this
|
||||
is what makes skipping that advice actually bite: not a compile error, a `false` you don't
|
||||
notice until two supposedly-identical entities land in the same `HashSet` and both show up.
|
||||
|
||||
Also unexpected going in: `getReference()` on a *missing* row throws `EntityNotFoundException`
|
||||
on first non-id access, not `ObjectNotFoundException`. The two names get used interchangeably in
|
||||
older Hibernate discussion; running it pins down which one this exact version actually throws
|
||||
(see the transcript for the full class name and message).
|
||||
|
||||
## Full captured transcript
|
||||
|
||||
```console
|
||||
Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id))
|
||||
Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id))
|
||||
Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id))
|
||||
Hibernate: create sequence book_seq start with 1 increment by 50
|
||||
Hibernate: create sequence widget_seq start with 1 increment by 25
|
||||
Hibernate: select next value for book_seq
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.Book */insert into book (author,title,version,id) values (?,?,?,?)
|
||||
binding parameter (1:VARCHAR) <- [Joshua Bloch]
|
||||
binding parameter (2:VARCHAR) <- [Effective Java]
|
||||
binding parameter (3:BIGINT) <- [0]
|
||||
binding parameter (4:BIGINT) <- [1]
|
||||
SEED: inserted Book id=1
|
||||
--- Step 1: session.get() on an existing id ---
|
||||
about to call session.get(Book.class, 1)
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
get() returned: Book{id=1, title=Effective Java, author=Joshua Bloch, version=0}
|
||||
--- Step 2: session.get() on a missing id ---
|
||||
about to call session.get(Book.class, 999001)
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [999001]
|
||||
get() returned: null (no exception thrown)
|
||||
--- Step 3: session.getReference() on an existing id ---
|
||||
getReference() returned proxy of class com.ankurm.hibernatedemo.model.Book$HibernateProxy -- no SELECT above this line
|
||||
now calling proxy.getTitle() ...
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
getTitle() returned 'Effective Java' -- the SELECT for this ran just above this line
|
||||
--- Step 4: session.getReference() on a missing id ---
|
||||
getReference() returned a proxy for a row that does not exist -- no exception yet: com.ankurm.hibernatedemo.model.Book$HibernateProxy
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [999001]
|
||||
accessing the proxy threw jakarta.persistence.EntityNotFoundException: No row with the given identifier exists for entity [com.ankurm.hibernatedemo.model.Book with id '999001']
|
||||
--- Step 5: proxy accessed after its session is closed ---
|
||||
session closed. proxy in hand: com.ankurm.hibernatedemo.model.Book$HibernateProxy
|
||||
accessing the proxy after close threw org.hibernate.LazyInitializationException: Could not initialize proxy [com.ankurm.hibernatedemo.model.Book#1] - no session
|
||||
--- Step 6: proxy identity vs a real loaded instance ---
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
real.getClass() = com.ankurm.hibernatedemo.model.Book
|
||||
proxy.getClass() = com.ankurm.hibernatedemo.model.Book$HibernateProxy
|
||||
proxy instanceof Book.class: true
|
||||
real.getClass() == proxy.getClass(): false
|
||||
real.equals(proxy) before proxy access: false
|
||||
```
|
||||
|
||||
[← Previous: 00 — Versions](00-versions.md) | [Next: 02 — merge() vs refresh() →](02-merge-vs-refresh.md)
|
||||
108
docs/02-merge-vs-refresh.md
Normal file
108
docs/02-merge-vs-refresh.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# 02 — merge() vs refresh()
|
||||
|
||||
[← Previous: 01 — get() vs load()](01-get-vs-load.md) | [Next: 03 — Inserting objects →](03-inserting-objects.md)
|
||||
|
||||
Backs [ankurm.com: merge() vs refresh()](https://ankurm.com/mastering-hibernate-7-merging-vs-refreshing-entities-for-robust-data-consistency/).
|
||||
|
||||
Source: [`MergeVsRefreshRunner`](../src/main/java/com/ankurm/hibernatedemo/scenario/MergeVsRefreshRunner.java),
|
||||
entity: [`Book`](../src/main/java/com/ankurm/hibernatedemo/model/Book.java) (note the `@Version`
|
||||
column — this scenario specifically needs a real optimistic-lock field, not just a plain one).
|
||||
Run it yourself with `./scripts/run.sh mergerefresh`; the full transcript is committed at
|
||||
[`docs/output/merge-vs-refresh.txt`](output/merge-vs-refresh.txt).
|
||||
|
||||
## The setup
|
||||
|
||||
1. Seed one `Book` row, `version=0`.
|
||||
2. Load it, then close the session — it is now **detached**, still holding `version=0`.
|
||||
3. A second, independent session loads the same row, edits the title, and commits. The row's
|
||||
`version` in the database is now `1`.
|
||||
4. Back on the original detached instance (still at `version=0`), edit a *different* field
|
||||
(`author`) and call `session.merge(detached)`.
|
||||
5. Separately: load the row fresh into a **managed** session, make an in-memory edit, don't
|
||||
flush it, then call `session.refresh(...)`.
|
||||
|
||||
## What actually happened
|
||||
|
||||
Step 4 does not silently overwrite the title from step 3. It throws:
|
||||
|
||||
```
|
||||
jakarta.persistence.OptimisticLockException: Row was already updated or deleted by another
|
||||
transaction for entity [com.ankurm.hibernatedemo.model.Book with id '1']
|
||||
```
|
||||
|
||||
`merge()` re-fetches the current row, sees the database is at `version=1` while the detached
|
||||
instance is still carrying `version=0`, and refuses the write. The title edited by the "other
|
||||
process" in step 3 survives untouched.
|
||||
|
||||
Step 5 is the opposite outcome for a structurally similar situation. The managed entity has an
|
||||
unflushed local edit (`author` set to a new value that was never sent to the database).
|
||||
`refresh()` re-runs the `SELECT` and overwrites every field with what's in the database right
|
||||
now — including the field with the pending edit. No exception. No warning. The edit is just
|
||||
gone:
|
||||
|
||||
```
|
||||
before refresh(): Book{id=1, title=Clean Code (2nd Edition), author=SOMEONE ELSE ENTIRELY (never flushed), version=1}
|
||||
after refresh(): Book{id=1, title=Clean Code (2nd Edition), author=Robert C. Martin, version=1} -- the local edit is gone, no exception was thrown
|
||||
```
|
||||
|
||||
## What surprised me building this
|
||||
|
||||
Going in, the plan was to show "merge() can silently overwrite concurrent changes" as the
|
||||
headline risk — that's the framing most write-ups of `merge()` use. Running it against a
|
||||
`@Version`-ed entity showed the opposite: with optimistic locking in place, `merge()` is the
|
||||
*safe* one here — it throws rather than clobbering someone else's committed write. `refresh()`
|
||||
is the one that destroys data silently, and it does it to an edit that was never even sent to
|
||||
the database yet. The risk isn't "which method can overwrite the database" — both can, that's
|
||||
their job. It's "which method fails loudly when the state it's holding is stale," and on a
|
||||
versioned entity that is `refresh()`, not `merge()` — precisely backwards from how the pairing
|
||||
usually gets described. Strip the `@Version` column out and this flips: an unversioned `merge()`
|
||||
would apply the stale write in step 4 without a peep. The column is not incidental to the
|
||||
result; it's the whole reason the result is what it is.
|
||||
|
||||
## Full captured transcript
|
||||
|
||||
```console
|
||||
Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id))
|
||||
Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id))
|
||||
Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id))
|
||||
Hibernate: create sequence book_seq start with 1 increment by 50
|
||||
Hibernate: create sequence widget_seq start with 1 increment by 25
|
||||
Hibernate: select next value for book_seq
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.Book */insert into book (author,title,version,id) values (?,?,?,?)
|
||||
binding parameter (1:VARCHAR) <- [Robert C. Martin]
|
||||
binding parameter (2:VARCHAR) <- [Clean Code]
|
||||
binding parameter (3:BIGINT) <- [0]
|
||||
binding parameter (4:BIGINT) <- [1]
|
||||
SEED: inserted Book{id=1, title=Clean Code, author=Robert C. Martin, version=0}
|
||||
--- Step 1: load the row, then close the session (entity is now detached) ---
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
detached instance in hand: Book{id=1, title=Clean Code, author=Robert C. Martin, version=0}
|
||||
--- Step 2: a second, independent session edits the same row and commits ---
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
Hibernate: /* update for com.ankurm.hibernatedemo.model.Book */update book set author=?,title=?,version=? where id=? and version=?
|
||||
binding parameter (1:VARCHAR) <- [Robert C. Martin]
|
||||
binding parameter (2:VARCHAR) <- [Clean Code (2nd Edition)]
|
||||
binding parameter (3:BIGINT) <- [1]
|
||||
binding parameter (4:BIGINT) <- [1]
|
||||
binding parameter (5:BIGINT) <- [0]
|
||||
second session committed: Book{id=1, title=Clean Code (2nd Edition), author=Robert C. Martin, version=1} -- version column has now advanced in the database
|
||||
--- Step 3: mutate the ORIGINAL detached instance (still holding the OLD version) and merge() it ---
|
||||
detached instance before merge (note the version and title are both stale): Book{id=1, title=Clean Code, author=Robert C. Martin (Uncle Bob), version=0}
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
merge() threw jakarta.persistence.OptimisticLockException: Row was already updated or deleted by another transaction for entity [com.ankurm.hibernatedemo.model.Book with id '1']
|
||||
the title change from Step 2 survives untouched -- merge() refused to apply a write built on a stale version
|
||||
--- Step 4: refresh() on a MANAGED entity with an unflushed local edit ---
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
before refresh(): Book{id=1, title=Clean Code (2nd Edition), author=SOMEONE ELSE ENTIRELY (never flushed), version=1}
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
after refresh(): Book{id=1, title=Clean Code (2nd Edition), author=Robert C. Martin, version=1} -- the local edit is gone, no exception was thrown
|
||||
```
|
||||
|
||||
[← Previous: 01 — get() vs load()](01-get-vs-load.md) | [Next: 03 — Inserting objects →](03-inserting-objects.md)
|
||||
263
docs/03-inserting-objects.md
Normal file
263
docs/03-inserting-objects.md
Normal file
@@ -0,0 +1,263 @@
|
||||
# 03 — Inserting objects efficiently
|
||||
|
||||
[← Previous: 02 — merge() vs refresh()](02-merge-vs-refresh.md) | [Back to README →](../README.md)
|
||||
|
||||
Backs [ankurm.com: Inserting objects efficiently](https://ankurm.com/mastering-hibernate-7-the-ultimate-guide-to-inserting-objects-efficiently/).
|
||||
|
||||
Sources: [`InsertIdentityRunner`](../src/main/java/com/ankurm/hibernatedemo/scenario/InsertIdentityRunner.java) /
|
||||
[`InsertSequenceRunner`](../src/main/java/com/ankurm/hibernatedemo/scenario/InsertSequenceRunner.java),
|
||||
entities [`WidgetIdentity`](../src/main/java/com/ankurm/hibernatedemo/model/WidgetIdentity.java) /
|
||||
[`WidgetSequence`](../src/main/java/com/ankurm/hibernatedemo/model/WidgetSequence.java).
|
||||
Run both with `./scripts/run.sh insert-identity` and `./scripts/run.sh insert-sequence`.
|
||||
Transcripts: [`docs/output/insert-identity.txt`](output/insert-identity.txt),
|
||||
[`docs/output/insert-sequence.txt`](output/insert-sequence.txt).
|
||||
|
||||
## The one-line difference that matters
|
||||
|
||||
Both entities persist 30 rows in a single transaction with **identical** settings:
|
||||
|
||||
```yaml
|
||||
hibernate.jdbc.batch_size: 25
|
||||
hibernate.order_inserts: true
|
||||
```
|
||||
|
||||
`WidgetIdentity` uses `@GeneratedValue(strategy = GenerationType.IDENTITY)`.
|
||||
`WidgetSequence` uses `@GeneratedValue(strategy = GenerationType.SEQUENCE)` with a matching
|
||||
`allocationSize = 25`. That's the entire diff between the two entity classes.
|
||||
|
||||
## What Hibernate's own statistics say happened
|
||||
|
||||
| | `entityInsertCount` | `prepareStatementCount` |
|
||||
|---|---|---|
|
||||
| `WidgetIdentity` (IDENTITY) | 30 | **30** |
|
||||
| `WidgetSequence` (SEQUENCE) | 30 | **4** |
|
||||
|
||||
`hibernate.jdbc.batch_size=25` did nothing at all for the `IDENTITY` run — every one of the 30
|
||||
inserts is its own round trip to the database (`prepareStatementCount` equals
|
||||
`entityInsertCount`). With `SEQUENCE`, Hibernate knows the id before the row is written, so it
|
||||
can queue inserts and batch them: 30 rows at a batch size of 25 means two insert batches
|
||||
(25 + 5), plus two calls to pull the next block of ids from `widget_seq` (the sequence's
|
||||
`allocationSize` is also 25, so the first 25 ids come from one call and the remaining 5 force a
|
||||
second) — four prepared statements total, for the same 30 rows.
|
||||
|
||||
## What surprised me building this
|
||||
|
||||
The number that surprised me was not "IDENTITY doesn't batch" — that's documented, if you know
|
||||
to look for it. It was seeing `prepareStatementCount` for `SEQUENCE` land at exactly **4**, not
|
||||
2. It's obvious in hindsight — `allocationSize` governs how often the sequence itself gets hit,
|
||||
independently of `batch_size` governing how the inserts get grouped — but "obvious in hindsight"
|
||||
and "what I would have guessed beforehand" are different things, and the gap between them is
|
||||
exactly what running this instead of describing it catches. If `allocationSize` had been left at
|
||||
JPA's default of `50` instead of matching `batch_size` at `25`, the sequence would only need one
|
||||
call for all 30 ids, and `prepareStatementCount` would drop to 3 — a change to a number that has
|
||||
nothing to do with batching, moving a number that looks like it's entirely about batching.
|
||||
|
||||
The practical version of this: if a switch to `SEQUENCE` doesn't produce the batching win the
|
||||
Hibernate docs promise, checking `hibernate.generate_statistics=true` and reading
|
||||
`prepareStatementCount` directly answers "is it actually batching" in a way that reading the
|
||||
`hibernate.jdbc.batch_size` value in a config file cannot — the config says what was requested,
|
||||
not what happened.
|
||||
|
||||
## Long-tail edge cases not covered above
|
||||
|
||||
- **`save()` vs `persist()`** — `save()` is Hibernate's own pre-JPA API and still works, but
|
||||
returns the generated id immediately rather than `void`, and can be called outside a
|
||||
transaction (where it will fail later, confusingly, at flush time). `persist()` is the
|
||||
JPA-portable choice; there's no scenario for this in this repo because the observable
|
||||
difference is in the method signature and portability, not in captured runtime behaviour.
|
||||
- **Bulk inserts via `StatelessSession`** — bypasses the persistence context and lifecycle
|
||||
callbacks entirely; worth a dedicated repository of its own rather than a profile bolted onto
|
||||
this one, since the interesting failure modes (cascades silently not firing, no dirty
|
||||
checking) need a scenario built around triggering them specifically.
|
||||
- **Native SQL batch inserts via `createNativeQuery` + `addBatch`** — sidesteps Hibernate's own
|
||||
batching machinery altogether; the batching behaviour at that point is entirely the JDBC
|
||||
driver's, not Hibernate's, which is a different post.
|
||||
|
||||
## Full captured transcripts
|
||||
|
||||
### `insert-identity.txt`
|
||||
|
||||
```console
|
||||
Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id))
|
||||
Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id))
|
||||
Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id))
|
||||
Hibernate: create sequence book_seq start with 1 increment by 50
|
||||
Hibernate: create sequence widget_seq start with 1 increment by 25
|
||||
--- inserting 30 WidgetIdentity rows (GenerationType.IDENTITY) ---
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-1]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-2]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-3]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-4]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-5]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-6]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-7]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-8]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-9]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-10]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-11]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-12]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-13]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-14]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-15]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-16]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-17]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-18]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-19]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-20]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-21]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-22]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-23]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-24]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-25]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-26]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-27]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-28]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-29]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-30]
|
||||
entityInsertCount = 30
|
||||
prepareStatementCount = 30
|
||||
(with IDENTITY, expect prepareStatementCount to land close to entityInsertCount -- each insert has to go to the database immediately to hand back the generated key, so there is nothing left for hibernate.jdbc.batch_size to batch)
|
||||
```
|
||||
|
||||
### `insert-sequence.txt`
|
||||
|
||||
```console
|
||||
Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id))
|
||||
Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id))
|
||||
Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id))
|
||||
Hibernate: create sequence book_seq start with 1 increment by 50
|
||||
Hibernate: create sequence widget_seq start with 1 increment by 25
|
||||
--- inserting 30 WidgetSequence rows (GenerationType.SEQUENCE, allocationSize=25) ---
|
||||
Hibernate: select next value for widget_seq
|
||||
Hibernate: select next value for widget_seq
|
||||
Hibernate: select next value for widget_seq
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-1]
|
||||
binding parameter (2:BIGINT) <- [1]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-2]
|
||||
binding parameter (2:BIGINT) <- [2]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-3]
|
||||
binding parameter (2:BIGINT) <- [3]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-4]
|
||||
binding parameter (2:BIGINT) <- [4]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-5]
|
||||
binding parameter (2:BIGINT) <- [5]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-6]
|
||||
binding parameter (2:BIGINT) <- [6]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-7]
|
||||
binding parameter (2:BIGINT) <- [7]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-8]
|
||||
binding parameter (2:BIGINT) <- [8]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-9]
|
||||
binding parameter (2:BIGINT) <- [9]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-10]
|
||||
binding parameter (2:BIGINT) <- [10]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-11]
|
||||
binding parameter (2:BIGINT) <- [11]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-12]
|
||||
binding parameter (2:BIGINT) <- [12]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-13]
|
||||
binding parameter (2:BIGINT) <- [13]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-14]
|
||||
binding parameter (2:BIGINT) <- [14]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-15]
|
||||
binding parameter (2:BIGINT) <- [15]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-16]
|
||||
binding parameter (2:BIGINT) <- [16]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-17]
|
||||
binding parameter (2:BIGINT) <- [17]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-18]
|
||||
binding parameter (2:BIGINT) <- [18]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-19]
|
||||
binding parameter (2:BIGINT) <- [19]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-20]
|
||||
binding parameter (2:BIGINT) <- [20]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-21]
|
||||
binding parameter (2:BIGINT) <- [21]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-22]
|
||||
binding parameter (2:BIGINT) <- [22]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-23]
|
||||
binding parameter (2:BIGINT) <- [23]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-24]
|
||||
binding parameter (2:BIGINT) <- [24]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-25]
|
||||
binding parameter (2:BIGINT) <- [25]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-26]
|
||||
binding parameter (2:BIGINT) <- [26]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-27]
|
||||
binding parameter (2:BIGINT) <- [27]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-28]
|
||||
binding parameter (2:BIGINT) <- [28]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-29]
|
||||
binding parameter (2:BIGINT) <- [29]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-30]
|
||||
binding parameter (2:BIGINT) <- [30]
|
||||
entityInsertCount = 30
|
||||
prepareStatementCount = 4
|
||||
(with SEQUENCE, the id is known before the row is written, so Hibernate can defer and batch the inserts -- expect prepareStatementCount well below entityInsertCount)
|
||||
```
|
||||
|
||||
[← Previous: 02 — merge() vs refresh()](02-merge-vs-refresh.md) | [Back to README →](../README.md)
|
||||
46
docs/output/get-vs-load.txt
Normal file
46
docs/output/get-vs-load.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id))
|
||||
Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id))
|
||||
Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id))
|
||||
Hibernate: create sequence book_seq start with 1 increment by 50
|
||||
Hibernate: create sequence widget_seq start with 1 increment by 25
|
||||
Hibernate: select next value for book_seq
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.Book */insert into book (author,title,version,id) values (?,?,?,?)
|
||||
binding parameter (1:VARCHAR) <- [Joshua Bloch]
|
||||
binding parameter (2:VARCHAR) <- [Effective Java]
|
||||
binding parameter (3:BIGINT) <- [0]
|
||||
binding parameter (4:BIGINT) <- [1]
|
||||
SEED: inserted Book id=1
|
||||
--- Step 1: session.get() on an existing id ---
|
||||
about to call session.get(Book.class, 1)
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
get() returned: Book{id=1, title=Effective Java, author=Joshua Bloch, version=0}
|
||||
--- Step 2: session.get() on a missing id ---
|
||||
about to call session.get(Book.class, 999001)
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [999001]
|
||||
get() returned: null (no exception thrown)
|
||||
--- Step 3: session.getReference() on an existing id ---
|
||||
getReference() returned proxy of class com.ankurm.hibernatedemo.model.Book$HibernateProxy -- no SELECT above this line
|
||||
now calling proxy.getTitle() ...
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
getTitle() returned 'Effective Java' -- the SELECT for this ran just above this line
|
||||
--- Step 4: session.getReference() on a missing id ---
|
||||
getReference() returned a proxy for a row that does not exist -- no exception yet: com.ankurm.hibernatedemo.model.Book$HibernateProxy
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [999001]
|
||||
accessing the proxy threw jakarta.persistence.EntityNotFoundException: No row with the given identifier exists for entity [com.ankurm.hibernatedemo.model.Book with id '999001']
|
||||
--- Step 5: proxy accessed after its session is closed ---
|
||||
session closed. proxy in hand: com.ankurm.hibernatedemo.model.Book$HibernateProxy
|
||||
accessing the proxy after close threw org.hibernate.LazyInitializationException: Could not initialize proxy [com.ankurm.hibernatedemo.model.Book#1] - no session
|
||||
--- Step 6: proxy identity vs a real loaded instance ---
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
real.getClass() = com.ankurm.hibernatedemo.model.Book
|
||||
proxy.getClass() = com.ankurm.hibernatedemo.model.Book$HibernateProxy
|
||||
proxy instanceof Book.class: true
|
||||
real.getClass() == proxy.getClass(): false
|
||||
real.equals(proxy) before proxy access: false
|
||||
71
docs/output/insert-identity.txt
Normal file
71
docs/output/insert-identity.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id))
|
||||
Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id))
|
||||
Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id))
|
||||
Hibernate: create sequence book_seq start with 1 increment by 50
|
||||
Hibernate: create sequence widget_seq start with 1 increment by 25
|
||||
--- inserting 30 WidgetIdentity rows (GenerationType.IDENTITY) ---
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-1]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-2]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-3]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-4]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-5]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-6]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-7]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-8]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-9]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-10]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-11]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-12]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-13]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-14]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-15]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-16]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-17]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-18]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-19]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-20]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-21]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-22]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-23]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-24]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-25]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-26]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-27]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-28]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-29]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetIdentity */insert into widget_identity (name,id) values (?,default)
|
||||
binding parameter (1:VARCHAR) <- [identity-30]
|
||||
entityInsertCount = 30
|
||||
prepareStatementCount = 30
|
||||
(with IDENTITY, expect prepareStatementCount to land close to entityInsertCount -- each insert has to go to the database immediately to hand back the generated key, so there is nothing left for hibernate.jdbc.batch_size to batch)
|
||||
104
docs/output/insert-sequence.txt
Normal file
104
docs/output/insert-sequence.txt
Normal file
@@ -0,0 +1,104 @@
|
||||
Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id))
|
||||
Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id))
|
||||
Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id))
|
||||
Hibernate: create sequence book_seq start with 1 increment by 50
|
||||
Hibernate: create sequence widget_seq start with 1 increment by 25
|
||||
--- inserting 30 WidgetSequence rows (GenerationType.SEQUENCE, allocationSize=25) ---
|
||||
Hibernate: select next value for widget_seq
|
||||
Hibernate: select next value for widget_seq
|
||||
Hibernate: select next value for widget_seq
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-1]
|
||||
binding parameter (2:BIGINT) <- [1]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-2]
|
||||
binding parameter (2:BIGINT) <- [2]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-3]
|
||||
binding parameter (2:BIGINT) <- [3]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-4]
|
||||
binding parameter (2:BIGINT) <- [4]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-5]
|
||||
binding parameter (2:BIGINT) <- [5]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-6]
|
||||
binding parameter (2:BIGINT) <- [6]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-7]
|
||||
binding parameter (2:BIGINT) <- [7]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-8]
|
||||
binding parameter (2:BIGINT) <- [8]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-9]
|
||||
binding parameter (2:BIGINT) <- [9]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-10]
|
||||
binding parameter (2:BIGINT) <- [10]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-11]
|
||||
binding parameter (2:BIGINT) <- [11]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-12]
|
||||
binding parameter (2:BIGINT) <- [12]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-13]
|
||||
binding parameter (2:BIGINT) <- [13]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-14]
|
||||
binding parameter (2:BIGINT) <- [14]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-15]
|
||||
binding parameter (2:BIGINT) <- [15]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-16]
|
||||
binding parameter (2:BIGINT) <- [16]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-17]
|
||||
binding parameter (2:BIGINT) <- [17]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-18]
|
||||
binding parameter (2:BIGINT) <- [18]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-19]
|
||||
binding parameter (2:BIGINT) <- [19]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-20]
|
||||
binding parameter (2:BIGINT) <- [20]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-21]
|
||||
binding parameter (2:BIGINT) <- [21]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-22]
|
||||
binding parameter (2:BIGINT) <- [22]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-23]
|
||||
binding parameter (2:BIGINT) <- [23]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-24]
|
||||
binding parameter (2:BIGINT) <- [24]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-25]
|
||||
binding parameter (2:BIGINT) <- [25]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-26]
|
||||
binding parameter (2:BIGINT) <- [26]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-27]
|
||||
binding parameter (2:BIGINT) <- [27]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-28]
|
||||
binding parameter (2:BIGINT) <- [28]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-29]
|
||||
binding parameter (2:BIGINT) <- [29]
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.WidgetSequence */insert into widget_sequence (name,id) values (?,?)
|
||||
binding parameter (1:VARCHAR) <- [sequence-30]
|
||||
binding parameter (2:BIGINT) <- [30]
|
||||
entityInsertCount = 30
|
||||
prepareStatementCount = 4
|
||||
(with SEQUENCE, the id is known before the row is written, so Hibernate can defer and batch the inserts -- expect prepareStatementCount well below entityInsertCount)
|
||||
41
docs/output/merge-vs-refresh.txt
Normal file
41
docs/output/merge-vs-refresh.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
Hibernate: create global temporary table HTE_book(rn_ integer not null, id bigint, version bigint, author varchar(255), title varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create global temporary table HTE_widget_sequence(rn_ integer not null, id bigint, name varchar(255), primary key (rn_)) transactional
|
||||
Hibernate: create table book (id bigint not null, author varchar(255), title varchar(255), version bigint not null, primary key (id))
|
||||
Hibernate: create table widget_identity (id bigint generated by default as identity, name varchar(255), primary key (id))
|
||||
Hibernate: create table widget_sequence (id bigint not null, name varchar(255), primary key (id))
|
||||
Hibernate: create sequence book_seq start with 1 increment by 50
|
||||
Hibernate: create sequence widget_seq start with 1 increment by 25
|
||||
Hibernate: select next value for book_seq
|
||||
Hibernate: /* insert for com.ankurm.hibernatedemo.model.Book */insert into book (author,title,version,id) values (?,?,?,?)
|
||||
binding parameter (1:VARCHAR) <- [Robert C. Martin]
|
||||
binding parameter (2:VARCHAR) <- [Clean Code]
|
||||
binding parameter (3:BIGINT) <- [0]
|
||||
binding parameter (4:BIGINT) <- [1]
|
||||
SEED: inserted Book{id=1, title=Clean Code, author=Robert C. Martin, version=0}
|
||||
--- Step 1: load the row, then close the session (entity is now detached) ---
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
detached instance in hand: Book{id=1, title=Clean Code, author=Robert C. Martin, version=0}
|
||||
--- Step 2: a second, independent session edits the same row and commits ---
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
Hibernate: /* update for com.ankurm.hibernatedemo.model.Book */update book set author=?,title=?,version=? where id=? and version=?
|
||||
binding parameter (1:VARCHAR) <- [Robert C. Martin]
|
||||
binding parameter (2:VARCHAR) <- [Clean Code (2nd Edition)]
|
||||
binding parameter (3:BIGINT) <- [1]
|
||||
binding parameter (4:BIGINT) <- [1]
|
||||
binding parameter (5:BIGINT) <- [0]
|
||||
second session committed: Book{id=1, title=Clean Code (2nd Edition), author=Robert C. Martin, version=1} -- version column has now advanced in the database
|
||||
--- Step 3: mutate the ORIGINAL detached instance (still holding the OLD version) and merge() it ---
|
||||
detached instance before merge (note the version and title are both stale): Book{id=1, title=Clean Code, author=Robert C. Martin (Uncle Bob), version=0}
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
merge() threw jakarta.persistence.OptimisticLockException: Row was already updated or deleted by another transaction for entity [com.ankurm.hibernatedemo.model.Book with id '1']
|
||||
the title change from Step 2 survives untouched -- merge() refused to apply a write built on a stale version
|
||||
--- Step 4: refresh() on a MANAGED entity with an unflushed local edit ---
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
before refresh(): Book{id=1, title=Clean Code (2nd Edition), author=SOMEONE ELSE ENTIRELY (never flushed), version=1}
|
||||
Hibernate: select b1_0.id,b1_0.author,b1_0.title,b1_0.version from book b1_0 where b1_0.id=?
|
||||
binding parameter (1:BIGINT) <- [1]
|
||||
after refresh(): Book{id=1, title=Clean Code (2nd Edition), author=Robert C. Martin, version=1} -- the local edit is gone, no exception was thrown
|
||||
Reference in New Issue
Block a user