了解Table和Transaction API之间的差异

时间:2010-06-22 09:59:34

标签: oracle design-patterns api plsql

朋友,

我通过另一个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调用?

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

2 个答案:

答案 0 :(得分:16)

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

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

对比度是这样的。请考虑创建新部门的这些业务规则:

  1. 新部门必须有名称和位置
  2. 新部门必须有经理,必须是现有员工
  3. 其他现有员工可能会被转移到新部门
  4. 新员工可能被分配到新部门
  5. 新部门必须至少分配两名员工(包括经理)
  6. 使用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