2017-07-21 4 views
0

Dans Java/Hibernate application J'ai deux classes Cat et Kitten dans une relation bidirectionnelle comme dépeignent ci-dessous:Bi-direction devient beaucoup plus lent que uni direction

public class Cat { 
    ... 
    @OneToMany(mappedBy="cat", fetch = FetchType.LAZY) 
    @OnDelete(action = OnDeleteAction.CASCADE) 
    @LazyCollection(LazyCollectionOption.EXTRA) 
    @Getter 
    @Setter 
    private List<Kitten> kittens = new LinkedList(); 
    public void addKitten(Kitten k) { 
    kittens.add(k); 
    } 
    ... 
} 

public class Kitten { 
    ... 
    @ManyToOne(fetch=FetchType.LAZY) 
    @Getter 
    @Setter 
    private Cat cat; 
    ... 
} 

Dans une grande boucle for 20000 Kitten sont ajouté à différentes entités Cat qui ont été créées précédemment. Le code important dans la boucle for ressemble à ceci:

.... 
Kitten k = new Kitten(); 
k.setAttribut("foo"); 
k.setCat(currentCat);  // (a) line 
currentCat.addKitten(k); // (b) line 
daoFactory.getKittenDao().save(k); 
... 

Le code fonctionne, mais en quelque sorte la performance est très lent. Dans une itération précédente (unidirectionnelle), l'application était beaucoup plus rapide. Depuis la version finale devrait fonctionner sur 1000000 Kitten il doit y avoir un moyen d'améliorer. Si je compare l'heure du code ci-dessus, cela prend environ 40 secondes. Si je simule l'uni-direction en supprimant la ligne (a) ou (b), cela prend 10 secondes dans les deux cas (mais plus tard si j'accède aux objets). Donc, ma question est: Est-ce que quelque chose me manque ou est-ce que la maintenance interne des relations bidirectionnelles est très lente dans Hibernate? Puisque l'uni-direction simule est beaucoup plus rapide, je m'attendrais à une exécution d'environ 15 secondes pour la bidirection.

Mise à jour:

Le code de sauvegarde des entités est à l'intérieur d'une mise en œuvre SAX-Parser DefaultHandler. Ainsi, lors de l'analyse syntaxique de la structure XML, un Cat est d'abord enregistré dans la fonction startElement() et plus tard le Kitten sera enregistré dans un autre appel startElement(). En ce qui concerne les suggestions/questions par @Bartun: J'ai jeté un coup d'oeil dans le dosage. Le problème est, en utilisant DAO et les fonctions startElement(), il est difficile de dire, quand exactement 50 entités ont été enregistrées pour vider la session. Un compteur pourrait faire l'affaire cependant. Cependant, cela n'explique pas l'impact sur les performances en établissant la bidirection. En tant que gestion des transactions, le ressort @Transactional est utilisé sur la fonction qui démarre l'analyse XML.

+1

Avez-vous profilé pour voir où vous saignez? – efekctive

+0

Juste une supposition, essayez persist() plutôt que save(). – Steve11235

+0

@ Steve11235 persistent le rend encore pire. Ensuite, le temps d'exécution est de 50 secondes. – Thanthla

Répondre

1

J'ai réduit e temps de traitement à une valeur que je peux vivre avec. Cela n'a pas vraiment résolu mon problème, mais réduit considérablement le temps. Si quelqu'un d'autre a le même problème, voici le code actuel et une courte explication.

public class Cat { 
    ... 
    @OneToMany(mappedBy="cat", fetch = FetchType.LAZY) 
    @OnDelete(action = OnDeleteAction.CASCADE) 
    @LazyCollection(LazyCollectionOption.EXTRA) 
    @Getter 
    @Setter 
    private Set<Kitten> kittens = new HashSet(); 
    public void addKitten(Kitten k) { 
    k.setCat(this); 
    if (Hibernate.isInitialized(kittens)) kittens.add(k); //line X 
    } 
    ... 
} 

public class Kitten { 
    ... 
    @ManyToOne(fetch=FetchType.LAZY) 
    @OnDelete(action = OnDeleteAction.CASCADE) 
    @Getter 
    @Setter 
    private Cat cat; 
    ... 
} 
  • Le plus important est line X. Dans le code original, la liste entière des chatons a été chargée à partir de la base de données, chaque fois qu'un chaton a été ajouté. Comme je l'ai trouvé here, le chaton ne doit pas être ajouté, si la liste n'est pas encore initialisée. Je sais, cela doit être fait avec soin, car chaque chaton doit être sauvegardé dans la base de données avant la première initialisation de la liste.Sinon, tout est désynchronisé.
  • Non illustré dans le code ci-dessus, j'ai changé la structure de la persistance chaton pour permettre les insertions batch (Thx Bartun pour les liens). Maintenant, tous les chatons sont sauvés à la fin du processus d'analyse en utilisant le traitement par lots au lieu de sauver chaque chaton seul.
  • Une petite amélioration a également été le passage d'un List à un Set pour activer plusieurs fetch joins plus tard dans le code.
0

Où ouvrez-vous la transaction? assurez-vous que vous utilisez une transaction

également u devrait utiliser des requêtes dosant pour ce montant

plus d'informations sur Batching https://vladmihalcea.com/how-to-batch-insert-and-update-statements-with-hibernate/

Vous pouvez également vérifier le nombre de chasse et d'autres choses à l'aide statictics de mise en veille prolongée https://www.thoughts-on-java.org/how-to-activate-hibernate-statistics-to-analyze-performance-issues/

Vous devez avoir le plein contrôle des flushes/batching quand vous traitez avec une telle quantité d'insterts