Anatomy of a JPA Entity
A JPA entity is a plain old Java object annotated with @Entity that Hibernate maps to a database table. At minimum it needs a no-argument constructor, a field annotated with @Id to identify the primary key, and getters/setters (or, increasingly, a record-friendly pattern for read-heavy DTOs, though mutable entities remain the norm). By default, the class name maps to a table of the same name and each field maps to a column of the same name, both of which can be overridden with @Table(name = "...") and @Column(name = "...").
Cricket analogy: An @Entity class is like a player's official ICC profile card — the class fields (name, id, battingAverage) map directly to the columns of that record, just as a player's stats map to fields tracked in the official database.
Identifiers and Generation Strategies
Every entity needs a primary key, marked with @Id, and typically paired with @GeneratedValue to let the database or Hibernate assign values automatically. GenerationType.IDENTITY delegates ID generation to the database's auto-increment column (common with MySQL and PostgreSQL's serial/identity columns), GenerationType.SEQUENCE uses a database sequence object and allows Hibernate to batch inserts more efficiently, and GenerationType.UUID (added in Hibernate 6 / Jakarta Persistence 3.1) generates a random UUID client-side, which is useful for distributed systems where IDs must be assignable before an insert.
Cricket analogy: GenerationType.IDENTITY is like a stadium assigning ticket numbers sequentially as fans pass through the turnstile, one at a time, whereas SEQUENCE is like the ticketing system pre-reserving a batch of numbers for a whole stand at once.
Mapping Fields and Constraints
Beyond the identifier, @Column lets you control the mapped column's name, nullability (nullable = false), length (length = 500 for a VARCHAR), and uniqueness (unique = true), while @Enumerated(EnumType.STRING) is the recommended way to persist Java enums as readable text rather than fragile ordinal integers. Validation annotations from Jakarta Bean Validation, like @NotNull, @Size, and @Email, are commonly layered on top of JPA mappings so that constraints are enforced both at the application layer (via @Valid on controller input) and, where mirrored with @Column(nullable = false), at the database layer too.
Cricket analogy: @Enumerated(EnumType.STRING) is like storing a dismissal type as 'LBW' or 'CAUGHT' in the scorecard instead of a cryptic numeric code 3 or 5 that only makes sense if you memorize the scoring manual.
@Entity
@Table(name = "books")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_seq")
@SequenceGenerator(name = "book_seq", sequenceName = "book_seq", allocationSize = 50)
private Long id;
@Column(name = "title", nullable = false, length = 250)
private String title;
@Column(unique = true, length = 13)
private String isbn;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private BookStatus status;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
protected Book() {
// required no-arg constructor for JPA
}
public Book(String title, String isbn, Author author) {
this.title = title;
this.isbn = isbn;
this.author = author;
this.status = BookStatus.DRAFT;
}
// getters and setters omitted for brevity
}Modeling Relationships
JPA supports four relationship annotations — @OneToOne, @OneToMany, @ManyToOne, and @ManyToMany — each of which can be configured with a fetch strategy (FetchType.LAZY or FetchType.EAGER) and a cascade behavior (CascadeType.PERSIST, MERGE, REMOVE, etc.). The @ManyToOne side is almost always the owning side because it holds the foreign key column via @JoinColumn, while the inverse @OneToMany side uses mappedBy to point back at the owning field rather than creating a second, redundant foreign key or join table.
Cricket analogy: A @ManyToOne from Player to Team is like each player's scorecard entry pointing to a single team ID, while the team's @OneToMany squad list is just the inverse view assembled by querying which players point to that team.
Default to FetchType.LAZY for @ManyToOne and @OneToOne associations by explicitly setting fetch = FetchType.LAZY, since the JPA default for these is EAGER — the opposite of most collection associations. Eager loading on the wrong side is a common source of unexpected N+1 queries and oversized result sets.
Avoid @Data from Lombok on JPA entities that participate in bidirectional relationships — the generated equals/hashCode and toString can recurse infinitely across the association (Book references Author, Author's toString lists Books, which reference Author again) and trigger a StackOverflowError or accidentally load the entire object graph.
- @Entity classes need a no-arg constructor and an @Id field; table/column names default to the class/field name but can be overridden.
- @GeneratedValue supports IDENTITY, SEQUENCE, and UUID strategies, each with different performance and distributed-system trade-offs.
- @Column controls nullability, length, and uniqueness; @Enumerated(EnumType.STRING) is preferred over ordinal enum storage.
- The @ManyToOne side owns the foreign key via @JoinColumn; the @OneToMany side is the inverse, declared with mappedBy.
- Default @ManyToOne/@OneToOne to FetchType.LAZY explicitly, since JPA's built-in default for those is EAGER.
- Cascade types like PERSIST and REMOVE control whether operations on the parent propagate to associated child entities.
- Avoid Lombok's @Data on entities with bidirectional associations to prevent recursive equals/hashCode/toString bugs.
Practice what you learned
1. What is the minimum requirement for a class to be a valid JPA entity?
2. Which generation strategy uses a database sequence object and allows Hibernate to batch inserts efficiently?
3. Why is @Enumerated(EnumType.STRING) generally preferred over the default ordinal mapping?
4. Which side of a @OneToMany / @ManyToOne relationship owns the foreign key?
5. What is JPA's default fetch type for a @ManyToOne association?
Was this page helpful?
You May Also Like
Spring Data JPA Basics
Learn how Spring Data JPA layers convention-driven repositories and auto-configuration on top of JPA and Hibernate to remove persistence boilerplate.
Repositories in Spring Data
Understand the Repository interface hierarchy in Spring Data — from Repository and CrudRepository to PagingAndSortingRepository and JpaRepository.
Querying with JPA
Go beyond basic repository methods to master JPQL, the Criteria API, pagination, and performance pitfalls like the N+1 select problem.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics