Add Hibernate 7 batches 2-6, batch 7, and batch 8: mapping styles, JPA annotations, natural IDs, @Immutable, stored procedures, in-memory test databases, JNDI mocking, proxies, associations, temporal mapping, named queries, HQL, Criteria API, EntityManager bootstrapping, Ehcache 3 L2 cache configuration, HikariCP connection pooling, Hibernate Validator CDI integration, aggregate functions, sorting, pagination, interceptors, and Hibernate Search 8 (Hibernate 7.4.5.Final + Spring Boot 4.1.1 + JDK 25)

This commit is contained in:
2026-09-20 06:06:42 +00:00
committed by Claude
commit 8568c0ce6c
330 changed files with 23668 additions and 0 deletions
@@ -0,0 +1,44 @@
package com.ankurm.hibernatedemo.immutable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
/** Child row for {@link RateWithAuditTrail}'s @Immutable collection. */
@Entity
public class AuditTrail {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "audit_seq")
private Long id;
private String note;
@ManyToOne
@JoinColumn(name = "rate_with_audit_id")
private RateWithAuditTrail rateWithAuditTrail;
protected AuditTrail() {
// JPA
}
public AuditTrail(String note) {
this.note = note;
}
public Long getId() {
return id;
}
public String getNote() {
return note;
}
@Override
public String toString() {
return "AuditTrail{id=%s, note=%s}".formatted(id, note);
}
}
@@ -0,0 +1,58 @@
package com.ankurm.hibernatedemo.immutable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.math.BigDecimal;
import org.hibernate.annotations.Immutable;
/**
* The headline case for docs/07-immutable-entities.md: an {@code @Immutable} entity with a mutable
* Java setter. Nothing stops the field mutation in the JVM -- the point is what happens (or
* doesn't) when the mutated instance is flushed.
*/
@Entity
@Immutable
public class ExchangeRate {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "rate_seq")
private Long id;
private String pair;
@jakarta.persistence.Column(precision = 19, scale = 4)
private BigDecimal rate;
protected ExchangeRate() {
// JPA
}
public ExchangeRate(String pair, BigDecimal rate) {
this.pair = pair;
this.rate = rate;
}
public Long getId() {
return id;
}
public String getPair() {
return pair;
}
public BigDecimal getRate() {
return rate;
}
/** Ordinary setter -- @Immutable is a Hibernate-engine concept, not a Java one. */
public void setRate(BigDecimal rate) {
this.rate = rate;
}
@Override
public String toString() {
return "ExchangeRate{id=%s, pair=%s, rate=%s}".formatted(id, pair, rate);
}
}
@@ -0,0 +1,56 @@
package com.ankurm.hibernatedemo.immutable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import java.math.BigDecimal;
import org.hibernate.annotations.Immutable;
/**
* Same shape as {@link ExchangeRate} but carries a {@code @Version} column, to check whether
* Hibernate 7.4.5 rejects the {@code @Immutable} + {@code @Version} combination outright, and
* whether the version column ever increments if it doesn't.
*/
@Entity
@Immutable
public class ExchangeRateVersioned {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "rate_v_seq")
private Long id;
private String pair;
@jakarta.persistence.Column(precision = 19, scale = 4)
private BigDecimal rate;
@Version
private Long version;
protected ExchangeRateVersioned() {
// JPA
}
public ExchangeRateVersioned(String pair, BigDecimal rate) {
this.pair = pair;
this.rate = rate;
}
public Long getId() {
return id;
}
public BigDecimal getRate() {
return rate;
}
public void setRate(BigDecimal rate) {
this.rate = rate;
}
public Long getVersion() {
return version;
}
}
@@ -0,0 +1,47 @@
package com.ankurm.hibernatedemo.immutable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.math.BigDecimal;
/**
* A perfectly ordinary, non-{@code @Immutable} entity, used as the control group when
* comparing {@code @Immutable} against {@code Session.setDefaultReadOnly(true)} and
* {@code Session.setReadOnly(entity, true)} -- those are Session/query-scoped read-only knobs
* that apply to entities that were never annotated at all.
*/
@Entity
public class PlainRate {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "plain_rate_seq")
private Long id;
private String pair;
@jakarta.persistence.Column(precision = 19, scale = 4)
private BigDecimal rate;
protected PlainRate() {
// JPA
}
public PlainRate(String pair, BigDecimal rate) {
this.pair = pair;
this.rate = rate;
}
public Long getId() {
return id;
}
public BigDecimal getRate() {
return rate;
}
public void setRate(BigDecimal rate) {
this.rate = rate;
}
}
@@ -0,0 +1,51 @@
package com.ankurm.hibernatedemo.immutable;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.annotations.Immutable;
/**
* A MUTABLE parent entity (no {@code @Immutable} on the class) whose collection is marked
* {@code @Immutable}. This isolates the collection-level annotation's own behaviour, per the
* article's "Advanced Usage: Immutable Collections" section, from the entity-level one.
*/
@Entity
public class RateWithAuditTrail {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "rate_audit_seq")
private Long id;
private String pair;
@Immutable
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<AuditTrail> auditTrails = new ArrayList<>();
protected RateWithAuditTrail() {
// JPA
}
public RateWithAuditTrail(String pair) {
this.pair = pair;
}
public Long getId() {
return id;
}
public String getPair() {
return pair;
}
public List<AuditTrail> getAuditTrails() {
return auditTrails;
}
}
@@ -0,0 +1,90 @@
package com.ankurm.hibernatedemo.immutable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import org.hibernate.annotations.Immutable;
/**
* Same 12-column shape as {@link WideMutableRow}, but @Immutable, for the flush-cost
* comparison in docs/07-immutable-entities.md.
*/
@Entity
@Immutable
public class WideImmutableRow {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "wide_immutable_seq")
private Long id;
private String f1;
private String f2;
private String f3;
private String f4;
private String f5;
private String f6;
private String f7;
private String f8;
private String f9;
private String f10;
private String f11;
private String f12;
protected WideImmutableRow() {
// JPA
}
public WideImmutableRow(String seed) {
this.f1 = seed;
this.f2 = seed;
this.f3 = seed;
this.f4 = seed;
this.f5 = seed;
this.f6 = seed;
this.f7 = seed;
this.f8 = seed;
this.f9 = seed;
this.f10 = seed;
this.f11 = seed;
this.f12 = seed;
}
public Long getId() { return id; }
public String getF1() { return f1; }
public void setF1(String v) { this.f1 = v; }
public String getF2() { return f2; }
public void setF2(String v) { this.f2 = v; }
public String getF3() { return f3; }
public void setF3(String v) { this.f3 = v; }
public String getF4() { return f4; }
public void setF4(String v) { this.f4 = v; }
public String getF5() { return f5; }
public void setF5(String v) { this.f5 = v; }
public String getF6() { return f6; }
public void setF6(String v) { this.f6 = v; }
public String getF7() { return f7; }
public void setF7(String v) { this.f7 = v; }
public String getF8() { return f8; }
public void setF8(String v) { this.f8 = v; }
public String getF9() { return f9; }
public void setF9(String v) { this.f9 = v; }
public String getF10() { return f10; }
public void setF10(String v) { this.f10 = v; }
public String getF11() { return f11; }
public void setF11(String v) { this.f11 = v; }
public String getF12() { return f12; }
public void setF12(String v) { this.f12 = v; }
}
@@ -0,0 +1,90 @@
package com.ankurm.hibernatedemo.immutable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
/**
* A wide (12-column) MUTABLE entity, used only to give per-entity dirty checking something
* non-trivial to compare on a full flush -- with 1-2 fields the per-field snapshot comparison
* cost is too small to distinguish from measurement noise. See docs/07-immutable-entities.md,
* "@Version and dirty-check cost".
*/
@Entity
public class WideMutableRow {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "wide_mutable_seq")
private Long id;
private String f1;
private String f2;
private String f3;
private String f4;
private String f5;
private String f6;
private String f7;
private String f8;
private String f9;
private String f10;
private String f11;
private String f12;
protected WideMutableRow() {
// JPA
}
public WideMutableRow(String seed) {
this.f1 = seed;
this.f2 = seed;
this.f3 = seed;
this.f4 = seed;
this.f5 = seed;
this.f6 = seed;
this.f7 = seed;
this.f8 = seed;
this.f9 = seed;
this.f10 = seed;
this.f11 = seed;
this.f12 = seed;
}
public Long getId() { return id; }
public String getF1() { return f1; }
public void setF1(String v) { this.f1 = v; }
public String getF2() { return f2; }
public void setF2(String v) { this.f2 = v; }
public String getF3() { return f3; }
public void setF3(String v) { this.f3 = v; }
public String getF4() { return f4; }
public void setF4(String v) { this.f4 = v; }
public String getF5() { return f5; }
public void setF5(String v) { this.f5 = v; }
public String getF6() { return f6; }
public void setF6(String v) { this.f6 = v; }
public String getF7() { return f7; }
public void setF7(String v) { this.f7 = v; }
public String getF8() { return f8; }
public void setF8(String v) { this.f8 = v; }
public String getF9() { return f9; }
public void setF9(String v) { this.f9 = v; }
public String getF10() { return f10; }
public void setF10(String v) { this.f10 = v; }
public String getF11() { return f11; }
public void setF11(String v) { this.f11 = v; }
public String getF12() { return f12; }
public void setF12(String v) { this.f12 = v; }
}