2012-04-19 37 views
7

Estoy escribiendo un procedimiento para tratar con el objeto definido por el usuario que se almacena en ANYDATA. El tipo de objeto y el nombre del atributo solo pueden conocerse en tiempo de ejecución, por lo que no puedo definir el elemento viable en la sección de declaración. En Java, puedo usar la reflexión para manejarlo, puedo conocer el nombre de la clase y el nombre de los campos. Entonces puedo acceder a los campos a través de la reflexión. ¿Hay alguna forma de hacerlo en PLSQL así? Lo que en mi cabeza ahora es crear una cadena sql en el procedimiento de forma dinámica y ejecutarlo. Pero no es lo que quiero exactamente.Reflexión en PLSQL?

Digamos que el usuario A definió un tipo de ADT como create or replace type Person_type as object (fname varchar2(10), lname varchar2(10)); y crea una instancia de objeto e inserta en ANYDATA.

En mi procedimiento, de alguna manera sé que tengo que tratar con el primer atributo de este objeto, que es fname. Así que si conocer el tipo de ADT en el primer lugar, el código será como:

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; 

Entonces, ¿qué debería hacer para que sea de forma dinámica? Gracias por adelantado.

+0

Si sabe que el 'tempAnydata' es realmente * * un' A.person_type' como seguramente lo hace (de lo contrario no podría hacer el 'GetObject (adobject)' con adobject exactamente de este tipo) entonces * también * sabe cuál es el primer atributo de ese tipo. ¿O me estoy perdiendo algo? –

+0

A.person_type es solo un ejemplo, podría ser cualquier tipo definido por el usuario. Puede ser B.anymal_type o algo más. – icespace

+1

Succión fuerte estática es una mierda (El viejo y malhumorado Smalltalker se aleja, murmurando para sí mismo :-) –

Respuesta

7

Debe usar ANYTYPE para describir el ANYDATA y asegurarse de que el tipo sea correcto. Luego puede acceder al atributo usando piecewise y getVarchar2.

La mayoría del código siguiente es para verificar el tipo, que no necesita si no está preocupado por la seguridad del tipo.

función para devolver el valor:

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

Tipos:

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

Ejecución de prueba:

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

Salidas :

Ford 
Paula 
+0

+1 - Eliminaré mi respuesta. Sigo pensando que la arquitectura del OP es peculiar, pero si están realmente cargadas, esta es la solución correcta – APC

+0

@APC Creo que debería mantener su respuesta, puede ser útil. He visto soluciones relacionales de objetos con más frecuencia que ANYDATA. (Aunque, en general, trato de evitarlos a los dos, siempre soy un poco escéptico de las soluciones demasiado genéricas.) –

+0

¡Increíble! Muchas gracias, jonearles. ¡Eso es realmente lo que quiero! ANYTYPE, encantado de conocerte. – icespace