Je dois traiter un fichier CSV et pour chaque enregistrement (ligne) persister une entité. En ce moment, je le fais de cette façon:
while ((line = reader.readNext()) != null) {
Entity entity = createEntityObject(line);
entityManager.save(entity);
i++;
}
où la méthode save(Entity)
est essentiellement juste un appel EntityManager.merge()
. Il y a environ 20 000 entités (lignes) dans le fichier CSV. Est-ce un moyen efficace de le faire? Cela semble être assez lent. Serait-il préférable d'utiliser EntityManager.persist()
? Cette solution est-elle en quelque sorte défectueuse?
MODIFIER
C'est un long processus (plus de 400 s) et j'ai essayé les deux solutions, avec persist
et merge
. Les deux prennent à peu près le même temps (459 vs 443). La question est de savoir si la sauvegarde des entités une par une comme celle-ci est optimale. Autant que je sache, Hibernate (qui est mon fournisseur JPA) implémente certaines fonctionnalités de cache/vidage, donc je ne devrais pas avoir à m'inquiéter à ce sujet.
L'API JPA ne vous fournit pas toutes les options pour rendre cela optimal. En fonction de la vitesse à laquelle vous souhaitez le faire, vous devrez rechercher des options spécifiques à ORM - Hibernate dans votre cas.
Choses à vérifier:
Donc, dans Ebean ORM, ce serait:
EbeanServer server = Ebean.getServer(null);
Transaction transaction = server.beginTransaction();
try {
// Use JDBC batch API with a batch size of 100
transaction.setBatchSize(100);
// Don't bother getting generated keys
transaction.setBatchGetGeneratedKeys(false);
// Skip cascading persist
transaction.setPersistCascade(false);
// persist your beans ...
Iterator<YourEntity> it = null; // obviously should not be null
while (it.hasNext()) {
YourEntity yourEntity = it.next();
server.save(yourEntity);
}
transaction.commit();
} finally {
transaction.end();
}
Oh, et si vous faites cela via JDBC brut, vous sautez la surcharge ORM (moins de création d'objets/garbage collection, etc.) - donc je n'ignorerais pas cette option.
Donc oui, cela ne répond pas à votre question, mais pourrait vous aider à rechercher plus d'ajustements d'insertion de lots spécifiques à l'ORM.
Je pense qu'une façon courante de procéder consiste à effectuer des transactions. Si vous commencez une nouvelle transaction et que vous persistez ensuite un grand nombre d'objets, ils ne seront pas réellement insérés dans la base de données tant que vous n'aurez pas validé la transaction. Cela peut vous permettre de gagner en efficacité si vous avez un grand nombre d'éléments à engager.
Vérifiez EntityManager.getTransaction
Pour le rendre plus rapide, au moins dans Hibernate, vous feriez un flush () et un clear () après un certain nombre d'insertions. J'ai fait cette approche pour des millions de disques et ça marche. C'est encore lent, mais c'est beaucoup plus rapide que de ne pas le faire. La structure de base est comme ceci:
int i = 0;
for(MyThingy thingy : lotsOfThingies) {
dao.save(thingy.toModel())
if(++i % 20 == 0) {
dao.flushAndClear();
}
}
Vous pouvez les écrire avec une instruction d'insertion SQL classique directement dans la base de données.