В моей работе пришлось столкнуться с задачей обмена данными ХМЛ через Шину(MDM), используя ADO. Есть некоторые особенности при построении такого обмена, о которых я попытаюсь рассказать в данной статье.

Особенности работы 1С+ADO+MSSQL и Oracle.

В моей работе пришлось столкнуться с задачей обмена данными ХМЛ через Шину, используя ADO. Есть некоторые особенности при построении такого обмена, о которых я попытаюсь рассказать в данной статье.

Чтобы не нагружать данную статью, шиной будем считать некую таблицу на MSSQL(Oracle), в которую записываются пакеты обмена(ХМЛ) для определенной базы данных. Изначально шина была построена на MSSQL. Привожу пример записи пакета в шину для MSSQL:

Процедура СоздатьПараметрЗапроса(extCommSQL, названиеПараметр, текстПараметр)
        Для Инд = 0 по extCommSQL.Parameters.Count - 1 Цикл
               Если extCommSQL.Parameters.Item(Инд).Name = НазваниеПараметр тогда
                    extCommSQL.Parameters.Item(Инд).Value = ТекстПараметр;
               КонецЕсли;
        КонецЦикла;
КонецПроцедуры

Функция ПолучитьСоединениеСБазой()
                 extConnSQL = Новый ComОбъект("ADODB.Connection");
                 СтрокаСоединения =  "Provider=SQLOLEDB.1;
                 |User ID=user;
                 |Pwd=pass;
                 |Data Source=server;
                 |Initial Catalog=Base";
                 extConnSQL.ConnectionString = СтрокаСоединения;
                 extConnSQL.Open();
                 Возврат extConnSQL;
КонецФункции

Функция ЗаписьВSQL(ИмяБазыПолучателя,ИмяСправочника,УИД,ХМЛ, ОписаниеОшибки, Соединение=Неопределено)
      txtQuery="[gate1C].sp_Write";
      СоединениеСБазой = ?(Соединение = Неопределено, ПолучитьСоединениеСБазойSQL(), Соединение);
     ЗапросАДО=Новый COMОбъект("ADODB.Command");
      ЗапросАДО.ActiveConnection=СоединениеСБазой;
      ТекстЗапроса=txtQuery;
      ЗапросАДО.CommandText=ТекстЗапроса;
      ЗапросАДО.CommandType = 4;

     СоздатьПараметрЗапроса(ЗапросАДО, "@ReceiverSystemCode", ИмяБазыПолучателя);
      СоздатьПараметрЗапроса(ЗапросАДО, "@EntityType", ИмяСправочника);
      СоздатьПараметрЗапроса(ЗапросАДО, "@EntityID",УИД);
      СоздатьПараметрЗапроса(ЗапросАДО, "@XMLtext", ХМЛ);
      ЗапросАДО.Prepared = true;
                 Попытка
                                ЗапросАДО.Execute();
                                Ошибка = Ложь;
                 Исключение
                                Ошибка = Истина;
                                ОписаниеОшибки = ОписаниеОшибки();
                 КонецПопытки;
                 Если Соединение = Неопределено Тогда
                                ЗакрытьСоединениеСБазой(СоединениеСБазой);
                 КонецЕсли;
                 Возврат не Ошибка;
КонецФункции


Понятно, что на сервере есть хранимая процедура(ЗапросАДО.CommandType = 4; 1- для запроса) [gate1C].sp_Write, с 4 параметрами, которая записывает данные в исходную таблицу. Здесь проблем нет, драйвер SQLOLEDB без проблем понимает и записывает большие пакеты. Пример функции чтения из шины для MSSQL:

Функция ЧтениеИзSQL(ИмяБазы)
     txtQuery="[gate1C].sp_GetAllUnLoaded";
     СоединениеСБазой=ПолучитьСоединениеСБазой();
       ЗапросАДО=Новый COMОбъект("ADODB.Command");
     ЗапросАДО.ActiveConnection=СоединениеСБазой;
     ТекстЗапроса=txtQuery;
     ЗапросАДО.CommandText=ТекстЗапроса;
     ЗапросАДО.CommandType = 4;
     СоздатьПараметрЗапроса(ЗапросАДО, "@ReceiverSystemCode", ИмяБазы);
     ЗапросАДО.Prepared = true;
     rsTABLE=ЗапросАДО.Execute();
     КоличествоПолейТаблица=rsTABLE.fields.Count-1;
     ТаблицаПоискаТаблица = Новый массив;
     Если (НЕ rsTABLE.EOF) Тогда
         ТаблицаПоискаТаблица = rsTABLE.GetRows().Выгрузить();
     КонецЕсли;

     тзРезультат = Новый ТаблицаЗначений;
     Для сч = 0 По КоличествоПолейТаблица Цикл
         тзРезультат.Колонки.Добавить(rsTABLE.fields.item(сч).name);
     КонецЦикла;

     Для Каждого стрТаблица Из ТаблицаПоискаТаблица Цикл
         стр=тзРезультат.Добавить();
         Для сч = 0 По КоличествоПолейТаблица Цикл
             попытка
                 стр[сч] = СокрЛП(стрТаблица[сч]);
             исключение
                 сообщить(ОписаниеОшибки());
             конецпопытки;
         КонецЦикла;
     КонецЦикла;
     ЗакрытьСоединениеСБазой(СоединениеСБазой);
     Возврат тзРезультат;
КонецФункции


Функция возвращает ТаблицуЗначений с результатами SELECT-а из шины. [gate1C].sp_GetAllUnLoaded также хранимая процедура. Вместо нее можно использовать обыкновенный SELECT, указав ЗапросАДО.CommandType = 1

При переводе шины на Oracle, столкнулся сразу с 2-умя сложностями. Для хранения пакетов XML в таблице Oracle использовался тип данных CLOB (необходимо зарегистрироваться для просмотра ссылки). Т.к. ODBC драйвер для Oracle не поддерживает пакеты больше 32кб, использовал драйвер OraOLEDB, Функция соединения с базой приведена ниже:

Функция ПолучитьСоединениеСШинойДанных() Экспорт
     extConnSQL = Новый ComОбъект("ADODB.Connection");
     СтрокаСоединения =  "Provider=OraOLEDB.Oracle;Data Source=(DESCRIPTION=(CID=orcl)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=oracle)(PORT=15)))(CONNECT_DATA=(SID=orcl)(SERVER=DEDICATED)));User Id=user;Password=pass;";
     extConnSQL.ConnectionString = СтрокаСоединения;
     extConnSQL.Open();
     Сообщить("Подключились к шине!");
     Возврат extConnSQL;
КонецФункции


Но и с этим драйвером оказалось не все гладко, т.к. на пакеты более 32 кб, также выдавалась ошибка. Проблема решалась установкой параметра

SPPrmsLOB = true;

Только после этого запись пакета стала производиться успешно. Код процедуры ниже:

Процедура СоздатьПараметрЗапросаСШинойДанных(extCommSQL, названиеПараметр, текстПараметр,Тип=205,Вид=1 )
     Параметр= extCommSQL.CreateParameter(названиеПараметр,Тип,Вид,СтрДлина(текстПараметр)+1,текстПараметр);
     extCommSQL.Parameters.append(Параметр);
КонецПроцедуры


Функция ЗаписьВШинуДанных(ИмяБазыПолучателя,ИмяСправочника,УИД,ХМЛ, ОписаниеОшибки, Соединение=Неопределено) Экспорт
      txtQuery="BUS_EXPORT.sendMessage";
      СоединениеСБазой = ?(Соединение = Неопределено, ПолучитьСоединениеСШинойДанных(), Соединение);
      ЗапросАДО=Новый COMОбъект("ADODB.Command");
      ЗапросАДО.ActiveConnection=СоединениеСБазой;
      ТекстЗапроса=txtQuery;
      ЗапросАДО.CommandText=ТекстЗапроса;
      ЗапросАДО.CommandType = 4;

      СоздатьПараметрЗапросаСШинойДанных(ЗапросАДО, "messageData"    , ХМЛ,202);
      СоздатьПараметрЗапросаСШинойДанных(ЗапросАДО, "messageObject"  , ИмяСправочника,12);
      СоздатьПараметрЗапросаСШинойДанных(ЗапросАДО, "messageDest"   СокрЛП(ИмяБазыПолучателя),12);
      СоздатьПараметрЗапросаСШинойДанных(ЗапросАДО, "messageID"      , УИД,12);
      ЗапросАДО.Prepared = true;
      ЗапросАДО.Properties("SPPrmsLOB").value=Истина;
     Попытка
         ЗапросАДО.Execute();
         Ошибка = Ложь;
     Исключение
         Ошибка = Истина;
         ОписаниеОшибки = ОписаниеОшибки();
     КонецПопытки;
     Если Соединение = Неопределено Тогда
         ЗакрытьСоединениеСШиной(СоединениеСБазой);
     КонецЕсли;
     Возврат не Ошибка;
КонецФункции


Для поля типа CLOB тип значения в CreateParameter - 202, для varchar2 -12

При чтении данных также не все просто, т.к. RecordSet хранимая процедура также возвращать не хотела, для возврата такого типа нужно устанавливать параметр

PLSQLRSet =Истина

Пример чтения из шины:

Функция ЧтениеИзШины(ИмяБазы) Экспорт
     txtQuery="BUS_IMPORT.getNextMessagebyType";
     СоединениеСБазой=ПолучитьСоединениеСШинойДанных();
       ЗапросАДО=Новый COMОбъект("ADODB.Command");
     ЗапросАДО.ActiveConnection=СоединениеСБазой;
     ТекстЗапроса=txtQuery;
     ЗапросАДО.CommandText=ТекстЗапроса;
     ЗапросАДО.CommandType = 4;
     Если ЗначениеЗаполнено(ИмяОбъекта) тогда
         ТипыПакетов=СокрЛп(ИмяОбъекта);
     иначе
         ТипыПакетов="ALL";
     КонецЕсли;
     СоздатьПараметрЗапросаСШинойДанных(ЗапросАДО, "MessageType", ТипыПакетов,12);
     ЗапросАДО.Prepared = true;
     ЗапросАДО.Properties("SPPrmsLOB").value=Истина;
     ЗапросАДО.Properties("PLSQLRSet").value=Истина;
     rsCursor=ЗапросАДО.Execute();

     КоличествоПолейТаблица=rsCursor.fields.Count-1;

     ТаблицаПоискаТаблица = Новый массив;
     Если (НЕ rsCursor.EOF) Тогда
         ТаблицаПоискаТаблица = rsCursor.GetRows().Выгрузить();
     КонецЕсли;

     тзРезультат = Новый ТаблицаЗначений;
     Для сч = 0 По КоличествоПолейТаблица Цикл
         тзРезультат.Колонки.Добавить(rsCursor.fields.item(сч).name);
     КонецЦикла;

     Для Каждого стрТаблица Из ТаблицаПоискаТаблица Цикл
         стр=тзРезультат.Добавить();
         Для сч = 0 По КоличествоПолейТаблица Цикл
         попытка
                 стр[сч] = СокрЛП(стрТаблица[сч]);
             исключение
                 сообщить(ОписаниеОшибки());
             конецпопытки;
         КонецЦикла;
     КонецЦикла;
     ЗапросАДО.Properties("SPPrmsLOB").value=False;
     ЗапросАДО.Properties("PLSQLRSet").value=False;
     ЗакрытьСоединениеСШинойДанных(СоединениеСБазой);
     Возврат тзРезультат;
КонецФункции


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