2010-05-05 7 views
22

Voici un exemple simple que j'ai créé après avoir lu plusieurs rubriques sur les insertions en masse jpa, j'ai 2 objets persistants Utilisateur et Site. Un utilisateur peut avoir beaucoup de site, donc nous avons une à plusieurs relations ici. Supposons que je souhaite créer un utilisateur et créer/lier plusieurs sites à un compte d'utilisateur. Voici à quoi ressemble le code, compte tenu de ma volonté d'utiliser l'insertion en bloc pour les objets Site.Insertion en bloc JPA/Hibernate (lot)

User user = new User("John Doe"); 

user.getSites().add(new Site("google.com", user)); 
user.getSites().add(new Site("yahoo.com", user)); 

EntityTransaction tx = entityManager.getTransaction(); 
tx.begin(); 
entityManager.persist(user); 
tx.commit(); 

Mais quand je lance ce code (j'utilise hiverne en tant que fournisseur d'implémentation JPA) Je vois suivant la sortie sql:

Hibernate: insert into User (id, name) values (null, ?) 
Hibernate: call identity() 
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?) 
Hibernate: call identity() 
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?) 
Hibernate: call identity() 

Donc, je signifie en vrac "réel" pas insérer des œuvres ou I suis confus?

Voici source code pour cet exemple de projet, c'est un projet maven donc vous n'avez qu'à télécharger et lancer mvn install pour vérifier la sortie.

mise à jour:

Après Ken Liu bien voulu conseiller, je l'ai objet Site désactivé génération automatique id:

User user = new User("John Doe"); 
    user.getSites().add(new Site(1, "google.com", user)); 
    user.getSites().add(new Site(2, "yahoo.com", user)); 
    entityManager.setFlushMode(FlushModeType.COMMIT); 
    EntityTransaction tx = entityManager.getTransaction(); 
    tx.begin(); 
    entityManager.persist(user); 
    tx.commit(); 

Maintenant, j'ai ligne suivante dans la sortie de débogage:

DEBUG: org. hibernate.jdbc.AbstractBatcher - Exécution de la taille du lot: 2

Cela fonctionne!

Répondre

19

Si vous utilisez la base de données pour générer des ID, Hibernate doit exécuter une requête pour générer la clé primaire pour chaque entité.

+0

Oh, c'est logique! Merci – abovesun

+0

J'ai mis à jour la question, pourriez-vous s'il vous plaît vérifier, merci – abovesun

+5

comment générez-vous vos clés maintenant? Vous devrez être sûr que vos clés sont uniques. –

5

J'ai trouvé beaucoup plus efficace de contourner l'hibernation pour les encarts en vrac. Vous devez supprimer ORM (mappage relationnel d'objet), mais vous pouvez toujours utiliser la connexion associée à la session en cours et la gestion des transactions.

Alors que vous perdez temporairement la commodité de votre ORM, le gain est important, en particulier si vous avez des IDs générés en mode natif car Hibernate devrait normalement effectuer un SELECT pour chaque INSERT.

Session.doWork est très pratique pour faciliter cela.

private MyParentObject saveMyParentObject(final MyParentObject parent, final List<MyChildObject> children) 
{ 
    transaction = session.beginTransaction(); 
    try 
    { 
     session.save(parent); // NOTE: parent.parentId assigned and returned here 

     session.doWork(new Work() 
     { 
      public void execute(Connection con) throws SQLException 
      { 
       // hand written insert SQL - can't use hibernate 
       PreparedStatement st = con.prepareStatement("INSERT INTO my_child (parent_id, name, ...) values (?, ?, ...)"); 

       for (MyChildObject child : children) 
       { 
        MyChildObject child = new MyChildObject(); 
        child.setParentId(parent.getParentId()); // assign parent id for foreign key 

        // hibernate can't help, determine jdbc parameters manually 
        st.setLong(1, child.getParentId()); 
        st.setString(2, child.getName()); 
        ... 
        st.addBatch(); 
       } 

       // NOTE: you may want to limit the size of the batch 
       st.executeBatch(); 
      } 
     }); 

     // if your parent has a OneToMany relationship with child(s), refresh will populate this 
     session.refresh(parent); 
     transaction.commit(); 
     return parent; 
    } 
    catch(Throwable e) 
    { 
     transaction.rollback(); 
     throw new RuntimeException(e); 
    } 
} 
+1

J'utilise la même technique que celle que vous avez fournie. Mais il y a toujours un problème; cette méthode prépare une instruction différente pour chaque insertion, car elle peut être affichée dans sql profiler. Pour augmenter les performances, il doit compiler ou préparer une instruction une fois puis appeler cette instruction compilée pour le reste des insertions. – Max

Questions connexes