Hibernate minimizes the trips
to the database in order to gain some performance. It is the flush mode
that controls how many times Hibernate hits the DB when it comes to
write operations. The behaviour of the flushing mechanism is
customizable. It is worth to know that the proper configuration of the
flush mode can have quite a big influence on the overall performance (especially while testing when the transaction is not committed).
There are four types of FlushMode in Hibernate:MANUAL, COMMIT, AUTO, ALWAYS.
I have created the following code snippet (placed within EJB) to present differences between them:
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void flushModeTest() { log.info("Setting FlushMode to: " + FlushMode.MANUAL); entityManager.unwrap(Session.class).setFlushMode(FlushMode.MANUAL); Human h = new Human(); h.setAnimal(new Animal()); entityManager.persist(h); log.info("Persist invoked"); log.info("Query for animals"); List<Animal> animals = entityManager.createQuery("from Animal", Animal.class).getResultList(); log.info("There are animals persisted: " + animals); }The effect of code execution is as follows:
Setting FlushMode to: MANUAL Persist invoked Query for animals Hibernate: select animal0_.animal_id as animal1_49_, animal0_.animal_name as animal2_49_ from animal animal0_ There are animals persisted: []
Despite the fact that we are using Container Managed Transactions (CMT) and Hibernate session is associated with JTA transaction - Human and Animal were not saved into the DB after transaction (TX) commit. We have a full control over flushing - nothing was flushed, nothing is committed. What is more, we have used JPQL query to ask DB about Animals. Such query does not use internal cache so the results are picked up from the DB. Again, nothing was flushed, nothing is visible - even within the same TX. We can invoke entityManager.flush() after entityManager.persist(h) and then the Animal entity will be found by JPQL query and Human with Animal will be saved into the DB after TX commit.
Next, I changed FlushMode to COMMIT. The result:
The third flush mode is AUTO:
Even more interesting is another situation. I have modified the code a little:
The last flush mode is ALWAYS:
I hope that this blog entry shed some light on the flushing modes in Hibernate. Use them with caution! :)
Setting FlushMode to: COMMIT Persist invoked Query for animals Hibernate: select animal0_.animal_id as animal1_67_, animal0_.animal_name as animal2_67_ from animal animal0_ There are animals persisted: [] Hibernate: insert into animal (animal_name, animal_id) values (?, ?) Hibernate: insert into human (animal_id, human_name, human_id) values (?, ?, ?)Hibernate flushes the data just before TX commit. It is quite intuitive.
The third flush mode is AUTO:
Setting FlushMode to: AUTO Persist invoked Query for animals Hibernate: insert into animal (animal_name, animal_id) values (?, ?) Hibernate: insert into human (animal_id, human_name, human_id) values (?, ?, ?) Hibernate: select animal0_.animal_id as animal1_73_, animal0_.animal_name as animal2_73_ from animal animal0_ There are animals persisted: [entity.Animal@48b5a02f]AUTO mode is the most interesting one. Hibernate flushes the data just before JPQL query in order to reflect the real state - we have invoked entityManager.persist(h) so we believe that JPQL query should return persisted entities - we are not interested that Hibernate decided to defer the flushing event.
Even more interesting is another situation. I have modified the code a little:
Human h = new Human(); entityManager.persist(h); log.info("Persist invoked");As you can see, Animal entity is not persisted now. The result of the code execution:
Setting FlushMode to: AUTO Persist invoked Query for animals Hibernate: select animal0_.animal_id as animal1_80_, animal0_.animal_name as animal2_80_ from animal animal0_ There are animals persisted: [] Hibernate: insert into human (animal_id, human_name, human_id) values (?, ?, ?)Hibernate is able to predict if flushing is necessary. That is a cool feature! The flushing occurred just before TX commit. I believe that Hibernate has sophisticated mechanism that allows to determine if flushing is necessary - it comes into play with complex queries.
The last flush mode is ALWAYS:
Setting FlushMode to: ALWAYS Persist invoked Query for animals Hibernate: insert into human (animal_id, human_name, human_id) values (?, ?, ?) Hibernate: select animal0_.animal_id as animal1_92_, animal0_.animal_name as animal2_92_ from animal animal0_ There are animals persisted: []Now, Hibernate does not take into account the fact that there is no use flushing because JPQL query does not correlate with persisted object. The main difference between AUTO and ALWAYS modes is that the first one flushes right before JPQL query only when it is necessary, whereas the latter one flushes right before JPQL query without any condition.
I hope that this blog entry shed some light on the flushing modes in Hibernate. Use them with caution! :)
This is really very precise and useful. Thanks for sharing.
ReplyDeleteSuper article. Bravo. Helped me a lot.
ReplyDelete