1

这是对是否可以在 PL/SQL 中使用对象组合的后续问题

该问题解决了如何在 PL/SQL 中创建相互依赖的对象类型(即每个对象的一个​​属性是对另一个对象的引用)。

我遇到的下一个问题与对象构造函数有关。这是代码(从逻辑上讲,学生存在于人体内)。此外,只是为了在评论中排除这一点,使用继承不是我的选择。

人物对象

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 构造函数中实例化 PERSON 中的 STUDENT 对象的以下行之外,此代码将编译而不会出现任何错误:

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

AREF必须引用一行。 你不能通过SELF,因为它不是参考。

完成这项工作的一种方法是创建一个隐藏表:

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

好的!由于我自己正在处理这个问题,我想分享我们是如何解决它的!!!

第一:没有直接的方法来解决 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 回答