Skip to main content

Hibernate 7 In-Memory Databases: H2, HSQLDB and Derby for Testing

A working H2 setup for Hibernate 7 unit tests, then a real side-by-side comparison against HSQLDB and Apache Derby: the Derby dialect class that no longer exists in hibernate-core, the H2 MODE= myth settled with identical DDL, a reserved-word column that fails only on H2, and a second-level-cache trap that silently changes what your test suite is actually testing.

Mastering Hibernate 7 @Immutable Entities: Performance, Safety, and Best Practices

@Immutable is a pure marker annotation that skips Hibernate's dirty-checking snapshot -- mutate a field and flush, and nothing happens: no UPDATE, no exception, just silent data loss of the in-memory change. This rewrite runs every claim against Hibernate 7.4.5 -- entity mutation, bulk HQL (rejected at translation time by default, a correction neither prior version made), collection mutation, @Immutable+@Version, and a real flush-cost measurement -- and compares the annotation head-to-head with Session.setReadOnly().

Hibernate 7 Natural IDs: What L1 and L2 Caching Actually Give You, Measured

Verified against Hibernate 7.4.5: a repeat bySimpleNaturalId lookup in the same session costs zero extra queries with no cache at all, but the bigger correction is that @NaturalIdCache populates its L2 region at insert time, not on first lookup -- so the textbook miss-then-hit story only applies to rows Hibernate didn't insert itself.

Mastering Hibernate 7 JPA Persistence Annotations: The Traps That Pass Code Review

Not another annotation catalogue: this rewrite reproduces the four JPA annotation traps that pass code review and fail in production in Hibernate 7.4.5 -- silent enum ordinal corruption, a JSON column that needs a dependency the starter doesn't provide, an access-strategy mismatch, and the equals/hashCode HashSet bug -- plus what Jakarta Persistence 3.2 actually added.

Hibernate Annotations vs. XML Mappings: Making the Right Choice in Hibernate 7

Hibernate 7.4.5 actually ships three XML mapping dialects, not two, and hbm.xml still boots unmodified with just a warning. This rewrite verifies which format wins on conflict, why xml-mapping-metadata-complete is an all-or-nothing kill switch, and what Hibernate's own mapping.xml dialect can do that JPA's orm.xml cannot.

find() vs getReference() in Hibernate 7: A Decision Matrix (and Why get()/load() Belong in the Same Conversation)

Read this code and predict whether it sends a SELECT to the database: @Transactional public void assignCategory(Long productId, Long categoryId) { Category category = em.getReference(Category.class, categoryId); Product product = em.find(Product.class, productId); product.setCategory(category); } If you said "two SELECTs" — one for each line — you would be half wrong. find() on Product does hit the database. getReference() on Category does not, unless categoryId is already in the first-level cache. The UPDATE to write category_id to the product row happens at flush; the category column only needs the ID, which the proxy already holds. That single avoided SELECT matters in bulk operations. It is also one of the most consistently misunderstood distinctions in Hibernate. This post is a decision guide: six scenarios, each with the right call and the reasoning.

@PrePersist and Friends: Five Lifecycle Callback Bugs You’ll Ship If You’re Not Careful

We had a callback that "audited every save" — except it silently skipped about half of them. The @PreUpdate on the AuditListener ran correctly for every web-layer save. It never ran for the nightly batch job. The batch used HQL bulk updates. Nobody remembered that bulk operations bypass the persistence context entirely, so lifecycle callbacks never fire for them. The audit log looked complete. It was missing six months of batch changes. That is bug four in this list. Here are all five, each one a real failure mode with the code that produces it and the fix. Technical Note: JPA vs. Hibernate Behavior It is important to distinguish that all annotations discussed in this guide (like @PrePersist, @PostUpdate, etc.) are defined by the Jakarta Persistence API (JPA) specification. Hibernate 7 serves as the implementation provider. While the API is standard, specific behaviors such as dirty checking algorithms, the exact timing of the flush, and session state transitions are governed by Hibernate-specific logic. The Problem: Fragmented Business Logic In many legacy applications, developers scatter logic like password encryption, audit logging, and data normalization across various controllers and services.

The Hibernate 7 Persistence Context: How Hibernate Tracks Your Entities (and Where It Gets Surprising)

Look at this Spring service. Eight lines, nothing exotic, no annotations missing as far as a junior reviewer can tell. Predict — before reading on — what happens when something calls markActive(42) against a real database. If you said "an UPDATE statement", you would be wrong. The actual mechanics are stranger and more interesting — and once you see them, a long list of mysterious behaviours suddenly make sense.