Имя: Пароль:
1C
1С v8
Обмен с внешней MySQL (решено)
0 Gsoom2010
 
30.01.13
19:56
Около года назад ( v8: Синхронизация с внешней MySQL ) я потратил массу времени на поиск ответа на вопрос как связать 1С и MySQL. В результате поисков использовал костыль о котором писать не имеет смысла. На днях как мне кажется подобрал не плохое решение этого вопроса, надеюсь кому-нибудь пригодится.
1 Gsoom2010
 
30.01.13
19:56
Алгоритм работы следующий:
План обмена регистрирует новые данные, процедура в плане обмена выбирает новые данные, выполняет прямое подключение к MySQL и выполняет запросы. После выполнения запросов происходит сброс регистрации для объектов.
Для подключения к MySQL использована внешняя компонента v7mysql.dll

В моём примере представлена односторонняя загрузка данных в MySQL, при необходимости реализация обратного процесса не составит труда.
В моём примере я не стал использовать пакетную загрузку запросов через INSERT INTO VALUES по той причине, что для моей задачи контроль исполнения каждого запроса гораздо важнее скорости их выполнения.

План обмена содержит реквизиты:
Сервер
Логин
Пароль
Порт
БД

В MySQL у объектов есть поля вида ref или registrar_ref, они содержат идентификаторы объектов 1С.
Для документов и справочников поле ref - PRIMARY KEY.
Для элементов регистра сведений поле id - AUTO INCRIMENT PRIMARY KEY.
2 Gsoom2010
 
30.01.13
19:57
Собственно сама процедура из модуля плана обмена:

// Процедура выполняет обмен данными с удалённой MsSQL для выбранного узла
Процедура WebВыполнитьСинхронизацию(УзелОбмена) Экспорт
   
   Сообщить("<============================================================>");    
   Сообщить("" + ТекущаяДата() + " - WEB синхронизация для узла: " + Строка(ЭтотОбъект) + "");

   // Загружаем компоненту
   Попытка
       ЗагрузитьВнешнююКомпоненту("v7mysql.dll");
   Исключение
       Сообщить("Ошибка! Не обнаружена компонента v7mysql.dll!");
       Возврат;
   КонецПопытки;

   // Создаём временную таблицу для парсинга и обработки запросов к объекту
   ТаблицаОбъектов = Новый ТаблицаЗначений;
   // Содержит ссылку на объект обмена
   ТаблицаОбъектов.Колонки.Добавить("Объект");
   // Содержит массив SQL запросов
   ТаблицаОбъектов.Колонки.Добавить("МассивЗапросов");
       
   //////////////////////////////////////    
   // Парсинг объектов плана обмена    //
   //////////////////////////////////////
   
   ВремяПарсингаНачало = ТекущаяДата();
   
   // Получаем изменения в узле
   ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(УзелОбмена, УзелОбмена.НомерОтправленного + 1);
   ТипДанныхУдаления = Тип("УдалениеОбъекта");

   
   // Перебираем элементы плана обмена
   Пока ВыборкаИзменений.Следующий() Цикл
               
       // В массиве харянятся запросы для объекта
       МассивЗапросов = Новый Массив;

       // Получаем измененный элемент
       Данные = ВыборкаИзменений.Получить();
           
       // Данные должны существовать
       Если Данные = Неопределено Тогда
           Продолжить;
       КонецЕсли;
       
       // Проверяем объект на удаление
       ЭтоУдаление = (ТипДанныхУдаления = ТипЗнч(Данные));
       
       // Получаем объект
       Если ЭтоУдаление тогда
           Объект        = Данные.Ссылка;
       Иначе
           Объект        = Данные;
       КонецЕсли;
       
       // Определяем тип объекта
       МетаданныеОбъекта = Объект.Метаданные();
       
       // Заполняем массив запросов для объекта
       Попытка
           
           // Документ (один объект один документ)
           Если МетаданныеОбъекта = Метаданные.Документы.ИмяДокумента тогда

               Если ЭтоУдаление тогда
                   МассивЗапросов.Добавить("DELETE FROM `" + БД + "`.`tablename1` WHERE `ref` = '" + Объект.Ссылка.УникальныйИдентификатор() + "'");
               Иначе
                   ЧастьЗапроса = "`number` = '" + СокрЛП(СтрЗаменить(Объект.Номер, "'", "")) + "'";
                   МассивЗапросов.Добавить("INSERT INTO `" + БД + "`.`tablename1` SET `ref` = '" + Объект.Ссылка.УникальныйИдентификатор() + "' , " + ЧастьЗапроса + " ON DUPLICATE KEY UPDATE " + ЧастьЗапроса);
               КонецЕсли;
               
           // Регистр сведений (один объект множество строк регистра)(сперва очищаем по регистратору, потом пишем).
           ИначеЕсли МетаданныеОбъекта = Метаданные.РегистрыСведений.ИмяРегистра тогда

               Регистратор = Объект.Отбор.Регистратор.Значение.УникальныйИдентификатор();
               
               // Для регистров сведений необходимо делать очистку по регистратору
               МассивЗапросов.Добавить("DELETE FROM `" + БД + "`.`tablename2` WHERE `registrar_ref` = '" + Регистратор + "'");
               
               Если Данные.Количество() > 0 Тогда
                   Для каждого Строка из Объект цикл
                       МассивЗапросов.Добавить("INSERT INTO `" + БД + "`.`tablename2` SET `registrar_ref` = '" + Регистратор + "' , `date` = '" + Формат(Дата(Строка.Период), "ДФ=ггггММддЧЧммсс") + "'");
                   КонецЦикла;
               КонецЕсли;
               
           // Справочник (один объект одна запись)
           ИначеЕсли МетаданныеОбъекта = Метаданные.Справочники.ИмяСправочника тогда

               Если ЭтоУдаление тогда
                   МассивЗапросов.Добавить("DELETE FROM `" + БД + "`.`tablename3` WHERE `ref` = '" + Объект.Ссылка.УникальныйИдентификатор() + "'");
               Иначе
                   ЧастьЗапроса = "`name` = '" + Объект.НаименованиеПолное + "'";
                   МассивЗапросов.Добавить("INSERT INTO `" + БД + "`.`tablename3` SET `ref` = '" + Объект.Ссылка.УникальныйИдентификатор() + "' , " + ЧастьЗапроса + " ON DUPLICATE KEY UPDATE " + ЧастьЗапроса);
               КонецЕсли;
                   
           Иначе
               Сообщить("    Объект не инициализирован для парсинга запросов: " + МетаданныеОбъекта);
               Продолжить;
           КонецЕсли;    

       Исключение
           Сообщить("    Ошибка парсинга объекта <" + Объект + "> (" + МетаданныеОбъекта + "): " + ОписаниеОшибки());
           Продолжить;
       КонецПопытки;
       
       // Добавляем в таблицу новый объект и запросы к нему
       ЗаписьОбъекта = ТаблицаОбъектов.Добавить();
       ЗаписьОбъекта.Объект = Объект;
       ЗаписьОбъекта.МассивЗапросов = МассивЗапросов;
       
   КонецЦикла;
   
   ВремяПарсинга = ТекущаяДата() - ВремяПарсингаНачало;
       
   //////////////////////////////////////    
   // Синхронизация с удалённой MySQL  //
   //////////////////////////////////////
   
   ВремяСинхронизацииНачало = ТекущаяДата();
   
   Если ТаблицаОбъектов.Количество() > 0 тогда    
               
       // Пытаемся открыть соединение с MySQL
       Попытка
           MySQLConnection = Новый("AddIn.MySQLConnection");
           MySQLConnection.Сервер = Сервер;
           MySQLConnection.Пользователь = Логин;
           MySQLConnection.Пароль = Пароль;
           MySQLConnection.Порт = Порт;
           MySQLConnection.БазаДанных = БД;
           MySQLConnection.НужноПереустановитьСоединение = 1;
           MySQLConnection.ИспользоватьСжатие = 1;
           MySQLConnection.ИспользоватьНовыеПароли = Истина;
 
           Если MySQLConnection.УстановитьСоединение() = 1 тогда
               Сообщить("    " + ТекущаяДата() + " - Соединение с MySQL установлено.");    
           Иначе
               Сообщить("    Не удалось установить соединение с MySQL: " + MySQLConnection.ПоследняяОшибка());
               Возврат;
           КонецЕсли;

       Исключение    
           Сообщить("    Ошибка соединения с MySQL: " + ОписаниеОшибки());
           Возврат;
       КонецПопытки;
       
       // Проверяем открытие соединения
       Если MySQLПроверкаСоединения(MySQLConnection)=Ложь тогда
           Сообщить("    Не установлено соединение с MySQL: " + MySQLConnection.ПоследняяОшибка());
           Возврат;
       КонецЕсли;
           
       // Перебираем объекты которые вошли в таблицу объектов для синхронизации
       Для каждого ОбъектСинхронизации из ТаблицаОбъектов цикл
   
           Попытка
               // Пытаемся выполнить запросы для объекта
               ОшибкаСинхронизацииОбъекта = Ложь;
               
               Для каждого SQLЗапрос из ОбъектСинхронизации.МассивЗапросов цикл
   
                   Если НЕ MySQLConnection.ВыполнитьЗапрос(SQLЗапрос) тогда
                       Сообщить("
                               |    Ошибка выполнения SQL запроса для объекта <" + ОбъектСинхронизации.Объект + ">
                               |    |- Ошибка: " + MySQLConnection.ПоследняяОшибка() + "
                               |    |- Запрос: " + SQLЗапрос);
                       ОшибкаСинхронизацииОбъекта = Истина;
                       Прервать;
                   КонецЕсли;
                   
               КонецЦикла;
               
               // Сброс регистрации для объекта происходит только если вся пачка запросов была выполнена без ошибок
               Если ОшибкаСинхронизацииОбъекта = Ложь тогда
                   ПланыОбмена.УдалитьРегистрациюИзменений(Ссылка, ОбъектСинхронизации.Объект);
               КонецЕсли;
               
           Исключение
               Сообщить("    Ошибка выполнения SQL запросов для объекта <" + ОбъектСинхронизации.Объект + ">: " + ОписаниеОшибки());
               Продолжить;
           КонецПопытки;
               
       КонецЦикла;
       
       // Закрываем соединение с MySQL
       MySQLConnection.ЗакрытьСоединение();
       Сообщить("    " + ТекущаяДата() + " - Cоединение с MySQL закрыто.");
                   
   Иначе
       Сообщить("    Нет объектов для синхронизации.");    
   КонецЕсли;
   
   ВремяСинхронизации = ТекущаяДата() - ВремяСинхронизацииНачало;
   
   // Сообщаем итоги о времени выполнения парсинга и синхронизации
   Сообщить("    Время парсинга:        " + Число(ВремяПарсинга) + " сек.
           |    Время синхронизации:    " + Число(ВремяСинхронизации) + " сек.");
   
КонецПроцедуры

Функция MySQLПроверкаСоединения(Соединение)
   
   Если Соединение.Подключен() тогда
     Если Соединение.ПроверитьСоединение() Тогда
         Возврат Истина;
     Иначе
         Возврат Ложь;
     КонецЕсли;
   Иначе  
       Возврат Ложь;
   КонецЕсли;
   
КонецФункции


Ну и возможно пригодятся функции и процедуры из основной формы объекта плана обмена:

Перем РегистрацияВНовыйУзел;

Процедура ПередЗаписью(Отказ)

   РегистрацияВНовыйУзел = ЭтоНовый();

КонецПроцедуры

Процедура ПриЗаписи(Отказ)

   Если РегистрацияВНовыйУзел Тогда

       // Регистрация изменений всех данных для узла
       ПланыОбмена.ЗарегистрироватьИзменения(Ссылка);  

   КонецЕсли;

КонецПроцедуры

Процедура ОсновныеДействияФормыСинхронизировать(Кнопка)
   ЭтотОбъект.WebTrackingВыполнитьСинхронизацию(Ссылка);
КонецПроцедуры

Процедура ОсновныеДействияФормыСбросРегистрации(Кнопка)
   ПланыОбмена.УдалитьРегистрациюИзменений(Ссылка);
КонецПроцедуры

Процедура ОсновныеДействияФормыПолнаяРегистрация(Кнопка)
   ПланыОбмена.ЗарегистрироватьИзменения(Ссылка);
КонецПроцедуры
3 Kreont
 
30.01.13
20:04
Красиво, подпишусь.
Хорошо что фото товаров не надо передавать, повезло :)
4 Gsoom2010
 
30.01.13
20:05
Дальше я буду прикручивать рендеринг документов из 1С к этому обмену и заливку файлов на Amazone S3... Так что всё впереди!
5 Gsoom2010
 
30.01.13
20:25
(3) А фото товаров можно отдельным блоком грузить через ftp, например после парсинга.