Hibernate Introduction
What Hibernate is, the object-relational impedance mismatch it solves, and a minimal entity and save example.
The object-relational impedance mismatch
Java programs think in objects — classes, fields, references between objects. Relational databases think in tables, rows, and foreign keys. Translating between the two by hand means writing repetitive code: building SQL strings, binding parameters, reading a ResultSet column by column, and manually constructing objects from the result — for every single entity in the application.
Hibernate is an ORM (Object-Relational Mapper) — and specifically, the most widely used implementation of the JPA (Jakarta Persistence API) specification. JPA defines the standard annotations and interfaces (@Entity, @Id, EntityManager); Hibernate is the engine underneath that actually implements them, translating object operations into SQL and result sets back into objects.
Your Java code Hibernate Database
┌─────────────────┐ ┌──────────────────────┐ ┌──────────────┐
│ new User(...) │ ---> │ generates INSERT SQL │ ---> │ users table │
│ entityManager │ │ manages the session │ │ │
│ .persist(user) │ │ tracks entity state │ │ │
└─────────────────┘ └──────────────────────┘ └──────────────┘
A minimal entity
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
private String fullName;
protected User() {
// JPA requires a no-arg constructor to instantiate entities via reflection
}
public User(String email, String fullName) {
this.email = email;
this.fullName = fullName;
}
public Long getId() { return id; }
public String getEmail() { return email; }
public String getFullName() { return fullName; }
}
@Entity marks the class as something Hibernate manages and maps to a table; @Table names that table explicitly (Hibernate would otherwise default to the class name); @Id marks the primary key field; @GeneratedValue delegates key generation to the database (an auto-increment column, in this case).
A minimal save example
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
User user = new User("ada@example.com", "Ada Lovelace");
entityManager.persist(user);
transaction.commit();
System.out.println(user.getId()); // populated after commit — the database-generated key
Behind that one persist() call, Hibernate:
- Generates the appropriate
INSERT INTO users (email, full_name) VALUES (?, ?)for whichever database is configured (MySQL, PostgreSQL, etc. — the same Java code works against any of them). - Binds
user's field values as query parameters. - Executes it and reads back the generated primary key, setting it onto
user.idvia reflection.
In a Spring Boot application you virtually never call EntityManager methods directly like this — Spring Data JPA (its own tutorial track) wraps this exact machinery behind repository interfaces. Understanding what Hibernate is doing underneath is what makes Spring Data JPA's behavior (and its gotchas, like lazy loading) make sense.
Common mistakes
- Forgetting the no-argument constructor — Hibernate instantiates entities via reflection and needs one, even if it's
protectedand never called directly by your own code. - Thinking of Hibernate as "just SQL generation" — its real job is also tracking each entity's state (transient, persistent, detached — covered in a later page) and translating changes made to Java objects back into SQL automatically.
- Manually writing repetitive INSERT/SELECT/UPDATE code instead of letting Hibernate (or Spring Data JPA on top of it) generate it — reintroducing exactly the boilerplate Hibernate exists to remove.