JPA and Hibernate Basics
What is ORM and why use it instead of plain JDBC?
ORM (Object-Relational Mapping) maps Java classes to tables and objects to rows, so you work with Product objects instead of writing SQL and reading ResultSet columns by hand.
- Removes boilerplate: no manual mapping, connection and statement handling.
- Gives you dirty checking, caching, lazy loading, cascading and optimistic locking for free.
- Database-independent queries (JPQL) and generated SQL per dialect.
- Cost: less control over SQL; you must understand what SQL it generates (N+1, extra updates).
What is the difference between JPA, Hibernate and Spring Data JPA?
| What it is | Example API | |
|---|---|---|
| JPA (Jakarta Persistence) | a specification: annotations and interfaces only | @Entity, EntityManager, JPQL |
| Hibernate | the most popular implementation of JPA, plus extra features | Session, @BatchSize, @DynamicUpdate |
| Spring Data JPA | a Spring abstraction on top of JPA that generates repositories | JpaRepository, derived queries, @Query |
Stack: your code → Spring Data JPA → JPA API → Hibernate → JDBC → database. Since Jakarta EE 9 the package is jakarta.persistence.*, not javax.persistence.*.
What is the persistence context? How do EntityManager and Session relate?
The persistence context is the set of managed entity instances for one unit of work (usually one transaction). It works as a first-level cache and identity map: one row = one Java object inside it, and Hibernate tracks changes to those objects.
EntityManager is the JPA interface to the persistence context; Hibernate's Session extends it. Get the native API with entityManager.unwrap(Session.class).
Explain the entity lifecycle states.
| State | Meaning | How you get there |
|---|---|---|
| Transient (new) | plain object, not known to Hibernate, no row | new Product() |
| Managed (persistent) | tracked by the persistence context; changes are flushed automatically | persist(), find(), query results, merge() result |
| Detached | has a row/id but the context is closed or cleared | transaction ends, detach(), clear(), serialization |
| Removed | scheduled for DELETE at flush | remove() on a managed entity |
What is the difference between persist() and merge()? What does Spring Data save() do?
persist(e): makes a new object managed; the same instance becomes managed. Returns void. Throws if given a detached entity.merge(e): copies the state of a detached (or new) object onto a managed instance and returns that managed copy; the argument stays detached.- Hibernate 6 deprecated
save(),update()andsaveOrUpdate(); usepersist()/merge(). - Spring Data
save(e)callspersistif the entity is new (id null or@Versionnull) elsemerge. Always use the returned object.
Product detached = new Product(1L, "Laptop"); // id set: not new
Product managed = productRepository.save(detached); // merge -> SELECT + copy
System.out.println(managed == detached); // falsefind() vs getReference() (get() vs load())?
find() / findById() | getReference() / getReferenceById() | |
|---|---|---|
| SQL | SELECT immediately (unless in the context) | no SQL; returns a lazy proxy |
| Row missing | returns null / empty Optional | EntityNotFoundException on first access |
| Use for | when you need the data | setting a foreign key: order.setUser(userRepo.getReferenceById(id)) |
Note: Hibernate's Session.load() is deprecated in Hibernate 6 in favour of getReference().
What are the rules for a JPA entity class?
- Annotated with
@Entityand has a primary key marked@Id(or@EmbeddedId). - Has a public or protected no-arg constructor (Hibernate instantiates it by reflection).
- Should not be
final, and its persistent methods should not be final, so Hibernate can create lazy proxies. - A top-level class; records cannot be entities (they are final and immutable) but are great as DTOs/projections.
@Entity
@Table(name = "products")
public class Product {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(nullable = false, length = 120)
private String name;
private BigDecimal price;
protected Product() {} // for Hibernate
public Product(String name, BigDecimal price) { this.name = name; this.price = price; }
}Explain the @GeneratedValue strategies.
| Strategy | How it works | Notes |
|---|---|---|
| IDENTITY | auto-increment column (MySQL) | INSERT must run immediately to get the id, so JDBC batch inserts are disabled |
| SEQUENCE | database sequence (PostgreSQL, Oracle) | best performance; allocationSize (default 50) lets Hibernate assign ids in memory |
| TABLE | a separate table simulates a sequence | slow (row locks); avoid |
| AUTO | Hibernate picks (SEQUENCE where supported, else a table) | be explicit in production |
| UUID | GenerationType.UUID (JPA 3.1 / Hibernate 6.2+) | good for distributed ids; larger index |
What does spring.jpa.hibernate.ddl-auto do? What should production use?
| Value | Effect |
|---|---|
| none | do nothing (Spring Boot default for non-embedded databases) |
| validate | check the schema matches the entities; fail at startup if not |
| update | add missing tables/columns; never drops; can leave a messy schema |
| create | drop and create the schema at startup |
| create-drop | create at startup, drop at shutdown (default for embedded H2) |
Tip: In production use validate or none and manage the schema with Flyway or Liquibase migrations.