Книга знаний

1С:Предприятие / Приемы программирования / Внешние компоненты

Внешняя компонента для безопасной работы с базами данных по интерфейсу ADO

Внешняя компонента для работы с базами данных по интерфейсу ADO.
Приводит типы данных, умеет работать со ссылочными типами 1С:Предприятие.
Автор статьи: romix | Редакторы: pectopatop
Последняя редакция №12 от 22.11.12 | История
URL: http://kb.mista.ru/article.php?id=454

Ключевые слова: ADO, база, ошибка, поле, формат, adodb, connection


Разработка для скачивания


http://infostart.ru/projects/1513/

Чем лучше AdoDB.Connection?



1) Умеет работать с различными типами данных, которые можно привести к строке, числу или дате.
2) Не теряет точность для вещественных чисел
3) Умеет читать дату и время из поля соответствующего типа.
4) Содержит удобный интерфейс для обновления записей (Insert или Update в зависимости от наличия записи с указанными ключивыми полями).
5) Поддерживает параметризацию (невозможно хакнуть программу подменой закавыченных значений).
6) Умеет работать со ссылочными типами 1С:Предприятие (справочники, документы), упаковывая их в 16-символьное представление

Чтение записей


Отличие от традиционного ADODB.Connection - умеет читать самые разные типы данных.
//*******************************************
Процедура Сформировать()
    //Открываем соединение
    ado.Open("Driver={Microsoft Access Driver (*.mdb)};Dbq="+ф_ИмяMDB+";Uid=Admin;Pwd=;");
    
    таб=СоздатьОбъект("Таблица");
    таб.ИсходнаяТаблица("Таблица");
    таб.ВывестиСекцию("Шапка");
    таб.Опции(0);
    
    
     Запрос="SELECT * FROM ТестоваяТаблица;";
    ado.Execute(Запрос);
    
    Пока ado.EOF=0 Цикл //Цикл по записям 
        таб.ВывестиСекцию("Строка|Начало");
        зн=ado.GetField("id"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("text1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("memo1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("Байт1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("Целое1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("ДлинноеЦелое1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("Single1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("Double1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("КодРепликации1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("ДатаВремя1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetFieldAsDateTime("ДатаВремя1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("Денежный1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("Логический1"); таб.ПрисоединитьСекцию("Строка|Данные");
        зн=ado.GetField("Действительное1"); таб.ПрисоединитьСекцию("Строка|Данные");
        ado.MoveNext(); //Переходим к след. записи 
    КонецЦикла;

  ado.Close(); //Закрываем соединение
  таб.Показать("Запрос");
КонецПроцедуры


Добавление записей



В этом примере я использую параметризацию: Prepare и Bind. Это позволяет исключить атаки злоумышленников, или сбои в работе, если я по какой-то причине не проставлю кавычки для экранирования текстовых полей. Полноценная параметризация не реализована.

//____________________________________________________________________________________________________
Процедура ф_Добавить()
  //  //Открываем соединение
  ado.Open("Driver={Microsoft Access Driver (*.mdb)};Dbq="+ф_ИмяMDB+";Uid=Admin;Pwd=;");
  
  //Подготовка запроса
  Запрос="INSERT INTO ТестоваяТаблица (text1,memo1,Байт1,Целое1,ДлинноеЦелое1, Single1, Double1,КодРепликации1,ДатаВремя1,Денежный1,Логический1,Действительное1) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)";
  ado.PrepareQuery(Запрос);
  ado.Bind("это text");
  ado.Bind("это memo");
  ado.Bind(257);
  ado.Bind(32767);
  ado.Bind(9999999);
  ado.Bind(12.34);
  ado.Bind(56.78);
  ado.Bind("{AB634001-F13D-11D0-A459-004095E1DAEA}");
  ado.Bind('26.01.2007');
  ado.Bind(33.33);
  ado.Bind(1);
  ado.Bind(111);
  ado.Execute();
  ado.Close(); //Закрываем соединение
    
КонецПроцедуры


Обновление записей


Данный пример смотрит, есть ли запись с указанным ID. Если ее нет, то добавляет (Insert) ее, затем делает Update.

//____________________________________________________________________________________________________
Процедура ф_Обновить()
  //  //Открываем соединение
  ado.Open("Driver={Microsoft Access Driver (*.mdb)};Dbq="+ф_ИмяMDB+";Uid=Admin;Pwd=;");
     Запрос="SELECT id FROM ТестоваяТаблица WHERE id=?;";
     ado.PrepareQuery(Запрос);
    ado.Bind(1);
    ado.Execute();
    Если ado.EOF=1 Тогда
        Запрос="INSERT INTO ТестоваяТаблица (id) VALUES (?)"; 
        ado.PrepareQuery(Запрос);
        ado.Bind(1);
        ado.Execute();
    КонецЕсли;    
    
      Запрос="UPDATE ТестоваяТаблица SET "+
      "text1=?,memo1=?,Байт1=?,Целое1=?,ДлинноеЦелое1=?, Single1=?, Double1=?,КодРепликации1=?,ДатаВремя1=?,Денежный1=?,Логический1=?,Действительное1=?"+
      " WHERE id=?";
      
      ado.PrepareQuery(Запрос);
      
      ado.Bind("это text");
      ado.Bind("это memo");
      ado.Bind(257);
      ado.Bind(32767);
      ado.Bind(9999999);
      ado.Bind(12.34);
      ado.Bind(56.78);
      ado.Bind("{AB634001-F13D-11D0-A459-004095E1DAEA}");
      ado.Bind('26.01.2007');
      ado.Bind(33.33);
      ado.Bind(1);
      ado.Bind(111);
      ado.Bind(1);
    
      ado.Execute();
          
  ado.Close(); //Закрываем соединение
    
КонецПроцедуры


Удобное обновление записей


Этот пример делает то же самое, что и предыдущий, но записан в более удобном для восприятия виде.

//____________________________________________________________________________________________________
Процедура ф_Обновить1()
  //  //Открываем соединение
  ado.Open("Driver={Microsoft Access Driver (*.mdb)};Dbq="+ф_ИмяMDB+";Uid=Admin;Pwd=;");
  ado.OpenRec("ТестоваяТаблица");
  ado.KeyField("id", 1); //Ключевое поле
  ado.DataField("text1", "это текст");
  ado.DataField("memo1", "это memo");
  ado.DataField("Байт1", 255);
  ado.DataField("Целое1", 32767);
  ado.DataField("ДлинноеЦелое1", 88889999);
  ado.DataField("Single1", 12.34);
  ado.DataField("Double1", 56.78);
  ado.DataField("КодРепликации1", "{AB634001-F13D-11D0-A459-004095E1DAEA}");
  ado.DataField("ДатаВремя1", '26.01.1976');
  ado.DataField("Денежный1", 100.55);
  ado.DataField("Логический1", 1);
  ado.DataField("Действительное1", 1234);
  ado.SaveRec();
  ado.Close(); //Закрываем соединение
    
КонецПроцедуры


Работа со ссылочными типами



Приведенный ниже пример обновляет таблицу, которая содержит код, наименование и ссылку на элемент товаров. Метод GetFieldAsObject("Товар") извлекает товар из таблицы как ссылку на объект 1С:Предприятие!

//____________________________________________________________________________________________________
Процедура ф_Реквизиты()
  //Открываем соединение
  ado.Open("Driver={Microsoft Access Driver (*.mdb)};Dbq="+ф_ИмяMDB+";Uid=Admin;Pwd=;");
  спр=СоздатьОбъект("Справочник.Товары");
  спр.ВыбратьЭлементы();
  Пока спр.ПолучитьЭлемент() = 1 Цикл
     Запрос="SELECT Код FROM Товары WHERE Код=?;";
     ado.PrepareQuery(Запрос);
    ado.Bind(0+спр.Код);
    ado.Execute();
    Если ado.EOF=1 Тогда
        Запрос="INSERT INTO Товары (Код) VALUES (?)"; 
        ado.PrepareQuery(Запрос);
        ado.Bind(0+спр.Код);
        ado.Execute();
    КонецЕсли;    
    
      Запрос="UPDATE Товары SET "+
      "Товар=?,Наименование=?"+
      " WHERE Код=?";
      
      ado.PrepareQuery(Запрос);
      
      ado.Bind(спр.ТекущийЭлемент());
      ado.Bind(спр.Наименование);
      ado.Bind(0+спр.Код);
    
      ado.Execute();
  КонецЦикла;

//Читаем элементы
     Запрос="SELECT * FROM Товары";
    ado.Execute(Запрос);
    
    Пока ado.EOF=0 Цикл //Цикл по записям 
        зн=ado.GetField("Код"); Сообщить("Код="+зн+" ");
        зн=ado.GetField("Наименование"); Сообщить("Наименование="+зн+" ");
        зн=ado.GetFieldAsObject("Товар"); Сообщить("Товар="+зн+" ");
        ado.MoveNext(); //Переходим к след. записи 
    КонецЦикла;
  ado.Close(); //Закрываем соединение
КонецПроцедуры


То же самое, с использованием OpenRec-SaveRec.

//____________________________________________________________________________________________________
Процедура ф_Реквизиты1()
  //Открываем соединение
  ado.Open("Driver={Microsoft Access Driver (*.mdb)};Dbq="+ф_ИмяMDB+";Uid=Admin;Pwd=;");
  
  //Пишем товары в таблицу
  спр=СоздатьОбъект("Справочник.Товары");
  спр.ВыбратьЭлементы();
  Пока спр.ПолучитьЭлемент() = 1 Цикл
      
      ado.OpenRec("Товары");
      ado.KeyField("Код", 0+спр.Код); //Ключевое поле
      ado.DataField("Наименование", спр.Наименование);
      ado.DataField("Товар", спр.ТекущийЭлемент());
      ado.SaveRec();
      
  КонецЦикла;

//Читаем элементы
     Запрос="SELECT * FROM Товары";
    ado.Execute(Запрос);
    
    Пока ado.EOF=0 Цикл //Цикл по записям 
        зн=ado.GetField("Код"); Сообщить("Код="+зн+" ");
        зн=ado.GetField("Наименование"); Сообщить("Наименование="+зн+" ");
        зн=ado.GetFieldAsObject("Товар"); Сообщить("Товар="+зн+" ");
        ado.MoveNext(); //Переходим к след. записи 
    КонецЦикла;
  ado.Close(); //Закрываем соединение
КонецПроцедуры



Получение имен полей по их номеру (13.02.2007)



     Запрос="SELECT * FROM ТестоваяТаблица;";
    ado.Execute(Запрос);
    
    Для й= 0 По ado.КоличествоПолей-1 Цикл
        Сообщить("Имя поля: "+ado.ИмяПоля(й));
    КонецЦикла;



Версия установленного в системе интерфейса ADO (27.01.2009)



При необходимости узнать текущую версию ADO можно воспользоваться Delphi-приложением вида:

uses
  comobj,
  sysutils,
  strutils,
  variants,
  dialogs; //и другие модули при потребности
//...
function GetADOVersion: Double;
var
  ADO: Variant;
begin
  try
    ADO := CreateOLEObject('adodb.connection');
    Result := StrToFloat(AnsiReplaceStr(ADO.Version,'.',','));
    ADO := Null;
  except
    Result := 0.0;
  end;
end;
//...
begin
  ShowMessage(FloatToStr(GetADOVersion)); //Покажем версию ADO
end.





Недостатки Windows-объекта ADODB.Connection (дополнение от pectopatop)


1) Существует ошибка в реализации данного объекта:
 ADODB.Connection имеет свойство KeepConnection, которое подразумевает сохранение самим объектом своего состояния подключенности (т.е. если возникают разрывы связи, компонент должен сам переподключаться). К сожалению, как показывает практика, разрывы связи бывают довольно часто (несколько раз в сутки, по Ethernet). Ошибка заключается в том, что ADODB.Connection не переустанавливает связь при разрыве. Более того, ИНОГДА, даже если вы специально после разрыва связи выполните команды .Close и .Open, все равно ADODB.Connection не установить связь, а лишь создаст видимость ее установления: флаг Active=true. Но на деле, при проверке такого фиктивного соединения (например, запрос таблицы), вылетает ошибка об отсутствии связи.
Ошибка эта повторяется довольно просто:
- создайте Delphi-проект (или даже на другом языке) с использованием ADODB.Connection, таблицы и DBGrid, связанных друг с другом. Сделайте property AdoDB.Connection.LoginPrompt = false. И задайте такую строку соединения с БД, чтобы она ссылалась на базу данных по локальной сети.
- добавьте Кнопку1 на форму, и поставьте на ее нажатие код, который будет выполнять методы .Close (в Try..Except..End), затем .Open у ADODB.Connection.
- добавьте Кнопку2 на форму, поставьте на ее нажатие код, который выведет состояние свойства ADODB.Connection.Active.
- запустите программу, нажмите Кнопку1, соединение произойдет. Проверьте это, нажав Кнопка2.
- теперь специально разорвите связь между вашим компьютером, и компьютером с БД. Как умеете: или выдерните прямо сетевой шнур, или отключите соединение..
- проверьте состояние ADODB.Connection, нажав Кнопка2: оно будет все ще думать, что оно подключено к БД.
- если вы затем вновь соедините сеть, то ADODB.Connection даже ничего и не заметит. А при попытке запроса через такое "убитое" соединение будет вылезать ошибка.

Будьте внимательны! Учитывайте данную ошибку. (повторяется на версиях ADO - 2.5, 2.8)

2) Кроме того, у ADODB.Connection существует свойство, которое указывает (в секундах) через какое время, если соединение не используется, оно само разорвет связь.
Закладка

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2011 | Mista.ru

Яндекс.Метрика