What Does the refresh() Method Do
The refresh() method in JPA (entityManager.refresh(entity)) forcefully reloads the state of a Managed entity directly from the relational database via an immediate SQL SELECT by...
The refresh() method in JPA (entityManager.refresh(entity)) forcefully reloads the state of a Managed entity directly from the relational database via an immediate SQL SELECT by primary key. It resynchronizes the entity’s in-memory Java fields with current database column values, irrevocably overwriting any unsaved local modifications and resetting the First-Level Cache dirty checking snapshot.
🟢 Junior Level
Core Purpose and Fundamental Behavior
By default, Hibernate relies on the First-Level Cache (L1): once an entity is managed in a session, subsequent find() invocations return the in-memory instance without querying the database. The refresh() method bypasses the L1 cache, forces a physical SQL SELECT against the database, and replaces the object’s current field values with fresh database state:
@Transactional
public void demonstrateRefresh() {
User user = entityManager.find(User.class, 1L); // user.getName() == "Ivan"
// 1. Mutate state locally in JVM memory
user.setName("Temporary Name");
// 2. Force database re-read
entityManager.refresh(user);
// Hibernate executes: SELECT id, name, email FROM users WHERE id = 1;
// 3. Local in-memory changes are completely overwritten!
System.out.println(user.getName()); // Prints "Ivan" from database!
// At transaction commit, ZERO SQL UPDATE statements are generated.
}
Key Rules of refresh()
- Requires
ManagedLifecycle State: Passing an entity in theDetachedorTransientstate immediately triggers:java.lang.IllegalArgumentException: Entity not managed. - Discards Unsaved In-Memory Mutations: All property changes made in Java before invoking
refresh()are permanently lost (unless explicitly synchronized viaflush()prior to refresh). - Resets Dirty Checking Snapshots: The baseline snapshot (
loadedState) in the L1 cache is updated with the fresh database values, marking the entity as clean.
REFRESH() EXECUTION LIFECYCLE
[ JVM Heap: User@0x1 ] [ Relational Database: users ]
name = "Changed in Memory" id = 1, name = "Ivan in DB"
│ │
│──── 1. entityManager.refresh(user) ──│
│ │
│<─── 2. SQL SELECT * WHERE id = 1 ────│
│ (Fetches committed DB row) │
▼ ▼
[ JVM Heap: User@0x1 ] [ Relational Database: users ]
name = "Ivan in DB" id = 1, name = "Ivan in DB"
(Snapshot updated; UPDATE canceled)
🟡 Middle Level
Production Use Cases for refresh()
- Database Triggers and Stored Procedures:
When relational database triggers (
BEFORE INSERT/BEFORE UPDATE) calculate column values (e.g., promotional codes, discount totals, status flags), Hibernate remains unaware of these backend computations.entityManager.persist(order); entityManager.flush(); // Sends physical INSERT; database trigger fires entityManager.refresh(order); // Reloads trigger-computed columns into memory - Retrieving Database Default Values (
DEFAULT): When table columns are initialized by the database engine (created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP), callingrefresh()fetches the server-generated timestamp. - Synchronizing After Direct Bulk Operations (
@Modifying): Bulk updates (UPDATE User u SET u.active = false) execute directly against the database engine, bypassing the activePersistenceContext. Entities already held in memory become stale. Callingrefresh()resynchronizes them with the database.
Pessimistic Locking with refresh()
The refresh() method allows developers to acquire pessimistic database locks dynamically on already loaded entities within a single SQL statement:
// Upgrades to an exclusive row-level lock: SELECT ... FOR UPDATE
entityManager.refresh(account, LockModeType.PESSIMISTIC_WRITE);
// Pessimistic lock with lock acquisition timeout
Map<String, Object> hints = Map.of("jakarta.persistence.lock.timeout", 3000); // 3 seconds timeout
entityManager.refresh(account, LockModeType.PESSIMISTIC_WRITE, hints);
Cascading Refresh (CascadeType.REFRESH)
By default, calling refresh(order) refreshes only the scalar columns of Order. Related child collections are not refreshed unless cascading is explicitly declared:
@Entity
public class Order {
@Id
private Long id;
@OneToMany(mappedBy = "order", cascade = CascadeType.REFRESH)
private List<OrderItem> items = new ArrayList<>();
}
When entityManager.refresh(order) is called, Hibernate automatically executes a cascading refresh() on each associated OrderItem element.
🔴 Senior Level
Internal Engine Mechanics: StatefulPersistenceContext Impact
Inside Hibernate (SessionImpl), refresh() is processed by DefaultRefreshEventListener:
- It validates the entity’s status in
entityEntries. If the entity is notStatus.MANAGED, it aborts with anIllegalArgumentException. - Any pending actions targeting the entity in the
ActionQueueare evaluated. - Hibernate constructs an SQL
SELECTcovering all mapped columns and executes it over JDBC. - Extracted column values are written into the target Java object instance (via direct field reflection or property setters).
- The Critical Snapshot Step: The
Object[] loadedStatearray insideEntityEntryis completely overwritten with the fresh column values. Consequently, subsequent dirty checking scans evaluate the entity as unchanged (not dirty). - Collection Handling (
PersistentCollection):- If an associated collection is uninitialized, it remains an uninitialized lazy proxy.
- If an associated collection is already loaded in memory, Hibernate clears its cached elements and re-initializes it from the database.
Modern Hibernate 6 Alternative: @Generated
In modern applications running on Hibernate 6+, manually calling flush() + refresh() to retrieve database-computed defaults or trigger values is considered an anti-pattern. Hibernate provides the @Generated annotation:
@Entity
public class Invoice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Hibernate 6 automatically populates this via SQL RETURNING / post-insert read
@Generated(event = EventType.INSERT)
@Column(name = "created_at", insertable = false, updatable = false)
private Instant createdAt;
@Generated(event = {EventType.INSERT, EventType.UPDATE})
@Column(name = "total_amount", insertable = false, updatable = false)
private BigDecimal totalAmount;
}
When using @Generated, Hibernate utilizes PostgreSQL’s INSERT ... RETURNING created_at, total_amount (or executes an immediate post-insert query), completely eliminating the need for manual flush() and refresh() calls.
4 Tricky Questions
1. What happens if you modify an entity field user.setEmail("new@mail.com"), call entityManager.refresh(user), and subsequently commit the transaction? Will an SQL UPDATE be issued?
Answer:
- Zero SQL
UPDATEstatements will be executed. - The
refresh()method executes an immediate SQLSELECT, reads the original email from the database, and overwrites the field in JVM heap memory. - Simultaneously, Hibernate updates the
loadedStatesnapshot inside thePersistenceContextto reflect the fresh database value. - During transaction commit, Dirty Checking compares the current in-memory field with the updated snapshot. Finding zero differences, Hibernate cancels update scheduling.
2. What happens if you call entityManager.refresh(user), but the underlying database row was deleted by a concurrent database transaction?
Answer:
- The
refresh()method executesSELECT * FROM users WHERE id = ?. - The database returns an empty
ResultSet(0 rows found). - The JPA specification mandates that Hibernate immediately throw:
jakarta.persistence.EntityNotFoundException: Entity no longer exists in the database. - The active transaction is automatically marked as
Rollback-Only.
3. We executed a Spring Data JPA query: @Modifying @Query("UPDATE User u SET u.active = false"). Why do already loaded entities still reflect active = true without calling refresh()?
Answer:
- The
@Modifyingannotation executes a direct bulk SQL/JPQLUPDATEagainst the database table, completely bypassing the First-Level Cache (PersistenceContext). - Entities already loaded into the current session have no awareness of the out-of-band database update and continue holding their initial in-memory state.
- To resynchronize, you must either invoke
entityManager.refresh(user)on each entity or configure@Modifying(clearAutomatically = true). TheclearAutomatically = truesetting clears the entire L1 cache, forcing subsequentfind()queries to re-read fresh records from the database.
4. Can invoking refresh() cause a NonUniqueObjectException if the session contains associated entities?
Answer:
- For the target entity being refreshed, identity collisions are impossible because Hibernate updates the existing Java instance in-place according to the Identity Map pattern.
- However, if a cascading
refresh(order)reloads a collection containing child entities that were previously detached and re-instantiated elsewhere in code outside the session, attempting to associate the duplicate child instance can trigger an identity collision in thePersistenceContext.
🎯 Interview Cheat Sheet
30-Second Elevator Pitch
“The
entityManager.refresh()method forcefully reloads aManagedentity’s state from the database via an immediate SQLSELECTby primary key. It discards unsaved in-memory property changes and updates the entity’s baseline snapshot (loadedState) in the First-Level Cache, preventing unwantedUPDATEstatements on commit. Key production use cases include capturing database trigger calculations, loading database defaults, resynchronizing after bulk@Modifyingqueries, and dynamically acquiring pessimistic locks viarefresh(entity, LockModeType.PESSIMISTIC_WRITE). In Hibernate 6+, using@Generatedis preferred over manualrefresh()for database-computed columns.”
Key Production Metrics & Health Checks
- SELECT Query Rate per Transaction: Excessive
refresh()calls indicate poor caching design or redundant reads of already managed data. - EntityNotFoundException Frequency: Spikes in this metric highlight concurrency issues where rows are deleted externally before transactional sync completes.
Red Flags to Avoid
- ❌ “I call
refresh()after everypersist()to verify the row was saved properly.” — A severe anti-pattern that doubles database queries without providing any real guarantees. - ❌ “You can call
refresh()on detached entities or REST DTOs.” —refresh()strictly requires entities in theManagedstate; it throwsIllegalArgumentExceptionotherwise. - ❌ “Calling
refresh()pushes in-memory changes to the database.” — Completely backwards:flush()pushes to the DB;refresh()reads from the DB and overwrites memory. - ❌ “Calling
refresh()commits the transaction.” —refresh()only executes a read query; the transaction remains open.
Related Topics
- What Are the Transient, Persistent, Detached, Removed States — Managed state
- What is the Difference Between persist() and merge() — Entity lifecycle transitions
- What is Dirty Checking in Hibernate — Snapshot updates
- How Does the Flush Mechanism Work in Hibernate — Database synchronization
- How to Implement Pessimistic Locking in JPA — Pessimistic lock modes