# Flushing, the First-Level Cache and Transactions — Hibernate / JPA

Source: https://www.skillbyai.com/en/hibernate-jpa/p-flush

> Explain when Hibernate sends SQL to the database and how transactions bound the persistence context.

## When does SQL actually run?

Hibernate does not execute SQL immediately for every change. It queues inserts, updates and deletes and sends them at **flush** time. With the default **`FlushModeType.AUTO`**, a flush happens **before transaction commit** and **before queries** whose results could be affected by pending changes, so queries see your own changes. You can flush explicitly with `em.flush()`, for example to trigger constraint violations early; the transaction can still roll back afterwards. In Spring, the persistence context is usually **scoped to the transaction**: it opens when a `@Transactional` method starts and closes when it ends, after which loaded entities are **detached**. All work in one transaction shares one **first-level cache**, so loading thousands of entities in one long transaction keeps them all in memory with their snapshots; for batch jobs, periodically **flush and clear** to keep memory bounded. Reads should also run in transactions, often with `@Transactional(readOnly = true)`, which lets Hibernate skip dirty-checking snapshots and lets some databases optimise.

## Batch processing with periodic flush and clear

Keeps the persistence context small while importing many rows.

```java
@Transactional
public void importProducts(List<ProductCsvRow> rows) {
    int i = 0;
    for (ProductCsvRow row : rows) {
        em.persist(new Product(row.sku(), row.title(), row.price()));
        if (++i % 50 == 0) {          // match hibernate.jdbc.batch_size
            em.flush();               // send queued INSERTs as a JDBC batch
            em.clear();               // detach everything: free memory and snapshots
        }
    }
}

@Transactional(readOnly = true)
public List<Product> cheapProducts(BigDecimal max) {
    return em.createQuery("select p from Product p where p.price <= :max", Product.class)
             .setParameter("max", max)
             .getResultList();         // no dirty-check snapshots needed
}
```

## A flush is not a commit

`em.flush()` sends SQL to the database inside the current transaction; the changes are still invisible to other transactions and can be rolled back. Commit is what makes them permanent.

**Quiz:** With the default flush mode, when does Hibernate flush pending changes?

- [x] Before commit and before queries that may be affected by the pending changes
- [ ] Only when the application shuts down
- [ ] After every setter call
- [ ] Never, unless flush() is called

*Answer:* Before commit and before queries that may be affected by the pending changes. FlushModeType.AUTO flushes before commit and before relevant queries.
