Понимание различий между таблицей и API транзакций

друзья,

В Том нить который я нашел через другой вопрос SO, упоминает таблицу и транзакционные API, и я пытаюсь понять разницу между ними.

API таблицы (TAPI), где нет доступа к базовым таблицам, и есть "геттеры" и "сеттеры" для получения информации.

например, чтобы выбрать адрес я:

   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);
   ...

таким образом, единственная разница, которую я вижу между ТАПИ и XAPI-это метод, в котором запись извлекается из базы данных, т. е. выбор по сравнению с вызовом PL/SQL?

что это? или я совсем упустил суть?

2 ответов


давайте начнем с API таблицы. Это практика опосредования доступа к таблицам через API PL/SQL. Итак, у нас есть пакет для каждой таблицы, который должен быть сгенерирован из словаря данных. Пакет представляет стандартный набор процедур для выдачи DML для таблицы и некоторые функции для получения данных.

по сравнению Транзакционный API представляет собой единицу работы. Он вообще не предоставляет никакой информации о базовых объектах базы данных. Транзакционные API предлагают лучшую инкапсуляцию и более чистый интерфейс.

контраст такой. Рассмотрим следующие бизнес-правила создания нового отдела:

  1. новый отдел должен иметь название и местоположение
  2. новый отдел должен иметь менеджера, который должен быть существующим сотрудником
  3. другие существующие сотрудники могут быть переведены в новый отдел
  4. новые сотрудники могут быть назначены новый отдел
  5. в новом отделе должно быть назначено не менее двух сотрудников (включая менеджера)

используя 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;
/

тогда как с транзакционным 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;
/

так почему разница в получении данных? Потому что транзакционный подход API препятствует generic 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);

...особенно, если запись сотрудника имеет столбцы LOB.

это также более эффективно, чем:

l_sal := emp_utils.get_sal(p_eno);
l_comm := emp_utils.get_comm(p_eno);

... если каждый из этих геттеров выполняет отдельную инструкцию SELECT. Что не неизвестно: это плохой OO практика, которая приводит к ужасной производительности базы данных.

сторонники табличных API утверждают, что они защищают разработчика от необходимости думать о SQL. Люди, которые осуждают их, не любят Table APIs по той же самой причине. Даже лучшие API таблиц, как правило, поощряют обработку RBAR. Если мы каждый раз пишем собственный SQL, мы с большей вероятностью выберем подход на основе набора.

использование транзакционных API не обязательно исключите использование get_resultset() функции. В API запросов по-прежнему много значения. Но он, скорее всего, будет построен из представлений и функций, реализующих объединения, чем выбирает отдельные таблицы.

кстати, я думаю, что построение транзакционных API поверх табличных API не является хорошей идеей: у нас все еще есть силосные операторы SQL вместо тщательно написанных соединений.

в качестве иллюстрации приведем две различные реализации транзакционной API для обновления заработной платы каждого сотрудника в регионе (регион-крупномасштабный раздел организации; отделы назначаются регионам).

в первой версии нет чистого SQL, только вызовы API таблиц, я не думаю, что это соломенный человек: он использует функциональность, которую я видел в пакетах 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 это подпруга, чтобы написать соединение нам нужно выбрать сотрудников по регионам. Гораздо сложнее использовать API таблицы, потому что регион не является ключом сотрудников.

справедливости ради, если у нас есть API таблицы, который поддерживает динамический SQL, все лучше, но все еще не идеально:

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;
/

последние слово

сказав Все это, есть сценарии, где таблицы API могут быть полезны, ситуации, когда мы хотим взаимодействовать только с отдельными таблицами довольно стандартными способами. Очевидным примером может быть производство или потребление каналов данных из других систем, например ETL.

если вы хотите исследовать использование табличных API, лучшим местом для начала является Steven Feuerstein Утилита Quest CodeGen (ранее QNXO). Это примерно так же хорошо, как генераторы TAPI сделать, и это бесплатно.


a table API (TAPI) - это простой API, который предоставляет основные операции CRUD для таблицы. Например, если у нас есть Таблер MYTABLE (id INTEGER PRIMARY KEY, text VACHAR2(30)), тогда ТАПИ будет что-то вроде:

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;

когда вы используете TAPIs, каждая таблица имеет TAPI, и каждая вставка, обновление и удаление проходит через TAPI.

API транзакций (XAPI) - это API, который работает на уровне транзакций, а не на индивидуальном уровне CRUD (хотя в некоторые случаи это будет такая же вещь.) Например, 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;

т. е. он выполняет всю транзакцию, которая может состоять из 1 или нескольких операторов DML.

сторонник ТАПИ сказал бы, что это неправильно закодировано и не должно содержать DML, но вместо этого вызовите код ТАПИ например:

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;

другие (например, Том кайт и я) посчитали бы это излишеством, не добавляя никакой реальной ценности.

таким образом, Вы можете иметь XAPIs в одиночку (путь Тома Кайта), или XAPIs, которые называют TAPIs (путь Стива Фойерштейна). Но некоторые системы имеют только TAPIs, который очень плохо - т. е. они оставляют это авторам пользовательского интерфейса, чтобы связать вместе необходимые вызовы TAPI, чтобы составить транзакцию. См.мой блог для последствий этого подход.