[Java] Exécuter un lot de requêtes SQL

Vincent Lecomte

Grâce à JDBC, il vous est possible de gagner en performances en exécutant un lot de requêtes – aussi appelé “batch” – pour effectuer des insertions, des mises à jour ou même des suppressions. Par défaut, toutes les requêtes seront considérées chacune comme étant des transactions, car l’auto-commit est activé lors de l’utilisation de l’objet Connection. Ce comportement pourra être modifié en utilisant la fonction “myConnection.setAutoCommit(false)”. Pour rappel, les SGBD prévoient des “niveaux d’isolation” pour l’exécution de vos transactions. Par exemple, le niveau le plus strict – Serializable – ne permet aucune lecture impropre (T2 lit une donnée mise à jour par T1 alors que cette dernière a été annulée – la valeur lue n’est donc pas valide), aucune lecture fantôme (risque d’obtenir des données en plus ou en moins mais non-validées) ni non-reproductible (lecture d’une valeur par T2 avant et après mise à jour par T1 ; différente aux deux exécutions). Le tableau ci-dessus indique les phénomènes qui peuvent se produire dans chaque niveau.

Les différents niveaux sont définis en tant que constantes dans la classe Connection.

Préparer la requête

En Java, pour exécuter une requête, nous pouvons utiliser les Statement, mais cela impose de devoir construire soi-même la chaine. Pour éviter les soucis classiques d’injection SQL, on peut se diriger vers les PreparedStatement. La requête est définie une seule fois, les paramètres sont indiqués dans l’ordre et par des “?”.

INSERT_QRY = "INSERT INTO FPRXML " 
  + "(FCACAR, FCASKU, FEAN13) VALUES (?, ?, ?);";

Pour obtenir un objet “PreparedStatement”, il faut utiliser la fonction “prepareStatement” de l’objet Connection que vous avez déclaré. En paramètre, nous passons la requête d’insertion définie ci-dessus.

psi = this.getC().prepareStatement(INSERT_QUERY);

Dans une boucle, nous allons effectuer un traitement pour insérer les différentes valeurs. Nous allons ajouter la requête sans pour autant l’exécuter directement. C’est comme si l’on créait un script SQL contenant plusieurs requêtes séparées par des point-virgules.

psi.setString(1, sCart);
psi.setInt(2, iSku); 
psi.setString(3, sISBN); 
psi.addBatch();

Exécution

Grâce à une variable de type entier servant de compteur, nous allons exécuter chaque lot toutes les 100 requêtes. Vous pouvez essayer de plus grandes valeurs comme 1000 ou 10 000 mais il se pourrait bien qu’en fonction du SGBD, vous soyez contraints de diminuer celle-ci.

// Exécution tous les 100 objets.
if ((iTrt + 1) % 100 == 0)
{
	int[] rs = psi.executeBatch();
	for (int i = 0; i < rs.length; i++)
	{
		int element = rs[i];
		iAdded += element;
	}
}

A chaque fois que l’on utilise “executeBatch”, un tableau d’entiers est retourné. Il contient le résultat pour chaque ligne de commande du lot. La valeur est soit supérieure ou égale à 0 et correspond alors au nombre de lignes affectées par la mise à jour, soit égale à l’une des deux constantes suivantes de la classe Statement : EXECUTE_FAILED (échec de la commande, pas d’exception) ou SUCCESS_NO_INFO (l’exécution a réussi mais aucune information n’est disponible quant au nombre de lignes affectées). Remarque : il convient d’utiliser une dernière fois “executeBatch” juste après la boucle, car il est possible que la condition contenue dans l’instruction “if…” n’ait pas été remplie.

Le traitement de l’exception “BatchUpdateException” est vivement recommandé ! Celle-ci propose une fonction “getUpdateCounts()” qui vous indiquera combien de lignes ont été mises à jour, pratique si vous souhaitez l’indiquer à l’utilisateur. Ensuite, il sera possible d’annuler la transaction grâce à la fonction “rollback” de l’objet Connection.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Copy link