2012-04-19 5 views
7

Я пишу процедуру для обработки пользовательского объекта, который хранится в ANYDATA. Тип объекта и имя атрибута могут быть известны только во время выполнения, поэтому я не могу определить его видимость для него в разделе объявления. В Java я могу использовать размышления, чтобы справиться с этим, я могу узнать имя класса и имя поля. Затем я могу получить доступ к полям через отражение. Есть ли способ сделать это в PLSQL? Что в моей голове сейчас создает динамическую структуру sql в процедуре и выполняет ее. Но я не хочу этого точно.Отражение в PLSQL?

Предположим, пользователь A определил тип ADT как create or replace type Person_type as object (fname varchar2(10), lname varchar2(10)); и создал экземпляр объекта и ввел его в ANYDATA.

В моей процедуре я знаю, что мне нужно иметь дело с первым атрибутом этого объекта, который является fname. Так что, если знать ADT типа в первую очередь, мой код будет как:

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; 

Так что я должен сделать, чтобы сделать его динамически? Заранее спасибо.

+0

Если вы знаете, что 'tempAnydata' это * действительно *' A.person_type', как вы, конечно, делать (иначе вы не могли бы сделать 'GetObject (ADObject)' с ADObject именно этого типа) то вы * также * знаете, что такое первый атрибут этого типа. Или я чего-то не хватает? –

+0

A.person_type - просто пример, это может быть любой пользовательский тип. Это может быть B.anymal_type или что-то еще. – icespace

+1

Статическая сильная типизация сосет. (сварливый старый Smalltalker блуждает, бормочет себе :-) –

ответ

7

Для описания ANYDATA необходимо указать ANYTYPE и убедиться, что все правильно. Затем вы можете получить доступ к атрибуту с помощью piecewise и getVarchar2.

Основная часть приведенного ниже кода предназначена для проверки типа, который вам не нужен, если вы не беспокоитесь о безопасности типов.

Функция Возвращаемое значение:

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

Типы:

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

выходы :

Ford 
Paula 
+0

+1 - Я удалю свой ответ. Я все еще думаю, что архитектура OP является своеобразной, но если они действительно неуправляемы, это правильное решение. – APC

+0

@APC. Я думаю, что вы должны сохранить свой ответ, это может быть полезно. Я видел объектно-реляционные решения чаще, чем ANYDATA. (Хотя в целом я стараюсь избегать их обоих, я всегда скептически отношусь к чрезмерно общим решениям.) –

+0

Удивительно! Спасибо ОЧЕНЬ много, jonearles. Это действительно то, что я хочу! ANYTYPE, приятно познакомиться. – icespace