# hibernate-demo Companion repository for a batch of twenty-six ankurm.com chapters on Hibernate 7: the persistence-context APIs (`get()` vs `getReference()`, `merge()` vs `refresh()`, batch inserts), mapping styles (annotations vs. XML, JPA persistence annotations, natural ids, immutable entities), a set of deeper operational topics (stored procedures, in-memory test databases, mocking JNDI datasources, proxies and lazy initialization, association mappings, date/time mapping, and named queries), the query layer (HQL, the Criteria API with a real generated metamodel, and bootstrapping `EntityManagerFactory` outside Spring entirely), second-level cache configuration with Ehcache 3 (entity caching, query caching, bulk-mutation cache invalidation, and JCache provider setup), HikariCP connection pooling (Spring Boot's default pool, raw non-Spring bootstrap, pool exhaustion, and leak detection), Jakarta Bean Validation's CDI integration (constraint validator dependency injection, with and without a running CDI container), aggregate functions (empty-result-set behavior, `select new` records, and HQL window functions), sorting (`@OrderBy`, `@SortNatural`/`@SortComparator`, dynamic-sort injection, and null precedence via `jakarta.persistence.criteria.Nulls`), pagination (`setFirstResult`/`setMaxResults`, `ScrollableResults`, when a `join fetch` actually falls back to in-memory pagination, and keyset pagination), interceptors (implementing `Interceptor` directly now that every method is a default method, session-scoped vs. globally-registered interceptors, and bulk updates bypassing them), and Hibernate Search 8 (full-text and keyword fields, fuzzy matching, `@IndexedEmbedded`, and `MassIndexer`). Every claim in those chapters 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. ## Versions | Component | Version | Notes | |---|---|---| | Spring Boot | `4.1.1` | parent POM | | Hibernate ORM | `7.4.5.Final` | what Boot 4.1.1 manages — see [`docs/00-versions.md`](docs/00-versions.md) for why this is *not* the same patch Boot 4.1.0 resolves | | Spring Framework | `7.0.9` | pulled in transitively by Boot 4.1.1 | | Jakarta Persistence | `3.2.0` | `jakarta.persistence-api` | | Java | `25` (Temurin 25.0.4.1 LTS) | | | H2 | `2.4.240` | in-memory, `DB_CLOSE_DELAY=-1` | | HSQLDB | `2.7.3` | in-memory | | Apache Derby | `10.16.1.1` | in-memory; needs `hibernate-community-dialects` (see below) | | simple-jndi | `0.25.0` | `com.github.h-thurow:simple-jndi`, test scope — **not** the abandoned `simple-jndi:simple-jndi` groupId | | JUnit Jupiter | `6.0.3` | via `spring-boot-starter-test` | | HikariCP | `7.0.2` | pulled in transitively by `spring-boot-starter-data-jpa`; no explicit dependency needed for the Spring-managed chapters | | `hibernate-hikaricp` | `7.4.5.Final` | added test-scope for chapter 19's raw, non-Spring `StandardServiceRegistry` bootstrap only | | Hibernate Validator | `9.1.3.Final` | what Boot 4.1.1's own BOM manages — confirmed against `spring-boot-dependencies-4.1.1.pom` | | `hibernate-validator-cdi` | `9.1.3.Final` | chapter 20 only; not in Boot's BOM, pinned to match | | `weld-se-core` | `6.0.4.Final` | chapter 20 only — resolves `jakarta.enterprise.cdi-api:4.1.0` transitively, verified with `mvn dependency:tree` before use | | `org.glassfish.expressly` | `6.0.0` | chapter 20 only — the EL implementation matching the `jakarta.el-api:6.0.1` Weld pulls in | | `hibernate-search-mapper-orm` | `8.4.0.Final` | chapter 25 — verified against `maven-metadata.xml` as the current GA line, not the `7.3.2.Final` this repo's own earlier post claimed; depends on `hibernate-core:7.4.0.Final`, compatible with this repo's `7.4.5.Final` pin | | `hibernate-search-backend-lucene` | `8.4.0.Final` | chapter 25 — the embedded, local-filesystem search backend; pulls in `lucene-core:9.12.3` transitively | **This repo was bumped this session from Boot `4.1.0` / Hibernate `7.4.1.Final` to Boot `4.1.1` / Hibernate `7.4.5.Final`.** If you see either of those older numbers anywhere outside `docs/00-versions.md`'s own explanation of the bump, that's stale and should be corrected — the live `pom.xml` and every chapter added or touched this session already reflect `4.1.1` / `7.4.5.Final`. See [`docs/00-versions.md`](docs/00-versions.md) for how these were verified (against `maven-metadata.xml`, not Maven Central's search index) and the trap worth knowing about if you bump the Spring Boot version again: two different Boot 4.1.x patch releases can resolve two different Hibernate patch releases from the same "4.1" line. ## 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). `mvn test` runs the full suite — **209 tests as of the last commit, 2 known errors** (see "Known issues" below; both predate this batch) — and takes a couple of minutes, since it boots Hibernate against H2, HSQLDB and Derby repeatedly, spins up a real embedded Tomcat for the OSIV chapter, boots a standalone Weld SE CDI container for the validation chapter, boots a Lucene-backed Hibernate Search index for every single test in the suite (chapter 25's `@Indexed` entity lives in the normally-scanned package, so its backend starts once per JVM alongside everything else), and runs a 5,500-iteration timing comparison for the named-query chapter. Three configuration decisions in this repo are easy to trip over if you're extending it rather than just reading it: - **`hibernate.cache.use_second_level_cache: false`** is pinned explicitly in [`application.yml`](src/main/resources/application.yml), on purpose. `hibernate-jcache` is on this project's classpath (chapter 06's natural-id L2 measurements need it), and Hibernate 7.4.5 turns the second-level cache **on by itself**, for every context in the repo, the moment it finds a `RegionFactory` via the service loader — with nothing configured anywhere. Before this was pinned, that silently broke an unrelated test (see [chapter 09](docs/09-testing-in-memory-databases.md#hibernate-jcache-on-the-classpath-turns-on-l2-for-everyone-whether-you-asked-or-not) for the verbatim failure and the `JCacheOnClasspathAutoEnablesL2Test` that pins it down). If you add a JCache/Ehcache/Infinispan dependency to a Hibernate project for one narrow purpose, assume it just turned L2 caching on globally unless you pin this back down yourself. - **`hibernate-community-dialects` is a required runtime dependency**, not an optional add-on, because `org.hibernate.dialect.DerbyDialect` no longer exists in `hibernate-core` as of Hibernate 6.2+. Derby's dialect moved to a different package, `org.hibernate.community.dialect.DerbyDialect`, in this separate artifact — see [chapter 09](docs/09-testing-in-memory-databases.md#headline-correction-derby-has-no-dialect-in-hibernate-7-without-an-extra-dependency) for both failure messages this produces if the dependency or the dialect setting is missing. - **`hibernate.search.backend.directory.root: target/lucene-indexes`** is pinned explicitly because chapter 25's `@Indexed` entity lives in this repo's normally-scanned package tree, so Hibernate Search bootstraps for **every** `@SpringBootTest` in the whole suite, not only chapter 25's own tests — confirmed the hard way, by watching an already-passing, unrelated test fail until this was configured. Adding an `@Indexed` entity to a Spring Boot app means configuring a search backend for the whole application, whether or not most of it ever searches anything. See [chapter 25](docs/25-hibernate-search.md#two-configuration-traps-that-break-the-whole-application-not-just-this-chapter). ## Three databases, and why H2, HSQLDB and Derby are all exercised side by side (chapter 09 in particular boots all three against the identical entity mapping), specifically to find where their dialect emulation actually diverges rather than repeating folklore about it — the reserved-word survey, the CHAR-padding behavior, and the Derby dialect relocation above are all things that only showed up by running all three, not by reading about one of them. **Testcontainers is discussed but not runnable in the sandbox this repo was built in** — there is no Docker binary and no `/var/run/docker.sock` available, so nothing Testcontainers-based was attempted or claimed to have been run. The in-memory engines cover fast, unit-level ORM testing; a real production database via Testcontainers is a separate, slower integration stage this repo doesn't attempt to substitute for. ## Chapter 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) | 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` | | [04 — Annotations vs. XML mappings](docs/04-annotations-vs-xml.md) | `hbm.xml` still works (WARN, not broken), `orm.xml` can define a whole entity, XML always wins on conflict, and the third XML dialect (`mapping.xml`) neither older article mentions | | [05 — JPA persistence annotations](docs/05-jpa-persistence-annotations.md) | `@Temporal`'s formal deprecation, the ORDINAL enum reorder failure reproduced, `@JdbcTypeCode(JSON)`'s hidden Jackson dependency, the surrogate-id `HashSet` trap, mixed access-type gotchas, and what's actually new in Jakarta Persistence 3.2 | | [06 — Natural IDs](docs/06-natural-ids.md) | `bySimpleNaturalId`, L1 vs L2 cache behavior measured with real query counts, the "L2 populates on INSERT, not first lookup" surprise, enforced natural-id immutability, and composite natural ids | | [07 — Immutable entities](docs/07-immutable-entities.md) | `@Immutable`'s actual mechanism (dirty-check exclusion, not a write guard), what it does and doesn't block, and a measured flush-cost comparison against `setReadOnly()` | | [08 — Stored procedures](docs/08-stored-procedures.md) | IN/OUT/INOUT against a real HSQLDB procedure, a genuine HSQLDB-driver result-set incompatibility, the parameter name-vs-position trap, and why procedure calls don't auto-flush | | [09 — Testing with in-memory databases](docs/09-testing-in-memory-databases.md) | Derby's relocated dialect, the `hibernate-jcache` classpath-pollution trap, the real (and not-Derby-exclusive) cross-database divergences, and schema/test isolation strategies | | [10 — Mocking JNDI datasources](docs/10-mocking-jndi-datasources.md) | Why `SimpleNamingContextBuilder` is gone, getting `simple-jndi` 0.25.0 actually working, the failure modes verbatim, and whether mock JNDI is still the right answer in 2026 | | [11 — Proxies and lazy initialization](docs/11-proxies-and-lazy-initialization.md) | What a `HibernateProxy` actually is, both `LazyInitializationException` message templates, `fetchgraph` vs `loadgraph` demonstrated, and what OSIV actually masks | | [12 — Association mappings](docs/12-association-mappings.md) | N+1 counted exactly, `MultipleBagFetchException` and its fixes, the cartesian-product trap, the `@OneToOne` lazy trap, and cascade/orphanRemoval's real (and not-so-real) failure modes | | [13 — Date and time mapping](docs/13-date-and-time-mapping.md) | Every basic temporal type round-tripped, the six `TimeZoneStorageType` constants measured under a JVM zone change, nanosecond rounding vs. truncation across databases, and `hibernate.jdbc.time_zone`'s raw-storage effect | | [14 — Named queries](docs/14-named-queries.md) | Startup validation's two different failure modes, Hibernate's extra `@NamedQuery` attributes, `orm.xml` named queries, and a measured (negative) answer to "are named queries faster?" | | [15 — HQL queries](docs/15-hql-queries.md) | The column-name-vs-field-name pitfall, `JOIN` vs `JOIN FETCH`, aggregates/pagination, bulk `UPDATE`/`DELETE` bypassing the persistence context, and what each flush mode actually suppresses (including why `GenerationType.IDENTITY` defeats an INSERT-based flush-mode test) | | [16 — Criteria API](docs/16-criteria-queries.md) | The real generated static metamodel (`Employee_`/`Department_`) vs. string paths, `root.join()` vs `root.fetch()`, aggregation and subqueries, `CriteriaUpdate`/`CriteriaDelete`, and when Criteria earns its ceremony over HQL | | [17 — Bootstrapping EntityManager](docs/17-entitymanager-bootstrap.md) | XML vs. Jakarta Persistence 3.2's `PersistenceConfiguration`, proof that a reused persistence-unit name does not trigger an XML lookup, the corrected `jakarta.persistence.PersistenceException` type, and a measured factory-vs-EntityManager creation cost | | [18 — Ehcache 3 L2 cache configuration](docs/18-ehcache-l2-configuration.md) | The `jakarta` classifier re-verified as a JAXB choice, not a JCache namespace switch; query-cache-without-entity-cache measured at zero SQL statements (not N+1); bulk HQL *and* native SQL updates both measured as NOT leaving the cache stale, and why; and the missing-timestamps-region "failure" that's actually just a WARN by default | | [19 — HikariCP connection pooling](docs/19-hikaricp-connection-pooling.md) | Confirming Spring Boot 4.1.1's default `DataSource` really is Hikari, a raw non-Spring `StandardServiceRegistry` wired to `hibernate-hikaricp`, pool exhaustion's exact `SQLTransientConnectionException`, and the undocumented 2000ms floor below which `leakDetectionThreshold` is silently disabled | | [20 — Hibernate Validator CDI integration](docs/20-hibernate-validator-cdi.md) | `@Inject` inside a `ConstraintValidator` measured both ways: a plain NullPointerException (wrapped in `ValidationException`) with no CDI container, and genuine, working injection through a real Weld SE container wired via `hibernate-validator-cdi`'s `ValidationExtension` | | [21 — Aggregate functions](docs/21-aggregate-functions.md) | Empty-result-set aggregates return `NULL`/`0`, never `NoResultException`; `select new` with a Java record; the Criteria API equivalent; and HQL window functions (`row_number()` etc.), present since Hibernate 6.2, not new in 7 | | [22 — Sorting](docs/22-sorting.md) | `@OrderBy` naming the property not the column, `@SortNatural`/`@SortComparator` on element collections, the dynamic-sort injection risk and its whitelist fix, Criteria `Order` across a join, null precedence via `jakarta.persistence.criteria.Nulls`, and case-insensitive sorting | | [23 — Pagination](docs/23-pagination.md) | `setFirstResult`/`setMaxResults` translating to the dialect's real syntax, `ScrollableResults`, exactly when a `join fetch` does and doesn't fall back to in-memory pagination (and the real `HHH90003004` warning code, not `HHH000104`), keyset pagination, and the total-count-query pattern | | [24 — Interceptors](docs/24-interceptors.md) | Implementing `Interceptor` directly now that every method is `default` (and where `EmptyInterceptor` actually went), the state-array-mutation contract, session-scoped vs. globally-registered interceptors, and bulk updates bypassing interceptor callbacks entirely | | [25 — Hibernate Search](docs/25-hibernate-search.md) | The real current GA version (`8.4.0.Final`, not `7.3.2.Final`), two bootstrap traps that break the whole application, full-text/keyword/generic field types, fuzzy matching, `@IndexedEmbedded`, and rebuilding the index with `MassIndexer` | ## Every surprising claim from chapters 01–03, mapped to a test Each row is one `./mvnw -Dtest=ClassName test` away from reproducing itself. | 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` | For chapters 04–17, the test classes are linked directly from each chapter rather than duplicated here — every source file, test class, and captured output file a chapter discusses is a relative link in that chapter. ```bash ./mvnw -Dtest=GetVsGetReferenceTest test ./mvnw -Dtest=MergeRefreshTest,OptimisticLockTest test ./mvnw -Dtest=IdentityBatchTest,SequenceBatchTest test ./mvnw -Dtest=AllocationSizeSweepTest test ./mvnw -Dtest=BatchSizeSweepTest test ./mvnw -Dtest=HqlQueryTest test ./mvnw -Dtest=CriteriaQueryTest test ./mvnw -Dtest=EntityManagerBootstrapTest test ./mvnw -Dtest='com.ankurm.hibernatedemo.cache.*Test' test ./mvnw -Dtest='com.ankurm.hibernatedemo.hikari.*Test' test ./mvnw -Dtest='com.ankurm.hibernatedemo.validation.*Test' test ./mvnw -Dtest=AggregateFunctionsTest test ./mvnw -Dtest=SortingTest test ./mvnw -Dtest=PaginationTest test ./mvnw -Dtest=InterceptorTest test ./mvnw -Dtest=HibernateSearchTest test ./mvnw test # the whole suite, 209 tests as of the last commit — see "Known issues" below ``` ### Known issues **`OrmXmlNamedQueryTest` (chapter 14) currently fails, and it predates chapters 15–17.** Verified by stashing every change made for chapters 15–17 and re-running the suite against the unmodified tree: the same two errors reproduce identically (`No query named 'XmlQueryEmployee.findBySalaryAboveXml'` and `No parameter named ':min' in query with named parameters []`), so this is not a regression from the HQL/Criteria/bootstrap work. It contradicts chapter 14's own claim that `orm.xml` named queries are "auto-discovered with no `persistence.xml`" under Spring Boot's default JPA autoconfiguration — that claim needs re-verification against the current Hibernate/Spring Boot versions (this repo was bumped mid-batch from Boot 4.1.0/Hibernate 7.4.1.Final to Boot 4.1.1/Hibernate 7.4.5.Final, which is one plausible place the behavior changed) before it's trusted again. Flagged here rather than fixed silently, since chapter 14 and its post (4877) are outside this batch's scope. ## CommandLineRunner scenarios The original narrative scenarios (chapters 01–03 only) 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 ``` Chapters 04–17 don't have `CommandLineRunner` profiles — they're covered entirely by JUnit tests and `javap`/`unzip` probes, captured the same way (see below). ## Regenerating captured output ```bash ./scripts/run-all.sh ``` Regenerates the `CommandLineRunner`-scenario files in `docs/output/` (chapters 01–03 only). `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. Every other transcript in `docs/output/` (chapters 04–17, and the test-suite transcripts for 01–03) was captured directly from `./mvnw -Dtest=... test`, `javap`, or `unzip -l` piped through the same cleanup approach, not regenerated by a single script — see each chapter for the exact command that produced its output. ## Output index `docs/output/` holds 101 raw, unedited transcripts, grouped here by topic prefix. | Prefix | Files | What it captures | |---|---|---| | `get-vs-*` | 2 | Chapter 01: the `CommandLineRunner` scenario and the `GetVsGetReferenceTest` run | | `merge-vs-refresh*` | 2 | Chapter 02: the `CommandLineRunner` scenario and the `MergeRefreshTest`/`OptimisticLockTest` run | | `insert-*` | 2 | Chapter 03: the identity vs. sequence `CommandLineRunner` scenarios | | `allocation-and-batch-size-sweeps.txt` | 1 | Chapter 03: the allocationSize and batch_size sweep test run | | `mappingstyle-*` | 1 | Chapter 04: the full `hbm.xml`/`orm.xml`/`mapping.xml` test run | | `persistenceannotations-*` | 1 | Chapter 05: the full persistence-annotations test run | | `naturalid-*` | 1 | Chapter 06: the full natural-id L1/L2/mutability/composite test run | | `immutable-*` | 2 | Chapter 07: the `@Immutable` behavior run, and the `javap` capture of the annotation itself | | `procedure-*` | 4 | Chapter 08: happy-path and failure-mode runs, the HSQLDB driver quirk reproduction, and the `javap` API-surface capture | | `testdb-*` | 9 | Chapter 09: dialect resolution, DDL generation, the reserved-word survey, cross-database behavior, schema isolation, startup timing, the Derby dialect failure, and the jcache classpath-pollution failure | | `jndi-*` | 6 | Chapter 10: the `SimpleNamingContextBuilder` removal proof, the simple-jndi jar listing, Boot's JNDI autoconfigure `javap` capture, Hibernate's own JNDI datasource `javap` capture, and both the filtered and unfiltered full test runs | | `proxy-*` | 7 | Chapter 11: proxy identity and `LazyInitializationException` (filtered and raw), entity-graph fetch behavior (filtered and raw), OSIV masking (filtered and raw), and the `javap` capture of proxy-related settings | | `association-*` | 4 | Chapter 12: N+1 counting, the multiple-bag-fetch/cartesian-product runs, the `@OneToOne` lazy trap, and cascade/orphanRemoval | | `datetime-*` | 7 | Chapter 13: basic temporal round trips, the `@Temporal` deprecation warning, both `@TimeZoneStorage` JVM-zone runs, nanosecond rounding on both H2 and HSQLDB, and `hibernate.jdbc.time_zone` | | `namedquery-*` | 4 | Chapter 14: startup validation, execution/projections, `orm.xml` named queries, and the pre-parsing performance comparison | | `hql-*` | 3 | Chapter 15: select/join pitfalls, aggregation/paging/bulk operations, and flush-mode behavior | | `criteria-*` | 3 | Chapter 16: predicates and the static metamodel, aggregation/subqueries, and bulk update/delete | | `bootstrap-persistenceconfiguration.txt` | 1 | Chapter 17: XML and programmatic bootstrap, the persistence-unit name-collision proof, the unconfigured-name exception, and the factory-creation timing | | `18-*` | 7 | Chapter 18: the JCache-namespace re-verification, entity L2 caching, query-cache-without-entity-cache, bulk HQL and native SQL update cache behavior, and both missing-timestamps-region runs (default and strict) | | `19-*` | 4 | Chapter 19: confirming Spring Boot's default `DataSource` is Hikari, the raw non-Spring bootstrap, pool exhaustion, and both leak-detection runs (the sub-2000ms silent-disable trap and a real leak caught at the floor) | | `20-*` | 2 | Chapter 20: `@Inject` failing with a wrapped NullPointerException under plain Bean Validation, and working correctly inside a running Weld SE container | | `21-*` | 4 | Chapter 21: empty-result-set aggregates, `select new` record construction under GROUP BY/HAVING, the Criteria API `avg()` equivalent, and the `row_number()` window function | | `22-*` | 6 | Chapter 22: `@OrderBy` property-vs-column resolution, `@SortNatural`/`@SortComparator`, the dynamic-sort injection guard, Criteria `Order` across a join, null precedence, and case-insensitive sorting | | `23-*` | 6 | Chapter 23: `LIMIT`/`OFFSET` translation, `ScrollableResults`, both join-fetch-pagination scenarios (root-ordered vs. collection-ordered), keyset pagination, and the total-count-query pattern | | `24-*` | 4 | Chapter 24: session-scoped interceptor state mutation, interceptor scoping, global registration via `hibernate.session_factory.interceptor`, and bulk updates bypassing interceptor callbacks | | `25-*` | 5 | Chapter 25: fuzzy full-text matching, exact keyword-field matching, sortable generic fields, `@IndexedEmbedded` association search, and `MassIndexer` index rebuilding | ## 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 │ └── check_links.py walk docs/*.md and verify every relative link resolves on disk ├── src/main/java/com/ankurm/hibernatedemo/ │ ├── HibernateDemoApplication.java │ ├── model/ Book, Note, Widget* entities (chapters 01–03) │ ├── scenario/ one CommandLineRunner per profile (chapters 01–03) │ ├── mappingstyle/ chapter 04 entities │ ├── persistenceannotations/ chapter 05 entities │ ├── naturalid/ chapter 06 entities │ ├── immutable/ chapter 07 entities │ ├── procedure/ chapter 08 entities │ ├── testdb/ chapter 09 entity │ ├── proxy/ chapter 11 entities │ ├── association/ chapter 12 entities │ ├── datetime/ chapter 13 entities │ ├── namedquery/ chapter 14 entities │ ├── query/ chapters 15–16 entities (Employee, Department — @Entity(name="QueryDept")) │ ├── bootstrap/ chapter 17 entity (BootstrapUser) │ ├── cache/ chapter 18 entities (CacheProduct, UncachedProduct) │ ├── hikari/ chapter 19 entity (PoolProbe) │ ├── validation/ chapter 20 classes (InventoryPolicy, PositiveInventory, │ │ PositiveInventoryValidator, StockLevel) -- not JPA entities │ ├── aggregate/ chapter 21 entities (Product, CategorySummary record) │ ├── sorting/ chapter 22 entities (Playlist, Song, LengthThenAlphaComparator, │ │ SongSortField) │ ├── pagination/ chapter 23 entities (Article, Comment) │ ├── interceptor/ chapter 24 entity + interceptor (Task, UppercasingInterceptor) │ └── search/ chapter 25 entities (Movie, Director) -- @Indexed, on the │ normally-scanned package tree; see README "Quickstart" above ├── src/test/java/com/ankurm/ │ ├── brokenprobe/ a deliberately broken entity kept outside the scanned package (chapter 14) │ └── hibernatedemo/ │ ├── GetVsGetReferenceTest.java, MergeRefreshTest.java, OptimisticLockTest.java, │ │ IdentityBatchTest.java, SequenceBatchTest.java, AllocationSizeSweepTest.java, │ │ BatchSizeSweepTest.java, ImmutableEntityTest.java, ImmutableBulkUpdateAllowedTest.java, │ │ ImmutableFlushCostTest.java (chapters 01–03, 07) │ ├── mappingstyle/, persistenceannotations/, naturalid/, procedure/, testdb/, │ │ proxy/, association/, datetime/, namedquery/, jndi/ (chapters 04–06, 08–14) │ ├── query/ HqlQueryTest.java, CriteriaQueryTest.java (chapters 15–16) │ ├── bootstrap/ EntityManagerBootstrapTest.java, plain JUnit, no Spring (chapter 17) │ ├── cache/ EntityL2CacheTest, QueryCacheWithoutEntityCacheTest, │ │ BulkUpdateBypassesCacheTest, MissingUpdateTimestampsRegionTest, │ │ CacheApiNamespaceTest -- all raw StandardServiceRegistry, no Spring (chapter 18) │ ├── hikari/ SpringAutoConfiguredHikariTest (Spring), HikariRawBootstrapTest (raw), │ │ HikariLeakDetectionTest, HikariPoolExhaustionTest (chapter 19) │ ├── validation/ PlainValidationNoCdiTest, CdiValidationTest (Weld SE) (chapter 20) │ ├── aggregate/ AggregateFunctionsTest (chapter 21) │ ├── sorting/ SortingTest (chapter 22) │ ├── pagination/ PaginationTest (chapter 23) │ ├── interceptor/ InterceptorTest (chapter 24) │ └── search/ HibernateSearchTest (chapter 25) └── docs/ ├── 00-versions.md .. 25-hibernate-search.md └── output/*.txt captured, unedited console transcripts (101 files) ``` ## License MIT — see [LICENSE](LICENSE).