def b = Book.get(1)
...
b.refresh()
refresh
Purpose
Refreshes a domain classes state from the database
Examples
Refreshing under a pessimistic write lock:
Book.withTransaction {
def book = Book.get(1)
book.refresh(lock: true)
if (book.title == 'Draft') {
book.title = 'Ready for review'
book.save(failOnError: true)
}
}
Description
Equivalent to the Hibernate refresh method.
Re-reads the state of the given instance from the underlying database. It is inadvisable to use this to implement long-running sessions that span many business tasks. However this method is useful in certain special circumstances. For example
-
where a database trigger alters the object state upon insert or update
-
after executing direct SQL (e.g. a bulk update) in the same Session
-
after inserting a Blob or Clob
Pass lock: true to reload the instance’s database state and version under a pessimistic WRITE lock. entity.refresh(lock: true) acquires the row lock before it reads, so the state and version it loads are the ones the lock protects; it never checks the already-loaded version, and it returns the same instance.
To request a different lock, pass a jakarta.persistence.LockModeType (or its name) instead of true. For example entity.refresh(lock: LockModeType.PESSIMISTIC_READ) reloads the instance under a pessimistic READ lock. lock: true is equivalent to lock: LockModeType.PESSIMISTIC_WRITE; lock: false and lock: LockModeType.NONE request no lock and behave like entity.refresh(). A value that is neither a boolean, a LockModeType, nor the name of one throws IllegalArgumentException. The semantics of each lock mode are those of the JPA LockModeType of the same name.
The lock modes behave as follows on Hibernate 5 and Hibernate 7:
| Lock mode | Database lock | Effect |
|---|---|---|
|
Exclusive row lock, typically |
The default, so |
|
Shared row lock, |
Other transactions cannot update the row but may take the same shared lock. Databases without shared row locks take an exclusive lock instead. |
|
Exclusive row lock |
As |
|
None |
The version is re-read when the transaction commits, and the commit fails if another transaction changed it. |
|
None |
The version is incremented when the transaction commits, so other transactions holding the previous version fail. |
|
None |
A plain refresh, exactly like |
An active transaction is required whenever a lock is requested. Use withTransaction as above or a GORM @Transactional method, and keep the lock and the work it protects in the same transaction. The lock is held until that transaction commits or rolls back. Calling refresh with a lock but without an active transaction throws jakarta.persistence.TransactionRequiredException. The instance must be attached to the current session: refreshing a detached instance under a lock throws IllegalArgumentException on every supported implementation, so re-attach it with attach first.
refresh(lock: true) discards unflushed changes to the entity. Configured refresh cascades can also discard unflushed changes to associated entities; this is not a refresh of the whole object graph. Call it before making decisions based on the entity’s state or applying mutations. It does not preserve or merge pending edits.
|
The lock applies to the refreshed entity’s own row. Associated entities reloaded through a refresh cascade are not guaranteed to be locked.
The database and transaction isolation level determine which state is visible to the locked refresh. refresh(lock: true) does not override isolation rules or guarantee that every concurrent attempt succeeds. Deadlocks, lock timeouts, and transaction serialization failures remain possible.
The same operation is available on a named connection and under a bound tenant:
Book.secondary.withTransaction {
def book = Book.secondary.get(1)
book.secondary.refresh(lock: true) (1)
}
| 1 | Reloads and locks the row on the secondary connection. The transaction and the instance must both belong to that connection; a transaction on the default datasource does not satisfy the requirement. |
Refreshing under a lock is supported by GORM for Hibernate 5 and Hibernate 7. Other datastores throw UnsupportedOperationException rather than silently falling back to a plain refresh.
entity.refresh() is unchanged. entity.refresh([:]), entity.refresh(lock: false) and entity.refresh(lock: LockModeType.NONE) behave exactly like entity.refresh(): a plain refresh with no lock and no transaction requirement. To lock by identifier and reload an already-managed instance under the lock, use DomainClass.lock(id, refresh: true). There is no entity.lock(refresh: true): that call throws IllegalArgumentException pointing to the two supported forms.