ADBC – объектно-ориентированное API, для взаимодействия с интерфейсом нативного SQL в ABAP. Может быть использован в том случае, когда необходим динамический доступ к нативному SQL, т.к. вызов через команды EXEC SQL не подразумевает динамической обработки.
Классы, представляющие ADBC начинаются с CL_SQL_* и CX_SQL_* (пакет SDB_ADBC). Основные из них:
- CL_SQL_STATEMENT – для выполнения SQL команд
- CL_SQL_PREPARED_STATEMENT – для выполнения заранее созданных SQL команд, с возможностью подстановки параметров
- CL_SQL_CONNECTION — для управления подключениями к БД
- CX_SQL_EXCEPTION – базовый класс исключений
ADBC не поддерживает обработки автоматическое заполнение мандантов на основе текущей сессии, поле мандант должно быть явно указано (sy-mandt). Вы должны позаботиться о том, чтобы приложение не использовало данные из других мандантов.
Выполнение SQL команд через CL_SQL_STATEMENT
Класс CL_SQL_STATEMENT содержит методы уровня инстанции для выполнения и получения данных из динамически созданных SQL выражений.
Инстанцию класса можно получить, используя оператор CREATE OBJECT, при необходимости передав ссылку на объект класса CL_SQL_CONNECTION, в котором содержится информация о подключении к БД. Если не передать ссылку, используется стандартное подключение к БД (см. тр. DBACOCKPIT).
DDL и DML выражения
Класс содержит следующие методы для выполнения DDL (язык определения данных) и DML (язык манипуляции данными) инструкций:
- EXECUTE_DDL – для таких команд как: CREATE, DROP или ALTER.
- EXECUTE_UPDATE – для таких команд как: INSERT, UPDATE или DELETE.
Оба метода содержат обязательный для заполнения параметр – STATEMENT типа string. Данный параметр должен содержать синтаксически корректное SQL выражение. Метод EXECUTE_UPDATE так же имеет возвращаемый параметр ROWS_PROCESSED, в котором заполняется количество строк таблицы, обработанных выражением.
В DML выражениях значения, передаваемые в базу данных, могут быть параметризированы, для того чтобы указать параметры необходимо воспользоваться символом «?». До выполнения SQL выражения, необходимо чтобы все параметры были заполнены, заполнение параметров осуществляется через метод SET_PARAM, с указанием ссылки на переменную. При этом по умолчанию, заполнение параметров происходит поочередно, слева направо в SQL выражении. После выполнения SQL выражения, все присвоения параметров теряются.
Следует знать, что использование DDL для создания таблиц (вместо SE11) крайне не рекомендуется, т.к. кроме созданных таблиц в БД, SAP создает записи в таблицах с метаданными (DD02L, DD03L и др.), из-за чего доступ к созданным таким способом таблицам будет ограничен. Для создания таблиц лучше использовать ФМ: DD_CREATE_TABLE.
Запросы
С помощью метода EXECUTE_QUERY можно выполнить запрос к БД. Так же как и предыдущие методы, он содержит обязательный параметр STATEMENT, который должен быть заполнен синтаксически верным SELECT выражением. Можно, как и для вышеописанных методов, задавать параметры, используя метод SET_PARAM.
В качестве результата, метод возвращает параметр RESULT_SET, который является ссылкой на класс CL_SQL_RESULT_SET. Методы данного класса позволяют получить доступ к результатам SQL запроса.
Для получения результата запроса служат следующие методы класса CL_SQL_RESULT_SET:
- SET_PARAM, NEXT, и CLOSE. Данные методы обеспечивают доступ к индивидуальным строкам и колонкам данных из результата. По умолчанию, доступ осуществляется слева на право. Метод NEXT переводит к следующей строке набора данных, если возвращает 1 – следующая строка считана, 0 – следующая строка не считана (достигнут конец набора данных). Чтение должно заканчиваться вызовом метода CLOSE.
- SET_PARAM_STRUCT, NEXT, и CLOSE. Доступ аналогичен вышеуказанному способу, но вместо доступа к индивидуальным столбцам, получаем целиком структуру строки.
- SET_PARAM_TABLE, NEXT_PACKAGE, и CLOSE. Метод NEXT_PACKAGE считывает указанное в параметре UPTO значение строк таблицы. Если параметр не задан, считываются все строки. При каждом новом вызове этого метода, в указанную таблицу добавляются новые записи, старые не удаляются.
Хранимые процедуры
Для запуска хранимых процедур используется метод EXECUTE_PROCEDURE. Метод содержит обязательный параметр PROC_NAME типа string, в который передается имя хранимой процедуры. Как и для вышеуказанных методов, можно задавать параметры, которые будут переданы в процедуру. Параметры, передаваемые в ХП, могут иметь разные типы направления, задается через необязательный параметр метода SET_PARAM – INOUT. Значения данного атрибута заполняются константами из класса CL_SQL_STATEMENT: C_PARAM_IN (по умолчанию), C_PARAM_OUT, и C_PARAM_INOUT.
Примеры работы с данным классом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
REPORT demo_adbc_ddl_dml_binding. PARAMETERS p_name TYPE c LENGTH 10 DEFAULT 'mytab'. SELECTION-SCREEN SKIP. PARAMETERS: p_create RADIOBUTTON GROUP grp, p_insert RADIOBUTTON GROUP grp, p_select RADIOBUTTON GROUP grp, p_drop RADIOBUTTON GROUP grp. SELECTION-SCREEN SKIP. PARAMETERS p_key TYPE i DEFAULT 1. CLASS adbc DEFINITION. PUBLIC SECTION. CLASS-METHODS main. PRIVATE SECTION. CLASS-DATA: dbname TYPE string, sql TYPE REF TO cl_sql_statement, wa1 TYPE c LENGTH 10, wa2 TYPE c LENGTH 10, err TYPE REF TO cx_sql_exception . CLASS-METHODS: create RAISING cx_sql_exception, insert RAISING cx_sql_exception, select RAISING cx_sql_exception, drop RAISING cx_sql_exception. ENDCLASS. CLASS adbc IMPLEMENTATION. METHOD main. dbname = 'ABAP_DOCU_DEMO_' && p_name. TRY. CREATE OBJECT sql. IF p_create = 'X'. create( ). MESSAGE 'Create was successful' TYPE 'S'. ELSEIF p_insert = 'X'. insert( ). MESSAGE 'Insert was successful' TYPE 'S'. ELSEIF p_select = 'X'. select( ). MESSAGE 'Select was successful' TYPE 'S'. ELSEIF p_drop = 'X'. drop( ). MESSAGE 'Drop was successful' TYPE 'S'. ENDIF. CATCH cx_sql_exception INTO err. MESSAGE err TYPE 'I' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. METHOD create. sql->execute_ddl( `CREATE TABLE ` && dbname && `( val1 char(10) NOT NULL,` && ` val2 char(10) NOT NULL,` && ` PRIMARY KEY (val1) )` ). ENDMETHOD. METHOD insert. DATA dref TYPE REF TO data. DO 100 TIMES. GET REFERENCE OF wa1 INTO dref. sql->set_param( dref ). GET REFERENCE OF wa2 INTO dref. sql->set_param( dref ). wa1 = sy-index. wa2 = sy-index ** 2. sql->execute_update( `INSERT INTO ` && dbname && ` VALUES (?,?)` ). ENDDO. ENDMETHOD. METHOD select. DATA: result TYPE REF TO cl_sql_result_set, msg TYPE c LENGTH 30, key TYPE c LENGTH 10, dref TYPE REF TO data, rc TYPE i. key = p_key. GET REFERENCE OF key INTO dref. sql->set_param( dref ). result = sql->execute_query( `SELECT val1, val2 ` && `FROM ` && dbname && ` ` && `WHERE val1 = ?` ). GET REFERENCE OF wa1 INTO dref. result->set_param( dref ). GET REFERENCE OF wa2 INTO dref. result->set_param( dref ). rc = result->next( ). IF rc > 0. WRITE: 'Result:' TO msg, wa1 TO msg+10, wa2 TO msg+20. ELSE. msg = 'No entry found'. ENDIF. result->close( ). MESSAGE msg TYPE 'I'. ENDMETHOD. METHOD drop. sql->execute_ddl( `DROP TABLE ` && dbname ). ENDMETHOD. ENDCLASS. START-OF-SELECTION. adbc=>main( ). |
Программа, выполняющая запрос и считывающая результат в таблицу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
REPORT demo_adbc_query. PARAMETERS p_carrid TYPE sflight-carrid. CLASS demo DEFINITION. PUBLIC SECTION. CLASS-METHODS main. PRIVATE SECTION. CLASS-DATA: BEGIN OF result_line, carrid TYPE sflight-carrid, connid TYPE sflight-connid, fldate TYPE sflight-fldate, END OF result_line, result_tab LIKE TABLE OF result_line. CLASS-METHODS display. ENDCLASS. CLASS demo IMPLEMENTATION. METHOD main. DATA: sql TYPE REF TO cl_sql_statement, result TYPE REF TO cl_sql_result_set, err TYPE REF TO cx_sql_exception, cols TYPE adbc_column_tab, dref TYPE REF TO data. APPEND 'CARRID' TO cols. APPEND 'CONNID' TO cols. APPEND 'FLDATE' TO cols. CREATE OBJECT sql. GET REFERENCE OF result_tab INTO dref. TRY. result = sql->execute_query( `SELECT carrid, connid, fldate ` && `FROM sflight ` && `WHERE mandt = ` && `'` && sy-mandt && `' AND` && ` carrid = ` && `'` && p_carrid && `'` ). result->set_param_table( itab_ref = dref corresponding_fields = cols ). IF result->next_package( ) > 0. SORT result_tab BY carrid connid fldate. display( ). ENDIF. CATCH cx_sql_exception INTO err. MESSAGE err TYPE 'I' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. METHOD display. DATA alv TYPE REF TO cl_salv_table. DATA err TYPE REF TO cx_salv_msg. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = alv CHANGING t_table = result_tab ). alv->display( ). CATCH cx_salv_msg INTO err. MESSAGE err TYPE 'I' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. ENDCLASS. START-OF-SELECTION. demo=>main( ). |
Создание и выполнение хранимой процедуры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
REPORT demo_adbc_stored_procedure. SELECTION-SCREEN BEGIN OF SCREEN 100. PARAMETERS incprice TYPE sflight-price. SELECTION-SCREEN END OF SCREEN 100. CLASS demo DEFINITION. PUBLIC SECTION. CLASS-METHODS main. PRIVATE SECTION. CLASS-METHODS call_screen. ENDCLASS. CLASS demo IMPLEMENTATION. METHOD main. DATA: sql TYPE REF TO cl_sql_statement, err TYPE REF TO cx_sql_exception, dref TYPE REF TO data. call_screen( ). CREATE OBJECT sql. TRY. sql->execute_ddl( `CREATE OR REPLACE PROCEDURE increase_price (x IN NUMBER) IS ` && `BEGIN ` && `UPDATE sflight SET price = price + x` && ` WHERE mandt = '` && sy-mandt && `'; ` && `END;` ). GET REFERENCE OF incprice INTO dref. sql->set_param( data_ref = dref inout = cl_sql_statement=>c_param_in ). sql->execute_procedure( proc_name = 'increase_price' ). CATCH cx_sql_exception INTO err. MESSAGE err TYPE 'I' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. METHOD call_screen. IF sy-dbsys <> 'ORACLE'. MESSAGE 'Example is only for Oracle SQL' TYPE 'I' DISPLAY LIKE 'E'. LEAVE PROGRAM. ENDIF. CALL SELECTION-SCREEN 100. IF sy-subrc <> 0. LEAVE PROGRAM. ENDIF. ENDMETHOD. ENDCLASS. START-OF-SELECTION. demo=>main( ). |
Выполнение заранее определенных SQL команд
Класс CL_SQL_PREPARED_STATEMENT позволяет выполнять заранее определенные SQL выражения, при этом меняя их параметры при необходимости. Данный класс является наследником от CL_SQL_STATEMENT.
SQL выражение передается в конструктор класса, через обязательный параметр STATEMENT. Как и для методов класса CL_SQL_STATEMENT доступны методы для определения параметров SQL выражения.
После выполнения ЛЕР БД, объект данного класса не может быть вновь использован. Если данный объект более не нужен, необходимо выполнить метод close, который освободит занятые ресурсы.
Пример программы использующей данный класс:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
REPORT demo_adbc_prepared_statement. CLASS demo DEFINITION. PUBLIC SECTION. CLASS-METHODS main. PRIVATE SECTION. CLASS-DATA: BEGIN OF result_line, carrid TYPE sflight-carrid, connid TYPE sflight-connid, END OF result_line, result_tab LIKE TABLE OF result_line. CLASS-METHODS display. ENDCLASS. CLASS demo IMPLEMENTATION. METHOD main. DATA: sql TYPE REF TO cl_sql_prepared_statement, result TYPE REF TO cl_sql_result_set, err TYPE REF TO cx_sql_exception, cols TYPE adbc_column_tab, carrid TYPE sflight-carrid, carrid_tab TYPE TABLE OF sflight-carrid, dref1 TYPE REF TO data, dref2 TYPE REF TO data. APPEND 'CARRID' TO cols. APPEND 'CONNID' TO cols. APPEND 'AA' TO carrid_tab. APPEND 'LH' TO carrid_tab. APPEND 'UA' TO carrid_tab. TRY. CREATE OBJECT sql EXPORTING statement = `SELECT carrid, connid ` && `FROM spfli ` && `WHERE mandt = '` && sy-mandt && `' AND carrid = ?`. GET REFERENCE OF carrid INTO dref1. GET REFERENCE OF result_line INTO dref2. sql->set_param( dref1 ). LOOP AT carrid_tab INTO carrid. result = sql->execute_query( ). result->set_param_struct( struct_ref = dref2 corresponding_fields = cols ). WHILE result->next( ) > 0. APPEND result_line TO result_tab. ENDWHILE. ENDLOOP. sql->close( ). display( ). CATCH cx_sql_exception INTO err. MESSAGE err TYPE 'I' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. METHOD display. DATA alv TYPE REF TO cl_salv_table. DATA err TYPE REF TO cx_salv_msg. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = alv CHANGING t_table = result_tab ). alv->display( ). CATCH cx_salv_msg INTO err. MESSAGE err TYPE 'I' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. ENDCLASS. START-OF-SELECTION. demo=>main( ). |
Обработка подключений
SQL выражения представленные классами CL_SQL_STATEMENT и CL_SQL_PREPARED_STATEMENT по умолчанию используют соединение для главной базы данных сервера приложений. При необходимости можно указать и другое подключение, если оно настроено (тр. DBACOCKPIT).
Статический метод GET_CONNECTION при удачном соединении с альтернативной БД, возвращает экземпляр класса CL_SQL_CONNECTION, который в дальнейшем может быть использован при создании объектов CL_SQL_STATEMENT и CL_SQL_PREPARED_STATEMENT через параметр конструктора — CON_REF. Если подключение необходимо контролировать для БД по умолчанию, объект может быть создан через оператор CREATE OBJECT.
Для закрытия подключения необходимо использовать метод CLOSE. При этом объекты, созданные с этим подключением, не могут быть далее использованы, если только подключение не осуществлялось к главной БД (БД по умолчанию).
Для обработки ЛЕР БД в данном классе присутствуют следующие методы: COMMIT – для подтверждения, ROLLBACK – для отмены. При создании экземпляра данного класса ЛЕР БД открывается неявно.
Обработка исключений
Для обработки исключений в ADBC используется класс CX_SQL_EXCEPTION. Кроме текста исключения, в данном классе присутствуют следующие атрибуты:
Атрибут | Значение |
DB_ERROR | «X», Если SQL выражение не выполнено СУБД. SQL_CODE и SQL_MESSAGE содержат подробную информацию. |
DBOBJECT_EXISTS | «X», Если вы пытаетесь создать объект, который уже существует. DB_ERROR так же содержит «X». |
DBOBJECT_NOT_EXISTS | «X», Если вы пытаетесь получить доступ к объекту, которого не существует. DB_ERROR так де содержит «X». |
DUPLICATE_KEY | «X», Если DML выражение нарушает уникальность ключа. DB_ERROR так де содержит «X». |
INTERNAL_ERROR | Внутренняя ошибка СУБД, для подробных данных смотрите журналы СУБД. |
INVALID_CURSOR | «X», если вы используете не активный курсор БД. |
SQL_CODE | Код ошибки в SQL выражении, Если DB_ERROR — «X». |
SQL_MESSAGE | Расшифровка кода ошибки в SQL выражении, если DB_ERROR — «X». |
SAP справка на тему ADBC.