这是Is it possible to use object composition in 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构造函数,但这只是猜测。
感谢。
答案 0 :(得分:1)
A REF
must refer to a row.您无法通过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;
/
答案 1 :(得分: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;
/