ABAP Object Services – Transaction Service

databaseИнструмент обработки транзакций позволяет разработчику контролировать процессы записи изменений из хранимых объектах в базу данных.

Transaction Service основан на классической схеме обновления данных, с небольшими дополнениями. Используя TS нет необходимости ручного вызова модулей обновлений. Кроме того, TS вводит такое понятие как ООП транзакция.

Компоненты Transaction Service

Рассмотрим используемые в TS компоненты. Сама по себе ООП транзакция это ссылка на объект, созданный с помощью менеджера транзакций.

tscomponents

Методы системного класса CL_OS_SYSTEM

GET_TRANSACTION_MANAGER(). Используется для получения ссылки на менеджер транзакций. Пример:

INIT_AND_SET_MODES. Инициализация и установка режима для транзакций верхнего уровня. Метод вызывается всего один раз для каждой программы (явно или неявно), если вызвать повторно приведет к ошибке. Если вы хотите переопределить настройки режимов, вызывать данный метод необходимо в самом начале программы, до обращения к другим сервисам ABAP Objects.  Обычно вызывается в LOAD-OF-PROGRAM или конструкторе класса приложения (Для WebDynpro приложений в WDDOINIT) . Описание режимов даётся далее.

Обратите внимание на возможную ошибку при использовании данного метода:

Агент класса получаем через статический атрибут, среда выполнения запускает статический конструктор в начале блока обработки, т.е. перед вызовом метода INIT_AND_SET_MODES, что приведет к ошибке. Правильный вызов:

GET_CLASS_AGENT. Относительно имени класса получает ссылку на агент класса.

GET_CLASS_AGENT_INFO. Получение информации об агенте класса, структура — OSTYP_CA_INFO.

Атрибуты класса CL_OS_SYSTEM

ACTIVE_CLASS_AGENT. Внутренняя таблица со всеми инициализированными агентами хранимых классов в рамках текущей внутренней сессии.
EXTERNAL_COMMIT. Режим транзакции.
INIT_STATE. Состояние инициализации Object Services, если 0 значит Object Services не инициализирован, во время инициализации статус 1, после 2.

 

Методы менеджера транзакций (класса, реализующий интерфейс IF_OS_TRANSACTION_MANAGER)

IF_OS_TRANSACTION_MANAGER~CREATE_TRANSACTION. Создаёт новую транзакцию и возвращает в качестве результата объект класса, реализующего интерфейс — IF_OS_TRANSACTION.

IF_OS_TRANSACTION_MANAGER~GET_CURRENT_TRANSACTION. Получение объекта текущей транзакции.

IF_OS_TRANSACTION_MANAGER~GET_TOP_TRANSACTION. Получение объекта на транзакцию верхнего уровня.

 

Методы транзакций (класса, реализующего интерфейс IF_OS_TRANSACTION)

IF_OS_TRANSACTION~START. Начинает транзакцию, если нет транзакции верхнего уровня, стартует транзакция верхнего уровня, иначе вложенная транзакция. Нельзя запускать вложенную транзакцию, если предыдущая вложенная транзакция не была завершена. Данный метод вызывается только для транзакций со статусом OSCON_TSTATUS_NEW. Метод вызывается только один раз для транзакции. После завершения транзакции нельзя снова начать транзакцию, необходимо создать новую транзакцию (через менеджер транзакций), можно использовать ту же ссылочную переменную.

IF_OS_TRANSACTION~END. Завершает транзакцию. Если транзакция верхнего уровня в ООП режиме, будет неявно вызван COMMIT WORK, что повлечет за собой вызов модуля обновления (CALL FUNCTION IN UPDATE TASK) и обновление хранимых объектов. Таким образом, после обращения к обновленному хранимому объекту, он вновь будет загружен из БД. Если по какой-либо причине завершение транзакции не удалось, статус транзакции останется тем, что был до вызова метода (OSCON_TSTATUS_RUNNING), разработчик должен решить, что ему делать с транзакцией.

IF_OS_TRANSACTION~UNDO. Завершает транзакцию. Изменения, сделанные в рамках текущей транзакции, откатываются на то состояние, в котором были объекты до старта транзакции. Если транзакция является транзакцией верхнего уровня в ООП режиме, будет неявно вызван ROLLBACK WORK. Механизм возврата отключен по умолчанию для неявно созданной транзакции верхнего уровня в режиме совместимости, т.к. система не может определить начальное состояние объектов (методы START, END не вызываются) Механизм возврата не возвращает в первоначальное состояние следующие объекты: Атрибуты обычных ABAP объектов, статические атрибуты, временные объекты, данные вне класса – внутренние таблицы, структуры и пр. Можно переопределить механизм возврата, расширив его на перечисленные объекты. Изменения в объектах фиксируются и обрабатываются агентом хранимого класса, у каждого из хранимых объектов есть события для уведомления агента об изменениях. Для сохранения состояния хранимого объекта и возвращения его состояния используются методы SET(GET) интерфейса IF_OS_STATE, структура этих методов основана на шаблоне проектирования «хранитель», но при этом используются специальный системный вызов SYSTEM-CALL OBJMGR GET STATE, созданный специально для Object Services (SAP не рекомендует его использовать в своих объектах, данный системный вызов считывает все атрибуты класса в специально сгенерированную для этого структуру). Схема вызовов при изменении атрибута будет выглядеть следующим образом:

tstrace

На первом шаге вызывается сгенерированный метод для изменения атрибута, перед основным кодом в данном методе происходит вызов макроса state_write_access из инклуда if_os_state_macros. Далее внутри макроса запускается событие write_access, на данное событие в агенте класса повешен обработчик, который внутри себя получает от хранимого объекта его состояние и записывает в специальную таблицу PM_UNDO_INFO.

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

IF_OS_TRANSACTION~END_AND_CHAIN.Завершает транзакцию и тут же создает и начинает новую. Если транзакция верхнего уровня в ООП режиме, хранимые объекты сохраняются в БД, но при этом не загружаются повторно из БД (что может быть гораздо оптимальнее, чем просто END). Возвращает ссылочную переменную на новую транзакцию. Если по какой-либо причине завершение транзакции не удалось, статус транзакции останется тем, что был до вызова метода (OSCON_TSTATUS_RUNNING), разработчик должен решить, что ему делать с транзакцией.

IF_OS_TRANSACTION~UNDO_AND_CHAIN. Отменяет предыдущую и начинает новую транзакцию, объекты так же не будут загружаться из БД. Возвращает ссылочную переменную на новую транзакцию. Пример использования:

IF_OS_TRANSACTION~REGISTER_CHECK_AGENT. Регистрирует агента проверки консистентности данных для хранимого объекта. Агент проверки — это класс, реализующий интерфейс IF_OS_CHECK. Данный интерфейс имеет метод IS_CONSISTENT, который возвращает переменную с типом OSCON_BOOLEAN. Как правило, интерфейс реализуют в классе хранимого объекта. Если проверка вернет OSCON_FALSE, изменения по объекту не будут записаны в БД. Проверка вызывается при выполнении методов END и END_AND_CHAIN, если какой-либо агент вернет ошибку, все остальные проверки не будут далее выполняться. Если агент возвращает ошибку, вызывается исключение CX_OS_AGENT_CHECK_FAILED, обработав которое можно изменить ошибочный объект и попытаться сохранить объекты заново, либо отменить изменения методом UNDO. Пример: добавим реализацию интерфейса IF_OS_CHECKв класс ZCL_SFLIGHT и реализуем его метод:

agent_check

Пытаемся сохранить с нулевой ценой:

check_error

Как видим при попытке завершить транзакцию, система не смогла пройти проверку класса агента.

Агент можно использовать для сохранения каких-либо атрибутов, которые надо заполнить непосредственно перед записью в БД, например, дату и время изменения, пользователя, менявшего объект и т.п.

Обычно регистрация агента, если он реализован в хранимом классе, происходит внутри метода INIT:

init_check

IF_OS_TRANSACTION~GET_STATUS. Получение статуса транзакции. Существуют следующие статусы:

  • OSCON_TSTATUS_NEW. Транзакция создана, но еще не начата, можно использовать только метод START.
  • OSCON_TSTATUS_RUNNING. Транзакция начата и является активной.
  • OSCON_TSTATUS_END_REQ. Транзакция была завершена вызовом метода END и ожидает окончания проверки агентом проверки или окончания обработчиков событий.
  • OSCON_TSTATUS_FIN_SUCCESS. Транзакция успешно завершена вызовом метода END.
  • OSCON_TSTATUS_FIN_UNDO. Транзакция отменена, объекты возвращены в их первоначальное состояние.
  • OSCON_TSTATUS_FIN_ABORT. Транзакция отменена, объекты не возвращены в их первоначальное состояние.

tsstatus

IF_OS_TRANSACTION~SET_MODE_UNDO_RELEVANT. Может использоваться для отключения механизма возврата объектов в первоначальное состояние (с целью оптимизации потребления памяти) после вызова метода UNDO (UNDO_AND_CHAIN). Статус транзакции в таком случае после отмены будет OSCON_TSTATUS_FIN_ABORT.

IF_OS_TRANSACTION~SET_MODE_UPDATE. Используется для транзакций верхнего уровня в режиме совместимости, может вызываться только один раз, устанавливает режим обновления для транзакции. Для ООП режима, режим обновления выставляется через системный класс CL_OS_SYSTEM (см. ниже). Если выбрать локальные или прямые обновления, неявно будет запущен оператор SET UPDATE TASK LOCAL при старте транзакции.

IF_OS_TRANSACTION~GET_MODES. Считывание вышеописанных параметров транзакции: режим обновления и т.п.

События транзакций:

IF_OS_TRANSACTION~SAVE_REQUESTED. Событие вызывается, когда транзакция верхнего уровня завершается методом END, до момента, когда агент класса запишет изменения. Данное событие может использоваться для заполнения хранимых атрибутов (например, даты, времени изменения объекта), либо вызова каких-либо внутренних методов перед самым сохранением объекта. Обратите внимание что в обработчиках этого события нельзя вызывать методы TS, если хочется предотвратить запись в БД, необходимо использовать агента проверки.

IF_OS_TRANSACTION~SAVE_PREPARED.  Событие вызывается, когда ООП транзакция верхнего уровня завершается методом END, после того как хранимые атрибуты были записаны в записи обновлений, но непосредственно до вызова COMMIT WORK. Событие используется обычно для сохранения каких-либо данных не связанных с объектом, путём вызова модуля обновления и т.п.

IF_OS_TRANSACTION~FINISHED. Событие срабатывает в конце транзакции, вне зависимости от того, как она была завершена (END,UNDO).

Атрибуты транзакций:

  • SUPER_TRANSACTION. Если атрибут содержит пустую ссылку, транзакция является транзакцией верхнего уровня.
  • SUBTRANSACTION. Если атрибут содержит пустую ссылку, транзакция находится на нижнем уровне.
  • CHAINED. Если abap_true, транзакция была запущена после вызова метода END_AND_CHAIN или UNDO_AND_CHAIN.
  • CHECK_AGENTS. Внутреняя таблица с зарегистрированными агентами проверки.
  • UNDO_RELEVANT. abap_true – механизм возврата включен.
  • TRANSACTION_STATE. Статус транзакции.
  • DATA_SAVE_STATE. Статус сохранения данных.
  • UPDATE_MODE. Режим обновления.
  • FLAG_UPDT_MODE_CHANGED. Выставляется, если для транзакции в режиме совместимости был изменен режим обновления.

 

Режимы транзакций

 

Режим определяется только для транзакции верхнего уровня. Есть два режима:

  • Режим совместимости. Параметр в методе инициализации — E_EXTERNAL_COMMIT равен значению OSCON_TRUE (по умолчанию). В классическом ABAP приложении, при обновлении базы данных, в системе регистрируются модули обновления, после вызова COMMIT WORK происходит их выполнение (синхронно или асинхронно). При использовании PS в таком приложении, будет неявно создана объектно-ориентированная транзакция верхнего уровня, в режиме совместимости. Все обращения к хранимым объектам будут обрабатываться непосредственно в ней. При вызове COMMIT WORK, ООП транзакция верхнего уровня будет завершена и создана новая. Изменения, сделанные до текущего момента в хранимых объектах, будут перенесены в БД. Аналогичным образом при вызове ROLLBACK WORK, все изменения в объектах будут отменены. В режиме совместимости нельзя использовать методы для старта (START), завершения транзакции (END) т.к. это приведет к возникновению дампа.
  • ООП Режим. Параметр в методе инициализации — E_EXTERNAL_COMMIT равен значению OSCON_FALSE.  Если в ABAP Objects Services была явным образом создана ООП транзакция (ООП Режим), для её подтверждения надо использовать методы TS (END – COMMIT WORK, UNDO – ROLLBACK WORK), нельзя использовать COMMIT WORKили ROLLBACK WORK, т.к. это приведет к ошибке времени выполнения. Явно созданная ООП транзакция верхнего уровня, при её завершении будет вызывать неявным образом COMMIT WORK. Любая другая созданная ООП транзакция, до момента завершения предыдущей, будет считаться вложенной. Вложенная транзакция должна быть закончена до окончания родительской транзакции. Вложенные транзакции никогда не вызывают COMMIT WORK после своего завершения. Изменения, сделанные в хранимых объектах до момента создания новой транзакции верхнего уровня в ООП режиме, будут проигнорированы.

По своему назначению, вложенные транзакции необходимы только ради того, чтобы иметь возможность откатываться в случае каких-либо проблем на то состояние, в котором были объекты до начала вложенной транзакции. Первоначальное состояние объектов берется из памяти, а не из БД. Система не позволяет использовать несколько уровней вложенности, следующая вложенная транзакция должна начинаться только тогда, когда закончила выполняться предыдущая (в противном случае возникнет ошибка времени выполнения):

transactions

Кроме режима транзакции, необходимо определить режим обновления. При завершении транзакции верхнего уровня (вызов COMMIT WORK в режиме совместимости, вызов метода END в ООП транзакциях), TS начинает обновлять измененные хранимые объекты в БД, путём запуска модуля обновления.

Существуют следующие режимы обновления:

  • Асинхронный режим (OSCON_DMODE_DEFAULTили OSCON_DMODE_UPDATE_TASK)
  • Синхронный режим (OSCON_DMODE_UPDATE_TASK_SYNC)
  • Прямое обновление (OSCON_DMODE_DIRECT)
  • Локальные обновления (OSCON_DMODE_LOCAL)

Режим обновления может быть явно задан при определении ООП транзакции SAP в SE93:

se93

Более подробно о режимах обновления читайте в предыдущей статье.

Состояние хранимых объектов при обработке транзакций

 

Обработка ООП транзакций может влиять на статусы хранимых объектов следующим образом:

  • Начало транзакции. В начале транзакции верхнего уровня, все объекты откатываются до состояния NOT_LOADED. Таким образом, если вы меняли, удаляли или создавали хранимые объекты до создания транзакции верхнего уровня, все ваши изменения будут утеряны. Подобное поведение не касается временных объектов, т.к. они не связаны с БД. Создание вложенной транзакции не оказывает никакого влияния не на хранимые объекты, не на временные объекты.
  • Конец транзакции. Если транзакция верхнего уровня завершается методом END, система записывает все изменения, что были внутри неё произведены, в БД. При завершении вложенной транзакции методом END, статусы хранимых объектов никак не изменяются. При завершении транзакций методом UNDO (если механизм отката включен), как вложенных, так и дочерних, статусы объектов возвращаются в первоначальное состояние (на момент старта транзакции). Для того чтобы не загружать объекты из БД слишком часто, объекты со статусом LOADED, сохраняют свой статус, даже если их статус в начале транзакции был NOT_LOADED. Статус LOADED сохраняется, даже если вы изменяли атрибуты или удаляли объекты, все будет восстановлено. Если механизм отката не активирован, ничего не происходит.

Классические операторы обработки SAP LUW– COMMIT WORK и ROLLBACK WORK, при обработке транзакций верхнего уровня в режиме совместимости, имеют тот же эффект, что и методы END, UNDO для ООП транзакций верхнего уровня: Всем хранимым объектам присваивается статус NOT_LOADED. Временные объекты остаются со статусом временный объект, даже после отката ROLLBACK WORK.

Методы END_AND_CHAIN и UNDO_AND_CHAIN так же могут изменять состояние объектов, в зависимости от первоначального состояния объекта:

  • END_AND_CHAIN. NEW – объект создался во время транзакции, после вызова метода его статус будет LOADED.  DELETED – объект удален во время транзакции, после вызова метода его статус будет NOT_LOADED.
  • UNDO_AND_CHAIN. NEW – объект был создан во время транзакции, после вызова метода объект будет иметь статус NOT_LOADED. DELETED – в зависимости от того, какой был статус до начала транзакции.  NOT LOADED останется NOT_LOADED, LOADED останется LOADED.

В процессе разработки, вы должны решить для себя, какой из методов завершения транзакции вы будете использовать END или END_AND_CHAIN и UNDOили UNDO_AND_CHAIN. Если вы хотите чтобы все загруженные ранее объекты, считались заново из БД, используйте END и UNDO, в противном случае CHAIN методы. Из-за того что загружаться заново из БД объекты не будут, использование CHAIN методов будет оптимальнее. В режиме совместимости, у вас нет такого выбора, все объекты, так или иначе, будут загружены заново для каждого нового LUW после доступа к ним.