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:
@@ -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);
|
||||
}
|
||||
}
|
||||
+56
@@ -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; }
|
||||
}
|
||||
Reference in New Issue
Block a user