The first-level cache is Hibernate’s mandatory, built-in caching mechanism that automatically stores persistent entities within the scope of a single Hibernate Session
.
🔹 Key characteristics:
✅ Enabled by default — you cannot disable it.
✅ Scoped to a single Session
— each session has its own first-level cache.
✅ Stores entities you load, save, or update during the session.
✅ Avoids repeated database queries: if you fetch the same entity multiple times in one session, Hibernate serves it from the cache instead of querying the database again.
✅ Automatically synchronizes changes: Hibernate tracks any modifications to cached entities and updates the database on flush()
or commit()
.
🔹 Example showing first-level cache in action:
Session session = sessionFactory.openSession();
session.beginTransaction();
User user1 = session.get(User.class, 1L); // SQL query sent to DB
User user2 = session.get(User.class, 1L); // NO SQL sent — served from first-level cache!
System.out.println(user1 == user2); // true — same object instance
session.getTransaction().commit();
session.close();
🔹 When is the cache cleared?
- When you call
session.clear()
→ explicitly evicts all cached entities. - When you call
session.evict(entity)
→ evicts a specific entity. - When the session is closed → cache is destroyed.
🔹 Why it matters:
- Improves performance by minimizing redundant database queries.
- Ensures consistency within a session: all reads of the same entity return the same in-memory object.
✅ Key takeaway:
The first-level cache is session-scoped, automatic, and essential for Hibernate’s performance and correctness — it ensures efficient data access and consistent object identity within a session.
What happens if data changed in database and i get them from cache ? what if i always need fresh data ?
🔹 If you load an entity into the session (which caches it) and then someone else changes the data directly in the database (e.g., another app or SQL query), your current session’s first-level cache won’t see the change:
- Any further calls like
session.get()
for the same entity ID will return the cached entity, not a fresh copy from the database. - This can lead to stale data in your app.
🔹 Example of stale data:
Session session = sessionFactory.openSession();
session.beginTransaction();
User user = session.get(User.class, 1L); // fetches from DB
// Someone else updates user with ID=1 directly in the database...
User sameUser = session.get(User.class, 1L); // served from first-level cache — still shows old data!
session.getTransaction().commit();
session.close();
✅ How to get fresh data inside the same session?
1️⃣ Use session.refresh(entity)
→ forces Hibernate to re-fetch the entity from the database and update the cached object.
session.refresh(user); // overwrites the cached user with fresh data from DB
2️⃣ Alternatively, you could evict the entity from the session’s cache:
session.evict(user); // remove from first-level cache
User freshUser = session.get(User.class, 1L); // triggers new SQL query to DB
✅ What if you always need fresh data?
- First-level cache cannot be disabled; it’s mandatory in Hibernate.
- But you can:
- Open a new session for every operation requiring fresh data.
- Call
session.refresh()
explicitly for entities where consistency is critical. - Use stateless sessions (
StatelessSession
) for read-only operations needing fully fresh data — but you lose features like automatic dirty checking, cascading, etc.
Session session = sessionFactory.openSession();
session.beginTransaction();
while (true) {
// Suppose we want to read updated data every 5 seconds
User user = session.get(User.class, 1L);
// Force Hibernate to bypass first-level cache and reload fresh data from DB
session.refresh(user);
System.out.println("Latest username: " + user.getUsername());
try {
Thread.sleep(5000); // wait 5 seconds
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
session.getTransaction().commit();
session.close();
🔹 Important note:
- First-level cache only affects your own session — if your app needs real-time data accuracy across users, you must combine database-level features (e.g., optimistic locking) or use second-level cache invalidation strategies.
✅ Key takeaway:
First-level cache improves performance by avoiding redundant queries, but it can lead to stale reads if external changes occur. Use session.refresh()
or evict/reload entities when you absolutely need fresh data.