1

これは、 PL / SQLでオブジェクトコンポジションを使用することは可能ですか?のフォローアップ質問です。

その質問は、相互に依存するPL / SQLでオブジェクト型を作成する方法に対処しました(つまり、各オブジェクトの1つの属性が他のオブジェクトへの参照です)。

私が遭遇した次の問題は、オブジェクトコンストラクターに関係しています。これがコードです(論理的には、学生は人の中に存在します)。また、コメントでこれを排除するために、継承を使用することは私にとってオプションではありません。

人物オブジェクト

CREATE OR REPLACE TYPE PERSON FORCE AUTHID DEFINER UNDER MYSCHEMA.BASE_OBJECT (
    student MYSCHEMA.student,
    additional attributes...

    CONSTRUCTOR FUNCTION PERSON
    RETURN SELF AS RESULT

) NOT FINAL; 

CREATE OR REPLACE TYPE BODY PERSON
AS
    CONSTRUCTOR FUNCTION PERSON
    RETURN SELF AS RESULT IS

    BEGIN
        self.student := NEW MYSCHEMA.STUDENT(self);
        RETURN;
    END;
END;

学生オブジェクト

CREATE OR REPLACE TYPE STUDENT FORCE AUTHID DEFINER UNDER MYSCHEMA.BASE_OBJECT (
    person REF MYSCHEMA.PERSON,

    CONSTRUCTOR FUNCTION STUDENT(p_person REF MYSCHEMA.PERSON)
    RETURN SELF AS RESULT

) NOT FINAL; 

CREATE OR REPLACE TYPE BODY STUDENT
AS

    CONSTRUCTOR FUNCTION STUDENT(p_person REF MYSCHEMA.PERSON)
    RETURN SELF AS RESULT IS

    BEGIN
        self.person := p_person;
        RETURN;
    END;
END;

このコードは、PERSON内のSTUDENTオブジェクトをインスタンス化するPERSONコンストラクター内の次の行を除いて、エラーなしでコンパイルされます。

self.student := NEW MYSCHEMA.STUDENT(self);

これは次のエラーをスローします:

Error(22,29): PLS-00306: wrong number or types of arguments in call to 'STUDENT'

それで、親愛なる友人たち、私はあなたの助けをもう一度求めます。STUDENTコンストラクターに暗黙的に渡される追加のパラメーターがあると推測していますが、それは単なる推測です。

ありがとう。

4

2 に答える 2

1

REF行を参照する必要があります。 参照ではないため、渡すことはできませんSELF

これを機能させる1つの方法は、非表示のテーブルを作成することです。

create table shadow_person of person;

そして、すべてのインスタンスがそのテーブルに秘密裏に行を作成します。

CONSTRUCTOR FUNCTION PERSON
RETURN SELF AS RESULT IS
    v_ref_person ref person;
BEGIN
    insert into shadow_person values(self)
    returning make_ref(shadow_person, object_id) into v_ref_person;

    self.a_student := new student(v_ref_person);

    RETURN;
END;

これは機能しているように見えますが、実際の本番環境では誰も望まないような、恐ろしい副作用があります。

完全なスクリプトは次のとおりです。

drop type base_object force;
drop type student force;
drop type person force;
drop table shadow_person;


create or replace type base_object is object (a varchar2(10)) not final;
/

CREATE OR REPLACE TYPE PERSON FORCE AUTHID DEFINER UNDER BASE_OBJECT (
    b varchar2(10),
    CONSTRUCTOR FUNCTION PERSON RETURN SELF AS RESULT
) NOT FINAL; 
/

CREATE OR REPLACE TYPE STUDENT FORCE AUTHID DEFINER UNDER BASE_OBJECT (
    c varchar2(10),
    a_person REF PERSON,
    CONSTRUCTOR FUNCTION STUDENT(p_person ref PERSON) RETURN SELF AS RESULT
) NOT FINAL; 
/

alter type person add attribute a_student student cascade;

create table shadow_person of person;

CREATE OR REPLACE TYPE BODY PERSON
AS
    CONSTRUCTOR FUNCTION PERSON
    RETURN SELF AS RESULT IS
        v_ref_person ref person;
    BEGIN
        insert into shadow_person values(self)
        returning make_ref(shadow_person, object_id) into v_ref_person;

        self.a_student := new student(v_ref_person);

        RETURN;
    END;
END;
/

CREATE OR REPLACE TYPE BODY STUDENT
AS
    CONSTRUCTOR FUNCTION STUDENT(p_person REF PERSON)
    RETURN SELF AS RESULT IS
    BEGIN
        self.a_person := p_person;
        RETURN;
    END;
END;
/


--Example of how to use it:
declare
    v_person person := person;
begin
    v_person.a := 'person a';
    v_person.b := 'b';
    v_person.a_student.a := 'student a';
    v_person.a_student.c := 'c';

    dbms_output.put_line(v_person.a_student.c);
end;
/
于 2013-01-16T06:17:35.763 に答える
1

Ok!私自身もこの問題に取り組んでいるので、どうやって解決したのかを共有したいと思います!!!

まず、Oracleのインメモリ循環依存関係を解決する直接的な方法はありません-しかし、ごまかすことはできます:-)

コメントのためだけに-REFは格納されたオブジェクトでのみ機能し、PL / SQLから簡単にアクセスできないため、ほとんどの場合オプションではありません。

ただし、継承とキャストを使用することで問題を回避できます。

属性がほとんどない基本型を定義し、その基本型の下に型を作成するだけです。これで、基本タイプには他のタイプへの参照がないため、すべてのタイプが基本タイプの属性を持つことができます-したがって、循環的ではありません:-)

サブタイプの属性にアクセスするときは、それらを適切なタイプにキャストして、それらの属性とメソッドにアクセスする必要があります。

あなたの場合、タイプBASE_OBJECTのすべての属性を定義するだけで、準備は完了です。

*これについて何も見つからなかったので、私はこれを理解したことを少し誇りに思っています!!!

CREATE OR REPLACE TYPE test_base FORCE IS OBJECT(id NUMBER) NOT FINAL;
/

CREATE OR REPLACE TYPE test_person FORCE UNDER test_base
  ( student test_base
   ,name        varchar2(100)
  );
/

CREATE OR REPLACE TYPE test_student FORCE UNDER test_base
  ( person test_base
   ,university  VARCHAR2(100)
  );
/

DECLARE
  pers test_person;
  stud test_student;
BEGIN
  pers := test_person(1, NULL, 'Mike Ross');
  stud := test_student(2, pers, 'Havard');
  pers.student := stud;

  DBMS_OUTPUT.PUT_LINE('Name: '||pers.NAME);

  DBMS_OUTPUT.PUT_LINE('University:'||TREAT(stud AS test_student).university);

END;
/
于 2014-04-02T12:44:57.423 に答える