PL/SQL中的反射机制?

9
我正在编写一个处理存储在ANYDATA中的用户定义对象的过程。对象类型和属性名称只能在运行时知道,因此我无法在声明部分中为其定义变量。在Java中,我可以使用反射来处理它,我可以知道类名和字段名。然后我可以通过反射访问字段。在PLSQL中是否有像Java那样的方法呢?目前我想到的是在过程中动态创建一个SQL字符串并执行它,但这并不是我想要的确切答案。
假设用户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;

那么我该怎么做才能使它动态化呢?提前感谢你。

如果你知道 tempAnydata 确实是一个 A.person_type,那么你肯定知道(否则你无法使用类型完全相同的 adobject 执行 GetObject(adobject))该类型的第一个属性是什么。或者我有所遗漏吗? - René Nyffenegger
person_type只是一个例子,它可以是任何用户定义的类型。它可能是animal_type或其他东西。 - icespace
1
静态强类型真烂。(一位愤世嫉俗的老Smalltalker自言自语着走开了) - Bob Jarvis - Слава Україні
1个回答

7
你需要使用ANYTYPE来描述ANYDATA并确保类型正确。然后你可以使用piecewisegetVarchar2访问属性。
下面的大部分代码都是用于检查类型,如果你不关心类型安全,则不需要这些代码。 返回值的函数:
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);

测试运行:

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

+1 - 我会删除我的答案。我仍然认为OP的架构很奇特,但如果他们真的被困扰了,这是正确的解决方案。 - APC
@APC 我认为你应该保留你的答案,它可能很有帮助。我看到过实现对象关系的解决方案,而 ANYDATA 并不常见。(虽然总的来说,我尽量避免使用它们; 对于过于通用的解决方案,我总是持怀疑态度。) - Jon Heller
太棒了!非常感谢您,jonearles。这确实是我想要的!ANYTYPE,很高兴认识你。 - icespace
这个(也是我来这里寻找的东西)的核心部分是anydata.convertobject(self).gettypename()(如果你在外面看,例如在OP的例子中,请将self替换为类型实例的名称,例如adobject)。 - William Robertson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接