Méthode publique · ORM_CSHX2 · Persistance

mth_EnregistrerTableau

Persiste en base tous les éléments du tableau interne m_tabResults en une seule transaction. Mélange libre d'INSERT, UPDATE et DELETE dans le même lot. Les INSERT peuvent être exécutés en mode batch (INSERT_Multi performant) ou en mode unitaire (récupération de l'ID auto-incrément).

PUBLIQUE INSERT UPDATE DELETE MySQL · MariaDB · PostgreSQL
01

📋 Description

mth_EnregistrerTableau persiste en une seule transaction tous les éléments présents dans m_tabResults. Pour chaque élément, l'opération SQL effectuée est déterminée par l'état de sa clé primaire — exactement la même convention de signe que mth_Enregistrer, mais appliquée en lot :

État de la PKOpérationMode d'exécution
= 0INSERTBatch ou unitaire selon p_bRécupérationID. PK auto-incrémentée par la base.
> 0UPDATEToujours unitaire. Mise à jour de l'enregistrement existant.
< 0DELETEToujours unitaire. Suppression de Abs(PK) avec cascade applicative.
🎯 Pourquoi cette méthode plutôt qu'une boucle sur mth_Enregistrer ?

Trois avantages qu'une boucle manuelle ne peut pas reproduire facilement :

Une seule transaction pour tout le lot : tout réussit ou rien n'est conservé. Pas de risque d'état partiel en base.

Mode batch INSERT_Multi pour les nouveaux enregistrements : les INSERT sont regroupés en requêtes INSERT_Multi qui sont entre 10 et 100× plus rapides qu'une série d'INSERT unitaires (gain croissant avec le volume).

Auto-positionnement des colonnes framework : SQL_INSERTED, SQL_ID_USER_INSERT, SQL_IP_USER, SQL_EXE, SQL_PROCEDURE_INSERT, SQL_STATUS, SQL_UUID sont alimentés automatiquement quand ils sont vides.

02

🔑 Signature

Déclaration
PROCÉDURE mth_EnregistrerTableau(
  LOCAL bOptimisationUpdate est un booléen = Faux,
  LOCAL sCallbackProgression est une chaîne = ""
) : (booléen, entier, chaîne)
ÉlémentValeur
VisibilitéPUBLIQUE

Paramètres

ParamètreTypeDéfautRôle
bOptimisationUpdatebooléenFauxSi Vrai, ne traite que les éléments en UPDATE marqués comme modifiés (p_bUpdated = Vrai). INSERT et DELETE restent toujours exécutés quel que soit ce paramètre. Voir §05.
sCallbackProgressionchaîne""Nom d'une procédure projet appelée à chaque itération avec la position courante et la taille totale. Utile pour mettre à jour une jauge IHM. Voir §06.
03

🔁 Comportement par élément

La méthode itère sur m_tabResults. Pour chaque élément, elle détermine l'opération à effectuer selon la convention de signe de la PK (identique à mth_Enregistrer). Un même tableau peut donc contenir un mélange libre d'INSERT, UPDATE et DELETE, traités en une seule passe.

Auto-positionnement des colonnes framework lors d'un INSERT

Pour chaque élément en INSERT, si les colonnes framework suivantes sont vides ou à zéro, elles sont alimentées automatiquement avant l'écriture :

ColonneValeur attribuée si vide
SQL_INSERTEDDate / heure courante (si bHorodatageAuto activé sur la classe)
SQL_ID_USER_INSERTID de l'utilisateur courant (depuis la session)
SQL_IP_USERIdentifiant du poste de travail courant
SQL_EXENom de l'exécutable en cours
SQL_PROCEDURE_INSERTNom de la procédure démarreuse de la transaction
SQL_STATUSDATA_ACCESSIBLE par défaut
SQL_UUIDUUID auto-généré (préfixe session + GUID brut 256 bits)

Si une colonne framework a déjà été positionnée par l'appelant (cas d'un import qui veut préserver les valeurs d'origine, par exemple), la valeur fournie est conservée.

💡 Cas avancé : INSERT avec ID imposé

Le couple PK / p_bNouvelEnregistrement permet de couvrir le cas particulier de l'import avec ID imposé — utile pour les migrations entre bases ou les restaurations qui doivent préserver les identifiants d'origine.

Quand p_bNouvelEnregistrement = Vrai sur un élément dont la PK est déjà renseignée (PK > 0), la méthode considère que l'opération est un INSERT et non un UPDATE. La PK fournie est conservée telle quelle dans la base au lieu d'être attribuée par auto-incrément.

Tableau récapitulatif complet :

PK = 0 · p_bNouvelEnregistrement = Faux/Vrai → INSERT auto-incrément
PK > 0 · p_bNouvelEnregistrement = Faux → UPDATE
PK > 0 · p_bNouvelEnregistrement = Vrai → INSERT avec ID imposé (import)
PK < 0 · (peu importe) → DELETE de Abs(PK)

🏷️ État de m_tabResults après un DELETE

Lors d'un DELETE en lot via la convention PK<0, l'élément du tableau interne m_tabResults reste présent après la suppression réussie en base. Ses membres mappés conservent les valeurs antérieures, mais le drapeau p_bRecordDeleted (alias français : p_bEnregistrementSupprimé) est positionné à Vrai pour signaler que cet élément représente un enregistrement supprimé.

Ce comportement est volontaire : il permet de continuer à exploiter le tableau après l'enregistrement (calculs, exports, journalisation) sans devoir recharger les données depuis la base. Les méthodes d'agrégat (mth_SommeValeurs, mth_ValeurMoyenne, mth_ValeurMaximale, mth_ValeurMinimale, mth_ValeursDistinctes, mth_Regroupement) excluent automatiquement les éléments marqués supprimés du calcul.

Pour purger physiquement le tableau des éléments supprimés, l'appelant doit le faire explicitement via TableauSupprime ou recharger les données depuis la base.

04

⚡ Modes d'exécution : batch vs unitaire

Pour les INSERT, la méthode propose deux modes d'exécution. Le choix se fait par élément via le flag p_bRécupérationID positionné sur l'objet :

p_bRécupérationIDModeComportement
Faux (défaut)BATCHLes INSERT sont regroupés en requêtes INSERT_Multi et envoyés à la base par paquets. Très performant sur les gros volumes. En contrepartie, la PK auto-incrémentée n'est pas répercutée sur l'objet.
VraiUNITAIREChaque INSERT est exécuté individuellement (équivalent à mth_Enregistrer par élément). L'ID auto-incrément est récupéré et écrit dans la PK de l'objet, utilisable ensuite côté appelant.

Les UPDATE et les DELETE sont toujours exécutés en mode unitaire, indépendamment de la valeur de p_bRécupérationID. Le batch ne s'applique qu'aux INSERT.

⚠️ Quand activer p_bRécupérationID = Vrai

Activer ce flag sur un élément si vous avez besoin, après l'enregistrement, de l'ID auto-incrément de cet enregistrement. Cas typiques :

• Création d'enregistrements liés dans un second temps (ex : créer un client puis ses commandes liées)

• Construction d'un référentiel ID → entité côté appelant

• Renvoi de l'ID au client web / mobile / API

Dans le cas inverse (import en masse, peuplement de référentiel sans réutilisation immédiate), laisser le mode batch (Faux) qui est nettement plus rapide.

ℹ️ Forçage automatique en mode metadata MODE_JOIN

Si la session ORM_CSHX2 fonctionne en mode metadata METADATA_MODE_JOIN (Mode 3), p_bRécupérationID est forcé à Vrai automatiquement sur tous les éléments en INSERT, car l'ID est requis par le framework pour l'INSERT metadata associé. Ce comportement est transparent pour l'appelant — pas d'action à entreprendre.

Taille des paquets en mode batch — p_NombreInsertSimultanés

En mode batch, les INSERT sont regroupés en paquets envoyés à la base par requêtes INSERT_Multi. La taille de ces paquets est paramétrable via la propriété publique p_NombreInsertSimultanés (getter / setter) :

// ── Lecture de la valeur courante ────────────────────────────── nTailleCourante est un entier = clClient:p_NombreInsertSimultanés() // ── Modification de la valeur ────────────────────────────────── clClient:p_NombreInsertSimultanés(500) // 500 INSERT par paquet // ── Réinitialisation à la valeur par défaut ──────────────────── clClient:p_NombreInsertSimultanés(0) // toute valeur ≤ 0 ramène à 250

Valeur par défaut : 250. Compromis raisonnable entre vitesse d'envoi et limites pratiques des moteurs SQL (taille des paquets réseau, nombre maximal de paramètres autorisés par requête, mémoire serveur consommée par le buffer SQL). Augmenter la valeur peut accélérer un import sur de très gros volumes ; la diminuer peut être nécessaire si la connexion ou le serveur impose une limite plus stricte sur la taille des requêtes.

💡 Quand ajuster cette valeur

Augmenter (500, 1000…) : import volumineux sur un réseau performant, gain mesurable au-delà du seuil par défaut.

Diminuer (50, 100) : connexion réseau lente ou peu fiable (timeouts à éviter), serveur SQL avec une limite stricte sur la taille des paquets, base partagée où l'on veut éviter de monopoliser la connexion sur de longues requêtes.

Garder 250 : dans tous les autres cas. La valeur par défaut est calibrée pour fonctionner sans surprise sur la plupart des configurations MySQL · MariaDB · PostgreSQL.

05

🎯 Optimisation par flag p_bUpdated

Quand un tableau contient des dizaines ou des centaines d'enregistrements et que seuls quelques-uns ont été modifiés, parcourir et réécrire l'ensemble est inutile. Le paramètre bOptimisationUpdate = Vrai permet de filtrer côté framework :

ÉlémentbOptimisationUpdate = Faux (défaut)bOptimisationUpdate = Vrai
INSERT (PK = 0)TraitéToujours traité
DELETE (PK < 0)TraitéToujours traité
UPDATE (PK > 0) avec p_bUpdated = VraiTraitéTraité
UPDATE (PK > 0) avec p_bUpdated = FauxTraitéIgnoré

Le flag p_bUpdated est positionné automatiquement sur un objet dès qu'un de ses membres mappés est modifié. Le code appelant n'a donc rien à gérer manuellement : les UPDATE qui n'ont rien à mettre à jour seront simplement passés au lieu d'émettre un UPDATE SQL inutile.

💡 Quand activer bOptimisationUpdate = Vrai

Activer dans tout flux où le tableau contient une majorité d'éléments inchangés et une minorité d'éléments modifiés. Cas typiques :

• Édition d'une grille (tableau de saisie) où l'utilisateur ne modifie que quelques lignes

• Synchronisation de référentiel où la plupart des entrées sont déjà à jour

• Recalcul partiel d'un état métier

Laisser à Faux (défaut) si tous les éléments sont systématiquement à réécrire (réinitialisation, recalcul complet…).

⚠️ INSERT et DELETE échappent à l'optimisation

Le filtrage bOptimisationUpdate ne s'applique qu'aux UPDATE. Un INSERT (PK = 0) ou un DELETE (PK < 0) sont des opérations intentionnellement explicites de l'appelant : ils sont toujours exécutés, indépendamment de p_bUpdated. C'est un choix de design qui évite qu'un INSERT ou un DELETE oublié reste silencieusement non appliqué.

06

📊 Callback de progression

Pour les traitements longs (gros volumes, IHM avec jauge), le paramètre sCallbackProgression permet de désigner une procédure projet qui sera appelée à chaque itération avec la position courante et la taille totale du tableau.

Signature de la procédure callback

PROCÉDURE MaProcédureProgression(LOCAL nCompteur est un entier, LOCAL nDimTotal est un entier) // Exemple : mise à jour d'une jauge Jauge(SAI_Jauge, (nCompteur * 100) / nDimTotal) Multitâche(-1) // laisser respirer l'IHM FIN

Côté appel, transmettre simplement le nom de la procédure dans sCallbackProgression :

(bProcessing, nErrorCode, sErrorMessage) = clClient:mth_EnregistrerTableau(Faux, "MaProcédureProgression")
⚠️ Restriction d'environnement

Le callback de progression est appelé uniquement quand le code s'exécute depuis le composant externe ORM_CSHX2 sur cible ApplicationWindows. Dans les autres contextes — service, web-service, projet WinDev classique non-composant, mobile — le paramètre est ignoré silencieusement et la méthode s'exécute normalement sans appeler le callback.

Cette restriction est volontaire : un service ou un web-service n'a pas d'IHM à rafraîchir, et appeler un callback dans ces contextes aurait un coût sans bénéfice.

07

🔄 Transaction automatique

La méthode ouvre une seule transaction au début du traitement et la valide à la fin si tout s'est bien déroulé, ou l'annule en cas d'erreur. Ce comportement garantit que l'intégralité du tableau est traitée de façon atomique : tout réussit ou rien n'est conservé en base.

Si une erreur survient pendant le traitement (verrouillage, contrainte SQL, etc.), la boucle s'interrompt immédiatement et la transaction est annulée. Aucune des modifications déjà appliquées au cours du lot n'est conservée.

ℹ️ Compatible avec une transaction parente

Comme toutes les méthodes ORM_CSHX2, mth_EnregistrerTableau respecte le modèle owner : si une transaction est déjà active à l'appel, elle ne touche pas à la transaction et c'est l'appelant qui en est responsable. Détails dans ORM_Transactions.

08

💡 Exemples

Mode 1 — INSERT batch performant (sans récupération d'ID)

Cas d'usage : import d'une liste de produits dans une table catalogue, où l'on n'a pas besoin de réutiliser les IDs immédiatement.

// ── Préparer 1000 nouveaux produits dans m_tabResults ─────────── clProduit est un Produit POUR i = 1 _À_ 1000 nIndice est un entier = TableauAjoute(clProduit:m_tabResults, allouer un Produit()) clProduit:m_tabResults[nIndice]:m_LIBELLE = "Produit " + i clProduit:m_tabResults[nIndice]:m_PRIX = 19.90 // PK reste à 0 → INSERT // p_bRécupérationID reste à Faux → mode batch FIN // ── Enregistrement en lot ─────────────────────────────────────── (bProcessing, nErrorCode, sErrorMessage) = clProduit:mth_EnregistrerTableau() SI bProcessing ALORS Info("1000 produits importés") SINON Erreur(sErrorMessage) FIN

Mode 2 — INSERT avec récupération des IDs (mode unitaire)

Cas d'usage : créer plusieurs enregistrements et conserver leurs IDs auto-incrémentés pour les réutiliser.

POUR i = 1 _À_ 10 nIndice est un entier = TableauAjoute(clClient:m_tabResults, allouer un Client()) clClient:m_tabResults[nIndice]:m_DENOMINATION = "Client " + i clClient:m_tabResults[nIndice]:p_bRécupérationID = Vrai // 🔑 mode unitaire FIN (bProcessing, nErrorCode, sErrorMessage) = clClient:mth_EnregistrerTableau() SI bProcessing ALORS // Les IDs auto-incrémentés sont disponibles dans chaque objet POUR i = 1 _À_ TableauOccurrence(clClient:m_tabResults, indVariable) Trace("Client créé : ID=" + clClient:m_tabResults[i]:m_ID_CLIENT) FIN FIN

Mode 3 — Mix INSERT / UPDATE / DELETE en une seule passe

Cas d'usage : sauvegarde d'une grille de saisie où l'utilisateur a ajouté, modifié et supprimé des lignes. Tout est traité atomiquement.

// ── m_tabResults contient un mélange ──────────────────────────── // • clClient:m_tabResults[1]:m_ID_CLIENT = 0 → INSERT (nouveau) // • clClient:m_tabResults[2]:m_ID_CLIENT = 42 → UPDATE (modifié) // • clClient:m_tabResults[3]:m_ID_CLIENT = -55 → DELETE (à supprimer) (bProcessing, nErrorCode, sErrorMessage) = clClient:mth_EnregistrerTableau() // ✅ Tout réussit ou rien n'est conservé. // Une seule transaction couvre les 3 opérations.

Mode 4 — Optimisation pour grille avec peu de modifications

Cas d'usage : 200 lignes affichées, l'utilisateur en modifie 3. Sans optimisation, 200 UPDATE seraient émis. Avec bOptimisationUpdate = Vrai, seulement 3.

(bProcessing, nErrorCode, sErrorMessage) = clClient:mth_EnregistrerTableau(Vrai) // 🎯 Seuls les éléments avec p_bUpdated = Vrai sont traités en UPDATE // INSERT et DELETE éventuels du tableau sont toujours exécutés.

Mode 5 — Avec callback de progression

Cas d'usage : import de 10 000 lignes avec une jauge utilisateur dans le composant externe.

// ── Procédure de progression côté projet ──────────────────────── PROCÉDURE AvancementImport(LOCAL nCompteur est un entier, LOCAL nDimTotal est un entier) Jauge(SAI_JaugeImport, (nCompteur * 100) / nDimTotal) LIB_Statut = "Import " + nCompteur + " / " + nDimTotal Multitâche(-1) FIN // ── Appel avec callback ───────────────────────────────────────── (bProcessing, nErrorCode, sErrorMessage) = clProduit:mth_EnregistrerTableau(Faux, "AvancementImport")

Mode 6 — Import avec IDs imposés (cas avancé)

Cas d'usage : migration depuis une base externe en préservant les IDs d'origine.

POUR CHAQUE ligne DE tabImport nIndice est un entier = TableauAjoute(clClient:m_tabResults, allouer un Client()) clClient:m_tabResults[nIndice]:m_ID_CLIENT = ligne.id_origine // PK > 0 clClient:m_tabResults[nIndice]:m_DENOMINATION = ligne.nom clClient:m_tabResults[nIndice]:p_bNouvelEnregistrement = Vrai // 🔑 force l'INSERT FIN (bProcessing, nErrorCode, sErrorMessage) = clClient:mth_EnregistrerTableau() // Les enregistrements sont créés avec leurs IDs d'origine préservés, // pas d'auto-incrément ; utile pour conserver l'intégrité référentielle // quand on migre plusieurs tables liées d'un coup.

Mode 7 — Ajuster la taille des paquets en mode batch

Cas d'usage : import de plusieurs centaines de milliers de lignes sur un réseau performant, où l'on veut maximiser le débit en augmentant la taille des paquets INSERT_Multi.

// ── Augmenter la taille des paquets pour cet import volumineux ── clProduit:p_NombreInsertSimultanés(1000) // ... peuplement de m_tabResults avec 200 000 produits ... (bProcessing, nErrorCode, sErrorMessage) = clProduit:mth_EnregistrerTableau() // ── Restaurer la valeur par défaut après l'import ────────────── clProduit:p_NombreInsertSimultanés(0) // retour à 250

Mode 8 — Diagnostic via mode verbose

Pour visualiser le détail des requêtes émises (BEGIN, INSERT_Multi, UPDATE individuels, DELETE en cascade, COMMIT) :

ORM_VerboseMode(Vrai) clClient:mth_EnregistrerTableau() ORM_VerboseMode(Faux)

Détails complets : ORM_VerboseMode.