Hibernate Performance
First-Level Cache in Hibernate
Understand Hibernate's automatic Session-level cache, how repeated entity lookups avoid duplicate SQL, and how it differs from the second-level cache.
What Is Caching in Hibernate?
Caching keeps recently used data available so an application can avoid repeating expensive database work. Hibernate provides a first-level cache for every session and can optionally use a shared second-level cache.
Built into every Session, scoped to one persistence context, and enabled by default.
Optional, shared across sessions, and configured through a compatible cache provider.
What Is the First-Level Cache?
The first-level cache is the persistence context associated with a Hibernate Session. When Hibernate loads an entity, the session keeps the managed instance in memory. A later lookup for the same entity identity in the same session can return that managed instance without another SQL select.
- It is enabled by default.
- It belongs to one session and is not shared across sessions.
- It stores managed entity instances and their state.
- It is cleared when the session is cleared or closed.
- It also supports identity consistency: one session normally returns one object instance for a given entity identity.
First-Level Cache Example
The second lookup occurs in the same session and uses the same entity type and identifier.
try (Session session = sessionFactory.openSession()) {
Employee first = session.find(Employee.class, 101L);
Employee second = session.find(Employee.class, 101L);
System.out.println(first == second); // true
}With SQL logging enabled, Hibernate normally shows one select for the first lookup. The second lookup is resolved from the current session's persistence context.
First-Level Cache Lifecycle
- A session is opened.
- Hibernate loads or persists an entity.
- The entity becomes managed in the session's persistence context.
- Repeated access to the same identity can use the managed instance.
- Hibernate performs dirty checking during flush.
- The session is cleared or closed and its first-level cache is discarded.
try (Session session = sessionFactory.openSession()) {
Employee employee = session.find(Employee.class, 101L);
employee.setName("Updated name");
// Hibernate detects the change during flush or commit.
session.flush();
}Managing the Persistence Context
| Method | Effect |
|---|---|
session.clear() | Detaches all managed entities and empties the session cache. |
session.evict(entity) | Detaches one entity from the persistence context. |
session.refresh(entity) | Reloads the entity state from the database and discards its in-memory changes. |
session.detach(entity) | JPA-style operation that detaches one entity where supported. |
session.close() | Closes the session and discards its first-level cache. |
Employee employee = session.find(Employee.class, 101L); session.evict(employee); // The entity is no longer managed by this session. Employee reloaded = session.find(Employee.class, 101L);
Large Loops and Batch Processing
The first-level cache grows as entities become managed. In a large import, periodically flush changes and clear the session to control memory usage.
for (int index = 1; index <= 1_000; index++) {
session.persist(new Employee("Employee " + index));
if (index % 50 == 0) {
session.flush();
session.clear();
}
}After clear(), previously managed objects become detached. Do not assume that references still point to managed entities; reattach or reload objects when the next operation requires management.
First-Level versus Second-Level Cache
| Characteristic | First-level cache | Second-level cache |
|---|---|---|
| Scope | One Session | Shared across sessions in a SessionFactory |
| Default | Enabled automatically | Optional and requires configuration |
| Lifetime | Session or persistence-context lifetime | Provider and SessionFactory lifetime |
| Purpose | Identity consistency and avoiding repeated work in one unit of work | Reducing database reads across sessions |
The first-level cache is not a replacement for a second-level cache. Use a second-level cache only after measuring access patterns, invalidation requirements, memory usage, and data staleness tolerance.
Entity Lookup versus Query Results
The persistence context primarily guarantees identity for managed entities. A query may still execute SQL each time unless query caching or application-level result reuse is configured. Do not assume that every repeated HQL query is automatically served without SQL.
Employee first = session.find(Employee.class, 101L);
List<Employee> results = session
.createQuery(
"from Employee where id = :id",
Employee.class)
.setParameter("id", 101L)
.getResultList();Best Practices
- Keep sessions short and aligned with a unit of work.
- Use one session consistently when repeated entity access should benefit from the first-level cache.
- Flush and clear during large batch operations.
- Do not share a session between threads.
- Remember that clearing the session detaches managed entities.
- Use SQL logging and measurements before optimizing cache behavior.
- Be careful with stale data when adding a second-level cache.
