What is Flush Mode in JPA and how does it affect persistence context synchronization?
In Java Persistence API (JPA), Flush Mode dictates when changes made to managed entities within the persistence context are synchronized with the underlying database. This mechanism is crucial for maintaining data consistency and controlling the performance of database interactions.
What is Flush Mode?
Flush Mode defines the strategy for pushing pending changes (insertions, updates, deletions) from the persistence context to the database. These changes are typically held in memory within the persistence context until a flush operation occurs. JPA provides two primary Flush Modes through the javax.persistence.FlushModeType enumeration: AUTO and COMMIT.
FlushModeType.AUTO
This is the default flush mode. In AUTO mode, the persistence provider automatically flushes pending changes to the database at the following times:
- Before a query is executed that might return entities affected by pending changes.
- Before a transaction is committed.
- When
EntityManager.flush()is explicitly called.
The primary goal of AUTO mode is to ensure that queries executed within the same transaction always see the most up-to-date state of the data, including changes not yet committed to the database. This guarantees data consistency from the application's perspective.
FlushModeType.COMMIT
In COMMIT mode, the persistence provider delays flushing pending changes to the database until the transaction is committed. The only other time a flush might occur is when EntityManager.flush() is explicitly invoked.
- Before a transaction is committed.
- When
EntityManager.flush()is explicitly called.
COMMIT mode does not automatically flush before queries. This can lead to performance benefits by reducing database writes, especially in transactions with many entity modifications, but it introduces a trade-off in terms of data visibility for queries.
How it Affects Persistence Context Synchronization
The chosen Flush Mode directly impacts how synchronized the in-memory state of the persistence context is with the actual database, particularly concerning data visibility for subsequent queries within the same transaction.
AUTOMode (High Synchronization): Ensures that any changes made to managed entities are flushed to the database before subsequent queries are executed. This means queries will always reflect the current state of the persistence context, preventing 'stale reads' within the same transaction. The persistence context is aggressively synchronized with the database's transactional view, even if not yet committed.COMMITMode (Delayed Synchronization): Defers flushing until transaction commit (or explicitflush()). This means that queries executed after modifications but before a commit might not see those modifications in the database. The persistence context's in-memory state is ahead of the database's transactional state until the flush occurs. This can lead to improved performance by batching writes but requires careful consideration to avoid unexpected query results.- Explicit
flush(): CallingEntityManager.flush()manually forces the synchronization of the persistence context with the database, regardless of the configured Flush Mode. This is useful when you need to ensure changes are visible to subsequent native SQL queries or database-level triggers/constraints before the transaction commits.
When to Use Each Mode
AUTO mode is generally recommended and is the default because it provides strong consistency guarantees, ensuring that queries see the most current data. It's suitable for most applications where data integrity and consistent reads are paramount.
COMMIT mode is typically used in specific scenarios where performance optimization is critical and the application can tolerate (or explicitly manage) the potential for queries not seeing uncommitted changes. This might include batch operations, processes with high write loads where no queries depend on immediate visibility of those writes, or when interacting with stored procedures that require intermediate database state.
Example: Setting Flush Mode
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FlushModeType;
import javax.persistence.Persistence;
public class FlushModeExample {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
try {
// Set flush mode to COMMIT for this EntityManager
em.setFlushMode(FlushModeType.COMMIT);
System.out.println("Flush Mode set to: " + em.getFlushMode());
// Perform some entity operations
MyEntity entity = new MyEntity("Test Name");
em.persist(entity);
System.out.println("Entity persisted, but not yet flushed to DB (in COMMIT mode)");
// A query here would NOT see 'entity' if FlushModeType.COMMIT is active
// List<MyEntity> result = em.createQuery("SELECT e FROM MyEntity e", MyEntity.class).getResultList();
// System.out.println("Query result count: " + result.size());
// To force a flush even in COMMIT mode:
// em.flush();
// System.out.println("Explicit flush performed.");
em.getTransaction().commit();
System.out.println("Transaction committed, changes flushed to DB.");
} catch (Exception e) {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
e.printStackTrace();
} finally {
em.close();
emf.close();
}
}
}