gipinani gipinani - 1 month ago 16
Java Question

Spring - Hibernate improve transaction performance with FlushMode

I'm trying to improve the performances of my asynk transactional method.

In this task I have to read almost 7500 record from a table, elaborate it, and insert/update a corresponding row in another table.

I'm using spring data jpa with hibernate.

In order to get a

ScrollableResults
I inject the
EntityManager
into my service.

Here how I get my
ScrollableResult
object:

Session session = (Session) em.unwrap(Session.class);
ScrollableResults res = session.createQuery("from SourceTable s")
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);


while (res.next()){
.... // em.flush() called every 40 cycles
}


Cycling on result take about 60 seconds.

And here the bottleneck. If inside my loop I execute a simple query:

query = em.createQuery("from DestTable d where d.item.id = :id", DestTable.class);

while (res.next()){
query.setParameter("id", myId).getSingleResult();
}


The execution time become x10 slower.. and takes about 600s.

I've tried to modify a parameter of my
Session
or of my
EntityManager
:
session.setFlushMode(FlushModeType.COMMIT);

em.setFlushMode(FlushModeType.COMMIT);


It increase the performance and removing the manual flush() method the work is done in 40s!!!

So my questions are:


  • What is the difference of call
    setFlushMode
    on
    session
    or on
    enityManager
    ?

  • Why
    setFlushMode(FlushModeType.COMMIT);
    increase in that way performance, and I cannot have same performance only by manually flushing entityManager?


Answer

The problem is that the default flush mode is FlushModeType.AUTO. In auto flush mode, Hibernate will flush before each query (only queries, not find operations). This means that in your above example, by default, Hibernate is flushing each time you call getSingleResult(). The reason it does this is because it's possible that the changes you have made would affect the results of your query and so Hibernate wants your query to be as accurate as possible and flushes first.

You don't see the performance hit in your first example because you are only issuing one query and scrolling through it. The best solution I have found is the one you mentioned which is just to set the flush mode to COMMIT. There should be no difference between calling setFlushMode on the Session or the EntityManager.

Comments