2

I have a Java Spring Boot application reading and writing data to a local Oracle 19c database.

I have the following CommandLineRunner:

    @Override
    public void run(String... args) {
        final EntityManager em = entityManagerFactory.createEntityManager();
        final EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        em.persist(customer());
//COMMENT1        em.flush();
/*COMMENT2
        Query q = em.createQuery("from " + Customer.class.getName() + " c");
        @SuppressWarnings("unchecked")
        final Iterator<Object> iterator = (Iterator<Object>) q.getResultList().iterator();
        while (iterator.hasNext()) {
            Object o = iterator.next();
            final Customer c = (Customer) o;
            log.info(c.getName());
        }
*/
        transaction.rollback();
    }

When I run this code, using a packet sniffer to monitor TCP traffic between the application and database, I see what I expect: nothing particularly interesting in the conversation, as the em.persist(customer()) will not be flushed.

When I include the code in COMMENT1, then I'm surprised to find that the conversation looks the same - there is nothing interesting after the connection handshake.

When I include the code in COMMENT2, however, then I get a more complete TCP conversation. Now, the captured packets show that the write operation was indeed flushed to the database, and I can also see evidence of the read operation to list all entities following it.

Why is it that the TCP conversation does not reflect the explicit flush() when only COMMENT1 is removed? Why do I need to include COMMENT2 to see an insert into customer... statement captured in the TCP connection?

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
Ben R.
  • 1,965
  • 1
  • 13
  • 23
  • I think this could answer your question: https://stackoverflow.com/questions/11048177/entitymanager-flush-vs-entitymanager-gettransaction-commit-what-should-i-p – Janos Vinceller Jul 02 '20 at 08:36
  • @JanosVinceller it doesn't. My question is "why does the flush not ACTUALLY flush to the DB" - which isn't answered in the other question. – Ben R. Jul 02 '20 at 11:27
  • javadoc for the flush method says that it - Synchronize the persistence context to the underlying database. https://docs.oracle.com/javaee/7/api/javax/persistence/EntityManager.html#flush-- The actual behavior may be implementation dependent. but what you see is alright as per the API. Moreover, once you have a EM, you are supposed to perform all db operations via EM (ONLY), so that you have the latest state. It is not at all necessary for the EM.flush() to push changes to the database immediately to exhibit that state, right? – vivek Jul 04 '20 at 20:43

2 Answers2

4

A call to flush() synchronizes your changes in the persistence context with the database but it may not commit the transaction immediately. Consider it as an optimization to avoid unnecessary DB writes on each flush. When you un-comment the 2nd block then you see the flush getting executed for sure. This happens because the EM ensures your select query gets all the results in the latest state from DB. It, therefore, commits the flushed changes (alongwith any other changes done via other transactions, if any).

vivek
  • 386
  • 3
  • 12
0
em.persist(customer());
persist does not directly insert the object into the database: 
it just registers it as new in the persistence context (transaction).

em.flush();
It flushes the changes to the database but doesn't commit the transaction. 
A query gets fired if there is a change expected in  database(insert/update/delete)
em.rollback or em.commit will actually rollback or commit the transaction

And all these scenario depends on the flush mode, and I think behaviour is
vendor dependent. Assuming Hibernate, most probably FlushMode is set to auto in
your application and so the desired result as in second scenario.
AUTO Mode : The Session is sometimes flushed before query execution in order to ensure 
that queries never return stale state
https://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/FlushMode.html

In spring boot I think you can set it as
spring.jpa.properties.org.hibernate.flushMode=AUTO
 
zain
  • 284
  • 2
  • 10