(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Использование Oracle COM Automation Feature. Профессионалу-разработчику

Родион Константинов

Впервые COM Automation Feature появилась в Oracle 8i на платформе Microsoft Windows и сразу же привлекла внимание разработчиков. Эта нововведение позволяет на сервере базы данных Oracle в хранимых процедурах PL/SQL и Java использовать технологию Component Object Model (COM). С ее помощью разработчику предоставляется механизм доступа к свойствам и методам COM-объектов, причем как к локальным, так и к удаленным (DCOM).

Технология COM - это объектно-ориентированная спецификация, предложенная компанией Microsoft для упрощения интеграции различных программ между собой. Каждый COM-объект, который создается в соответствии с этой спецификацией, может взаимодействовать с другими COM-объектами. Одним из расширений этой технологией является так называемая "автоматизация" (Automation) - позволяющая одному приложению программно управлять другим. В ней различаются клиентская часть(automation controller) и серверная часть(automation object). Сервер автоматизации - это объект который управляется клиентом.

Архитектура

Центральным элементом технологии COM Automation является интерфейс IDispatch, позволяющий клиенту делать запросы к объекту-серверу. В обязательном порядке IDispatch обеспечивает следующую функциональность:

  • Вызов метода объекта
  • Получение значения атрибута объекта
  • Установка значения атрибута объекта

Oracle COM Automation Feature представляет собой оболочку над интерфейсом IDispatch, и предоставляет доступ ко всем его методам через удобный PL/SQL API. Также доступ к интерфейсу IDispatch возможен из хранимых процедур Java (использование Java в данной статье не рассматривается).
На рис 1 показана архитектура Oracle COM Automation Feature.

Рис. 1 Архитектура Oracle COM Automation Feature

Программный интерфейс COM Automation Feature реализован через вызовы внешних процедур Oracle (external procedure).

Алгоритм работы выглядит следующим образом:

  • интерпретатор PL/SQL посылает сообщение прослушивателю (listener) запустить процесс External Procedure Handler(EPH) - extproc.exe для текущей пользовательской сессии;
  • интерпретатор PL/SQL передает процессу EPH имя вызываемой внешней процедуры, параметры и путь к файлу DLL (orawpcomVER.dll), содержащим внешнюю процедуру;
  • процесс EPH загружает в память DLL и транслирует запрос на исполнение внешней процедуры в соответствующий вызов Win32 API, который и производит манипуляции с COM-объектом.

Установка

Рис.2 Выбор опции Oracle COM Automation Feature

В процессе инсталляции, в каталог ;ORACLE_HOMEbin помещаются файлы динамических библиотек, все остальные файлы будут располагаться в каталоге ORACLE_HOMEcom.

После создания экземпляра базы данных необходимо сконфигурировать сетевой прослушивающий процесс Oracle Listener. В случае использования инструмента Net Configuration Assistant, файлы listener.ora и tnsnames.ora создаются автоматически, но также их можно создать и вручную. Далее приведены прмеры этих файлов.

#пример файла listener.ora
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)
(HOST = myserver)(PORT = 1521))
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC0))
)
)

SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = C:oracleproduct10.2.0db)
(PROGRAM = extproc)
)
(SID_DESC =
(SID_NAME=ORCL)
(ORACLE_HOME = C:oracleproduct10.2.0db)
)
)

#пример файла tnsnames.ora

EXTPROC_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS =
        (PROTOCOL = IPC)
        (KEY = EXTPROC0)
    )
    (CONNECT_DATA = (SID = plsextproc))
    )
  )

Проверить правильность конфигурации можно запустив утилиту LSNRCTL с параметром status - должен присутствовать сервис PLSExtProc.

               C:>Lsnrctl status
               . . . 
               Service "PLSExtProc" has 1 instance(s).
               Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
               The command completed successfully

В ходе установки пакет ORDCOM, который предоставляет PL/SQL API для работы с COM не компилируется. Разработчик должен скомпилировать его самостоятельно в схеме приложения.

Для демонстрации работы создадим пользователя testcom и дадим ему необходимые полномочия:

               CREATE USER testcom IDENTIFIED BY testcom
               DEFAULT TABLESPACE users
               TEMPORARY TABLESPACE temp
               QUOTA UNLIMITED ON users;

               GRANT connect, resource TO testcom;
               GRANT create library TO testcom;

Необходимо отметить, что пользователь, в схеме которого будет компилироваться пакет ORDCOM, должен иметь привилегию CREATE LIBRARY.

Подключимся вновь созданным пользователем в среде SQL*Plus и создадим пакет ORDCOM:

               CONNECT testcom/testcom;
               @c:oracleproduct10.2.0dbcomcomwrap.sql

Работа с COM-объектами в PL/SQL

В Oracle COM Automation Feature предоставляются следующие подпрограммы:

  • CreateObject
  • DestroyObject
  • GetLastError
  • GetProperty
  • SetProperty
  • InitArg
  • InitOutArg
  • GetArg
  • SetArg
  • Invoke

Общая схема работы с COM-объектом такова:

  • создание объекта
  • вызов методов, установка/чтение свойств объекта
  • уничтожение объекта
DECLARE
  
  -- все методы возвращают результат работы - значение типа HRESULT
  -- в шестнадцатиричном формате имеет вид 0x800nnnnn в десятичном -214nnnnnnn
  hResult      binary_integer := 0;

  
  -- переменные для хранения ссылок на объекты
  AppHandle       binary_integer := -1;
  WorkbooksHandle binary_integer := -1;
  WorkbookHandle  binary_integer := -1;
  DummyHandle     binary_integer := -1;

BEGIN
  
  -- в качестве первого параметра передается строковый идентификатор COM-объекта - 
  -- Program ID, имеющий соответствующий ему Globally Unique Identifier (GUID), 128-битное
  -- число  однозначно идентифицирующее COM-объект
  hResult := OrdCOM.CreateObject('Excel.Application', 0, '', AppHandle);
    
  -- обработка ошибок
  -- Вызов любого метода API(кроме самого GetLastError) очищает информацию о
  -- предыдущих ошибках
  IF ( hResult !=0 ) THEN
    OrdCOM.GetLastError(error_src, error_description, error_helpfile, error_helpID);
    dbms_output.put_line ('Error creating application, aborting: ' // hResult);
    dbms_output.put_line(error_src);
    dbms_output.put_line(error_description);
    dbms_output.put_line(error_helpfile);
  END IF;  
  
  hResult := OrdCOM.GetProperty(AppHandle, 'WorkBooks', 0, WorkBooksHandle);

  
  -- Передача параметров в вызываемый метод
  -- инициализация коллекции параметров
  OrdCOM.InitArg();

  
  -- для вызова метода добавления новой рабочей книги WorkBooks.Add
  -- используется шаблон xlWBATWorksheet -4167,  I4-тип данных
  -- вызов SetArg инициализирует первый параметр
  -- последующие вызовы будут инициализировать второй, третий и т.д параметры.
  OrdCOM.SetArg(-4167,'I4'); 

  hResult := OrdCOM.Invoke(WorkbooksHandle, 'Add',1,WorkbookHandle);    

  hResult := OrdCOM.Invoke(AppHandle, 'Quit', 0, DummyHandle);  

  
  -- уничтожение объекта
  hResult := OrdCOM.Destroy(AppHandle);
END;

Мы рассмотрели стандартную схему работы с COM-объектом. Компания Oracle облегчила работу тем разработчикам, которые будут использовать COM Automation Future для работы с файлами формата Microsoft Office (Word, Excel, PowerPoint), в каталоге ORACLE_HOMEcomdemos имеются наглядные примеры и готовые пакеты процедурреализующие стандартные действия с учетом специфики каждого типа файла.

Например пакет ORDExcel предоставляет следующие возможности для работы с MS Excel:

  • FUNCTION CreateExcelWorkSheet
  • FUNCTION InsertData
  • FUNCTION InsertDataReal
  • FUNCTION GetDataNum
  • FUNCTION GetDataStr
  • FUNCTION GetDataReal
  • FUNCTION GetDataDate
  • FUNCTION InsertData
  • FUNCTION InsertChart
  • FUNCTION SaveExcelFile
  • FUNCTION ExitExcel

Пример использования в реальных проектах

Теперь посмотрим как Oracle COM Automation Feature можно использовать в реальных проектах. Как разработчик хранилищ данных я регулярно сталкиваюсь с ситуацией когда часть необходимых для загрузки данных заказчик хранит в нескольких(иногда их большое количество) файлах формата MS Excel. Если формат листов представляет из себя таблицу то можно такой файл подключить к БД Oracle при помощи механизма General Connectivity или экспортировать в текстовый формат и загрузить данные утилитой SQL*Loader. Но в случае когда формат сложный, скажем в виде отчета, то для его обработки необходимо написать парсер на каком-либо из высокоуровневых языков. Такое разделение ETL на клиентскую и серверную часть вносит дополнительное усложнение в процесс разработки. Использование же COM Automation Future дает возможность обойтись только сервером БД и языком PL/SQL.

Файл может содержать отчетность за несколько дней и имеет следующий формат:

Рис.3 Формат файла MS Excel для загрузки в СУБД Oracle

Для работы с уже имеющимися файлами я добавил три функции в пакет стандартный пакет ORDExcel:

  • CreateExcelApplication - создает объект Excel.Application но не добавляет новых рабочих книг и листов
  • OpenExcelFile - открывает существующий файл .XLS и рабочий лист в нем
  • ConverFormulaA1 - Преобразовывает ссылки стиля R1C1 в стиль A1

Исходный код указанных функций приведен ниже.

FUNCTION CreateExcelApplication(servername VARCHAR2) RETURN binary_integer IS
BEGIN
	dbms_output.put_line('Creating Excel application...');
	i := OrdCOM.CreateObject('Excel.Application', 0, servername,applicationToken);
	
IF ( i != 0 ) THEN ORDCOM.GetLastError(error_src, error_description, error_helpfile, error_helpID); dbms_output.put_line(error_src); dbms_output.put_line(error_description); dbms_output.put_line(error_helpfile); END IF;
dbms_output.put_line('Invoking Workbooks...');
i := ORDCOM.GetProperty(applicationToken, 'WorkBooks', 0, WorkBooksToken); IF ( i != 0 ) THEN ORDCOM.GetLastError(error_src, error_description, error_helpfile, error_helpID); dbms_output.put_line(error_src); dbms_output.put_line(error_description); dbms_output.put_line(error_helpfile); END IF;
RETURN i; END CreateExcelApplication;
FUNCTION OpenExcelFile(filename VARCHAR2, sheetname VARCHAR2) RETURN binary_integer IS BEGIN dbms_output.put_line('Opening Excel file ' // filename //' ...'); ORDCOM.InitArg(); ORDCOM.SetArg(filename, 'BSTR');
i := ORDCOM.Invoke(WorkBooksToken, 'Open', 1, DummyToken); IF ( i != 0 ) THEN ORDCOM.GetLastError(error_src, error_description, error_helpfile, error_helpID); dbms_output.put_line(error_src); dbms_output.put_line(error_description); dbms_output.put_line(error_helpfile); END IF;
dbms_output.put_line('Opening WorkBook');
i := ORDCOM.GetProperty(applicationToken, 'ActiveWorkbook', 0, WorkBookToken); IF ( i != 0 ) THEN ORDCOM.GetLastError(error_src, error_description, error_helpfile, error_helpID); dbms_output.put_line(error_src); dbms_output.put_line(error_description); dbms_output.put_line(error_helpfile); END IF;
dbms_output.put_line('Invoking WorkSheets..');
i := ORDCOM.GetProperty(applicationToken, 'WorkSheets', 0, WorkSheetToken1); IF ( i != 0 ) THEN ORDCOM.GetLastError(error_src, error_description, error_helpfile, error_helpID); dbms_output.put_line(error_src); dbms_output.put_line(error_description); dbms_output.put_line(error_helpfile); END IF;
dbms_output.put_line('Invoking WorkSheet'); ORDCOM.InitArg(); ORDCOM.SetArg(sheetname,'BSTR');
i := ORDCOM.GetProperty(WorkBookToken, 'Sheets', 1, WorkSheetToken); -- можно получить ссылку на активный лист, а не на заданный именем -- i := ORDCOM.GetProperty(applicationToken, 'ActiveSheet', 0, WorkSheetToken); IF ( i != 0 ) THEN ORDCOM.GetLastError(error_src, error_description, error_helpfile, error_helpID); dbms_output.put_line(error_src); dbms_output.put_line(error_description); dbms_output.put_line(error_helpfile); END IF;
dbms_output.put_line('Opened ');
RETURN i; END OpenExcelFile;
FUNCTION ConvertFormulaA1(formulaR1C1 VARCHAR2) RETURN VARCHAR2 IS res VARCHAR2(10); BEGIN ORDCOM.InitArg(); ORDCOM.SetArg(formulaR1C1,'BSTR'); ORDCOM.SetArg(-4150,'I4'); ORDCOM.SetArg(-4151,'I4');
i := ORDCOM.Invoke(applicationToken, 'ConvertFormula', 3, res); IF ( i != 0 ) THEN ORDCOM.GetLastError(error_src, error_description, error_helpfile, error_helpID); dbms_output.put_line(error_src); dbms_output.put_line(error_description); dbms_output.put_line(error_helpfile); END IF;
RETURN res; END ConvertFormulaA1;

Для хранения данных полученных из файла MS Excel создадим таблицу SHOPS_REPORTS_TBL

CREATE TABLE SHOPS_REPORTS_TBL
(
  SHOP_NAME    VARCHAR2(32),
  MANAGER_NAME VARCHAR2(128),
  PERIOD       DATE,
  SALE         NUMBER(10,2),
  PURCHASE     NUMBER(10,2),
  CHARGES      NUMBER(10,2)
);

Следующий пример кода загружает данные из файла MS Excel с данными о продажах в таблицу SHOPS_REPORTS_TBL

DECLARE 
  result INTEGER;
  range_count INTEGER;
  rec shops_reports_tbl%ROWTYPE;
BEGIN
  -- создаем объект Excel.Application
  result := ORDExcel.CreateExcelApplication('');
  -- открываем файл c:load_data.xls и лист Sheet1
  result := ORDExcel.OpenExcelFile('c:DataSalesload_data.xls', 'Sheet1');

  -- читаем Фамилию ответственного менеджера из ячейки B1
  rec.manager_name := ORDExcel.GetDataStr('B1');
  dbms_output.put_line(shop_name);
  
  -- читаем наименование магазина из ячейки B3
  rec.shop_name := ORDExcel.GetDataStr('B3');
  dbms_output.put_line(manager);
  
  -- цикл по 31 дню
  FOR range_count IN 4..34 LOOP
    BEGIN
      dbms_output.put_line(ORDExcel.ConverFormulaA1('R5C' // range_count) );
      -- т.к. данные расположены горизонтально в столбцах D5 E5 F5... то 
      -- ссылку задаем в стиле R1C1 а затем конвертируем в стиль A1
      rec.period := ORDExcel.GetDataDate(ORDExcel.ConverFormulaA1('R5C' // range_count));
      IF rec.period IS NULL THEN
        -- если данные заканчиваются раньше, выходим из цикла
        EXIT;
      END IF;
      dbms_output.put_line(measure_date);
      
      -- читаем значения показателей
      rec.sale     := ORDExcel.GetDataReal(ORDExcel.ConverFormulaA1('R6C' // range_count));
      rec.purchase := ORDExcel.GetDataReal(ORDExcel.ConverFormulaA1('R7C' // range_count));
      rec.charges  := ORDExcel.GetDataReal(ORDExcel.ConverFormulaA1('R8C' // range_count));
      
      -- записываем в таблицу данные за один день
      INSERT INTO shops_reports_tbl VALUES rec;
    END;
  END LOOP;
  COMMIT;
    
  result := ORDExcel.ExitExcel();
END;

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

SELECT * FROM shops_reports_tbl

SHOP_NAME       MANAGER_NAME            PERIOD           SALE           PURCHASE        CHARGES
---------------------------------------------------------------------------------------------
Химки-33	Иванова И.И.		01.01.2007	254584,00	67700,00	15000,00
Химки-33	Иванова И.И.		02.01.2007	348900,00	118500,00	16000,00

Заключение

С использованием COM Automation Future разработчики приложений для СУБД Oracle могут задействовать всю мощь технологии COM. Классическим примером использования служит работа с файлами MS Ofice в хранимых процедурах PL/SQL. Необходимо отметить, что COM Automation Future доступен только для СУБД Oracle на платформе MS Windows.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 15.08.2007 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Oracle Database Standard Edition 2 Named User Plus License
Oracle Database Personal Edition Named User Plus Software Update License & Support
Oracle Database Standard Edition 2 Processor License
Oracle Database Personal Edition Named User Plus License
Oracle Database Personal Edition Named User Plus License
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
CASE-технологии
СУБД Oracle "с нуля"
Компьютерные книги. Рецензии и отзывы
Новые материалы
Все о PHP и даже больше
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100