简体   繁体   中英

Question about Hibernate session.flush()

I want to inquire about what actually the flush method does in the following case:

for (int i = 0; i < myList.size(); i++) {
    Car c = new Car( car.get(i).getId(),car.get(i).getName() );
    getCurrentSession().save(c);
    if (i % 20 == 0)
        getCurrentSession().flush();
}

Does this means that after the iteration 20, the cache is flushed, and then the 20 held memory objects are actually saved in the database ?

Can someone please explain to me what will happen when the condition is true.

From the javadoc of Session#flush :

Force this session to flush. Must be called at the end of a unit of work, before committing the transaction and closing the session (depending on flush-mode , Transaction.commit() calls this method).

Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.

In other words, flush tells Hibernate to execute the SQL statements needed to synchronize the JDBC connection's state with the state of objects held in the session-level cache. And the condition if (i % 20 == 0) will make it happen for every i multiple of 20.

But, still, the new Car instances will be held in the session-level cache and, for big myList.size() , you're going to eat all memory and ultimately get an OutOfMemoryException . To avoid this situation, the pattern described in the documentation is to flush AND clear the session at regular intervals (same size as the JDBC batch size) to persist the changes and then detach the instances so that they can be garbage collected:

13.1. Batch inserts

When making new objects persistent flush() and then clear() the session regularly in order to control the size of the first-level cache.

 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(); 

The documentation mentions in the same chapter how to set the JDBC batch size.

See also

Depends on how the FlushMode is set up.

In default configuration Hibernate tries to sync up with the database at three locations.

1. before querying data
2. on commiting a transaction
3. explictly calling flush

If the FlushMode is set as FlushMode.Manual, the programmer is informing hibernate that he/she will handle when to pass the data to the database.Under this configuration the session.flush() call will save the object instances to the database.

A session.clear() call acutally can be used to clear the persistance context.

// Assume List to be of 50 
for (int i = 0; i < 50 ; i++) {
        Car c = new Car( car.get(i).getId(),car.get(i).getName() );
        getCurrentSession().save(c);
    // 20 car Objects which are saved in memory syncronizes with DB 
        if (i % 20 == 0)
            getCurrentSession().flush();

}

Few more pointers regarding why the flushing should match batch size To enable batching you need to set the jdbc batch size

// In your case 
hibernate.jdbc.batch_size =20

One common pitfall in using batching is if you are using single object update or insert this goes fine.But in case you are using mutiple objects leading to multiple inserts /updates then you will have to explicitly set the sorting mechanism.

For example

// Assume List to be of 50 
    for (int i = 0; i < 50 ; i++) {
            Car c = new Car( car.get(i).getId(),car.get(i).getName() );
        // Adding accessory also in the card here
            Accessories a=new Accessories("I am new one");
            c.add(a);
        // Now you got two entities to be persisted . car and accessory 
        // Two SQL inserts 
            getCurrentSession().save(c);
        // 20 car Objects which are saved in memory syncronizes with DB 
        // Flush here clears the car objects from 1st level JVM cache
            if (i % 20 == 0)
            getCurrentSession().flush();
                    getCurrentSession().clear();
    }

Here in this case two sql are generated 1 for insert in car 1 for insert in accessory

For proper batching you will have to set the

<prop  key="hibernate.order_inserts">true</prop>

so that all the inserts for car is sorted together and all inserts of accessories are sorted together.By doing so you will have 20 inserts firing in a batch rather then 1 sql firing at a time.

For different operation under one transaction, you can have a look at http://docs.jboss.org/hibernate/core/3.2/api/org/hibernate/event/def/AbstractFlushingEventListener.html

Yes every 20 loop, sql is generated and executed for the unsaved objects. Your should also set batch mode to 20 to increase performances.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM