了解Table和Transaction API之间的差异

Ian*_*ter 11 oracle api plsql design-patterns

朋友们,

我通过另一个SO问题找到的Ask Tom 线程提到了Table和Transactional API,我试图理解它们之间的区别.

表API(TAPI)是无法访问基础表的地方,并且有"getters"和"setter"来获取信息.

例如,要选择一个地址,我会:

   the_address := get_address(address_id);
Run Code Online (Sandbox Code Playgroud)

代替:

   select the_address
   from some_table
   where identifier = address_id
Run Code Online (Sandbox Code Playgroud)

然后要更改地址,我将调用另一个负责更改的TAPI:

   ...
   change_address(address_id, new_address);
   ...
Run Code Online (Sandbox Code Playgroud)

事务API(XAPI)再次没有直接访问权限来修改表中的信息,但我可以从中进行选择?(这是我的理解有点朦胧的地方)

要选择一个地址我会:

   select the_address
   from some_table
   where identifier = address_id
Run Code Online (Sandbox Code Playgroud)

然后改变它我会打电话

   ...
   change_address(address_id, new_address);
   ...
Run Code Online (Sandbox Code Playgroud)

因此,我可以在TAPI和XAPI之间看到的唯一区别是从数据库中检索记录的方法,即Select与PL/SQL调用?

是吗?还是我完全错过了这一点?

APC*_*APC 16

让我们从Table API开始.这是通过PL/SQL API调解对表的访问的实践.因此,我们每个表都有一个包,应该从数据字典中生成.该包提供了一组标准的过程,用于针对表发出DML以及一些用于检索数据的函数.

相比之下,Transactional API代表一个工作单元.它根本不公开任何有关底层数据库对象的信息.事务性API提供更好的封装和更清晰的界面.

对比是这样的.考虑用于创建新部门的这些业务规则:

  1. 新部门必须有名称和位置
  2. 新部门必须有一名经理,他必须是现有的员工
  3. 其他现有员工可能会被转移到新部门
  4. 新员工可能被分配到新部门
  5. 新部门必须至少分配两名员工(包括经理)

使用Table API,事务可能如下所示:

DECLARE
    dno pls_integer;
    emp_count pls_integer;
BEGIN
    dept_utils.insert_one_rec(:new_name, :new_loc, dno);
    emp_utils.update_one_rec(:new_mgr_no ,p_job=>'MGR’ ,p_deptno=>dno);
    emp_utils.update_multi_recs(:transfer_emp_array, p_deptno=>dno);
    FOR idx IN :new_hires_array.FIRST..:new_hires_array.LAST LOOP
        :new_hires_array(idx).deptno := dno;
    END LOOP;
    emp_utils.insert_multi_recs(:new_hires_array);
    emp_count := emp_utils.get_count(p_deptno=>dno); 
    IF emp_count < 2 THEN
        raise_application_error(-20000, ‘Not enough employees’);
    END IF;
END;
/
Run Code Online (Sandbox Code Playgroud)

而使用Transactional API则更简单:

DECLARE
    dno subtype_pkg.deptno;
BEGIN
    dept_txns.create_new_dept(:new_name
                                , :new_loc
                                , :new_mgr_no
                                , :transfer_emps_array
                                , :new_hires_array
                                , dno);
END;
/
Run Code Online (Sandbox Code Playgroud)

那么为什么检索数据的区别呢?因为Transactional API方法不鼓励泛型get()函数,以避免盲目使用低效的SELECT语句.

例如,如果您只想要雇员的工资和佣金,请查询...

select sal, comm
into l_sal, l_comm
from emp
where empno = p_eno;
Run Code Online (Sandbox Code Playgroud)

......比执行这个更好......

l_emprec := emp_utils.get_whole_row(p_eno);
Run Code Online (Sandbox Code Playgroud)

...尤其是如果Employee记录具有LOB列.

它也比以下更有效:

l_sal := emp_utils.get_sal(p_eno);
l_comm := emp_utils.get_comm(p_eno);
Run Code Online (Sandbox Code Playgroud)

...如果每个getter都执行一个单独的SELECT语句.这不是未知的:这是一个糟糕的OO实践,导致可怕的数据库性能.

Table API的支持者认为它们是基于它们保护开发人员不需要考虑SQL.出于同样的原因,弃用它们的人不喜欢Table API .即使是最好的Table API也倾向于鼓励RBAR处理.如果我们每次编写自己的SQL时都更有可能选择基于集合的方法.

使用Transactional APis不一定排除get_resultset()函数的使用.查询API仍然有很多价值.但它更可能是由视图和函数构建的,而不是在各个表上实现连接而不是SELECT.

顺便说一句,我认为在Table API之上构建Transactional API并不是一个好主意:我们仍然使用孤立的SQL语句而不是仔细编写的连接.

作为说明,这里有两个不同的事务API实现,用于更新区域中每个Employee的工资(Region是组织的大规模部分; Departments被分配到Regions).

第一个版本没有纯SQL只是Table API调用,我不认为这是一个吸管人:它使用我在Table API包中看到的那种功能(虽然有些使用动态SQL而不是命名SET_XXX()程序) .

create or replace procedure adjust_sal_by_region
    (p_region in dept.region%type
           , p_sal_adjustment in number )
as
    emps_rc sys_refcursor;
    emp_rec emp%rowtype;
    depts_rc sys_refcursor;
    dept_rec dept%rowtype;
begin
    depts_rc := dept_utils.get_depts_by_region(p_region);

    << depts >>
    loop
        fetch depts_rc into dept_rec;
        exit when depts_rc%notfound;
        emps_rc := emp_utils.get_emps_by_dept(dept_rec.deptno);

        << emps >>
        loop
            fetch emps_rc into emp_rec;
            exit when emps_rc%notfound;
            emp_rec.sal := emp_rec.sal * p_sal_adjustment;
            emp_utils.set_sal(emp_rec.empno, emp_rec.sal);
        end loop emps;

    end loop depts;

end adjust_sal_by_region;
/
Run Code Online (Sandbox Code Playgroud)

SQL中的等效实现:

create or replace procedure adjust_sal_by_region
    (p_region in dept.region%type
           , p_sal_adjustment in number )
as
begin
    update emp e
    set e.sal = e.sal * p_sal_adjustment
    where e.deptno in ( select d.deptno 
                        from dept d
                        where d.region = p_region );
end adjust_sal_by_region;
/
Run Code Online (Sandbox Code Playgroud)

这比嵌套游标循环和先前版本的单行更新好得多.这是因为在SQL中,编写我们需要按区域选择Employees的连接是很简单的.使用Table API要困难得多,因为Region不是Employees的关键.

公平地说,如果我们有一个支持动态SQL的Table API,事情会更好但仍然不理想:

create or replace procedure adjust_sal_by_region
    (p_region in dept.region%type
           , p_sal_adjustment in number )
as
    emps_rc sys_refcursor;
    emp_rec emp%rowtype;
begin
    emps_rc := emp_utils.get_all_emps(
                    p_where_clause=>'deptno in ( select d.deptno 
                        from dept d where d.region = '||p_region||' )' );

    << emps >>
    loop
        fetch emps_rc into emp_rec;
        exit when emps_rc%notfound;
        emp_rec.sal := emp_rec.sal * p_sal_adjustment;
        emp_utils.set_sal(emp_rec.empno, emp_rec.sal);
    end loop emps;

end adjust_sal_by_region;
/
Run Code Online (Sandbox Code Playgroud)

遗言

说了这么多,有些情况下Table API可能很有用,当我们只想以相当标准的方式与单个表进行交互时.一个明显的例子可能是生成或消费来自其他系统的数据馈送,例如ETL.

如果你想研究Table API的使用,最好的起点是Steven Feuerstein的Quest CodeGen Utility(以前的QNXO).这和TAPI生成器一样好,并且它是免费的.


Ton*_*ews 10

表API(TAPI)是一个简单的API,它为表提供基本的CRUD操作.例如,如果我们有一个tableR MYTABLE (id INTEGER PRIMARY KEY, text VACHAR2(30)),那么TAPI将是这样的:

package mytable_tapi is
    procedure create_rec (p_id integer, p_text varchar2);
    procedure update_rec (p_id integer, p_text varchar2);
    procedure delete_rec (p_id integer);
    function get_rec (p_id integer) returns mytable%rowtype;
end;
Run Code Online (Sandbox Code Playgroud)

当您使用TAPI时,每个表都有一个TAPI,每次插入,更新和删除都通过TAPI.

事务API(XAPI)是一种在事务级别而不是在单个CRUD级别工作的API(尽管在某些情况下这将是相同的事情).例如,处理银行交易的XAPI可能如下所示:

package banking_xapi is
    procedure make_transfer (p_from_account integer, p_to_account integer,
                             p_amount number);
    ... -- other XAPI procs
end;
Run Code Online (Sandbox Code Playgroud)

make_transfer过程可能不会执行单个插入,更新或删除.它可能会做这样的事情:

procedure make_transfer (p_from_account integer, p_to_account integer,
                         p_amount number)
is
begin
    insert into transfer_details (from_account, to_account, amount)
       values (p_from_account, p_to_account, p_amount);

    update accounts set balance = balance-p_amount
    where account_no = p_from_account;

    update accounts set balance = balance+p_amount
    where account_no = p_to_account;
end;
Run Code Online (Sandbox Code Playgroud)

即它执行整个事务,可能包含1个或多个DML语句.

一个TAPI支持者会说这是编码错误,不应该包含DML,而是调用TAPI代码,如:

procedure make_transfer (p_from_account integer, p_to_account integer,
                         p_amount number)
is
begin
    transactions_tapi.insert_rec (p_from_account, p_to_account, p_amount);

    accounts_tapi.update_rec (p_from_account, -p_amount);

    accounts_tapi.update_rec (p_to_account, p_amount);
end;
Run Code Online (Sandbox Code Playgroud)

其他人(比如Tom Kyte和我自己)会认为这有点过分,没有增加任何实际价值.

所以你可以单独使用XAPI(Tom Kyte的方式),或者称为TAPI的XAPI(Steve Feuerstein的方式).但是有些系统单独使用TAPI,这实际上很差 - 即它们将它留给用户界面的编写者将必要的TAPI调用串起来组成一个事务.请参阅我的博客,了解该方法的含义.