朋友,
我通过另一个SO问题找到的问汤姆thread提到了Table和Transactional API,我试图理解它们之间的区别。
表API(TAPI)是无法访问基础表的地方,并且有“getters”和& “塞特犬”获取信息。
例如,选择一个地址我会:
the_address := get_address(address_id);
而不是:
select the_address
from some_table
where identifier = address_id
然后更改地址我将调用另一个负责更改的TAPI:
...
change_address(address_id, new_address);
...
事务API(XAPI)再次没有直接访问权限来修改表中的信息,但我可以从中进行选择? (这是我的理解有点朦胧的地方)
要选择一个地址,我会:
select the_address
from some_table
where identifier = address_id
然后改变它我会打电话给
...
change_address(address_id, new_address);
...
因此,我可以在TAPI和XAPI之间看到的唯一区别是从数据库中检索记录的方法,即Select与PL / SQL调用?
是吗?还是我完全错过了这一点?答案 0 :(得分:16)
让我们从Table API开始。这是通过PL / SQL API调解对表的访问的实践。因此,我们每个表都有一个包,应该从数据字典中生成。该包提供了一组标准的过程,用于针对表发出DML以及一些用于检索数据的函数。
通过比较,Transactional API代表一个工作单元。它根本不公开任何有关底层数据库对象的信息。事务性API提供更好的封装和更清晰的界面。
对比度是这样的。请考虑创建新部门的这些业务规则:
使用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;
/
使用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;
/
那为什么检索数据的区别呢?因为Transactional API方法不鼓励泛型get()
函数,以避免盲目使用低效的SELECT语句。
例如,如果您只想要雇员的工资和佣金,请查询...
select sal, comm
into l_sal, l_comm
from emp
where empno = p_eno;
...比执行这个更好......
l_emprec := emp_utils.get_whole_row(p_eno);
...特别是如果Employee记录有LOB列。
它也比以下更有效率。
l_sal := emp_utils.get_sal(p_eno);
l_comm := emp_utils.get_comm(p_eno);
...如果每个getter都执行一个单独的SELECT语句。这不是未知的:这是一个糟糕的OO实践,导致可怕的数据库性能。
表API的支持者认为它们是基于它们保护开发人员不需要考虑SQL。弃用它们的人不喜欢表API 出于同样的原因。即使是最好的Table API也倾向于鼓励RBAR处理。如果我们每次编写自己的SQL时都更有可能选择基于集合的方法。
使用Transactional APis不一定排除使用get_resultset()
函数。查询API仍然有很多价值。但它更有可能是在视图和函数中构建实现连接而不是单个表上的SELECT。
顺便说一句,我认为在Table API之上构建Transactional API并不是一个好主意:我们仍然使用孤立的SQL语句而不是仔细编写的连接。
作为一个例子,这里有两个不同的事务API实现,用于更新Region中每个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;
/
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;
/
这比嵌套游标循环和先前版本的单行更新要好得多。这是因为在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;
/
最后一句话
说了这么多,有些情况下Table API可能很有用,当我们只想以相当标准的方式与单个表进行交互时。一个明显的例子可能是从其他系统产生或消费数据馈送,例如ETL。
如果你想调查Table API的使用,最好的起点是Steven Feuerstein的Quest CodeGen Utility(以前的QNXO)。这和TAPI生成器一样好,并且它是免费的。
答案 1 :(得分: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;
当您使用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;
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;
即。它执行整个事务,可能包含一个或多个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;
其他人(比如Tom Kyte和我本人)会认为这有点过分,没有增加任何实际价值。
所以你可以单独使用XAPI(Tom Kyte的方式),或者称为TAPI的XAPI(Steve Feuerstein的方式)。但是有些系统单独使用TAPI,非常差 - 即他们将其留给用户界面的编写者将必要的TAPI调用串起来组成一个事务。有关该方法的含义,请参阅my blog。