In Oracle SQL
it's possible to use REF
and DEREF
functions to pass logical pointers to objects stored in database tables.
But there are no such thing as pointer or reference in PL/SQL
so you need a workaround.
Below some approaches for possible workarounds in PL/SQL. I apologize in advance for any errors in the code. It's intended rather to demonstrate approaches than for use in production.
Approach 1 - cached access method
Generic idea is to access entities through function which cache results:
create or replace package EntitytAccess as
procedure GetCompany1(
pCompanyId in number,
pCompany out nocopy pkg_company.typ_company
);
function GetCompany2(
pCompanyId in number
) return pkg_company.typ_company;
procedure ClearCompanyCache;
end;
Access package body:
create or replace package body EntitytAccess as
type typ_company_cache is table of pkg_company.typ_company index by number;
var_company_cache typ_company_cache;
procedure GetCompany1(
pCompanyId in number,
pCompany out nocopy pkg_company.typ_company
)
is
begin
if( var_company_cache.exists(pCompanyId) )
pCompany := var_company_cache(pCompanyId);
else
pCompany := pkg_company.get(pCompanyId);
var_company_cache(pCompanyId) := pCompany;
end if;
end;
function GetCompany2(
pCompanyId in number
) return pkg_company.typ_company
is
begin
if(not var_company_cache.exists(pCompanyId) )
var_company_cache(pCompanyId) := pkg_company.get(pCompanyId);
end if;
return var_company_cache(pCompanyId);
end;
procedure ClearCompanyCache
is
begin
var_company_cache.Delete;
end;
end;
Usage:
declare
c pkg_company.typ_company;
e pkg_employee.typ_employee;
begin
EntityAccess.GetCompany2(12345,c);
for e in (select employeeid from employee where companyid=12345)
loop
e := pkg_employee.get(12345, c); -- c passed by reference in out nocopy
-- and c.companyid stored inside e.
pkg_employee.process(e); -- No need to pass company, but inside process()
-- method you need to call either
-- EntityAccess.GetCompany1(e.companyid, var_c)
-- or
-- var_c := EntityAccess.GetCompany2(e.companyid)
end loop;
end;
Approach 2 - environment package
Because package state belongs to one session you can use package variables to hold current processing state and reference it when needed.
create or replace package ProcessEnvironment as
var_current_company pkg_company.typ_company;
-- may be more "global" variables here
end;
/
create or replace package body ProcessEnvironment as
end;
Usage:
declare
e pkg_employee.typ_employee;
begin
ProcessEnvironmant.var_current_company := pkg_company.get(12345);
for e in (select employeeid from employee where companyid=12345)
loop
e := pkg_employee.get(12345, ProcessEnvironmant.var_current_company);
-- c passed by reference in out nocopy
-- and c.companyid stored inside e.
pkg_employee.process(e); -- No need to pass company, but inside process()
-- method you references
-- ProcessEnvironmant.var_current_company
-- with appropriate checks
end loop;
end;
Mixed approach
Seems that in case of Approach 1
instances copied from collection, especially if accessed with function GetCompany2()
. Copy constructor may be fast enough but produce some overhead.
For Approach 2
there are some checks must be present in code of business logic function so it's maintenance overhead just from another point of view.
To deal with both problems you can use cache, but hold only one value inside a package:
create or replace package EntitytAccess as
procedure GetCompany(
pCompanyId in number,
pCompany out nocopy pkg_company.typ_company
);
end;
Package body:
create or replace package body EntitytAccess as
var_cached_company pkg_company.typ_company;
procedure GetCompany(
pCompanyId in number,
pCompany out nocopy pkg_company.typ_company
)
is
begin
if( (var_cached_company is null)
or
(var_cached_company.comanyid != pCompanyId)
)then
var_company_cache := pkg_company.get(pCompanyId);
end if;
pCompany := var_cached_company;
end;
end;
Usage:
declare
c pkg_company.typ_company;
e pkg_employee.typ_employee;
begin
EntityAccess.GetCompany(12345,c);
for e in (select employeeid from employee where companyid= c.companyid)
loop
e := pkg_employee.get(c.companyid , c); -- c passed by reference in out nocopy
-- and c.companyid stored inside e.
pkg_employee.process(e); -- No need to pass company, but inside process()
-- method you need to call
-- EntityAccess.GetCompany(e.companyid, var_c)
-- where var_c is company object variable.
end loop;
end;