PL / SQL: Any trick to avoid cloning objects?
If we assign an object variable to another object variable in pl / sql, the object is cloned because pl / sql does not work with references. For example, the following code will print two different sentences:
create or replace type cla as object -- class (would be very complex)
(
name varchar2(50)
);
declare
o1 cla;
o2 cla;
begin
o1 := cla('hi cloning world');
o2 := o1;
o1.name = 'goodbye cloning world';
dbms_output.put_line('o1.name: ' || o1.name);
dbms_output.put_line('o2.name: ' || o2.name);
end;
So, I thought I could encapsulate the object in another object (external object), and if I assign the external object to another external object, then the internal object will not be cloned:
create or replace type cla as object -- class (would be very complex)
(
name varchar2(10)
);
create or replace type eo_c as object -- class to encapsulate objects
(
o cla -- encapsulation is easy
);
declare
eo eo_c;
eo2 eo_c;
begin
eo := eo_c( cla('n1') ); -- eo is easy to create
dbms_output.put_line('eo.name: ' || eo.o.name); -- real object is easy to access
eo2 := eo; -- eo_c object is cloned, cla object shouldn't be cloned
eo.o.name := 'n2'; -- if eo.o == eo2.o then we are changing both names
dbms_output.put_line('eo.name: ' || eo.o.name);
dbms_output.put_line('eo2 name: ' || eo2.o.name);
end;
But this again prints two different sentences, so the inner object is cloned as well.
Is it possible to encapsulate an object in some other type of variable to avoid cloning the inner object? Or more generally, is there some kind of trick to avoid cloning an object while still allowing an easy way to work with it?
source to share
Based on Alex's suggestion (use an associative array), I created a package that encapsulates objects, so we can use them in an abstract way, as if they were references:
create or replace type cla as object -- complex class
(
name varchar2(10)
);
create or replace package eo as -- package to encapsulate objects
type ao_t -- type for hash (associative array)
is table of cla
index by varchar2(100);
o ao_t; -- hash of objects
end;
declare
o1 varchar2(100);
o2 varchar2(100);
begin
o1 := 'o1'; -- objects are hash indexes now
eo.o(o1) := new cla('hi'); -- store new object into the hash
o2 := o1; -- assign object == assign index
eo.o(o1).name := 'bye'; -- change object attribute
dbms_output.put_line('eo.o(o1).name: ' || eo.o(o1).name);
dbms_output.put_line('eo.o(o2).name: ' || eo.o(o2).name); -- equal?
end;
Now "bye" is written twice as expected with object references. The trick is that both o1 and o2 contain the same ~ reference for the same object. The syntax is a little more complex, but still very similar to standard object manipulation when accessing attributes and methods.
Assigning an object to another is just like the standard assignment of an object:
o2 := o1;
The same for using an object as a function argument:
afunc(o1);
Internally, afunc () will simply use o1 with the same special syntax for accessing methods or attributes (and no special syntax for assigning):
eo.o(o1).attrib := 5; eo.o(o1).method('nice'); o3 := o1;
The only requirement to use this trick is to add a hash (type and variable) to the eo package for each class that we want to encapsulate.
Update : index value based on variable name:
o1 := 'o1';
it can be a problem if, for example, we create an object in funcion, since the function needs to know all the values used in the rest of the program to avoid repeating the value. The solution is to take the value from the hash size:
o1 := eo.o.count;
This brings us to another problem: the hash content is persistent (since it's in the package), so more and more objects will be added to the hash when objects are created (even if objects are created by the same function). The solution is to remove the object from the hash when we're done with the object:
eo.o(o1) = null;
So the fixed program would be:
create or replace type cla as object -- complex class
(
name varchar2(10)
);
create or replace package eo as -- package to encapsulate objects
type ao_t -- type for hash (associative array)
is table of cla
index by varchar2(100);
o ao_t; -- hash of objects
end;
declare
o1 varchar2(100);
o2 varchar2(100);
begin
o1 := eo.o.count; -- index based on hash size
eo.o(o1) := new cla('hi'); -- store new object into the hash
o2 := o1; -- assign object == assign index
eo.o(o1).name := 'bye'; -- change object attribute
dbms_output.put_line('eo.o(o1).name: ' || eo.o(o1).name);
dbms_output.put_line('eo.o(o2).name: ' || eo.o(o2).name); -- equal?
eo.o(o1) = null; -- remove object
eo.o(o2) = null; -- remove object (redundant)
end;
source to share