WebSphere Message Broker Version 8.0.0.5 Betriebssysteme: AIX, HP-Itanium, Linux, Solaris, Windows, z/OS

Sehen Sie sich die Informationen zur aktuellen Produktversion im IBM Integration Bus Version 9.0 an.

CREATE PROCEDURE-Anweisung

Die Anweisung CREATE PROCEDURE definiert eine aufrufbare Funktion oder Prozedur.

Sie können auch die Anweisung CREATE FUNCTION zum Definieren einer aufrufbaren Funktion oder Prozedur verwenden, die auch als Routine bezeichnet wird.

Jede Routine hat einen Namen, der innerhalb des Schemas, der die Routine angehört, eindeutig sein muss. Routinenamen können daher nicht überlappen. Wenn der Broker einen überlappenden Routinenamen findet, wird eine Ausnahme ausgegeben.

Syntax

Syntaxdiagramm lesenSyntaxdiagramm überspringen
>>-CREATE--| Routinetyp |--Routinename-------------------------->

>--(--| Parameterliste |--)--+-----------------+---------------->
                             '-| Rückgabetyp |-'   

>--+-------------+--+--------------+--| Routinehauptteil |-----><
   '-| Sprache |-'  '-| Ergebnis |-'                         

Routinetyp

|--+-FUNCTION--+------------------------------------------------|
   '-PROCEDURE-'   

Parameterliste

   .-,-----------------.   
   V                   |   
|----+---------------+-+----------------------------------------|
     '-| Parameter |-'     

Parameter

        (1)                                                
|--+-IN-----+--Parametername--+-+----------+--Datentyp-+-------->
   +-OUT----+                 | '-CONSTANT-'           |   
   '-INOUT--'                 |           (2)          |   
                              +-NAMESPACE--------------+   
                              '-NAME-------------------'   

   .-NULLABLE-.   
>--+----------+-------------------------------------------------|
   '-NOT NULL-'   

Rückgabetyp

                      .-NULLABLE-.   
|--RETURNS--Datentyp--+----------+------------------------------|
                      '-NOT NULL-'   

Sprache

|--LANGUAGE--+-ESQL---------+-----------------------------------|
             |          (3) |   
             +-DATABASE-----+   
             +-.NET---------+   
             +-CLR----------+   
             '-JAVA---------'   

Ergebnis

|--DYNAMIC RESULT SETS--ganze Zahl------------------------------|

Routinehauptteil

|--+-Anweisung-------------------------------------------------------------+--|
   '-EXTERNAL--NAME--Name_der_externen_Routine--+------------------------+-'   
                                                +-.NetTypeInfo-----------+     
                                                '-JavaClassLoaderService-'     

.NetTypeInfo

|--ASSEMBLY--Assemblierungsname--------------------------------->

   .------------------------------------------.   
   V  (4)                                     |   
>--------+----------------------------------+-+-----------------|
         +-APPDOMAIN--Domänenname-----------+     
         +-VERSION--Version-----------------+     
         +-CULTURE--Kultur------------------+     
         '-PUBLICKEYTOKEN--Public-Key-Token-'     

JavaClassLoaderService

|--CLASSLOADER--ClassLoaderConfigurableServiceName--------------|

Anmerkungen:
  1. Hat die Routine den Typ FUNCTION (Funktion), ist der Richtungsanzeiger (IN, OUT oder INOUT) optional für jeden Parameter. Zu Dokumentationszwecken wird jedoch dringend empfohlen, einen Richtungsanzeiger für alle neuen Routinen festzulegen. Wenn Sie keine Richtung angeben, wird der Standardwert IN verwendet.
  2. Wenn die Klausel NAMESPACE oder NAME verwendet wird, ist ihr Wert implizit CONSTANT und hat den Typ CHARACTER (Zeichen). Weitere Informationen zur Verwendung von CONSTANT-Variablen finden Sie im Abschnitt DECLARE-Anweisung.
  3. Hat die Routine den Typ FUNCTION, kann für LANGUAGE nicht DATABASE angegeben werden.
  4. Jeder kann nur einmal angegeben werden.

Parameterrichtungen

Parametern, die an Routinen übergeben werden, ist stets eine Richtung zugeordnet. Es gibt folgende Richtungsarten:
IN
Der Wert des Parameters kann nicht von der Routine geändert werden. Ein NULL-Wert für den Parameter ist zulässig und kann an die Routine übermittelt werden.
OUT
Wenn er von der aufgerufenen Routine empfangen wird, hat der in die Routine eingestellte Parameter immer den Wert NULL als korrekten Datentyp. Dieser Wert wird gesetzt, unabhängig von dem Parameterwert vor dem Aufruf der Routine. Die Routine darf den Wert des Parameters ändern.
INOUT
INOUT ist sowohl ein IN-Parameter als auch ein OUT-Parameter. Er übergibt einen Wert an die Routine, der dann von ihr geändert werden kann. Ein NULL-Wert für den Parameter ist zulässig und kann sowohl in die Routine eingestellt als auch aus ihr heraus übermittelt werden.
NULLABLE
Diese optionale Klausel gibt an, dass der Wert des Parameters einen Nullwert enthalten kann. NULL ist die Standardklausel, falls diese Klausel ausgelassen wird; andernfalls wir die Klausel NOT NULL angegeben.
NOT NULL
Diese optionale Klausel gibt an, dass der Wert des Parameters keinen Nullwert enthalten darf. Wenn ein Parameter als NOT NULL markiert ist und im Aufruf ein Nullwert übergeben wird, löst dies eine Ausnahme aus. Hinweis: Zurzeit kann diese Klausel nur in Verbindung mit LANGUAGE CLR oder LANGUAGE .NET verwendet werden.

Hat die Routine den Typ FUNCTION (Funktion), ist der Richtungsanzeiger (IN, OUT, INOUT) für jeden Parameter optional. Wir empfehlen jedoch dringend, aus Dokumentationsgründen einen Richtungsanzeiger für alle neuen Routinen mit beliebigem Typ festzulegen.

ESQL-Variablen, die als CONSTANT deklariert sind (oder Verweise auf als CONSTANT deklarierte Variablen) dürfen nicht die Richtung OUT oder INOUT haben.

ESQL-Routinen

ESQL-Routinen sind in ESQL geschrieben und ihre LANGUAGE-Klausel gibt die Sprache ESQL an. Als Hauptteil einer ESQL-Routine wird gewöhnlich eine Verbindungsanweisung in der Form BEGIN … END verwendet, die mehrere Anweisungen zur Verarbeitung der Parameter enthält, die an die Routine übermittelt wurden.

ESQL-Beispiel 1

Im folgenden Beispiel sehen Sie dieselbe Prozedur wie in Beispiel 1 einer Datenbankroutine, allerdings als ESQL-Routine und nicht als gespeicherte Prozedur implementiert. Die CALL-Syntax und Ergebnisse dieser Routine entsprechen denen in:
CREATE PROCEDURE swapParms (
  IN parm1 CHARACTER,
  OUT parm2  CHARACTER,
  INOUT parm3 CHARACTER )
BEGIN
   SET parm2 = parm3;
   SET parm3 = parm1;
 END; 

ESQL-Beispiel 2

Diese Beispielprozedur zeigt die rekursive Verwendung einer ESQL-Routine. Sie analysiert die Syntax einer Baumstruktur an sämtlichen Stellen am und unterhalb des angegebenen Ausgangspunkts und dokumentiert anschließend das Ergebnis:

SET OutputRoot.MQMD = InputRoot.MQMD;

  DECLARE answer CHARACTER;
  SET     answer = '';

  CALL navigate(InputRoot.XMLNS, answer);
  SET OutputRoot.XMLNS.Data.FieldNames = answer;


  CREATE PROCEDURE navigate (IN root REFERENCE, INOUT answer CHARACTER)
  BEGIN
    SET answer = answer || 'Reached Field... Type:' || CAST(FIELDTYPE(root) AS CHAR)||
    ': Name:' || FIELDNAME(root) || ': Value :' || root || ': ';

    DECLARE cursor REFERENCE TO root;
    MOVE cursor FIRSTCHILD;
    IF LASTMOVE(cursor) THEN
      SET answer = answer || 'Field has children... drilling down ';
    ELSE
      SET answer = answer || 'Listing siblings... ';
    END IF;

    WHILE LASTMOVE(cursor) DO
      CALL navigate(cursor, answer);
      MOVE cursor NEXTSIBLING;
    END WHILE;

    SET answer = answer || 'Finished siblings... Popping up ';
  END;

Lautet die Eingabenachricht wie folgt:

<Person>
  <Name>John Smith</Name>
  <Salary period='monthly' taxable='yes'>-1200</Salary>
</Person>

dann erstellt die Prozedur die folgende Ausgabe, die manuell formatiert wurde:

  Reached Field... Type:16777232: Name:XML: Value :: Field has children... drilling down 
  Reached Field... Type:16777216: Name:Person: Value :: Field has children... drilling down 
  Reached Field... Type:16777216: Name:Name: 
  Value :John Smith: Field has children... drilling down 
  Reached Field... Type:33554432: Name:: 
  Value :John Smith: Listing siblings... Finished siblings... Popping up
  Finished siblings... Popping up 
  Reached Field... Type:16777216: Name:Salary:
  Value :-1200: Field has children... drilling down 
  Reached Field... Type:50331648: Name:period: 
  Value :monthly: Listing siblings... Finished siblings... Popping up
  Reached Field... Type:50331648: Name:taxable: 
  Value :yes: Listing siblings... Finished siblings... Popping up 
  Reached Field... Type:33554432: Name:: 
  Value :-1200: Listing siblings... Finished siblings... Popping up 
  Finished siblings... Popping up 
  Finished siblings... Popping up 
  Finished siblings... Popping up

.NET-Routinen

Eine .NET-Routine wird als .NET-Methode implementiert und hat die LANGUAGE-Klausel .NET oder CLR. Bei .NET-Routinen muss der Name_der_externen_Routine den Klassennamen und den Namen der aufzurufenden .NET-Methode enthalten. Geben Sie den Namen_der_externen_Routine wie im folgenden Beispiel an:

>>--"-- Klassenname---.---Methodenname--"--------------><
Dabei steht Name_Klasse für die Klasse, die die Methode enthält, und Name_der_Methode für die Methode, die aufgerufen werden soll. Wenn die Klasse Teil eines Namespace oder eine verschachtelte Klasse ist, muss die Klassen-ID die Namen aller Namespaces und verschachtelten Klassen enthalten, z. B. "IBM.Broker.test.MyOuterClass.MyNestedClass.MyMethod"

Der Broker durchsucht das GAC und die AppDomain-Basisposition nach der .NET-Klasse.

Jede .NET-Methode, die Sie aufrufen möchten, muss eine öffentliche statische Methode sein. Darüber hinaus müssen alle Parameter in Zuordnungstabellen für Datentypen von ESQL nach .NET aufgelistet sein. Wenn die Methode einen Rückgabetyp besitzt, muss dieser außerdem in der IN-Datentypzuordnungstabelle aufgelistet sein.

Die Signatur der .NET-Methoden muss mit der Deklaration der ESQL-Routinen der Methode übereinstimmen. Außerdem müssen folgende Regeln eingehalten werden:
  • Stellen Sie sicher, dass der Name der .NET-Methode, einschließlich des Klassennamens und der Namespace-Qualifikationsmerkmale, dem externen Namen (EXTERNAL NAME) der ESQL-Prozeduren entspricht.
  • Wenn die .NET-Methode keinen Rückgabetyp besitzt, geben Sie keine RETURNS-Klausel in der Definition der ESQL-Routinen an. Wenn die .NET-Methode jedoch einen Rückgabetyp besitzt, müssen Sie eine RETURNS-Klausel in der Definition der ESQL-Routinen angegeben.
  • Stellen Sie sicher, dass alle Typen und Anweisungen der Parameter gemäß den in Zuordnungstabellen für Datentypen von ESQL nach .NET aufgelisteten Regeln der ESQL-Deklaration entsprechen.
  • Stellen Sie sicher, dass der Rückgabetyp der Methoden dem Datentyp der RETURNS-Klausel entspricht.
  • Setzen Sie EXTERNAL NAME in Anführungszeichen, weil er mindestens "Klasse.Methode" enthalten muss.
  • Wenn Sie eine überlastete .NET-Methode aufrufen möchten, müssen Sie eine separate ESQL-Definition für jede überlastete Methode erstellen und jeder ESQL-Definition einen eindeutigen Routinenamen geben.
.NET Type Info
Die Klausel im Abschnitt .NET Type Info gilt nur für LANGUAGE .NET-Routinen.
ASSEMBLY
ASSEMBLY gibt die .NET-Assembly an, in der sich die aufzurufende Methode befindet. Wenn sich die Assembly im GAC befindet, genügt der Assemblyname (z. B. “MyAssembly”). Befindet sich die Assembly jedoch nicht im GAC, muss der vollständig qualifizierte Pfad der Assembly angegeben werden.
APPDOMAIN
Dieser Parameter gibt den Namen der APPDOMAIN an, in die die Assembly geladen und in der die Methode ausgeführt werden sollen. Wird diese Klausel weggelassen, wird APPDOMAIN auf den Namen der Anwendung gesetzt, zu der der Nachrichtenfluss gehört. Gehört der Nachrichtenfluss zu keiner Anwendung, wird APPDOMAIN auf den Namen des Nachrichtenflusses gesetzt.
VERSION
Dieser Parameter gibt die genaue Version der zu ladenden Assembly an. Ist die Version nicht angegeben, wird die erste Version verwendet, die in der genannten Assembly gefunden wird.
CULTURE
Mit diesem Parameter kann eine genaue Kultur für die Assembly angegeben werden. Der Standardwert für die Kultur lautet 'neutral'.
PUBLICKEYTOKEN
Wenn sich die zu ladende Assembly im GAC befindet, muss das Public-Key-Token bereitgestellt werden. Befindet sich die Assembly nicht im GAC, ist diese Klausel optional. Bei der Suche nach einer Assembly gilt die vom .NET-Framework definierte Suchreihenfolge. Ausführliche Informationen hierzu finden Sie im MSDN. Es gilt jedoch die gekürzte Version. Wenn der Assemblyname nicht vollständig qualifiziert ist, wird die Basis für die AppDomain verwendet. Ist der Assemblyname vollständig qualifiziert (d. h., Version und Public-Key-Token wurden angegeben), wird erst das GAC durchsucht und dann der AppDomain-Basisordner.

.NET-Routine Beispiel 1

Definiert eine Prozedur, die eine .NET-Methode darstellt, die eine Systemzeichenfolge (System:String) mit drei Parametern mit unterschiedlichen Anweisungen zurückgibt.
CREATE PROCEDURE Swap ( 
       IN a INT NOT NULL, 
       OUT b INT NOT NULL, 
       INOUT c INT NOT NULL ) RETURNS CHARACTER NOT NULL
LANGUAGE .NET
EXTERNAL NAME "FunctionTests.SwapString"
ASSEMBLY "C:\coding\test projects\MyAssembly"
APPDOMAIN "MyDomain";
Mit dem folgenden ESQL-Code kann Swap aufgerufen werden.
CALL Swap( intVar1, intVar2, intVar3 ) INTO ReturnVar;
-- oder
SET ReturnVar = Swap ( intVar1, intVar2, intVar3);

.NET-Routine Beispiel 2

Definiert eine Prozedur, die eine .NET-Methode, die keinen Rückgabewert besitzt, mit drei nullfähigen Parametern mit unterschiedlichen Anweisungen darstellt.

CREATE PROCEDURE SwapNullable ( 
       IN a INTEGER NULLABLE, 
       OUT b INTEGER NULLABLE, 
       INOUT c INTEGER NULLABLE )
LANGUAGE CLR
EXTERNAL NAME "FunctionTests.SwapStringNullable"
ASSEMBLY "MyAssembly2"
APPDOMAIN "MyDomain";
SwapNullable muss mit dem folgenden ESQL-Code aufgerufen werden.
CALL SwapNullable(intVar1, intVar2, intVar3);

Mustercode für verschiedene .NET-Sprachen, der Methoden für beide Beispiele bereitstellt

C#

public class FunctionTests
{

  public static string Swap(int pIn, out int pOut, ref int pInout)
  {
    pOut = pInout;
    pInout = pIn;
    return "Finished";
  }

  public static void SwapNullable(long? pIn, out long? pOut, ref long? pInout)
  {
    pOut = pInout;
    pInout = pIn;
  }

}

VB

Public Class FunctionTests

  Shared Function Swap(ByVal pIn As Integer, <Out()> ByRef pOut As Integer, ByRef pInout As Integer) As String
    pOut = pInout
    pInout = pIn
    Return "Finished"
  End Function

  Shared Sub SwapNullable(ByVal pIn As Long?, ByRef pOut As Long?, ByRef pInout As Long?)
    pOut = pInout
    pInout = pIn
  End Sub

End Class

F#

module FunctionTests

  let Swap( pIn : int, [<Out>] pOut : byref<int> ,  pInOut : byref<int> ) = (
    pOut <- pInout
    pInout <- pIn
    let temp = "Finished"
    temp
  )

  let SwapNullable(  pIn : Nullable<int64>, [<Out>] pOut : byref<Nullable<int64>> ,  pInOut : byref<Nullable<int64>> ) = (
    pOut <- pInout
    pInout)
  )

C++ / CLi

public ref class FunctionTests
{
public:

  static String^ Swap(int pIn, [Out] int% pOut, int% pInout)
  {
    pOut = pInout;
    pInout = pIn;
    String^ temp = "Finished";
    return temp;
  }

  static void SwapNullable(Nullable<long long> pIn, [Out] Nullable<long long>% pOut, Nullable<long long>% pInout)
  {
    pOut = pInout;
    pInout = pIn;
  }
}

Java-Routinen

Eine Java™-Routine wird als Java-Methode implementiert und hat die LANGUAGE-Klausel JAVA. Bei Java-Routinen muss der Name_der_externen_Routine den Klassennamen und den Namen der aufzurufenden Java-Methode enthalten. Geben Sie den Namen_der_externen_Routine wie im folgenden Beispiel an:
>>--"-- Klassenname---.---Methodenname--"--------------><
dabei steht Klassenname für die Klasse, die die Methode enthält, und Methodenname gibt die Methode an, die aufgerufen werden soll. Wenn die Klasse Teil eines Pakets ist, muss der Teil mit der Klassen-ID das komplette Paketpräfix einschließen, z. B. "com.ibm.broker.test.MyClass.myMethod".

Für die Suche nach der Java-Klasse verwendet der Broker die in Implementierung von Java-Klassen beschriebene Suchmethode.

Jede Java-Methode, die Sie aufrufen möchten, muss die folgende Basissignatur haben:
public static <Rückgabetyp> <Methodenname> (< 0 - N Parameter>)

Dabei muss der <Rückgabetyp> in der Liste der Java-Datentypen IN in der Tabelle in der Zuordnung von ESQL zum Java-Datentyp enthalten sein (ausgenommen REFERENCE, da dieser Typ als Rückgabewert unzulässig ist) oder dem Java-Datentyp 'void' entsprechen. Die Parameterdatentypen müssen ebenfalls aus der Tabelle der Zuordnung von ESQL zum Java-Datentyp stammen. Zudem darf die Java-Methode keine exception throws-Klausel (Ausnahmeauslöser) in ihrer Signatur haben.

Die Signatur der Java-Methoden muss der Deklaration der ESQL-Routinen der Methode entsprechen. Außerdem müssen die folgenden Regeln eingehalten werden:
  • Stellen Sie sicher, dass der Name der Java-Methode, einschließlich des Klassennamens und der Paketqualifikationsmerkmale, dem externen Namen (EXTERNAL NAME) der Prozeduren entspricht.
  • Wenn der Java-Rückgabetyp 'void' lautet, geben Sie in der Definition der ESQL-Routinen keine RETURNS-Klausel an. Lautet der Java-Rückgabetyp jedoch 'not void', müssen Sie eine RETURNS-Klausel in der Definition der ESQL-Routinen angeben.
  • Stellen Sie sicher, dass alle Typen und Anweisungen der Parameter gemäß den in der Tabelle in der Zuordnung von ESQL zum Java-Datentyp aufgelisteten Regeln der ESQL-Deklaration entsprechen.
  • Stellen Sie sicher, dass der Rückgabetyp der Methoden dem Datentyp der RETURNS-Klausel entspricht.
  • Stellen Sie den EXTERNAL NAME (externen Namen) in Anführungszeichen, da er zumindest 'class.method' (Klasse.Methode) enthalten muss.
  • Wenn Sie eine überlastete Java-Methode aufrufen möchten, müssen Sie eine separate ESQL-Definition für jede überlastete Methode erstellen und jeder ESQL-Definition einen eindeutigen Routinenamen geben.

Die Klausel im Abschnitt 'JavaClassLoader' gilt nur für LANGUAGE JAVA-Routinen. CLASSLOADER ist eine optionale Klausel. Wenn Sie diese Klausel nicht angeben, wird die Java-Klasse vom Klassenladeprogramm 'EGShared' geladen. Sie finden weitere Informationen hierzu in den Abschnitten Klassen für JavaCompute-Knoten laden und Konfigurierbarer JavaClassLoader-Service.

Sie können in der Java-Methode die API des benutzerdefinierten Java-Knotens verwenden, sofern Sie die in Einschränkungen bei Java-Routinen dokumentierten Einschränkungen beachten. Weitere Informationen zur Verwendung der Java-API finden Sie im Abschnitt Benutzerdefinierten Java-Knoten kompilieren.

Beispiel 1 einer Java-Routine

Diese Routine enthält drei Parameter unterschiedlicher Richtungen und gibt eine Ganzzahl zurück, die dem Java-Rückgabetyp java.lang.Long entspricht.

CREATE FUNCTION  myProc1( IN P1 INTEGER, OUT P2 INTEGER, INOUT P3 INTEGER )
 RETURNS INTEGER
 LANGUAGE JAVA 
 EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod1";

Sie können für den Aufruf von myProc1 folgenden ESQL-Ausdruck verwenden:

CALL myProc1( intVar1, intVar2, intVar3) INTO intReturnVar3;
-- oder
SET intReturnVar3 = myProc1( intVar1, intVar2, intVar3);

Beispiel 2 einer Java-Routine

Diese Routine enthält drei Parameter unterschiedlicher Richtungen und hat den Java-Rückgabetyp void (typenlos).

CREATE PROCEDURE myProc2( IN P1 INTEGER, OUT P2 INTEGER, INOUT P3 INTEGER )
 LANGUAGE JAVA 
 nEXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod2";

Sie müssen für den Aufruf von myProc2 folgenden ESQL-Ausdruck verwenden:

CALL myProc2(intVar1, intVar2, intVar3);

Die folgende Java-Klasse bietet eine Methode für jedes der Java-Beispiele:

package com.ibm.broker.test;

class MyClass {
public static Long myMethod1( Long P1, Long[] P2 Long[] P3) { ... }
public static void myMethod2( Long P2, Long[] P2 Long[] P3) { ... }

 /*  Wenn eine dieser Methoden aufgerufen wird:
    P1 kann den Wert NULL haben oder nicht (abhängig vom Wert von intVar1).
    P2[0] hat stets den Wert NULL (unabhängig vom Wert von intVar2).
    P3[0] kann den Wert NULL haben oder nicht (abhängig vom Wert von intVar3).  
    Alles entspricht der Verwendung von LANGUAGE ESQL-Routinen. 
    Wenn diese Methoden Werte zurückgeben:
         intVar1 ist unverändert
         intVar2 kann immer noch den Wert NULL oder einen anderen Wert haben
         intVar3 kann denselben Wert oder einen anderen Wert haben
     Alles entspricht der Verwendung von LANGUAGE ESQL-Routinen.
     
    Wenn myMethod1 Folgendes zurückgibt: intReturnVar3 ist entweder NULL (wenn die
    Methode NULL zurückgibt) oder enthält den Wert, den die 
    Methode zurückgegeben hat.
 */
}

Beispiel 3 einer Java-Routine

Im folgenden Beispiel lautet die LANGUAGE-Klausel JAVA und es ist ein externer Name (EXTERNAL NAME) für eine Java-Methode mit der Bezeichnung myMethod1 in Klasse com.ibm.broker.test.MyClass angegeben. Außerdem ist ein konfigurierbarer JavaClassLoader-Service mit der Bezeichnung myClassLoader angegeben, der zum Laden der Java-Klasse com.ibm.broker.test.MyClass verwendet werden soll.
CREATE FUNCTION myMethod1 ( IN P1 INTEGER, IN P2 INTEGER )
  RETURNS INTEGER
  LANGUAGE JAVA
  EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod1"
  CLASSLOADER "myClassLoader";

Zuordnung von ESQL zum Java-Datentyp

In der folgenden Tabelle werden die Zuordnungen von ESQL zu Java zusammengefasst.
Anmerkungen:
  • Nur die Java-Skalarwrapper werden an Java übergeben.
  • Die ESQL-Skalartypen werden Java-Datentypen als Objektwrapper oder Objektwrappergruppen zugeordnet, abhängig von der Richtung des Prozedurparameters. Jeder Wrapperbereich enthält genau ein Element.
  • Durch die Verwendung von Skalarobjektwrappern können NULL-Werte an Java-Methoden übermittelt und von dort abgerufen werden.
ESQL-Datentypen 1 Java IN-Datentypen Java INOUT- und OUT-Datentypen
INTEGER, INT java.lang.Long java.lang.Long []
FLOAT java.lang.Double java.lang.Double[]
DECIMAL java.math.BigDecimal java.math.BigDecimal[]
CHARACTER, CHAR java.lang.String java.lang.String[]
BLOB byte[] byte[][]
BIT java.util.BitSet java.util.BitSet[]
DATE com.ibm.broker.plugin.MbDate com.ibm.broker.plugin.MbDate[]
TIME 2 com.ibm.broker.plugin.MbTime com.ibm.broker.plugin.MbTime[]
GMTTIME 2 com.ibm.broker.plugin.MbTime com.ibm.broker.plugin.MbTime[]
TIMESTAMP 2 com.ibm.broker.plugin.MbTimestamp com.ibm.broker.plugin.MbTimestamp[]
GMTTIMESTAMP 2 com.ibm.broker.plugin.MbTimestamp com.ibm.broker.plugin.MbTimestamp[]
INTERVAL Wird nicht unterstützt Wird nicht unterstützt
BOOLEAN java.lang.Boolean java.lang.Boolean[]
REFERENCE (auf eine Nachrichtenbaumstruktur) 3 4 5 6 com.ibm.broker.plugin.MbElement com.ibm.broker.plugin.MbElement[] (Wird für INOUT unterstützt, nicht für OUT)
ROW Wird nicht unterstützt Wird nicht unterstützt
LIST Wird nicht unterstützt Wird nicht unterstützt
  1. Variablen, die als konstant deklariert werden (oder Referenzen auf Variablen, die als konstant deklariert werden), dürfen nicht die Richtung INOUT oder OUT aufweisen.
  2. Die in der Java-Variablen eingestellte Zeitzone ist nicht wichtig; Sie erhalten die erforderliche Zeitzone im ausgegebenen ESQL.
  3. Der Verweisparameter kann bei Übergabe an eine Java-Methode nicht den Wert NULL haben.
  4. Der Verweis kann bei Übergabe an eine -Java-Methode nicht die Richtung OUT haben.
  5. Wenn ein MbElement von Java als INOUT-Parameter an ESQL zurückgegeben wird, muss es auf eine Position in derselben Nachrichtenbaumstruktur verweisen wie die, auf die das MbElement bei der Übergabe an die aufgerufene Java-Methode verwies.

    Wenn beispielsweise ein ESQL-Verweis auf OutputRoot.XML.Test als INOUT-MbElement an eine Java-Methode übergeben wird, bei Rückkehr des Aufrufs aber ein anderes MbElement an ESQL zurückgegeben wird, muss dieses andere Element ebenfalls auf eine Position innerhalb der OutputRoot-Baumstruktur verweisen.

  6. Ein MbElement kann nicht mit der RETURNS-Klausel an eine Java-Methode zurückgegeben werden, da dieser Verweis von einer ESQL-Routine nicht zurückgegeben werden kann. Dagegen kann ein MbElement (unter Berücksichtigung der unter 5 aufgeführten Bedingungen) als INOUT-Richtungsparameter zurückgegeben werden.

Ein Verweis auf eine Skalarvariable kann im Aufruf einer Java-Methode verwendet wurde, vorausgesetzt, der Datentyp der Variablen, auf die verwiesen wird, entspricht dem entsprechenden Datentyp in der Java-Programmsignatur.

Einschränkungen bei Java-Routinen

Für Java-Routinen, die aus ESQL aufgerufen werden, gelten folgende Einschränkungen:
  • Die Java-Methode muss threadsicher sein (simultan verwendbar).
  • Datenbankverbindungen müssen den JDBC-Typ 2 oder 4 haben. Zudem sind Datenbankoperationen nicht Teil einer Brokertransaktion und daher nicht durch einen externen Ressourcenkoordinator steuerbar (wie dies in einer XA-Umgebung der Fall ist).
  • Die API des benutzerdefinierten Java-Knotens darf nur von dem Thread verwendet werden, der die Java-Methode aufgerufen hat.

    Innerhalb der Methode können Threads erstellt werden. Erstellte Threads dürfen die Java-APIs jedoch nicht verwenden, und die Steuerung muss wieder an den Broker zurückgegeben werden.

    Alle Einschränkungen hinsichtlich der Verwendung der Java-API gelten auch für Java-Methoden, die aus ESQL aufgerufen werden.

  • Aus ESQL aufgerufene Java-Methoden dürfen die MbNode-Klasse nicht verwenden. Sie können also keine Objekte vom Typ MbNode erstellen oder Methoden für ein vorhandenes MbNode-Objekt aufrufen.
  • Wenn Sie WebSphere MQ- oder JMS-Aufgaben in einer Java-Methode aufrufen möchten, die aus ESQL aufgerufen wurde, müssen Sie die Richtlinien für die Ausführung von WebSphere MQ- und JMS-Aufgaben in einem benutzerdefinierten Knoten einhalten. Weitere Informationen finden Sie unter Planung für benutzerdefinierte Empfangsknoten.

Implementierung von Java-Klassen

Mit einer der beiden folgenden Methoden können Java-Klassen auf einem Broker innerhalb einer Java-Archivdatei (JAR-Datei) implementiert werden:
  1. Fügen Sie die JAR-Datei zur Brokerarchivdatei (BAR-Datei) hinzu.

    Die effizienteste und flexibelste Methode zur Implementierung auf dem Broker besteht darin, die JAR-Datei zur BAR-Datei hinzuzufügen. Sie können diese Aufgabe manuell oder automatisch über das WebSphere Message Broker Toolkit ausführen.

    Wenn das WebSphere Message Broker Toolkit die korrekte Java-Klasse in einem geöffneten referenzierten Java-Projekt im Arbeitsbereich findet, wird die Java-Klasse automatisch in eine JAR-Datei kompiliert und zur BAR-Datei hinzugefügt. Diese Prozedur ist mit der Prozedur zur Implementierung eines JavaCompute-Knotens in einer JAR-Datei identisch (siehe Klassenladen für benutzerdefinierte Knoten).

    Wenn Sie eine JAR-Datei über das WebSphere Message Broker Toolkit implementieren, lädt der erneut implementierte Fluss die in der BAR-Datei enthaltene JAR-Datei.

    Ebenso werden die Dateien erneut geladen, wenn der Nachrichtenfluss, der auf eine Java-Klasse verweist, gestoppt und erneut gestartet wird. Stellen Sie sicher, dass Sie alle Flüsse, die auf die zu aktualisierende JAR-Datei verweisen, stoppen und neu starten (bzw. erneut implementieren). Auf diese Weise verhindern Sie, dass einige Flüsse mit der alten Version der JAR-Datei und andere Flüsse mit der neuen Version ausgeführt werden.

    Mit dem WebSphere Message Broker Toolkit werden nur JAR-Dateien, nicht jedoch eigenständige Java-Klassendateien implementiert.

  2. Speichern Sie die JAR-Datei an einer der folgenden Speicherpositionen:
    1. im Ordner Arbeitspfad/shared-classes/ auf der Maschine, auf der der Broker ausgeführt wird
    2. in der Umgebungsvariablen CLASSPATH auf der Maschine, auf der der Broker ausgeführt wird

    Diese Aktion muss manuell durchgeführt werden, das WebSphere Message Broker Toolkit kann hierfür nicht verwendet werden.

    Bei diesem Verfahren werden bei einer erneuten Implementierung des Nachrichtenflusses die Java-Klassen, auf die verwiesen wird, nicht erneut geladen, und der Nachrichtenfluss wird nicht gestoppt und neu gestartet. In diesem Fall können die Klassen nur neu geladen werden, indem Sie den Broker stoppen und neu starten.

Damit der Broker eine Java-Klasse finden kann, müssen Sie sicherstellen, dass sie sich in einem der folgenden Verzeichnisse befindet. Wenn der Broker die angegebene Klasse nicht finden kann, wird eine Ausnahme generiert.

Zwar stehen Ihnen bei der Implementierung der JAR-Datei die zuvor genannten Möglichkeiten zur Verfügung, die Verwendung des WebSphere Message Broker Toolkits bei der Implementierung der BAR-Datei bietet jedoch die größte Flexibilität bei der erneuten Implementierung der JAR-Datei.

Datenbankroutinen

Datenbankroutinen sind Routinen, die als in Datenbank gespeicherte Prozeduren implementiert werden. Ihre LANGUAGE-Klausel lautet DATABASE, und sie müssen den Routinetyp PROCEDURE haben.

Beim Schreiben gespeicherter Prozeduren in Sprachen wie C müssen Sie NULL-Anzeiger verwenden, um zu gewährleisten, dass Ihre Prozedur die Daten ordnungsgemäß verarbeiten kann.

Obwohl die Datenbankdefinitionen einer gespeicherten Prozedur zwischen den Datenbanken variieren, gilt dies nicht für die ESQL, mit der sie aufgerufen werden. Die Namen, die Parametern im ESQL-Ausdruck zugeordnet werden, müssen nicht mit den in der Datenbank angegebenen Namen übereinstimmen. Der externe Name der Routine - einschließlich aller Paket- oder Containerspezifikationen - muss jedoch seinem definiertem Namen in der Datenbank entsprechen.

Die Klausel DYNAMIC RESULT SETS ist nur für Datenbankroutinen zulässig. Sie ist nur erforderlich, wenn eine gespeicherte Prozedur mehrere Ergebnisse zurückgibt. Der Ganzzahlparameter für diese Klausel muss 0 (null) oder mehr sein und die Anzahl der Ergebnismengen angeben, die zurückgegeben werden sollen.

Die optionale RETURNS-Klausel ist erforderlich, wenn eine gespeicherte Prozedur einen einzelnen skalaren Wert zurückgibt.

Die Klausel EXTERNAL NAME gibt den Namen an, unter dem die Datenbank die Routine kennt. Dies kann ein qualifizierter oder ein nicht qualifizierter Name sein, wobei das Qualifikationsmerkmal der Name des Datenbankschemas ist, in dem die Prozedur definiert ist. Wenn Sie keinen Schemanamen angeben, wird der Benutzername der Datenbankverbindung als das Schema verwendet, in dem sich die Prozedur befindet. Ist die erforderliche Prozedur nicht in diesem Schema vorhanden, müssen Sie einen expliziten Schemanamen angeben - entweder in der Routinedefinition oder in der Anweisung zum Aufrufen der Routine bei Laufzeit. Weitere Informationen zur dynamischen Auswahl des Schemas, das die Routine enthält, finden Sie in der CALL-Anweisung. Wenn ein qualifizierter Name verwendet wird, muss der Name in Anführungszeichen gesetzt werden.

Eine vollständig qualifizierte Routine hat in der Regel folgendes Format:
 EXTERNAL NAME "mySchema.myProc";
Gehört die Prozedur jedoch zu einem Oracle-Paket, wird das Paket als Teil des Prozedurnamens behandelt. Geben Sie daher wie folgt einen Schemanamen und den Paketnamen an:
EXTERNAL NAME "mySchema.myPackage.myProc";  

Auf diese Weise kann zwar das Schema, aber nicht der Paketname in der CALL-Anweisung dynamisch gewählt werden.

Wenn der Name der Prozedur SQL-Platzhalter enthält (das Prozentzeichen (%) und der Unterstrich (_)), wird die Prozedur vom Broker so geändert, dass sie das Datenbank-Escapezeichen direkt vor dem Platzhalter aufführen. Durch dieses Verfahren wird sichergestellt, dass die Datenbank die Platzhalter als Literale empfängt. Angenommen, die folgende Klausel wird vom Broker geändert, so wird "mySchema.Proc\_" an die Datenbank übermittelt (vorausgesetzt, dass es sich bei dem Datenbank-Escapezeichen um einen umgekehrten Schrägstrich handelt).
EXTERNAL NAME "mySchema.Proc_";  
Für alle externen Prozeduren gelten folgende Einschränkungen:
  • Eine gespeicherte Prozedur kann in der Datenbank nicht überladen werden. Sie gilt als überladen, wenn mehrere Prozeduren mit identischem Namen in demselben Datenbankschema existieren. Wenn der Broker eine überlappende Prozedur feststellt, gibt er eine Ausnahmebedingung aus.
  • Parameter dürfen nicht den Datentyp ESQL REFERENCE, ROW, LIST oder INTERVAL haben.
  • Benutzerdefinierte Namen können nicht als Parameter oder als Rückgabewerte verwendet werden.
ExternalRoutineName ist bei LANGUAGE DATABASE-Routinen nicht optional und enthält den Schemanamen, Paketnamen und Prozedurnamen der Routine, die aufgerufen werden soll. Geben Sie den Namen_der_externen_Routine wie folgt an:
>>--"Schemaname---.---Paketname---.---Prozedurname--"--------------><
Dabei gilt Folgendes:
  • Schemaname ist optional.
  • Paketname ist optional und gilt nur für Oracle-Datenquellen. Wenn Sie einen Paketnamen bereitstellen, müssen Sie auch einen Schemanamen angeben.
  • Prozedurname ist nicht optional.

Beispiel 1 einer Datenbankroutine

Nachfolgend sehen Sie die ESQL-Definition einer gespeicherten Prozedur, die einen einzelnen skalaren Wert und einen OUT-Parameter zurückgibt:

CREATE PROCEDURE myProc1 (IN P1 INT, OUT P2 INT)
RETURNS INTEGER
LANGUAGE DATABASE
EXTERNAL NAME "myschema.myproc";

Rufen Sie die Routine myProc1 mit folgender ESQL auf:

/*mittels der Aufrufsyntax der CALL-Anweisung*/
CALL myProc1(intVar1, intVar2) INTO intReturnVar3;

/*oder mittels der Funktionsaufrufsyntax*/
SET intReturnVar3 = myProc1(intVar1, intVar2);

Beispiel 2 einer Datenbankroutine

Der folgende ESQL-Code zeigt, wie in DB2 gespeicherte Prozeduren definiert und aufgerufen werden:

ESQL-Definition:
DECLARE inputParm CHARACTER;
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER;

SET inputParm = 'Hello';
SET inputOutputParm = 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );

CREATE PROCEDURE swapParms (
  IN parm1 CHARACTER,
  OUT parm2  CHARACTER,
  INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;

Sie tragen diese gespeicherte Prozedur in DB2 ein, indem Sie das folgende Script in eine Datei (z. B. test1.sql) kopieren

-- DB2 Example Stored Procedure
DROP PROCEDURE dbSwapParms @                   
CREATE PROCEDURE dbSwapParms
( IN in_param CHAR(32), 
  OUT out_param CHAR(32),
  INOUT inout_param CHAR(32))
LANGUAGE SQL
BEGIN   
SET out_param = inout_param;  
    SET inout_param = in_param;
END @
Führen Sie die Datei jetzt in einer DB2-Eingabeaufforderung aus:
db2 -td@ -vf test1.sql 
Die Ausführung dieses Codes sollte folgende Ergebnisse liefern:
  • Der Wert des IN-Parameters ändert sich nicht (und kann sich per Definition nicht ändern).
  • Der Wert des OUT-Parameters wird zu "World".
  • Der Wert des INOUT-Parameters wird zu "Hello".

Beispiel 3 einer Datenbankroutine

Der nachfolgende ESQL-Code zeigt, wie in Oracle gespeicherte Prozeduren definiert und aufgerufen werden:

ESQL-Definition:
DECLARE inputParm CHARACTER;
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER;

SET inputParm = 'Hello';
SET inputOutputParm = 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );

CREATE PROCEDURE swapParms (
  IN parm1 CHARACTER,
  OUT parm2  CHARACTER,
  INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;

Sie tragen diese gespeicherte Prozedur in Oracle ein, indem Sie das folgende Script in eine Datei (z. B. test1.sql) kopieren

CREATE OR REPLACE PROCEDURE dbSwapParms  
( in_param IN VARCHAR2 ,
  out_param OUT VARCHAR2,
  inout_param IN OUT VARCHAR2 ) 
AS 
BEGIN 
  out_param := inout_param;
  inout_param := in_param; 
END; 
/
Führen Sie die Datei jetzt aus:
sqlplus Benutzer-ID/Kennwort  @test1.sql
Die Ausführung dieses Codes sollte folgende Ergebnisse liefern:
  • Der Wert des IN-Parameters ändert sich nicht (und kann sich per Definition nicht ändern).
  • Der Wert des OUT-Parameters wird zu "World".
  • Der Wert des INOUT-Parameters wird zu "Hello".

Beispiel 4 einer Datenbankroutine

Der nachfolgende ESQL-Code zeigt, wie in SQL Server gespeicherte Prozeduren definiert und aufgerufen werden:

ESQL-Definition:
DECLARE inputParm CHARACTER;
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER;

SET inputParm = 'Hello';
SET inputOutputParm = 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );

CREATE PROCEDURE swapParms (
  IN parm1 CHARACTER,
  INOUT parm2  CHARACTER,
  INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;

Sie tragen diese gespeicherte Prozedur in SQL Server ein, indem Sie das folgende Script in eine Datei (z. B. test1.sql) kopieren

-- SQLServer Example Stored Procedure 
DROP PROCEDURE dbSwapParms 
go                                                   
CREATE PROCEDURE dbSwapParms 
 @in_param     CHAR(32), 
 @out_param    CHAR(32) OUT, 
 @inout_param  CHAR(32) OUT 
AS 
  SET NOCOUNT ON
  SET @out_param   = @inout_param 
  SET @inout_param = @in_param 
go 
Führen Sie jetzt folgende Datei aus:
isql -UBenutzer-ID -PKennwort -SServer -dDatenquelle -itest1.sql

SQL Server behandelt OUTPUT-Parameter aus gespeicherten Prozeduren als INPUT/OUTPUT-Parameter. Wenn Sie diese als OUT-Parameter in Ihrer ESQL deklarieren, tritt während der Ausführung ein Typabweichungsfehler auf. Zur Verhinderung dieser Abweichung müssen Sie OUTPUT-Parameter von SQL Server in Ihrer ESQL als INOUT-Parameter deklarieren.

Die Option SET NOCOUNT ON sollte aus den folgenden Gründen wie im Beispiel oben für gespeicherte SQL-Prozeduren verwendet werden:
  1. um den Umfang der vom SQL-Server an den Broker zurückgegebenen Daten zu reduzieren;
  2. damit Ergebnisse korrekt zurückgegeben werden.
Die Ausführung dieses Codes sollte folgende Ergebnisse liefern:
  • Der Wert des IN-Parameters ändert sich nicht (und kann sich per Definition nicht ändern).
  • Der Wert des OUT-Parameters wird zu "World".
  • Der Wert des INOUT-Parameters wird zu "Hello".

Beispiel 5 einer Datenbankroutine

Der folgende ESQL-Code veranschaulicht, wie gespeicherte Sybase-Prozeduren definiert und aufgerufen werden:

ESQL-Definition:
DECLARE inputParm CHARACTER;
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER;

SET inputParm = 'Hello';
SET inputOutputParm = 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );

CREATE PROCEDURE swapParms (
  IN parm1 CHARACTER,
  INOUT parm2  CHARACTER,
  INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;

Sie tragen diese gespeicherte Prozedur in Sybase ein, indem Sie das folgende Script in eine Datei (z. B. test1.sql) kopieren.

-- Gespeicherte SYBASE-Prozeduren - Beispiel 
DROP PROCEDURE dbSwapParms 
go                                                   
CREATE PROCEDURE dbSwapParms 
 @in_param     CHAR(32), 
 @out_param    CHAR(32) OUT, 
 @inout_param  CHAR(32) OUT 
AS 
  SET @out_param   = @inout_param 
  SET @inout_param = @in_param 
go 
Führen Sie jetzt folgende Datei aus:
isql -U<Benutzer-ID> -P<Kennwort> -S<Server> -D<Datenquelle> -itest1.sql

Sybase handhabt OUTPUT-Parameter in gespeicherten Prozeduren wie INPUT/OUTPUT-Parameter. Wenn Sie diese als OUT-Parameter in Ihrer ESQL deklarieren, tritt während der Ausführung ein Typabweichungsfehler auf. Um diese Abweichung zu verhindern, müssen Sie die OUTPUT-Parameter von Sybase in ESQL als INOUT-Parameter deklarieren.

Die Ausführung dieses Codes sollte folgende Ergebnisse liefern:
  • Der Wert des IN-Parameters ändert sich nicht (und kann sich per Definition nicht ändern).
  • Der Wert des OUT-Parameters wird zu "World".
  • Der Wert des INOUT-Parameters wird zu "Hello".

Beispiel 6 einer Datenbankroutine

Der folgende ESQL-Code zeigt, wie in Informix gespeicherte Prozeduren definiert und aufgerufen werden:

ESQL-Definition:
DECLARE inputParm CHARACTER 'Hello';
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );

CREATE PROCEDURE swapParms (
  IN parm1 CHARACTER,
  INOUT parm2  CHARACTER,
  INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;

Sie tragen diese gespeicherte Prozedur in Informix ein, indem Sie das folgende Script in eine Datei (z. B. test1.sql) kopieren

DROP SPECIFIC PROCEDURE dbSwapParms;
CREATE PROCEDURE dbSwapParms
  (       inParm     CHAR(20),
   OUT    outParm    CHAR(20),
   INOUT  inoutParm  CHAR(20))

   SPECIFIC dbSwapParms

    LET outParm   = inoutParm;
    LET inoutParm = inParm;
END PROCEDURE; 
Führen Sie jetzt folgende Datei aus:

Geben Sie über die Shell-Umgebung des Informix-Servers Folgendes ein:

dbaccess <Datenbankname> <vollständig qualifizierter Pfad/test1.sql>
Die Ausführung dieses Codes sollte folgende Ergebnisse liefern:
  • Der Wert des IN-Parameters ändert sich nicht (und kann sich per Definition nicht ändern).
  • Der Wert des OUT-Parameters wird zu "World".
  • Der Wert des INOUT-Parameters wird zu "Hello".

Für die in Informix gespeicherten Prozeduren gelten folgende Einschränkungen:

Beispiel 7 einer Datenbankroutine

Dieses Beispiel zeigt, wie Sie eine gespeicherte Prozedur aufrufen, die zwei Ergebnismengen und einen OUT-Parameter zurückgibt:

CREATE PROCEDURE myProc1 (IN P1 INT, OUT P2 INT)
  LANGUAGE DATABASE
  DYNAMIC RESULT SETS 2
  EXTERNAL NAME "myschema.myproc";

Rufen Sie myProc1 mit folgender ESQL auf:

/* using a field reference */
CALL myProc1(intVar1, intVar2, Environment.RetVal[], OutputRoot.XMLNS.A[])
/* using a reference variable*/
CALL myProc1(intVar1, intVar2, myReferenceVariable.RetVal[], myRef2.B[])
Bemerkungen | Marken | Downloads | Bibliothek | Support | Feedback

Copyright IBM Corporation 1999, 2014Copyright IBM Corporation 1999, 2014.

        
        Letzte Aktualisierung:
        
        Letzte Aktualisierung: 2015-02-28 16:21:29


ReferenzthemaReferenzthema | Version 8.0.0.5 | ak04970_