2012-04-19 11 views
7

Sto scrivendo una procedura per gestire l'oggetto definito dall'utente che è archiviato in ANYDATA. Il tipo di oggetto e il nome dell'attributo possono essere conosciuti solo in fase di esecuzione, quindi non posso definirne la viabile nella sezione declare. In Java, posso usare il reflection per affrontarlo, posso sapere il nome della classe e il nome del campo. Quindi posso accedere ai campi attraverso la riflessione. C'è un modo per farlo in PLSQL come quello? Ciò che nella mia testa in questo momento sta creando una stringa SQL nella procedura in modo dinamico ed eseguirla. Ma non è quello che voglio esattamente.Riflessione in PLSQL?

Diciamo che l'utente A ha definito un tipo di ADT come create or replace type Person_type as object (fname varchar2(10), lname varchar2(10)); e crea un'istanza di oggetto e lo inserisce in ANYDATA.

Nella mia procedura, in qualche modo so che devo occuparmi del primo attributo di questo oggetto, che è fname. Quindi, se conoscere il tipo ADT al primo posto, il mio codice sarà come:

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; 

Quindi cosa devo fare per fare in modo dinamico? Grazie in anticipo.

+0

Se si sa che il 'tempAnydata' è * veramente * un' A.person_type' come sicuramente fare (altrimenti non si poteva fare il GetObject (adobject ')' con adobject di esattamente questo tipo) quindi * anche * sai qual è il primo attributo di quel tipo. O mi sta sfuggendo qualcosa? –

+0

A.person_type è solo un esempio, potrebbe essere qualsiasi tipo definito dall'utente. Potrebbe essere B.anymal_type o qualcos'altro. – icespace

+1

La tipizzazione forte statica fa schifo. (Scontrosa vecchia Smalltalker vaga, borbottando :-) –

risposta

7

È necessario utilizzare ANYTYPE per descrivere ANYDATA e verificare che il tipo sia corretto. Quindi è possibile accedere all'attributo utilizzando piecewise e getVarchar2.

La maggior parte del codice riportato di seguito è per il controllo del tipo, che non è necessario se non si è interessati alla sicurezza del tipo.

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; 
/

Tipi::

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); 

Test Run:

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; 
/

uscite

Funzione per il valore di ritorno :

Ford 
Paula 
+0

+1 - Cancellerò la mia risposta. Continuo a pensare che l'architettura dell'OP sia peculiare, ma se sono veramente ingombranti questa è la soluzione corretta – APC

+0

@APC Penso che dovresti mantenere la risposta, potrebbe essere utile. Ho visto soluzioni relazionali agli oggetti più spesso di ANYDATA. (Anche se in generale cerco di evitarli entrambi, sono sempre un po 'scettico riguardo a soluzioni troppo generiche.) –

+0

Incredibile! Grazie mille, jonearles. Questo è davvero quello che voglio! ANYTYPE, piacere di conoscerti. – icespace