Previous Page: Table de consignation des événements  Next Page: Questions fréquentes

Utilisation du langage SQL dans des événements XML

La section suivante contient des informations qui vous permettront d'insérer du code SQL dans les événements XML.

Tous les exemples font référence à une hypothétique table vide appelée EMP. La méthode de génération de clé primaire utilisée pour obtenir des valeurs de clé primaire importe peu dans les exemples de cette section.

CREATE TABLE EMP
(
     EMPNO NUMERIC(8) NOT NULL,
    FNAME VARCHAR2(64),

    CONSTRAINT PK_EMPNO PRIMARY KEY (EMPNO)   
);

Le préfixe d'espace de nom jdbc utilisé dans toute cette section est implicitement lié à l'espace de nom « urn:dirxml:jdbc » lorsqu'il est cité en dehors d'un document XML.


Introduction

Vos pouvez utiliser du code SQL incorporé dans un événement XML. Tout comme il est possible d'installer des déclencheurs de base de données sur une table pour provoquer des effets secondaires dans une base de données, le code SQL incorporé dans des événements XML joue le rôle d'un déclencheur virtuel doté des mêmes fonctionnalités.

Le code SQL est incorporé aux événements XML par l'intermédiaire des éléments jdbc:statement et jdbc:sql. jdbc:statement peut contenir un ou plusieurs éléments jdbc:sql.

L'exemple de code XML suivant comporte une instruction SQL incorporée.

<input xmlns:jdbc="urn:dirxml:jdbc">
     <add class-name="EMP">
           <add-attr name="LNAME">
                <value>Doe</value>
           </add-attr>
      </add>
      <jdbc:statement>
            <jdbc:sql> UPDATE EMP SET FNAME = 'John'</jdbc:sql>
      </jdbc:statement>
</input>

Comme l'objet Abonné résout les événements <add> pour insérer des instructions, le code XML ci-dessous serait résolu comme suit :

INSERT INTO DIRXML.EMP(LNAME)VALUES('Doe');
UPDATE DIRXML.EMP SET FNAME = 'John';

IMPORTANT :  Utilisez des éléments et attributs qui indiquent l'espace de nom en préfixe pour incorporer du code SQL (sinon, le pilote ne le reconnaîtra pas). Dans l'exemple ci-dessus, l'espace de nom est « urn:dirxml:jdbc ». Le préfixe est l'identificateur qui figure à droite de l'identificateur « xmlns ». Dans l'exemple ci-dessus, le préfixe est « jdbc ». En pratique, le préfixe peut être ce que vous voulez, à condition qu'il soit lié au bon espace de nom.


Substitution de variables

Au lieu de vous imposer une analyse syntaxique des valeurs des champs d'une association DirXML, l'objet Abonné prend en charge la substitution de variable dans les instructions SQL incorporées. Exemple :

<input xmlns:jdbc="urn:dirxml:jdbc">
    <modify class-name="EMP">
        <association>EMPNO=1,table=EMP,schema=DIRXML
        </association>
         <modify-attr name="LNAME">
            <add-value>
                <value>DoeRaeMe</value>
            </add-value>
         </modify-attr>
    </modify>
     <jdbc:statement>
            <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE
           EMPNO = {$EMPNO}</jdbc:sql>
     </jdbc:statement>
</input>

Les marques de réservation variables doivent respecter la syntaxe modèle des valeurs d'attribut XSLT : {$<nomchamp>} et l'élément d'association doit précéder l'élément jdbc:statement dans le document XML ou figurer en tant qu'enfant de l'élément jdbc:statement. La marque <nomchamp> doit faire référence à l'un des noms d'attribut RDN dans la valeur d'association. Dans l'exemple ci-dessus, il n'existe qu'un seul attribut de dénomination, « EMPNO ».

Un événement <add> est le seul qui n'exige pas d'élément d'association pour traiter les instructions SQL incorporées avec substitution de variable, parce que l'association n'a pas encore été créée. Par ailleurs, les instructions SQL incorporées qui utilisent la substitution de variable doivent suivre, et non précéder, l'événement <add>. Exemple :

<input xmlns:jdbc="urn:dirxml:jdbc">
     <add class-name="EMP">
           <add-attr name="LNAME">
                <value>Doe</value>
           </add-attr>
      </add>
     <jdbc:statement>
           <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE 
          EMPNO = {$EMPNO}</jdbc:sql>
      </jdbc:statement>
</input>

Pour empêcher le suivi des informations personnelles, il est possible d'utiliser {$$password} pour désigner le contenu d'un élément <password> dans le même document.

<input xmlns:jdbc="urn:dirxml:jdbc">
<add class-name="EMP">
<add-attr name="LNAME">
<value>Doe</value>
</add-attr>
<password>NOVELL</password>
</add>
<jdbc:statement>
<jdbc:sql>CREATE USER DOE IDENTIFIED BY
{$$password}</jdbc:sql>
</jdbc:statement>
</input>


Placement des instructions

De la même manière que les déclencheurs de base de données peuvent être exécutés avant ou après une instruction qui les déclenche, le code SQL incorporé peut être placé avant ou après l'événement XML déclenchant. Les exemples suivants illustrent comment incorporer du code SQL avant ou après un événement XML.


Avant le déclencheur

<input xmlns:jdbc"urn:dirxml:jdbc">
      <jdbc:statement> <association>EMPNO=1,table=EMP,schema=DIRXML</association>
<jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE EMPNO = ${EMPNO}</JDBC:SQL>

      </jdbc:statement>
     <modify class-name="EMP">
          <association>EMPNO=1,table=EMP,schema=DIRXML</association>
          <modify-attr name="LNAME">
               <remove-all-values/> 
               <add-value>
                   <value>Doe</value>
               </add-value>
          </modify-attr>
     </modify>
</input>

Les données XML ci-dessus sont converties comme suit :

UPDATE DIRXML.EMP SET FNAME = 'John' WHERE EMPNO = 1;
UPDATE DIRXML.EMP SET LNAME = 'Doe' WHERE EMPNO = 1;


Après le déclencheur

<input xmlns:jdbc"urn:dirxml:jdbc">
     <modify class-name="EMP">
          <association>EMPNO=1,table=EMP,schema=DIRXML</association>
          <modify-attr name="LNAME">
               <remove-all-values/> 
               <add-value>
                   <value>Doe</value>
               </add-value>
          </modify-attr>
     </modify>
     <jdbc:statement> <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE EMPNO = ${EMPNO}</jdbc:sql>
     </jdbc:statement>
</input>

Les données XML ci-dessus sont converties comme suit :

UPDATE DIRXML.EMP SET FNAME = 'John' WHERE EMPNO = 1;
UPDATE DIRXML.EMP SET LNAME = 'Doe' WHERE EMPNO = 1;


Transactions manuelles et automatiques

Vous pouvez regrouper manuellement du code SQL et des événements XML à l'aide des deux attributs personnalisés suivants :


jdbc:transaction-type

Cet attribut possède deux valeurs : « manual » et « auto ». Par défaut, la plupart des événements XML qui présentent un intérêt sont des transactions de type « manual ». Le paramètre « manual » permet aux événements XML d'être convertis en plusieurs instructions SQL.

Le type de transaction « auto » est attribué par défaut aux événements SQL incorporés, car certaines instructions SQL ne peuvent pas être incluses dans une transaction manuelle.

<input xmlns:jdbc="urn:dirxml:jdbc">
     <add class-name="EMP" jdbc:transaction-type="auto">
            <add-attr name="LNAME">
                  <value>Doe</value>
            </add-attr>
      </add>
      <jdbc:statement>
            <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE
           EMPNO = {$EMPNO}</jdbc:sql>
      </jdbc:statement>
</input>

Les données XML ci-dessus sont converties comme suit :

INSERT INTO EMP(LNAME) VALUES('Doe');
/* COMMIT; //implicit */

UPDATE EMP SET FNAME = 'John' WHERE EMPNO = 1;
/* COMMIT; //implicit */


jdbc:transaction-id

L'objet Abonné ignore cet attribut, sauf si l'attribut jdbc:transaction-type de l'élément a par défaut ou explicitement la valeur « manual ». Le code XML suivant représente un exemple de transaction manuelle :

<input xmlns:jdbc="urn:dirxml:jdbc">
     <add class-name="EMP" jdbc:transaction-id="0">
           <add-attr name="LNAME">
                <value>Doe</value>
           </add-attr>
      </add>
      <jdbc:statement jdbc:transaction-type="manual" jdbc:transaction-id="0">
            <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE
           EMPNO = {$EMPNO}</jdbc:sql>
      </jdbc:statement>
</input>

Le code XML ci-dessus est converti comme suit :

INSERT INTO EMP(LNAME) VALUES('Doe');
UPDATE EMP SET FNAME = 'John' WHERE EMPNO = 1;
COMMIT; /* explicit */


Niveau d'isolation de transaction

Outre le regroupement d'instructions, les transactions permettent de préserver l'intégrité des données d'une base de données. Les transactions peuvent verrouiller les données afin d'empêcher tout accès concurrent ou toute modification. Le réglage des verrous est déterminé par le niveau d'isolation d'une transaction. En général, le niveau d'isolation par défaut utilisé par le pilote est suffisant et ne doit pas être modifié.

L'attribut personnalisé jdbc:isolation-level vous permet de régler le niveau d'isolation de transaction en cas de besoin. Cinq valeurs possibles sont définies dans l'interface java.sql.Connection :

Le niveau d'isolation de transaction par défaut du pilote est « lecture validée ». En cas de transaction manuelle, l'attribut jdbc:isolation-level doit être placé sur le premier élément de la transaction. Cet attribut est ignoré sur les éléments qui suivent. Exemple :

<input xmlns:jdbc="urn:dirxml:jdbc">
     <add class-name="EMP" jdbc:transaction-id="0"
      jdbc:isolation-level="serializable">
            <add-attr name="LNAME">
                 <value>Doe</value>
            </add-attr>
       </add>
       <jdbc:statement jdbc:transaction-type="manual"
       jdbc:transaction-id="0">
            <jdbc:sql>UPDATE EMP SET FNAME = 'John' 
             WHERE EMPNO = {$EMPNO}</jdbc:sql>
       </jdbc:statement>
</input>

Les données XML ci-dessus sont converties comme suit :

INSERT INTO EMP(LNAME) VALUES('Doe');
UPDATE EMP SET FNAME = 'John' WHERE EMPNO = 1;
COMMIT; /* explicit */


Type d'instruction

Le pilote exécute les instructions SQL incorporées, sans les comprendre. L'interface JDBC définit plusieurs méthodes d'exécution de différents types d'instructions SQL. Le tableau suivant recense ces méthodes.

Type d'instruction Méthode d'exécution

SELECT

Statement.executeQuery(String)

INSERT

Statement.executeUpdate(String)

UPDATE

Statement.executeUpdate(String)

DELETE

Statement.executeUpdate(String)

CALL ou EXECUTE

L'une quelconque des instructions ci-dessus

Statement.execute(String)

La solution la plus simple est d'assigner toutes les instructions SQL à la méthode execute(). Par défaut, c'est la méthode retenue par le pilote. Certains pilotes de fabricants tiers, notamment le pilote JDBC d'Oracle, mettent incorrectement en oeuvre les méthodes utilisées pour déterminer le nombre de résultats générés par la méthode execute(). En conséquence, le pilote peut être pris dans une boucle infinie qui entraîne une forte utilisation de l'unité centrale. Pour éviter ce problème, il est possible d'utiliser l'attribut jdbc:type sur n'importe quel élément jdbc:statement pour en assigner les instructions SQL aux méthodes executeQuery() ou executeUpdate() au lieu de la méthode execute() par défaut.

L'attribut jdbc:type possède deux valeurs : « update » et « query ». Il convient de définir la valeur « update » pour les instructions INSERT, UPDATE ou DELETE et la valeur « query » pour les instructions SELECT. En l'absence de cet attribut, le pilote assigne toutes les instructions SQL à la méthode execute(). S'il est placé sur un autre élément que jdbc:statement, cet attribut est ignoré.

Novell recommande d'affecter la valeur d'attribut jdbc:type="query" à toutes les instructions SELECT et l'attribut jdbc:type="update" à toutes les instructions INSERT, UPDATE et DELETE.

Le code XML suivant contient un exemple d'attribut jdbc:type  :

<input xmlns:jdbc="urn:dirxml:jdbc">
     <add class-name="EMP">
           <add-attr name="LNAME">
                <value>Doe</value>
           </add-attr>
      </add>
      <jdbc:statement jdbc:type="update">
           <jdbc:sql>UPDATE EMP SET FNAME = 'John' 
           WHERE EMPNO = {$EMPNO}</jdbc:sql>
      </jdbc:statement>
</input>


Requêtes SQL

Pour prendre pleinement en charge les fonctionnalités de requête d'une base de données et éviter la difficile conversion des requêtes SQL natives au format XML, le pilote prend en charge le traitement des requêtes SQL natives. Les instructions SELECT peuvent être incorporées à des documents XML exactement comme les autres instructions SQL.

Par exemple, si le contenu de la table EMP était le suivant :

EMPNO FNAME LNAME

1

'Jean'

'Untel'

Le document XML ci-dessous donnerait un document de sortie contenant un seul ensemble de résultats.

<input xmlns:jdbc="urn:dirxml:jdbc">
      <jdbc:statement jdbc:type="query">
           <jdbc:sql>SELECT * FROM EMP</jdbc:sql>
      </jdbc:statement>
</input>

<output xmlns:jdbc="urn:dirxml:jdbc">
      <jdbc:result-set jdbc:number-of-rows="1">
           <jdbc:row jdbc:number="1">
                <jdbc:column jdbc:name="EMPNO"
                           jdbc:position="1"
                           jdbc:type="java.sql.Types.DECIMAL
                      <jdbc:value>l</jdbc:value>
                 </jdbc:column>
                 <jdbc:column jdbc:name="FNAME" 
                             jdbc:position="2"
                             jdbc:type="java.sql.Types.VARCHAR>
                       <jdbc:value>John</jdbc:value>
                  </jdbc:column>
                  <jdbc:column jdbc:name="LNAME"
                           jdbc:position="3" jdbc:type="java.sql.Types.VARCHAR>
                        <jdbc:value>Doe</jdbc:value>
                   </jdbc:column>
            </jdbc:row>
       </jdbc:result-set>
       <status level="success"/>
</output> 

Les requêtes SQL produisent toujours un élément jdbc:result-set unique, que l'ensemble de résultats contienne ou non des lignes. Si l'ensemble de résultats est vide, l'attribut jdbc:number-of-rows sera défini à zéro.

Il est possible d'incorporer plusieurs requêtes dans un document. Les requêtes SQL n'exigent pas que les tables référencées soient connues du pilote, alors que les requêtes XML l'exigent.


Instructions en langage DDL (Data Definition Language - Langage de définition de données)

Il est généralement impossible d'exécuter une instruction DDL dans un déclencheur de base de données, car la plupart des bases n'autorisent pas les transactions DML et DDL mixtes. Bien que les déclencheurs virtuels ne permettent pas de surmonter cette limite de transaction, ils permettent l'exécution d'instructions DDL en tant qu'effets secondaires d'un événement XML. Exemple :

<input xmlns:jdbc="urn:dirxml:jdbc">
     <add class-name="EMP">
           <add-attr name="LNAME">
                <value>Doe</value>
          </add-attr>
      </add>
      <jdbc:statement>
           <jdbc:sql>CREATE USER DIRXML IDENTIFIED BY NOVELL </jdbc:sql>
      </jdbc:statement>
</input>

Les données XML ci-dessus sont converties comme suit :

INSERT INTO EMP(LNAME) VALUES('Doe');
/* COMMIT;  //implicit */

CREATE USER DIRXML IDENTIFIED BY NOVELL;
/* COMMIT;  //implicit */

L'utilisation des attributs jdbc:transaction-id et jdbc:transaction-type pour regrouper des instructions DML et DDL en une seule transaction entraînerait l'annulation de cette transaction dans la plupart des bases de données. Comme les instructions DDL sont généralement exécutées en tant que transactions distinctes, il est possible que l'instruction d'insertion de l'exemple ci-dessus aboutisse et que l'instruction de création d'utilisateur soit restaurée à son état initial. Par contre, il est impossible que l'instruction d'insertion échoue et que l'instruction de création d'utilisateur aboutisse. Le pilote arrête d'exécuter des transactions en chaîne dès que la première transaction est annulée.


Opérations logiques

Comme il est généralement impossible de mélanger des instructions DML et DDL dans une seule transaction, un événement unique peut se composer d'une ou de plusieurs transactions. Les attributs jdbc:op-id et jdbc:op-type peuvent être utilisés pour regrouper plusieurs transactions en une seule opération logique. Dans un regroupement de ce type, tous les membres de l'opération sont traités comme une seule entité pour ce qui concerne leur état. Autrement dit, si un membre de l'opération échoue, tous les membres renvoient le même niveau d'état. De même, tous les membres partagent le même type d'état.

<input xmlns:jdbc="urn:dirxml:jdbc">
     <add class-name="EMP" jdbc:op-id="0" 
          jdbc:op-type="password-set-operation">      
           <add-attr name="LNAME">
                <value>Doe</value>
          </add-attr>
          <password>NOVELL</password>
      </add>
      <jdbc:statement jdbc:op-id="0">
           <jdbc:sql>CREATE USER DIRXML IDENTIFIED BY {$$password} </jdbc:sql>
      </jdbc:statement>
</input>

L'attribut jdbc:op-type est ignoré sur tous les éléments à l'exception du premier élément de l'opération.


Meilleures pratiques

Dans un souci de performance, il est préférable d'appeler une seule procédure stockée qui contient plusieurs instructions plutôt que d'incorporer plusieurs instructions SQL dans un document XML. Exemple :

<input xmlns:jdbc="urn:dirxml:jdbc">
      <add class-name="EMP">
            <add-attr name="LNAME">
                 <value>Doe</value>
            </add-attr>
       </add>
       <jdbc:statement>
            <jdbc:sql>CALL PROCEDURE SET_FNAME('John') </jdbc:sql>
       </jdbc:statement>
</input>

est préférable à :

<input xmlns:jdbc="urn:dirxml:jdbc">
      <add class-name="EMP">
            <add-attr name="LNAME">
                 <value>Doe</value>
           </add-attr>
      </add>
      <jdbc:statement> <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE EMPNO = {$EMPNO}</jdbc:sql> </jdbc:statement> <jdbc:statement> <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE EMPNO = {$EMPNO}</jdbc:sql>      <jdbc:statement> <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE EMPNO = {$EMPNO}</jdbc:sql> </jdbc:statement> <jdbc:statement> <jdbc:sql>UPDATE EMP SET FNAME = 'John' WHERE EMPNO = {$EMPNO}</jdbc:sql> </jdbc:statement></input>



  Previous Page: Table de consignation des événements  Next Page: Questions fréquentes