web-dev-qa-db-fra.com

Question sur Hibernate session.flush ()

Je veux demander ce que fait réellement la méthode flush dans le cas suivant:

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

Cela signifie-t-il qu'après l'itération 20, le cache est vidé et les 20 objets de mémoire en attente sont réellement enregistrés dans la base de données?

Quelqu'un peut-il m'expliquer s'il vous plaît ce qui se passera quand la condition est vraie.

28
Mahmoud Saleh

Depuis le javadoc de Session#flush :

Forcer cette session à vider. Doit être appelé à la fin d'une unité de travail, avant de valider la transaction et fermer la session (selon flush-mode , Transaction.commit () appelle cette méthode).

Flushing est le processus de synchronisation du sous-jacent magasin persistant avec persistable Etat tenu en mémoire.

En d'autres termes, flush indique à Hibernate d'exécuter les instructions SQL nécessaires pour synchroniser l'état de la connexion JDBC avec l'état des objets contenus dans le cache de niveau session. Et la condition if (i % 20 == 0) le produira pour chaque i multiple de 20.

Mais, néanmoins, les nouvelles instances Car seront conservées dans le cache de niveau session et, pour la grande myList.size(), vous allez manger toute la mémoire et obtenir finalement une OutOfMemoryException. Pour éviter cette situation, le modèle décrit dans la documentation est flushETclear la session à intervalles réguliers (taille identique à celle du lot JDBC) pour conserver les modifications, puis détacher les instances peuvent être des ordures collectées:

13.1. Inserts par lots

Lors de la création de nouveaux objets persistants flush () puis clear () la session régulièrement afin de contrôler la taille du cache de premier niveau.

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

La documentation indique dans le même chapitre comment définir la taille du lot JDBC.

Voir également

50
Pascal Thivent

Dépend de la configuration du FlushMode.

Dans la configuration par défaut, Hibernate tente de se synchroniser avec la base de données à trois emplacements.

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

Si la variable FlushMode est définie sur FlushMode.Manual, le programmeur indique à Hibernate qu'il gérera le moment où les données seront transmises à la base de données. Sous cette configuration .__, l'appel session.flush() enregistrera les instances d'objet dans la base de données.

Un appel session.clear() peut être utilisé pour effacer le contexte de persistance.

2
frictionlesspulley
// 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();

}

Peu plus d'indications sur la raison pour laquelle le vidage doit correspondre à la taille du lot Pour activer le traitement par lots, vous devez définir la taille du lot jdbc. 

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

Si vous utilisez la mise à jour d'un seul objet ou que vous insérez cette méthode, l'utilisation d'un traitement par lot constitue un piège courant. Mais au cas où vous utiliseriez plusieurs objets menant à plusieurs insertions/mises à jour, vous devrez définir explicitement le mécanisme de tri.

Par exemple 

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

Ici dans ce cas deux sql sont générés 1 pour insérer dans la voiture 1 pour insérer dans l'accessoire 

Pour que le traitement par lots soit correct, vous devez définir le paramètre 

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

de sorte que tous les inserts pour voiture soient triés ensemble et que tous les inserts d'accessoires soient triés ensemble.En faisant cela, vous aurez 20 inserts tirant dans un lot plutôt que 1 sql à la fois.

Pour une opération différente sous une transaction, vous pouvez consulter http://docs.jboss.org/hibernate/core/3.2/api/org/hibernate/event/def/AbstractFlushingEventListener.html

1
Manish Singh

Oui toutes les 20 boucles, SQL est généré et exécuté pour les objets non sauvegardés. Vous devez également définir le mode de traitement par lots sur 20 pour augmenter les performances.

0
user333306