Experimenting with JPA 's flush()..

.. and not being able to come to a concrete conclusion!

I took some notes from: 1, 2, 3, 4 and JSR 317 - JPA Specification. Lets see what people have to say about the EntityManager#flush()..

In some cases you want the SQL instructions to be executed immediately; generally when you need the result of some side effects, like an autogenerated key, or a database trigger.
What em.flush() does is to empty the internal SQL instructions cache, and execute it immediately to the database.

flush() sends SQL instructions to the database like INSERT, UPDATE etc. It will not send a COMMIT, so if you have an exception after a flush(), you can still have a complete rollback.

When you call EntityManager.flush(), queries for inserting/updating/deleting associated entities are executed in the database. Any constraint failures (column width, data types, foreign key) will be known at this time.

When you call EntityManager.persist(), it only makes the entity get managed by the EntityManager and adds it (entity instance) to the Persistence Context. An Explicit flush() will make the entity now residing in the Persistence Context to be moved to the database (using a SQL). Without flush(), this (moving of entity from Persistence Context to the database) will happen when the transaction to which this Persistence Context is associated is committed.

It seems like there is a common agreement on what flush does: Sends the required SQL Query to the database and applies any side - effects (such as auto-generated id 's..) to the managed Entities. However, this does not mean that the Queries are committed!

But what does the JPA Specification have to say about it?

The flush method can be used by the application to force synchronization. It applies to entities associated with the persistence context. The EntityManager and Query setFlushMode methods can be used to control synchronization semantics. 
When queries are executed within a transaction if the flush mode setting for the persistence context is AUTO (the default) the persistence provider is responsible for ensuring that all updates to the state of all entities in the persistence context which could potentially affect the result of the query are visible to the processing of the query. The persistence provider implementation may achieve this by flushing those entities to the database or by some other means.  
If FlushModeType.COMMIT is specified, flushing will occur at transaction commit; the persistence provider is permitted, but not required, to perform to flush at other times ie. the effect of updates made to entities in the persistence context upon queries is unspecified.

An Example Please..

This is my starting point for the following examples, but I modified the main class slightly, so please see, App.java
package biz.tugay.learnflush;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
 
public class App {
 
    private final static EntityManager em = PersistenceUtil.getEntityManager();
    private final static TestTable testTable = new TestTable();
    static {
        testTable.setData("learnflush");
    }
 
    public static void main(String[] args) {
        // By default, FlushModeType is AUTO, set it to COMMIT explicitly..
        em.setFlushMode(FlushModeType.COMMIT);
        final EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        em.persist(testTable);
        System.out.println("TestTable id: " + testTable.getId());
        em.close();
        PersistenceUtil.closeFactory();
    }
}

And the output for me is as follows..
TestTable id: 12020

Koray, what is it that you are trying to show us here?
So in this example, I am setting the FlushModeType to FlushModeType.COMMIT explicitly.. And I am expecting that JPA not to send the SQL Query to the database actually, so the TestTable entity should not have an id, since it is assigned by the Database Management System, isn 't it? Well, it seems like not, but please see what JPA Specification has to say about FlushModeType.COMMIT, the Persistence Provider is free to flush at any time it wants.. So I guess, Hibernate here is flushing when I call persist? So when you flush, SQL statements are definitely sent to the DBMS,  but when you do not, you are never so sure..

Heads Up!
In this example, we never called commit so the data is never written to the table!

Another Example Please..

Please see the following code..
package biz.tugay.learnflush;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
 
public class App {
 
    private final static EntityManager em = PersistenceUtil.getEntityManager();
    private final static TestTable testTable = new TestTable();
    static {
        testTable.setData("learnflush");
    }
 
    public static void main(String[] args) {
        em.persist(testTable);
        final EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        System.out.println("TestTable id: " + testTable.getId());
        em.close();
        PersistenceUtil.closeFactory();
    }
}

For me, the output is as follows:
TestTable id: 0

It does not matter, whether  I set the FlushModeType to AUTO or COMMIT.. A very big difference is, persist is not in the Transaction here! Now, we can see flush in action:
package biz.tugay.learnflush;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
 
public class App {
 
    private final static EntityManager em = PersistenceUtil.getEntityManager();
    private final static TestTable testTable = new TestTable();
    static {
        testTable.setData("learnflush");
    }
 
    public static void main(String[] args) {
        em.persist(testTable);
        final EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        em.flush();
        System.out.println("TestTable id: " + testTable.getId());
        em.close();
        PersistenceUtil.closeFactory();
    }
}

The output for me is:
TestTable id: 12037
We still have not commited anything to the DB! There is still no data in the DB.

Another Example Please..

package biz.tugay.learnflush;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
 
public class App {
 
    private final static EntityManager em = PersistenceUtil.getEntityManager();
    private final static TestTable testTable = new TestTable();
    static {
        testTable.setData("learnflush");
    }
 
    public static void main(String[] args) {
        em.persist(testTable);
        final EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        System.out.println("TestTable id: " + testTable.getId());
        transaction.commit();
        System.out.println("TestTable id: " + testTable.getId());
        em.close();
        PersistenceUtil.closeFactory();
    }
}

And the output for me is as follows:
TestTable id: 0
TestTable id: 12042

Conclusions?

  • Sending the SQL statements, independent of the FlushModeType, seems to depend on whether you persist within a transaction or not..
  • When you persist within a transaction, it seems like Hibernate also flushes automatically, even if the FlushModeType is COMMIT. This seems to be allowed by JPA. Flushing further has no extra effect.
  • When you persist outside of a transaction, you can simply flush to get the side-effects, such as auto generated ids, see constraint validations, etc..
  • Whether you flushed or not, you must commit, if you want your data to be persistent in the database..