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?

+3


source to share


1 answer


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;

      

+1


source







All Articles