2012-04-19 13 views
7

Ich schreibe eine Prozedur, um mit benutzerdefiniertem Objekt umzugehen, das in ANYDATA gespeichert wird. Der Objekttyp und der Attributname können nur zur Laufzeit bekannt sein, daher kann ich das Viable nicht im Deklarationsabschnitt definieren. In Java kann ich mit Reflektion umgehen, ich kann den Klassennamen und den Feldnamen kennen. Dann kann ich durch Reflexion auf die Felder zugreifen. Gibt es eine Möglichkeit, es in PLSQL so zu machen? Was in meinem Kopf gerade jetzt ist, erstellen Sie eine SQL-Zeichenfolge in der Prozedur dynamisch und führen Sie es aus. Aber genau das will ich nicht.Reflexion in PLSQL?

Angenommen, Benutzer A hat einen ADT-Typ wie create or replace type Person_type as object (fname varchar2(10), lname varchar2(10)); definiert und eine Objektinstanz erstellt und in ANYDATA eingefügt.

In meiner Prozedur weiß ich irgendwie, ich muss mit dem ersten Attribut dieses Objekts umgehen, das ist fname. Also, wenn die adt Art an erster Stelle wissen, wird mein Code wie:

declare 
    adobject A.Person_type; -- HERE! I don't know the type yet, so I can't define adobject! 
    tempAnydata anydata; 
    rt number; 
    vbuffer varchar2(10); 
    ... 
begin 
    select somecolumn 
    into tempAnydata 
    from sometable 
    where something='something' for update; 

    rt := tempAnydata.GetObject(adobject); 

    vbuffer := adobject.fname; -- HERE! I don't know the attribute name is fname! 
    -- deal with vbuffer here 
end; 

Also, was soll ich es dynamisch tun, um? Danke im Voraus.

+0

Wenn Sie wissen, dass die 'tempAnydata' ist * wirklich * ein' A.person_type' wie Sie sicherlich tun (sonst könnte man die 'GetObject (ADObject nicht tun)' mit ADObject von genau dieser Art) dann weißt du * auch *, was das erste Attribut dieses Typs ist. Oder fehlt mir etwas? –

+0

A.person_type ist nur ein Beispiel, es könnte ein beliebiger benutzerdefinierter Typ sein. Es könnte B.anymal_type oder etwas anderes sein. – icespace

+1

Statische starke Typisierung saugt. (mürrisch alt Smalltalker wandert ab und murmelt zu sich selbst :-) –

Antwort

7

Sie müssen ANYTYPE verwenden, um die ANYDATA zu beschreiben und sicherzustellen, dass der Typ korrekt ist. Dann können Sie auf das Attribut zugreifen, indem Sie piecewise und getVarchar2 verwenden.

Der größte Teil des unten stehenden Codes dient zur Überprüfung des Typs, den Sie nicht benötigen, wenn Sie sich nicht mit der Typensicherheit befassen.

Funktionswert zurück:

create or replace function get_first_attribute(
    p_anydata in out anydata --note the "out" - this is required for the "piecewise" 
) return varchar2 is 
    v_typecode pls_integer; 
    v_anytype anytype; 
begin 
    --Get the typecode, and the ANYTYPE 
    v_typecode := p_anydata.getType(v_anytype); 

    --Check that it's really an object 
    if v_typecode = dbms_types.typecode_object then 
     --If it is an object, find the first item 
     declare 
      v_first_attribute_typecode pls_integer; 
      v_aname   varchar2(32767); 
      v_result   pls_integer; 
      v_varchar  varchar2(32767); 
      --Variables we don't really care about, but need for function output 
      v_prec   pls_integer; 
      v_scale   pls_integer; 
      v_len   pls_integer; 
      v_csid   pls_integer; 
      v_csfrm   pls_integer; 
      v_attr_elt_type anytype; 
     begin 
      v_first_attribute_typecode := v_anytype.getAttrElemInfo(
       pos   => 1, --First attribute 
       prec   => v_prec, 
       scale   => v_scale, 
       len   => v_len, 
       csid   => v_csid, 
       csfrm   => v_csfrm, 
       attr_elt_type => v_attr_elt_type, 
       aname   => v_aname); 

      --Check typecode of attribute 
      if v_first_attribute_typecode = dbms_types.typecode_varchar2 then 
       --Now that we've verified the type, get the actual value. 
       p_anydata.piecewise; 
       v_result := p_anydata.getVarchar2(c => v_varchar); 

       --DEBUG: Print the attribute name, in case you're curious 
       --dbms_output.put_line('v_aname: '||v_aname); 

       return v_varchar; 
      else 
       raise_application_error(-20000, 'Unexpected 1st Attribute Typecode: '|| 
        v_first_attribute_typecode); 
      end if; 
     end; 
    else 
     raise_application_error(-20000, 'Unexpected Typecode: '||v_typecode); 
    end if; 
end; 
/

Arten:

create or replace type Person_type as object (fname varchar2(10), lname varchar2(10)); 

create or replace type other_type as object (first_name varchar2(10), poetry clob); 

Testlauf:

declare 
    --Create records 
    v_type1 person_type := person_type('Ford', 'Prefect'); 
    v_type2 other_type := other_type('Paula', 'blah blah...'); 
    v_anydata anydata; 
begin 
    --Convert to ANYDATA. 
    --Works as long as ANYDATA is an object with a varchar2 as the first attribute. 
    v_anydata := anydata.convertObject(v_type1); 
    dbms_output.put_line(get_first_attribute(v_anydata)); 

    v_anydata := anydata.convertObject(v_type2); 
    dbms_output.put_line(get_first_attribute(v_anydata)); 
end; 
/

Ausgänge :

Ford 
Paula 
+0

+1 - Ich werde meine Antwort löschen. Ich denke immer noch, dass die Architektur des OP eine eigenartige ist, aber wenn sie wirklich lumbered ist, ist dies die korrekte Lösung – APC

+0

@APC Ich denke, dass Sie Ihre Antwort behalten sollten, kann es hilfreich sein. Ich habe öfter objektrelationale Lösungen gesehen als ANYDATA. (Obwohl ich im Allgemeinen versuche, sie beide zu vermeiden; ich bin immer etwas skeptisch gegenüber allzu allgemeinen Lösungen.) –

+0

Erstaunlich! Vielen Dank, jonearles. Das ist wirklich was ich will! ANYTYPE, schön dich zu treffen. – icespace