Toutes les méthodes mentionnées fonctionnent mais seront lentes, surtout si la source des données insérées se trouve dans une autre table. Tout d'abord, même avec batch_size>1
, l'opération d'insertion sera exécutée en plusieurs requêtes SQL. Deuxièmement, si les données sources se trouvent dans une autre table, vous devez les récupérer avec d'autres requêtes (et dans le pire des cas charger toutes les données en mémoire) et les convertir en insertions massives statiques. Troisièmement, avec un appel persist()
séparé pour chaque entité (même si le lot est activé), vous allez saturer le cache de premier niveau de l'EntityManager avec toutes ces instances d'entités.
Mais il y a une autre option pour Hibernate. Si vous utilisez Hibernate en tant que fournisseur JPA, vous pouvez revenir à l'HQL qui prend en charge nativement les insertions massives avec un sous-sélecteur d'une autre table. L'exemple :
Session session = entityManager.unwrap(Session::class.java)
session.createQuery("insert into Entity (field1, field2) select [...] from [...]")
.executeUpdate();
Si cela fonctionne dépendra de votre stratégie de génération d'identifiant. Si l'ID de Entity
est généré par la base de données (par exemple auto-incrémentation MySQL), cela réussira. Si l'ID de Entity
est généré par votre code (surtout vrai pour les générateurs UUID), cela échouera avec une exception "méthode de génération d'ID non prise en charge".
Cependant, dans ce dernier scénario, ce problème peut être surmonté par une fonction SQL personnalisée. Par exemple, dans PostgreSQL, j'utilise l'extension uuid-ossp qui fournit la fonction uuid_generate_v4()
, que je registre enfin dans mon dialogue personnalisé :
import org.hibernate.dialect.PostgreSQL10Dialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.PostgresUUIDType;
public class MyPostgresDialect extends PostgreSQL10Dialect {
public MyPostgresDialect() {
registerFunction( "uuid_generate_v4",
new StandardSQLFunction("uuid_generate_v4", PostgresUUIDType.INSTANCE));
}
}
Ensuite, je registre cette classe en tant que dialogue hibernate :
hibernate.dialect=MyPostgresDialect
Enfin, je peux utiliser cette fonction dans la requête d'insertion en masse :
SessionImpl session = entityManager.unwrap(Session::class.java);
session.createQuery("insert into Entity (id, field1, field2) "+
"select uuid_generate_v4(), [...] from [...]")
.executeUpdate();
Le plus important est le SQL sous-jacent généré par Hibernate pour accomplir cette opération et il s'agit d'une seule requête :
insert into entity ( id, [...] ) select uuid_generate_v4(), [...] from [...]
0 votes
Veuillez vérifier ma réponse, j'espère qu'elle vous sera utile : stackoverflow.com/a/50694902/5380322
0 votes
@Cepr0 Merci, mais je fais déjà cela (j'accumule dans une liste et j'appelle
saveAll
. J'ai simplement ajouté un exemple de code minimal pour reproduire le problème.0 votes
Avez-vous défini la propriété
hibernate.jdbc.batch_size
?0 votes
@Cepr0 Oui. (voir ci-dessus)
3 votes
C'est incorrect, cela doit être sous cette forme:
spring.jpa.properties.hibernate.jdbc.batch_size
0 votes
@Cepr0 Merci, rieckpil l'a déjà mentionné et j'ai ajusté mon code en conséquence. Cependant, il ne fait toujours pas de lot.
0 votes
Ce que vous montrez est l'insertion de lot. L'insertion en bloc est une technique beaucoup plus rapide, mais elle est spécifique à la base de données et n'est pas prise en charge par JPA.