Hibernate: Flush() and Commit()

Hibernate: flush() and commit()

In the Hibernate Manual you can see this example

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

for (int i = 0; i < 100000; i++) {
Customer customer = new Customer(...);
session.save(customer);
if (i % 20 == 0) { // 20, same as the JDBC batch size
// flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}

tx.commit();
session.close();

Without the call to the flush method, your first-level cache would throw an OutOfMemoryException

Also you can look at this post about flushing

Why we need flush in hibernate?

Forces the session to flush. It is used to synchronize session data with database.
When you call session.flush(), the statements are executed in database but it will not committed.

If you dont call session.flush() and if you call session.commit() , internally commit() method executes the statement and commits.

So commit()= flush+commit.

So seesion.flush() just executes the statements in database (but not commits) and statements are NOT IN MEMORY anymore. It just forces the session to flush.

Flush mainly used when you are dealing with thousands and millions of records.
So while dealing with such number of records we use batch update and flush.

Session session = SessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Employee emp = new Employee(.....);
session.save(emp);
}
tx.commit();
session.close();

In above example if you do not call flush it may throw OutOfMemoryError.

You can check out this post about flushing

Hibernate session.flush() even though autocommit is set

Autocommit and session.flush() are two different things:

  1. Autocommit sets the autocommit mode on the underlying JDBC transaction. It basically means that every SQL statement (SELECT, UPDATE, INSERT, DELETE, ...) is executed in its own transaction.
  2. session.flush() tells Hibernate to synchronize the in-memory state with the database, so to write SQL statements to the JDBC connection. See more here: What's the use of session.flush() in Hibernate

So while I do not know why entities in your example only get persisted to the database in one case, it likely has nothing to do with the autocommit mode.

Regarding hibernate transaction and flush

session.flush() will do the flushing process which is about doing dirty check for all persistent objects managed by the hibernate session.If an object is considered to be dirty (i.e any values of the object stored in that hibernate session are different from the corresponding record in the database ) , hibernate will issue UPDATE SQLs to the DB immediately in order to synchronize these differences to make the object stored in the hibernate session has the same values with the corresponding database record.

However ,just issuing the UPDATE SQL does not mean that the modified data is actually saved to the DB ,you have to COMMIT the transaction in order to confirm saving the modified data to the DB actually .It also means that you can ROLLBACK the changes made by the UPDATE SQL if any errors are found after issuing the UPDATE SQL but before committing the transaction.

The flushing behavior of a hibernate session is determined by the FlushMode parameters which can be configured by session.setFlushMode() . The default value is FlushMode.AUTO such that session.flush() will occurs automatically before committing an transaction and execution of the query.

So , when session.getTransaction.commit() is called in the default FlushMode , session.flush() will be executed implicitly before execution of session.getTransaction.commit().

Hibernate not flushing entity update on transaction commit

I spent just 4 days to realize the what the problem was. Awesome...(To be honest, I took advantage of all the stuff I had to read about JPA and Hibernate to get rid completely of OSIV).

When I defined my two datasources (Products and Users) configs, I annotated all the classes inside ProductsDataSourceConfiguration as @Primary, making them the default values when called without explicitly setting a name.

The problem was with PlatformTransactionManager in it, that was called as transaction manager for both my datasources, while actually being crafted just for Products one.

To solve the issue I had to specify the secondary transaction manager name on @Transactional annotation used on methods referring to secondary (Users) datasource:

@Transactional("usersTransactionManager")
@Service
public class UserCommitService {
...

Flushing the Hibernate Session is taking a very long time

Once my code reached session.flush it is not doing anything even
after waiting for 30 min.

On the contrary, the database seems to be doing way too much. It's just that you don't see any progress because the database is struggling to cope with the huge amount of work that you submitted.

Is this the correct way to batch commit?

The short answer is No.

You don't have to fetch millions of rows from the DB. You have better options:

  1. You can do the processing in the database, so that you don't pay the price of extracting data and sending it over the network, only to process it in Java.
  2. If you can't process it in the DB, then you need to use a batch processor that only fetches small chunks of data at a time. This way, you can even parallelize the batch processing, which should reduce the overall processing time.


Related Topics



Leave a reply



Submit