Foundation · Build
Entity Modeling
@Entity, @Id, @Column, and @Version map a class to a table; identifier strategy and schema ownership are decisions, not defaults to accept.
Primary source: Jakarta Persistence API: Entity Mapping
Lesson 3 of 17
1d streakThe Architectural Question
ddl-auto=update asks Hibernate to alter a live schema from mapping differences, with dialect-dependent and incomplete behavior. It cannot express deliberate renames or data migrations, so production drift remains uncontrolled.
What this guide proves
- @Entity
- @Id
- GenerationType
Entity identity guidelines: Primary keys and sequence strategies
JPA entities represent relational database rows with persistent identity. Choosing between IDENTITY (database auto-increment) and SEQUENCE has profound impacts on Hibernate batch inserting performance.
| Strategy | DB Mechanism | Batch Inserts Supported? | When to Choose |
|---|---|---|---|
| GenerationType.IDENTITY | PostgreSQL BIGSERIAL / MySQL AUTO_INCREMENT | NO (Hibernate must execute INSERT immediately to obtain generated ID) | Simple single-row writes, legacy schemas. |
| GenerationType.SEQUENCE | Database Sequence with allocationSize (e.g. 50) | YES (IDs fetched in blocks of 50, allowing JDBC batch inserts) | High-throughput production applications (Recommended). |
| UUIDv7 (Ordered UUID) | Time-ordered 128-bit UUID generated in application | YES (No DB roundtrip needed for ID generation) | Distributed systems, microservices, decoupled client generation. |
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
@SequenceGenerator(name = "product_seq", sequenceName = "product_id_seq", allocationSize = 50)
private Long id;
@Column(nullable = false, unique = true, length = 64)
private String sku;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal price;
@Version // Optimistic locking guard!
private Long version;
protected Product() {} // Required by Hibernate proxies
public Product(String sku, BigDecimal price) {
this.sku = Objects.requireNonNull(sku);
this.price = Objects.requireNonNull(price);
}
}The equals and hashCode contract for entities
Architectural Boundary · Do Not Confuse
Database Generated @Id in equals/hashCode
Natural Business Key in equals/hashCode
Before an entity is saved, its generated @Id is null. If equals/hashCode rely on id, adding a new entity to a HashSet before persisting it and searching for it afterward will fail because the entity's hashCode mutates upon insert! Always base equals/hashCode on a unique natural business key (e.g. sku or email).
Break it deliberately: The mutating hashCode HashSet corruption bug
Using generated IDs in hashCode() causes entities to disappear inside Java Collections after saving:
Set<Product> set = new HashSet<>();
Product product = new Product("SKU-99", new BigDecimal("49.99")); // id is null
set.add(product); // Placed into bucket based on hashCode(null) = 0!
productRepository.save(product); // DB generates id = 42L!
// Now product.hashCode() evaluates to 42, but the object is still in bucket 0!
boolean found = set.contains(product); // FALSE! Object is lost in the Set!The Golden Rule
Check your understanding
Active Recall Checkpoint
Why can GenerationType.IDENTITY prevent Hibernate JDBC batch inserts for those inserts?
Hint: When must the INSERT statement execute?
Show answer & explanation
With identity generation, the database assigns the id during the INSERT, so Hibernate may need to execute that INSERT immediately before it can know the identifier. Sequence-based strategies can batch differently; verify the selected strategy and driver behavior.
Active Recall Checkpoint
Why does using generated @Id in hashCode() break HashSet collections?
Hint: Does hashCode change after persisting?
Show answer & explanation
The entity has a null ID before saving. After saving, the ID is assigned, changing the hashCode and preventing the HashSet from finding the entity in its original bucket.
Runtime Architecture & Flow
Structure under inspection
Entity Modeling
Part 1
@Entity
Part 2
@Id
Part 3
GenerationType
The connectors show containment and responsibility—not time or call order.
Synthesis & Production Verification
Production Acceptance Criteria
- Map a Product with UUID id, unique SKU column, and @Version field
- Predict the DDL difference between GenerationType.UUID and IDENTITY
- Run the app against a Flyway-managed schema in validate mode
Key Takeaway
Design entities with stable identity strategies, private fields, and class-based hashCode to avoid hash collision bugs across lifecycle states.
Architectural & Technical Review
Core Interview Questions
Q01What are the trade-offs of UUIDv4 versus BIGSERIAL primary keys?Show answer
BIGSERIAL provides sequential B-Tree index locality but leaks business volume; UUIDv4 allows client generation but causes B-Tree index fragmentation.
Q02Why should entity equals() and hashCode() avoid relying on database generated IDs?Show answer
An unpersisted entity has a null ID. Once persisted, its ID changes, corrupting HashSet lookups.
Q03Why should Lombok @Data be avoided on JPA entities?Show answer
@Data generates equals/hashCode methods across all fields, including lazy relationships, triggering accidental N+1 queries.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Using Java records for mutable JPA entities — Java records are immutable and cannot support the runtime proxying and dirty checking mechanisms required by Hibernate.
- Pitfall: Exposing public setters on all entity fields — Indiscriminate setters bypass domain invariants; prefer meaningful business methods for state changes.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources