JPA Pitfalls (10): Query Flush Mode

This blog article is part of a “series of blog articles” about common pitfalls using JPA and ways to avoid them. In this article, we describe unexpected results of JPQL queries with respect of the query flush mode.

JPA supports two flush modes when executing a query within a transaction: COMMIT and AUTO.

  • Flush mode AUTO means all updates of the current transaction are visible to the query execution. This is usually done by flushing the updates prior to the execution of the query. As a consequence, query execution might result in a optimistic lock exception with flush mode AUTO.
  • According to the JPA spec the effect of updates with respect to the query result is unspecified, when using mode COMMIT. Usually the persistence provider simply does not flush updates on query execution. This may be used as a performance optimization, since flushing might be an expensive operation. However, it might lead to unexpected query results:
Employee e = ... // Employee with weekyhours 40
e.setWeeklyhours(41);
q = em.createQuery(
  "SELECT e FROM Employee e WHERE e.weeklyhours > 40");
q.setFlushMode(FlushModeType.COMMIT);
List<Employee> result = q.getResultList();
// result.contains(e) -> false

When executing the query, the employee in the database still has the old value weeklyhours = 40, so the employee is not part of the query result.

It is also possible that the query result includes an entity that does not match the where clause of the JPQL query:

Employee e = ... // Employee with weekyhours 40
e.setWeeklyhours(41);
q = em.createQuery(
  "SELECT e FROM Employee e WHERE e.weeklyhours = 40");
q.setFlushMode(FlushModeType.COMMIT);
List<Employee> result = q.getResultList();
// result.contains(e) -> true, 
// but e does not match the where clause

Again, the employee in the database still has the old value weeklyhours = 40, so this time it is included in the query result. Since the entity is already loaded in the current transaction the query result will include the current version of the employee. But the current version has updated the weeklyhours to 41 which does not match the where clause of the JPQL query.

Recommendation: Use the query optimization flush mode COMMIT only if the modifications in the current transaction do not affect the query result.

You can find an example of this pitfall and its solution in the class QueryFlushModeExperiment in this project: https://github.com/akquinet/jpapitfalls.git.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

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

Google photo

You are commenting using your Google 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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.