Flushing is the process of synchronizing the state of the persistence context with the underlying database.
The EntityManager
and the Hibernate Session
expose a set of methods, through which the application developer can change the persistent state of an entity.
The persistence context acts as a transactional write-behind cache, queuing any entity state change.
Like any write-behind cache, changes are first applied in-memory and synchronized with the database during the flush time.
The flush operation takes every entity state change and translates it to an INSERT
, UPDATE
or DELETE
statement.
Note
|
Because DML statements are grouped together, Hibernate can apply batching transparently. See the Batching chapter for more information. |
The flushing strategy is given by the flushMode
of the current running Hibernate Session
.
Although Jakarta Persistence defines only two flushing strategies ({jpaJavadocUrlPrefix}FlushModeType.html#AUTO[AUTO
] and {jpaJavadocUrlPrefix}FlushModeType.html#COMMIT[COMMIT
]),
Hibernate has a much broader spectrum of flush types:
- ALWAYS
-
Flushes the
Session
before every query. - AUTO
-
This is the default mode, and it flushes the
Session
only if necessary. - COMMIT
-
The
Session
tries to delay the flush until the currentTransaction
is committed, although it might flush prematurely too. - MANUAL
-
The
Session
flushing is delegated to the application, which must callSession.flush()
explicitly in order to apply the persistence context changes.
By default, Hibernate uses the AUTO
flush mode which triggers a flush in the following circumstances:
-
prior to committing a
Transaction
-
prior to executing a JPQL/HQL query that overlaps with the queued entity actions
-
before executing any native SQL query that has no registered synchronization
In the following example, an entity is persisted, and then the transaction is committed.
link:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/AutoFlushTest.java[role=include]
link:extras/flushing-auto-flush-commit-example.sql[role=include]
Hibernate logs the message prior to inserting the entity because the flush only occurred during transaction commit.
Note
|
This is valid for the |
A flush may also be triggered when executing an entity query.
link:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/AutoFlushTest.java[role=include]
link:extras/flushing-auto-flush-jpql-example.sql[role=include]
The reason why the Advertisement
entity query didn’t trigger a flush is that there’s no overlapping between the Advertisement
and the Person
tables:
link:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/AutoFlushTest.java[role=include]
When querying for a Person
entity, the flush is triggered prior to executing the entity query.
link:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/AutoFlushTest.java[role=include]
link:extras/flushing-auto-flush-jpql-overlap-example.sql[role=include]
This time, the flush was triggered by a JPQL query because the pending entity persisting action overlaps with the query being executed.
When executing a native SQL query, a flush is always triggered when using the EntityManager
API.
EntityManager
link:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/AutoFlushTest.java[role=include]
If you bootstrap Hibernate natively, and not through Jakarta Persistence, by default,
the Session
API will trigger a flush automatically when executing a native query.
Session
link:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/HibernateAutoFlushTest.java[role=include]
To flush the Session
, the query must use a synchronization:
Session
synchronizationlink:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/AutoFlushTest.java[role=include]
Jakarta Persistence also defines a COMMIT flush mode, which is described as follows:
If
FlushModeType.COMMIT
is set, the effect of updates made to entities in the persistence context upon queries is unspecified.
When executing a JPQL query, the persistence context is only flushed when the current running transaction is committed.
COMMIT
flushing on JPQLlink:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/CommitFlushTest.java[role=include]
link:extras/flushing-commit-flush-jpql-example.sql[role=include]
Because the Jakarta Persistence doesn’t impose a strict rule on delaying flushing, when executing a native SQL query, the persistence context is going to be flushed.
COMMIT
flushing on native SQLlink:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/CommitFlushTest.java[role=include]
link:extras/flushing-commit-flush-sql-example.sql[role=include]
Note
|
The |
The ALWAYS
flush mode triggers a persistence context flush even when executing a native SQL query against the Session
API.
COMMIT
flushing on native SQLlink:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/AlwaysFlushTest.java[role=include]
link:extras/flushing-always-flush-sql-example.sql[role=include]
Both the EntityManager
and the Hibernate Session
define a flush()
method that, when called, triggers a manual flush.
Hibernate also provides a MANUAL
flush mode so the persistence context can only be flushed manually.
MANUAL
flushinglink:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/ManualFlushTest.java[role=include]
link:extras/flushing-manual-flush-example.sql[role=include]
The INSERT
statement was not executed because there was no manual flush()
call.
Note
|
The |
From a database perspective, a row state can be altered using either an INSERT
, an UPDATE
or a DELETE
statement.
Because entity state changes are automatically converted to SQL statements, it’s important to know which entity actions are associated with a given SQL statement.
INSERT
-
The
INSERT
statement is generated either by theEntityInsertAction
orEntityIdentityInsertAction
. These actions are scheduled by thepersist
operation, either explicitly or through cascading thePersistEvent
from a parent to a child entity. DELETE
-
The
DELETE
statement is generated by theEntityDeleteAction
orOrphanRemovalAction
. UPDATE
-
The
UPDATE
statement is generated byEntityUpdateAction
during flushing if the managed entity has been marked modified. The dirty checking mechanism is responsible for determining if a managed entity has been modified since it was first loaded.
Hibernate does not execute the SQL statements in the order of their associated entity state operations.
To visualize how this works, consider the following example:
link:../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/flush/FlushOrderTest.java[role=include]
link:extras/flushing-order-example.sql[role=include]
Even if we removed the first entity and then persist a new one, Hibernate is going to execute the DELETE
statement after the INSERT
.
Tip
|
The order in which SQL statements are executed is given by the |
The ActionQueue
executes all operations in the following order:
-
OrphanRemovalAction
-
EntityInsertAction
orEntityIdentityInsertAction
-
EntityUpdateAction
-
QueuedOperationCollectionAction
-
CollectionRemoveAction
-
CollectionUpdateAction
-
CollectionRecreateAction
-
EntityDeleteAction