Потоки и локаторы

database-219x218

Начиная с версии 7.0, EhP2 в ABAP была введена новая концепция обработки внутренних (внешних) данных — потоковая обработка данных.

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

Фильтрующие потоки в настоящее время не реализованы и не рассматриваются в данной статье.

Потоки данных – такие потоки, которые напрямую соединены как читающий поток к источнику данных или как записывающий поток к приемнику данных.

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

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

LOB – Large object, может быть двух видов: BLOB или CLOB. BLOB – Binary large object. Поле в таблице базы данных с типом данных RAWSTRING. CLOB – Character large object. Поле в таблице базы данных с типом данных STRING.

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

Приемник данных – хранилище, в котором записывающий поток сохраняет свои данные. В качестве приемника может быть LOB поле в базе данных, файл, строка или внутренняя таблица. В настоящее время запись реализована для полей таблиц БД, строк и внутренних таблиц.

Потоки данных всегда имеют одно направление – на чтение или на запись.

Кроме направления потоки данных могут иметь следующие типы: бинарный поток, символьный поток. Бинарный поток содержит данные с байтовым типом данных (x, xstring), родовой тип xsequence (для полей таблиц RAWSTRING). Символьный поток содержит данные с символьным типом данных (с, string).

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

Свойства потоков:

  • Вид потока – поток данных или фильтрующий поток
  • Направление потока – на чтение или на запись
  • Тип потока – символьный поток или бинарный поток.

Классы и интерфейсы, реализующие в ABAP концепцию потоков, лежат в пакете SABP_STREAMS_AND_LOCATORS. Следующая диаграмма демонстрирует основные классы и интерфейсы:

Управление потоками

 

Классы для работы с потоками данных начинаются с «CL_ABAP_» и содержат следующие идентификаторы:

  • Resource – источник для чтения или приемник для записи, в настоящее время поддерживаются: строки (STRING), LOB поля в БД (DB), внутренние таблицы (ITAB).
  • Type – тип потока, бинарный (X) и строковый (C).
  • Direction – направление потока, запись (WRITER) и чтение (READER).

Имена классов для фильтрующих потоков начинаются с «CL_ABAP_FILTER_» и именуются по тем же правилам. В настоящее время фильтрующие потоки не реализованы (Версия 7.02-7.31).

Основные методы, используемые для работы с потоками, описаны в интерфейсах и суперклассах как методы ядра (для просмотра можно воспользоваться программой RSKMETH), следовательно, их реализация обеспечивается средой выполнения ABAP. Основные методы при работе с потоками:

  • DATA_AVAILABLE возвращает X если еще есть несчитанные данные.
  • IS_X_READER возвращает X если является бинарным потоком.
  • READ возвращает строку с заданным числом байтов или символов из потока.
  • SKIP пропускает заданное количество байт или символов в потоке.
  • WRITE запись данных потока в приемник.
  • CLOSE закрывает поток (после чего читать или писать уже нельзя).
  • IS_CLOSED возвращает X если поток закрыт.
  • SET_MARK установить метку, к которой можно будет возвратиться позже.
  • IS_MARK_SUPPORTED возвращает X если метки поддерживаются.
  • RESET_TO_MARK возврат к метке.

Потоки к строкам

Для создания потоков, источником или приемником которых служат строки, используется следующий набор классов:

  • CL_ABAP_STRING_C_READER
  • CL_ABAP_STRING_C_WRITER
  • CL_ABAP_STRING_X_READER
  • CL_ABAP_STRING_X_WRITER

Данные классы являются потомками от абстрактных классов потоков внутренних данных — CL_ABAP_MEMORY_….

Пример программы с потоками для строк:

Потоки к внутренним таблицам

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

  • CL_ABAP_ITAB_C_READER
  • CL_ABAP_ITAB_C_WRITER
  • CL_ABAP_ITAB_X_READER
  • CL_ABAP_ITAB_X_WRITER

Данные классы являются потомками от абстрактных классов потоков внутренних данных — CL_ABAP_MEMORY_….

Пример программы с потоками для внутренних таблиц:

Обработка LOB полей базы данных

Обработка LOB полей БД возможна с помощью потоков и локаторов, при этом есть следующие особенности их использования:

Локаторы:

  • Связываются с LOB полями для чтения или записи через LOB дескрипторы, запись подразумевает копирование данных из других локаторов, возможности прямой записи из переменных ABAP программы через локатор в базу данных не существует (для этого используются записывающие потоки).
  • Имеют доступ к вложенным последовательностям в LOB полях и их свойствам
  • Позволяют копировать LOB поля, без переноса данных на сервер приложений
  • За счёт обработки на стороне СУБД повышается и нагрузка на неё
  • Для тех СУБД, где нет поддержки локаторов, она эмулируется на сервере приложений.

Потоки:

  • Связываются, как и локаторы через LOB дескрипторы
  • Данные LOB полей обрабатываются последовательно, используя методы потоков
  • Не нагружают дополнительно СУБД.

Количество LOB дескрипторов на один DB LUW ограничено 1000. Открытых потоков может быть не более 16.

Пример копирования через локатор LOB поля, без переноса его содержимого на сервер приложений:

Пример считывания из БД с помощью потока:

Под LOB дескрипторами понимаются интерфейсы IF_ABAP_DB_LOB_HANDLE (*_BLOB_HANDLE, *_CLOB_HANDLE) и классы которые их внедряют. В примерах описанных выше используются классы, которые создаются и привязываются к LOB полю через дополнение INTO. Кроме того вместе классов можно использовать интерфейсы и дополнение CREATING в SQL операторе SELECT, для того чтобы создавать объекты в зависимости от типов полей, пример использования:

В данном случае создается локатор для поля description. В зависимости от вида поля (BLOB или CLOB) создается экземпляр определенного класса (CL_ABAP_DB_C_LOCATOR или CL_ABAP_DB_X_LOCATOR). Данное дополнение можно использовать и динамически, по тем же правилам что и в других ABAP командах: CREATING… (текст команды создания). Ключевое слово LOCATOR (READER) определяет, какой объект мы создаем. Дополнение [ALL [OTHER] [BLOB|CLOB]] COLUMNS [blob1 blob2 … clob1 clob2 …] используется тогда, когда выбор происходит в структуру (таблицу).

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

Объявление LOB структуры происходит следующим способом:

TYPES dtype TYPE dbtab lob_handle_type FOR lob_handle_columns
[lob_handle_type FOR lob_handle_columns
…                                   ].

В dbtab должна быть указана база данных или ракурс из словаря. Поле lob_handle_type определяет тип LOB дескриптора:

… { READER|LOCATOR|{LOB HANDLE} }
| { WRITER|LOCATOR } …

READER определяет поля с типами:

  • CL_ABAP_DB_X_READER для BLOB
  • CL_ABAP_DB_C_READER для CLOB

WRITER определяет поля с типами:

  • CL_ABAP_DB_X_WRITER для BLOB
  • CL_ABAP_DB_C_WRITER для CLOB

LOCATOR определяет поля с типами:

  • CL_ABAP_DB_X_LOCATOR для BLOB
  • CL_ABAP_DB_C_LOCATOR для CLOB

LOB HANDLE определяет поля с типами:

  • IF_ABAP_DB_BLOB_HANDLE для BLOB
  • IF_ABAP_DB_CLOB_HANDLE для CLOB

Дополнения READER и WRITER нельзя указывать вместе. Дополнение WRITER так же не может быть использовано с дополнением LOB HANDLE.

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

Синтаксис следующий:

… { COLUMNS blob1 blob2 … clob1 clob2 … }
    | { ALL [OTHER] [BLOB|CLOB] COLUMNS } …

  • COLUMNS – определяет индивидуальный перечень полей
  • ALL OTHER BLOB|CLOB COLUMNS – включает остальные BLOB|CLOB поля не перечисленные при индивидуальном определении
  • ALL BLOB|CLOB COLUMNS – включает все BLOB|CLOB поля таблицы
  • ALL OTHER COLUMNS – включает все поля (BLOB и CLOB) не перечисленные в индивидуальном определении
  • ALL COLUMNS – включает все CLOB и BLOB поля из таблицы

Программа, демонстрирующая объявление LOB структур:

Кроме выше указанного способа LOB структуры могут быть объявлены в словаре или через BEGIN OF…END OF. Для более подробной информации рекомендую ознакомиться с документацией в ABAP (ссылки ниже).

Использование потоков в OpenSQL командах

Потоки могут быть следующих видов:

  • CL_ABAP_DB_C_READER – для чтения данных из СLOB полей
  • CL_ABAP_DB_X_READER – для чтения данных из BLOB полей
  • CL_ABAP_DB_C_WRITER – для записи данных в СLOB поля
  • CL_ABAP_DB_X_WRITER – для записи данных в BLOB поля

Основные методы при работе с потоками обработки LOB полей такие же, как и для других потоков (см. выше). В зависимости от направления потока различается и их процесс создания. Как было видно в примере выше, потоки для чтения LOB полей создаются автоматически через атрибут INTO оператора SELECT или через дополнение CREATING.

Создание записывающих потоков происходит с помощью операторов: INSERT, UPDATE, MODIFY. В зависимости от СУБД, обработка записывающих потоков может отличаться:

  • В MaxDB или Oracle компоненты, которые не являются LOB дескрипторами, записываются сразу после выполнения команд обновления. Если обновление не может быть выполнено, в соответствующие поля (sy-subrc, sy-dbcnt) записываются коды ошибок и число обработанных записей, при этом записывающие потоки не создаются. Если обновление может быть выполнено, создаются записывающие потоки, при этом перенос данных из них происходит в момент, когда последний из них будет закрыт.
  • В других СУБД компоненты, не являющиеся LOB дескрипторами, не переносятся в БД сразу же после операторов обновления. При этом записывающие потоки всегда создаются. Данные переносятся в БД только тогда, когда будет закрыт последний записывающий поток, открытый для этого оператора. Соответственно между выполнением оператора и закрытием последнего записывающего потока, статус операции является неопределенным. В поле sy-subrc после оператора будет записан код ошибки 2, а в sy-dbcnt значение -1. Перед закрытием потока необходимо получить указатель на объект класса CL_ABAP_SQL_CHANGING_STMNT, через интерфейс IF_ABAP_DB_WRITER используя метод GET_STATEMENT_HANDLE. С помощью метода GET_STATE данного класса можно получить статус OSQL операции, а с помощью метода GET_DB_COUNT количество обработанных записей. Кроме того с помощью данного объекта можно закрыть все открытые для записи потоки. Если операция не может быть выполнена (например, есть дубликаты в ключевых полях), система выдаст исключения CX_STREAM_IO_EXCEPTION или CX_CLOSE_RESOURCE_ERROR.

Записывающий поток будет открытым до тех пор, пока не будет выполнена отмена изменений через ROLLBACK, либо закрыт явным образом через метод CLOSE. В случае выполнения COMMIT WORK при открытых записывающих потоках, система вызовет динамическую ошибку — COMMIT_STREAM_ERROR.

Читающие потоки закрываются неявным способом в циклическом операторе SELECT после оператора ENDSELECT, а так же после окончания DB LUW.

Пример программы, заполняющей LOB поле через записывающий поток:

Программа, считывающая записанные выше данные:

Использование локаторов в OpenSQL

В настоящее время локаторы бывают двух видов:

  • CL_ABAP_DB_C_LOCATOR – для CLOB полей
  • CL_ABAP_DB_X_LOCATOR – для BLOB полей

Важные методы локаторов:

  • GET_LENGTH – получает размер LOB поля подключенного к локатору
  • FIND – поиск последовательности данных относительно смещения, возвращает позицию в LOB где встречается указанный для поиска шаблон. В Unicode системах размер символьного шаблона ограничен 1333 символами, в не Unicode системах 2666. Для байтового шаблона размер ограничен 2666 байтами.
  • CLOSE – закрытие локатора и освобождение занятых им ресурсов
  • IS_CLOSED – возвращает X если локатор закрыт
  • GET_SUBSTRING – используется для CLOB полей, получает последовательность символов с указанным смещением и длинной
  • GET_BYTES – тоже самое но для BLOB полей.

Локаторы могут создаваться только через оператор SELECT и его атрибут INTO, как это было показано выше. Создание локаторов не может быть инициировано при изменении данных таблиц. После создания, локаторы могут быть использованы для копирования LOB полей между записями таблицы.

Из-за нагрузки на СУБД закрытие локаторов должно происходить как можно скорее, причем в отличие от потоков, локаторы не закрываются автоматически при использовании циклического оператора SELECT, после команды ENDSELECT. Неявное закрытие локаторов происходит только после окончания LUW БД.

Программа копирующее LOB поле через локатор:

Чтение данных, используя локатор:

Справка на тему использования LOB и потоков: Потоки и локаторы в OSQL, Потоки в обработке внутренних данных. Видео обзор.

  • Екатерина

    Добрый день!
    Подскажите, пожалуйста, в каких случаях целесообразно использование потоков и локаторов в abap?
    Заранее спасибо.

    • Astrafox

      Здравствуйте, если эту концепцию расширят для работы с файлами будет весьма полезно. А пока их использование ограничивается редкими случаями, когда требуется хранить большие объекты данных в таблицах БД.