HOWTO développement Sashipa

 Fonctionnalités avancées des selectQueryBuilder en Sashipa.

Vue générale

Un élément selectQueryBuilder est le générateur d'une requête SQL. Ce même élément peut définir complètement une requête, ou être utilisé pour compléter une requête déjà créée par un composant. Il n'est donc pas toujours complet : par exemple on ne spécifie jamais de clause select dans la requête d'un listForm : c'est le listForm qui s'en charge.

Voici le squelette d'une requête selectQueryBuilder :

  <selectQueryBuilder distinctRequired='no' type='list'>
    <castFilterSet>
      ...
    </castFilterSet>
    <selectStatementBuilder>
      ...
    </selectStatementBuilder>
    <fromStatementBuilder>
      ...
    </fromStatementBuilder>
    <whereStatementBuilder>
      ...
    </whereStatementBuilder>
  </selectQueryBuilder>

Le détail des éléments contenus est dans la suite du document. On remarque d'ores et déjà les attributs généraux :

Les castFilters

Une requête est filtrée (ou non) par le filtre de l'écran qui la contient. Un filtre étant une valeur de clef primaire. Un castFilter est un critère qui viendra s'ajouter dans la clause Where de la requête SQL générée. Ce critère est de type 'colonnes_clef_primaire = filtre' ou bien 'colonnes_clef_etrangere = filtre', ou bien encore une composition de sous-castFilters.

On spécifie un ensemble de castFilters pour la requête. Lorsque la requête reçoit un filtre, elle tente de l'appliquer à chacun jusqu'à ce que l'un d'entre eux accepte le filtre. Elle prend le critère du premier qui l'accepte.

Voyons un exemple :

  <castFilterSet filterSensitive='yes' autoCastFilter='yes' 
                 stuckWhenNoFilter='no'>
    <fkCastFilter>
      <instanceFk schemaFk='fk_tblVoyage_tblPersonne' />
    </fkCastFilter>
    <pkCastFilter>
      <instanceTable schemaTable='tblVoyage' />
    </pkCastFilter>
  </castFilterSet>

Les attributs de l'élément castFilterSet prennent leur valeur par défaut si on ne les précise pas. Je les ai mis ici avec leur valeur par défaut.

Clause Select

Une clause select est composée d'instances de colonnes ou de groupes d'instances de colonnes. Voici une clause select qui contient un élément de chaque type disponible. Elle est basée sur une clause from fictive dont la mainInstanceTable est la table 'tblPersonne'.

  <selectStatementBuilder autoPrimaryKey='yes'>
    <instanceColumnList>
      <instanceColumn schemaColumn='tblPersonne_Nom' />
      <referencedUserKeyColumns join='inner'>
        <instanceFk schemaFk='fk_tblCongresparticipant_tblCongres' />
      </referencedUserKeyColumns>
      <agregatInstanceColumn type='count' schemaFk='fk_tblVoyage_tblPersonne' 
                             letterCount='6' sort='desc'>
        <title>Voyages</title>
      </agregatInstanceColumn>
      <instanceFk schemaFk='fk_tblPersonne_tblGenre' />
      <manualColumn>                            <!-- Syntaxe specifique MySQL -->
        <schemaColumnRef schemaColumn='tblPersonne_NumeroParAnnee' />
        <contentOfExpression>ifnull(max(NumeroParAnnee), 0)+1</contentOfExpression>
      </manualColumn>
    </instanceColumnList>
  </selectStatementBuilder>

Par défaut dans les requêtes de type 'list', les colonnes de la clef primaire de la table principale de la clause From, sont incluses dans la clause Select. On peut annuler ce comportement en positionnant l'attribut autoPrimaryKey à 'no'.

Une instanceColumn est une colonne d'une instanceTable. Elle contient donc l'instance de table qu'elle référence (son surnom doit correspondre à une instance de table de la clause from). Si l'on ne spécifie pas d'instance de table, c'est celle par défaut de la schemaTable qui est utilisée.

L'élément referencedUserKeyColumns est un raccourci d'écriture. Il rajoute les instances de colonnes qui composent la userKey de la table référencée ET il ajoute la jointure dans la clause from. Sans qu'il soit nécessaire de le faire.

L'élément agregatInstanceColumn crée une colonne calculée. Il en existe deux sortes : celles de type 'count' qui dénombre simplement des enregistrements liés, et celles opérant des calculs sur des colonnes. Dans ce dernier cas, il est nécessaire de décrire la jointure de la table dépendante manuellement dans la clause from (cf exemple ici). Sinon ce n'est pas nécessaire.

L'élément instanceFk ajoute les instance de colonnes qui composent une clef étrangère.

On peut enfin écrire manuellement une expression SQL gràce à l'élément manualColumn. Dans le cas d'une requête de sélection qui renvoie la valeur par défaut d'une colonne pour le mode ajout, on spécifie la schemaColumn qui est concernée, puis le contenu de l'expression. Attention : l'emploi de cet élément peut empècher la compatibilité de votre code Sashipa avec divers SGBD.

Clause From

Voyons la syntaxe d'une clause from. Une clause from est composée d'un élément mainInstanceTable et d'une liste de fkJoin pour les jointures.

  <fromStatementBuilder>
    <mainInstanceTable schemaTable='tblPersonne' />

    <fkJoin join='inner'>
      <instanceFk schemaFk='fk_tblPersonne_tblGenre' />
    </fkJoin>

    <fkJoin join='left'>
      <instanceFk schemaFk='fk_tblPersonne_tblPays_Residence'>
        <startInstanceTable schemaTable='tblPersonne' />
      </instanceFk>
      <referencedInstanceTable schemaTable='tblPays' nickName='pays_residence' />
      <XxxCriteriaXxx> ... </XxxCriteriaXxx>
    </fkJoin>

    <fkJoin join='inner'>
      <instanceFk schemaFk='fk_tblPersonne_tblPays_Nationalite'>
        <startInstanceTable schemaTable='tblPersonne' />
      </instanceFk>
      <referencedInstanceTable schemaTable='tblPays' nickName='pays_naissance' />
    </fkJoin>
  </fromStatementBuilder>

Chaque instance de table (éléments mainInstanceTable, startInstanceTable, referencedInstanceTable) ont un attribut obligatoire schemaTable, et un surnom éventuel dans la requête SQL.

Les jointures (éléments fkJoin) ont un attribut précisant le type (inner, left, right, full, nojoin). On utilisera le plus souvent 'left' si la clef étrangère peut être nulle, et 'inner' si la clef étrangère est obligatoirement renseignée.

Les instances de tables sont optionnelles, car elles peuvent être déduites par défaut des clefs étrangères. On les précise si l'on souhaite les renommer dans la requête SQL. Ici on précise un startInstanceTable non renommé uniquement à titre d'exemple, pour montrer à quel endroit on le met.

Le critère est optionnel aussi. S'il existe, il sera ajouté au critère de jointure avec un ET logique (le AND en SQL). Les critères sont décrits plus bas dans ce document, avec la clause Where.

Clause Where

Une clause where contient une suite de constantCriteria, manualCriteria, et/ou de criteriaBuilder. Le séparateur logique est obligatoirement le AND. Si vous désirez ajouter des critères avec des OR, il faut les mettre dans un criteriaBuilder dont le mode est 'or'.

Clause Where : les critères constantCriteria

Vous pouvez spécifier un critère SQL de façon semi-manuelle, par exemple :

  <whereStatementBuilder>
    <constantCriteria>
      <instanceColumn schemaColumn='adr_ContactAdresseLabel' />
      <endOfCriteria>is not null</endOfCriteria>
    </constantCriteria>
  </whereStatementBuilder>

Le critère généré est la concaténation de l'instance de colonne et du contenu de l'élément endOfCriteria.

Le contenu de endOfCriteria est libre, vous pouvez y mettre le critère SQL que vous voulez. Attention cependant si vous souhaitez changer ultérieurement de SGBD : ce critère peut provoquer des incompatibilités.

Clause Where : les critères manualCriteria

Vous pouvez spécifier un critère SQL de façon complètement manuelle, par exemple :

  <whereStatementBuilder>
    <manualCriteria>
      <contentOfCriteria>ContactAdresseLabel is not null</contentOfCriteria>
      <instanceColumnList>
        <instanceColumn schemaColumn='adr_ContactAdresseLabel' />
      </instanceColumnList>
    </manualCriteria>
  </whereStatementBuilder>

Remarques :

Clause Where : les critères criteriaBuilder

Un criteriaBuilder est un critère composé d'un ensemble de sous-critères. Ces sous-critères peuvent être des constantCriteria, des manualCriteria, des researchCriteria (voir explication plus bas) ou même d'autres criteriaBuilder.

  <whereStatementBuilder>
    <criteriaBuilder mode='and' emptyAction='stuck'
                     defaultSubEmptyAction='ignore'>

      <!-- ... sous-criteres ... -->

    </criteriaBuilder>
  </whereStatementBuilder>

Voyons les attributs :

Clause Where : les critères researchCriteria

Un researchCriteria est un critère dynamique qui se modifie en fonction d'un champ. Pour d'obscures raisons historiques, il ne peut être contenu directement par la clause Where. On le met donc dans un élément criteriaBuilder. Je montre ici deux researchCriteria, le premier va prendre une valeur dans un fkField, l'autre dans un champ simple :

  <whereStatementBuilder>
    <criteriaBuilder mode='and' emptyAction='stuck'
                     defaultSubEmptyAction='ignore'>
      
      <researchCriteria emptyAction='stuck'>
        <castSchemaValue>
          <fkCastFilter>
            <instanceFk schemaFk='fk_tblPersonne_tblGenre' />
          </fkCastFilter>
        </castSchemaValue>
        <fieldRef field='fieldFkGenre' />
      </researchCriteria>

      <researchCriteria>
        <castSchemaValue>
          <instanceColumn schemaColumn='tblPersonne_Nom' />
        </castSchemaValue>
        <fieldRef field='fieldNom' />
      </researchCriteria>
      
    </criteriaBuilder>
  </whereStatementBuilder>

Un élément researchCriteria est composé d'un castSchemaValue et d'une référence à un champ du même écran (screen). Ce champ doit être défini et nommé auparavant (dans un researchForm ou un cardForm par exemple).

Un élément castSchemaValue peut réceptionner des valeurs de clefs étrangères ou primaires (au moyen de castFilter) ou bien des valeurs de colonnes directement (avec une instanceColumn).

Dans cet exemple, le premier researchCriteria demande une valeur (emptyAction='stuck') et bloquera le contenu entier du criteriaBuilder si le champ 'fieldFkGenre' est vide. De plus, le criteriaBuilder bloquera la requête (ou son criteriaBuilder parent) s'il est vide. En revanche, le deuxième researchCriteria n'apparaîtra dans la requête que si le champ 'fieldNom' contient une valeur. Mais il sera ignoré sinon et la requête sera tout de même générée.

© Copyright 2003 Sashipa-Melba Team. Ce document de la technologie Sashipa-Melba est sous licence GNU FDL Vous pouvez le copier et modifier librement les copies tant que cette mention apparaît clairement.