(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 Personal Edition Named User Plus License
Oracle Database Standard Edition 2 Processor License
FastReport VCL 6 Standard Edition Single License
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
CASE-технологии
СУБД Oracle "с нуля"
eManual - электронные книги и техническая документация
Новые материалы
Новые программы для Windows
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100