📖 Guide · Architecture · ORM_CSHX2
Jointures multi-niveaux
Mécanisme complet de gestion des jointures SQL dans ORM_CSHX2. Ce guide couvre les 9 champs de la structure ST_ORM_JOINTURE, la déclaration dans le constructeur des classes métier, les jointures chaînées multi-niveaux qui permettent de traverser plusieurs niveaux de relations sans SQL manuel, et l'hydratation profonde automatique des objets liés.
Architecture
ST_ORM_JOINTURE · 9 champs
LEFT · INNER · RIGHT JOIN
Chaînage · Hydratation
Toute jointure ORM est décrite par une instance de ST_ORM_JOINTURE stockée dans le tableau de classe ::m_TabJointures. Elle porte toute l'information nécessaire pour trois opérations distinctes : générer la clause JOIN…ON, construire les colonnes JNT0n dans le SELECT, et hydrater l'objet lié après exécution.
ST_ORM_JOINTURE est une Structure
// 🔗 Côté GAUCHE du ON : nouvelle table ajoutée par le JOIN
sLeftTable est une chaîne
sLeftField est une chaîne
// 🔗 Côté DROIT du ON : table déjà présente (FROM ou JOIN précédent)
sRightTable est une chaîne
sRightField est une chaîne
// 🔧 Type, alias et critères additionnels
sJoinType est une chaîne // "INNER", "LEFT", "RIGHT"
sJoinAlias est une chaîne // Alias respecté tel quel dans le SQL
sAdditionalCriteria est une chaîne
// 📤 Mapping objet et projection SELECT
sObjectName est une chaîne
sQueryFields est une chaîne
FIN
Détail des 9 membres
🔗Côté GAUCHE du ON — nouvelle table ajoutée par le JOIN
sLeftTablechaîne
Nom de la nouvelle table ajoutée par le JOIN (côté gauche du ON).
Ex.JOIN Contact AS ContactUser ON ContactUser.Contact_Id = User.d_Contact_Id_User
→ sLeftTable = "Contact"
sLeftFieldchaîne
Clé de jointure côté gauche.
Ex.Pour le JOIN ci-dessus → sLeftField = "Contact_Id"
🔗Côté DROIT du ON — table déjà présente dans la requête
sRightTablechaîne
Table déjà présente dans la requête (issue du FROM ou d'un JOIN précédent).
Ex.JOIN Contact AS ContactUser ON ContactUser.Contact_Id = User.d_Contact_Id_User
→ sRightTable = "User"
sRightFieldchaîne
Clé de jointure côté droit.
Ex.Pour le JOIN ci-dessus → sRightField = "d_Contact_Id_User"
🔧Type, alias et critères additionnels
sJoinTypechaîne
Type de jointure SQL.
Valeurs"INNER" · "LEFT" · "RIGHT"
sJoinAliaschaîne
Alias SQL de la table jointe. Respecté tel quel dans le SQL généré (la casse est préservée). Permet de référencer la même table physique plusieurs fois dans la même requête (ex. créateur / modificateur).
sAdditionalCriteriachaîne · optionnel
Condition supplémentaire injectée dans le ON de la jointure. Vide si aucun critère additionnel.
Ex.sAdditionalCriteria = "AND fam.ACTIF = 1"
📤Mapping objet et projection SELECT
sObjectNamechaîne
Nom du membre WLangage de la classe métier qui portera l'objet lié hydraté après le SELECT.
Ex.sObjectName = "m_clFamille"
sQueryFieldschaîne
Liste des colonnes de la table droite à inclure dans le SELECT.
Ex.sQueryFields = "NOM_FAMILLE,CODE_FAMILLE"
ℹ️ Pas de champ sCodeSQL préconstruit
Contrairement à une approche naïve, la structure ne contient pas de clause JOIN préconstruite. Le composant reconstitue la clause dynamiquement depuis les 7 premiers champs au moment de l'exécution, ce qui lui permet de gérer automatiquement la casse des identifiants selon le provider actif (MySQL · MariaDB · PostgreSQL).
Chaque classe métier déclare ses jointures dans son constructeur, en alimentant le tableau ::m_TabJointures. Ce tableau est partagé au niveau classe — il est construit une seule fois pour toutes les instances.
// Constructeur de cArticle — jointure simple articles → familles
LOCAL
stJoin est une ST_ORM_JOINTURE
stJoin.sLeftTable = "articles"
stJoin.sLeftField = "FK_FAMILLE"
stJoin.sRightTable = "familles"
stJoin.sRightField = "ID_FAMILLE"
stJoin.sJoinType = "LEFT"
stJoin.sJoinAlias = "fam"
stJoin.sAdditionalCriteria = "AND fam.ACTIF = 1"
stJoin.sObjectName = "m_clFamille"
stJoin.sQueryFields = "NOM_FAMILLE,CODE_FAMILLE"
TableauAjouteLigne(::m_TabJointures, stJoin)
Pour traverser plusieurs niveaux de relations (articles → familles → rayons → départements), chaque JOIN supplémentaire référence l'alias du JOIN précédent dans sLeftTable.
Niveau 2 — familles → rayons
// sLeftTable = alias du niveau 1 ("fam"), pas "familles"
stJoin.sLeftTable = "fam"
stJoin.sLeftField = "FK_RAYON"
stJoin.sRightTable = "rayons"
stJoin.sRightField = "ID_RAYON"
stJoin.sJoinType = "LEFT"
stJoin.sJoinAlias = "ray"
stJoin.sAdditionalCriteria = ""
stJoin.sObjectName = "m_clRayon"
stJoin.sQueryFields = "NOM_RAYON"
TableauAjouteLigne(::m_TabJointures, stJoin)
Niveau 3 — rayons → départements
stJoin.sLeftTable = "ray"
stJoin.sLeftField = "FK_DEP"
stJoin.sRightTable = "departements"
stJoin.sRightField = "ID_DEP"
stJoin.sJoinType = "LEFT"
stJoin.sJoinAlias = "dep"
stJoin.sAdditionalCriteria = ""
stJoin.sObjectName = "m_clDepartement"
stJoin.sQueryFields = "NOM_DEPARTEMENT"
TableauAjouteLigne(::m_TabJointures, stJoin)
SQL résultant
LEFT JOIN familles fam
ON fam.ID_FAMILLE = articles.FK_FAMILLE AND fam.ACTIF = 1
LEFT JOIN rayons ray
ON ray.ID_RAYON = fam.FK_RAYON
LEFT JOIN departements dep
ON dep.ID_DEP = ray.FK_DEP
⚠️ Ordre d'insertion dans ::m_TabJointures
Les jointures doivent être déclarées dans l'ordre de dépendance : parent avant enfant. fam avant ray qui référence fam.FK_RAYON. Un ordre incorrect produit une erreur SQL Unknown column 'fam.FK_RAYON' à l'exécution.
Deux jointures vers la même table physique (ex. utilisateurs pour le créateur et le modificateur d'un enregistrement) sont différenciées par leur sJoinAlias, leur sObjectName, et leur index dans ::m_TabJointures (qui détermine le numéro JNT0n).
// JOIN 1 — créateur
stJoin.sLeftTable = "commandes"
stJoin.sLeftField = "SQL_ID_USER"
stJoin.sRightTable = "utilisateurs"
stJoin.sRightField = "ID_USER"
stJoin.sJoinType = "LEFT"
stJoin.sJoinAlias = "usr_crea"
stJoin.sObjectName = "m_clUtilisateurCréateur"
TableauAjouteLigne(::m_TabJointures, stJoin) // → JNT01
// JOIN 2 — modificateur
stJoin.sLeftTable = "commandes"
stJoin.sLeftField = "SQL_ID_USER_UPDATE"
stJoin.sRightTable = "utilisateurs"
stJoin.sRightField = "ID_USER"
stJoin.sJoinType = "LEFT"
stJoin.sJoinAlias = "usr_modif"
stJoin.sObjectName = "m_clUtilisateurModificateur"
TableauAjouteLigne(::m_TabJointures, stJoin) // → JNT02
sQueryFields liste les colonnes de la table droite à inclure dans le SELECT. Ces colonnes sont préfixées par l'alias de la jointure et automatiquement aliasées sous la forme JNT0n_NOMCOLONNE dans la requête générée :
// sQueryFields = "NOM_FAMILLE,CODE_FAMILLE"
-- SELECT produit :
fam.NOM_FAMILLE AS JNT01NOM_FAMILLE,
fam.CODE_FAMILLE AS JNT01CODE_FAMILLE
Le numéro dans JNT01 correspond à la position (base 1) de la jointure dans ::m_TabJointures. Le composant gère automatiquement la génération des alias et leur résolution lors de l'hydratation des objets liés.