How do JPA and Hibernate define the AUTO flush mode

Introduction

The Persistence Context acts as a transactional-write behind cache for the incoming entity state transitions, and all changes are synchronized with the database during flushing.

Although both the Hibernate Session and the JPA EntityManager define a flush() method to manually trigger this process, it’s much more convenient to let Hibernate manage the Persistence Context flushing. Unfortunately, there’s is a major difference between how JPA and Hibernate define the automatic flushing mechanism.

When using the default AUTO flush mode, the Persistence Context should be flushed as follows:

  • before the transaction is committed,
  • before running a JPQL or HQL query
  • before executing a native SQL query

As previously explained, Hibernate triggers the AUTO flush only for the first two events, and the native SQL queries must either override the ALWAYS flush mode using the Query#setFlushMode(FlushMode flushMode) method or add a table space synchronization (e.g. SQLQuery#addSynchronizedEntityClass(Class entityClass), SQLQuery#addSynchronizedEntityName(String entityName), SQLQuery#addSynchronizedQuerySpace(String querySpace)).

This is only required for the native Hibernate API when using a Session explicitly.

Since Hibernate 5.2, if you bootstrap Hibernate using JPA (e.g. persistence.xml), then even the Hibernate FlushType.AUTO will behave just like its JPA counterpart.
Only if you bootstrap Hibernate using the native mechanism, will the Hibernate Session use the legacy FlushType.AUTO behavior.

JPA AUTO flushing

JPA is more strict, and the AUTO flush mode must trigger a flush before any query. More the section 3.10.8 of the Java Persistence API specification says that the AUTO flush mode should ensure that all pending changes are visible by any executing query.

This can be demonstrated by executing the following method:

assertTrue(((Number) entityManager
    .createNativeQuery("select count(*) from Post")
    .getSingleResult()).intValue() == 0);
    
Post post = new Post("Hibernate");
post.setId(1L);
entityManager.persist(post);

assertTrue(((Number) entityManager
    .createNativeQuery("select count(*) from Post")
    .getSingleResult()).intValue() == 1);

When running this test, Hibernate generates the following SQL statements:

SELECT COUNT(*) FROM Post

INSERT INTO post (title, version, id) 
VALUES ('Hibernate', 0, 1)

SELECT COUNT(*) FROM Post

So, the flush was triggered and the INSERT statement was executed prior to running the SELECTstatement.

This doesn’t happens when using a Session:

assertTrue(((Number) entityManager
    .createQuery("select count(*) from Post")
    .getSingleResult()).intValue() == 0);

Post post = new Post("Hibernate");
post.setId(1L);
entityManager.persist(post);

Session session = entityManager.unwrap(Session.class);
assertTrue(((Number) session
    .createSQLQuery("select count(*) from Post")
    .uniqueResult()).intValue() == 0);

This time, Hibernate generates the following statements:

SELECT COUNT(*) FROM Post

SELECT COUNT(*) as col_0_0_ 
FROM post blogentity0_

INSERT INTO post (title, version, id) 
VALUES ('Hibernate', 0, 1)

If you enjoyed this article, I bet you are going to love my book as well.

Conclusion

Flushing is a very important concept for any ORM too,a dn Hibernate is no different.
As a rule of thumb, it’s better to make sure that native SQL queries don;t return inconsistent results when using the Hibernate Session API.

If you liked this article, you might want to subscribe to my newsletter too.

Advertisements

6 thoughts on “How do JPA and Hibernate define the AUTO flush mode

  1. Excellent post, Vlad!

    Now I’d like to know if all versions of JPA do flushing before executing a native SQL query or just the last one (JPA 2.1)?

    1. Thanks. Many believe that JPA is enough, but when you read the specs, you realize that many concepts are not very strict and the abstraction starts leaking into each provider.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s