Delphi для профессионалов

         

ГЛАВА 19


Использование ADO средствами Delphi

Наряду с традиционными инструментами доступа к данным Borland Database Engine и ODBC в приложениях Delphi можно применять технологию Microsoft ActiveX Data Objects (ADO), которая основана на возможностях СОМ, а именно интерфейсов OLE DB.

Технология ADO завоевала популярность у разработчиков, благодаря универсальности — базовый' набор интерфейсов OLE DB имеется в каждой современной операционной системе Microsoft. Поэтому для обеспечения доступа приложения к данным достаточно лишь правильно указать провайдер соединения ADO и затем переносить программу на любой компьютер, где имеется требуемая база данных и, конечно, установленная ADO.

В Палитре компонентов Delphi есть страница ADO, содержащая набор компонентов, позволяющих создавать полноценные приложения БД, обращающиеся к данным через ADO.

В этой главе рассматриваются следующие вопросы:

 краткий обзор технологии ADO, доступных провайдеров ADO, а также работающих в ней объектов и интерфейсов;  как создать соединение с базой данных через ADO в приложении Delphi;

 применение объекта набора записей ADO в приложении;  как использовать таблицы, запросы SQL и хранимые процедуры;  что такое команды и объекты команды ADO.

 

Основы ADO


Технология Microsoft ActiveX Data Objects обеспечивает универсальный доступ к источникам данных из приложений БД. Такую возможность предоставляют функции набора интерфейсов, созданные на основе общей модели объектов СОМ и описанные в спецификации OLE DB.

Технология ADO и интерфейсы OLE DB обеспечивают для приложений единый способ доступа к источникам данных различных типов (рис. 19.1). Например, приложение, использующее ADO, может применять одинаково сложные операции и к данным, хранящимся на корпоративном сервере SQL, и к электронным таблицам, и локальным СУБД. Запрос SQL, направленный любому источнику данных через ADO, будет выполнен.

Рис. 19.1. Схема доступа к данным через ADO

Возникает вопрос: каким образом источники данных смогут выполнить этот запрос?

За серверы БД беспокоиться не стоит, обработка запросов SQL — это их основная обязанность. Но как быть с файловыми последовательностями, электронными таблицами, файлами электронной почты и т. д.? Здесь на помощь приходят механизмы ADO и интерфейсы OLE DB.

OLE DB представляет собой набор специализированных объектов СОМ, инкапсулирующих стандартные функции обработки данных, и специализированные функции конкретных источников данных и интерфейсов, обеспечивающих передачу данных между объектами.

Согласно терминологии ADO, любой источник данных (база данных, электронная таблица, файл) называется хранилищем данных, с которым при помощи провайдера данных взаимодействует приложение. Минимальный набор компонентов приложения может включать объект соединения, объект набора данных, объект процессора запросов.

Примечание 

Объекты OLE DB создаются и функционируют так же, как и другие объекты СОМ. Каждому объекту соответствует идентификатор класса CLSID, хранящийся в системном реестре. Для создания объекта используется метод CoCreateinstance и соответствующая фабрика класса. Объекту соответствует набор интерфейсов, к методам которых можно обращаться после создания объекта.

В результате приложение обращается не прямо к источнику данных, а к объекту OLE DB, который "умеет" представить данные (например, из файла электронной почты) в виде таблицы БД или результата выполнения запроса SQL.

Технология ADO в целом включает в себя не только сами объекты OLE DB, но и механизмы, обеспечивающие взаимодействие объектов с данными и приложениями. На этом уровне важнейшую роль играют провайдеры ADO, координирующие работу приложений с хранилищами данных различных типов.

Такая архитектура позволяет сделать набор объектов и интерфейсов открытым и расширяемым. Набор объектов и соответствующий провайдер может быть создан для любого хранилища данных без внесения изменений в исходную структуру ADO. При этом существенно расширяется само понятие данных — ведь можно разработать набор объектов и интерфейсов и для нетрадиционных табличных данных. Например, это могут быть графические данные геоинформационных систем, древовидные структуры из системных реестров, данные CASE-инструментов и т. д.

Так как технология ADO основана на стандартных интерфейсах СОМ, которые являются системным механизмом Windows, это сокращает общий объем работающего программного кода и позволяет распространять приложения БД без вспомогательных программ и библиотек.

Примечание 

Нижеследующее описание спецификации OLE DB представлено в соответствии с официальной терминологией Microsoft для данной предметной области.

Спецификация OLE DB различает следующие типы объектов, которые будут рассмотрены ниже.

 Перечислитель (Enumerator) выполняет поиск источников данных или других перечислителей. Используется для обеспечения функционирования провайдеров ADO.  Объект-источник данных (Data Source Object) представляет хранилище данных.  Сессия (Session) объединяет совокупность объектов, обращающихся к одному хранилищу данных.  Транзакция (Trasaction) инкапсулирует механизм выполнения транзакции.  Команда (Command) содержит текст команды и обеспечивает ее выполнение. Командой может быть запрос SQL, обращение к таблице БД и т. д.  Набор рядов (Rowset) представляет собой совокупность строк данных, являющихся результатом выполнения команды ADO.  Объект-ошибка (Error) содержит информацию об исключительной ситуации.

Рассмотрим функциональные возможности основных объектов и интерфейсов OLE DB.

 

Перечислители


Объекты- перечислители обеспечивают поиск любых объектов ADO, которые имеют доступ к источникам данных. При этом другие перечислители также видны в данном перечислителе.

Первичный поиск источников данных осуществляется в провайдере ADO. Перечислители могут отбирать только источники данных конкретных типов, поэтому провайдер обеспечивает доступ к конкретному типу хранилища данных.

В составе ADO имеется системный корневой перечислитель, который выполняет начальный поиск других перечислителей и источников данных. Его можно использовать, зная его идентификатор класса CLSID_OLEDB_ENUMERATOR.

 Примечание 

В Delphi GUID глобального перечислителя содержится в файле \Delphi7\Source \Vcl\OleDB.pas.

CLSID_OLEDB_ENrjMERATOR: TGUID= '{C8B522DO-5CF3-11CE-ADE5-OOAA0044773D}

Функции перечислителя содержатся в интерфейсе isourcesRowset. Метод

function GetSourcesRowset(const punkOuter: lUnknown; const riid: TGUID; cPropertySets: UINT; rgProperties: PDBPropSetArray; out ppSourcesRowset: lUnknown): HResult; stdcall;

возвращает ссылку на объект набора рядов (см. выше), содержащий сведения о найденных источниках данных или перечислителях.

 

Объекты соединения с источниками данных


Внутренний механизм ADO, обеспечивающий соединение с хранилищем данных, использует два типа объектов. Это объекты-источники данных и объекты-сессии.

Объект-источник данных обеспечивает представление информации о требуемом реальном источнике данных и подключение к нему.

Для ввода сведений о хранилище данных используется интерфейс iDBProperties. Для успешного подключения необходимо задать обязательные сведения. Вероятно, для любого хранилища данных будет актуальной информация об его имени, пользователе и пароле. Однако каждый тип хранилища имеет собственные уникальные настройки. Для получения списка всех обязательных параметров соединения с данным хранилищем можно воспользоваться методом

function GetPropertylnfo(cPropertylDSets: UINT; rgPropertylDSets: PDBPropIDSetArray; var pcPropertylnfoSets: UINT; out prgPropertylnfoSets: PDBPropInfoSet; ppDescBuffer: PPOleStr): HResult; stdcall;

который возвращает заполненную структуру DBPROPINFO.

PDBPropInfo = ^TDBPropInfo;

 DBPROPINFO = packed record

pwszDescription: PWideChar;

dwPropertylD: DBPROPID;

dwFlags: DBPROPFLAGS;

vtType: Word;

vValues: OleVariant;

end; 

TDBPropInfo = DBPROPINFO;

Для каждого обязательного параметра в элементе dwFlags устанавливается значение DBPROPFLAGS_REQUIRED.

Для инициализации соединения необходимо использовать метод

function Initialize: HResult; stdcall;

интерфейса iDBinitiaiize объекта-источника данных.

 

Сессия


Из объекта-источника данных можно создавать объекты-сессии. Для этого используется метод

function CreateSession(const punkOuter: lUnknown; const riid: TGUID; out ppDBSession: lUnknown}: HResult; stdcall;

интерфейса iDBCreateSession. Сессия предназначена для обеспечения работы транзакций и наборов рядов.

 

Транзакции


Управление транзакциями в OLE DB реализовано на двух уровнях. Во-первых, всеми необходимыми методами обладает объект сессии. Он имеет интерфейсы ITransaction, ITransactionJoin, ITransactionLocal, ITransactionObject.

Внутри сессии транзакция управляется интерфейсами ITransactionLocal, ItransactionSC, ITransaction и их методами StartTransaction, Commit, Rollback.

Во-вторых, для объекта сессии можно создать объект транзакции при помощи метода

function GetTransactionObject(ulTransactionLevel: UINT; out ppTransactionObject: ITransaction): HResult; stdcall;

интерфейса ITransactionObject, который возвращает ссылку на интерфейс объекта-транзакции.

 

Наборы рядов


Объект-набор рядов является основным объектом ADO, обеспечивающим работу с данными. Он инкапсулирует совокупность рядов из источника данных, механизмы навигации по рядам и поддержания рядов в актуальном состоянии.

Объект сессии имеет обязательный интерфейс IOpenRowset с методом

function OpenRowset(const punkOuter: lUnknown; pTablelD: PDBID; plndexID: PDBID; const riid: TGUID; cPropertySets: UINT; rgPropertySets: PDBPropSetArray; ppRowset: PlUnknown): HResult; stdcall;

который открывает необходимый набор рядов.

В зависимости от возможностей источника данных набор рядов может поддерживать различные интерфейсы. Но пять из них являются обязательными:

IRowset — обеспечивает навигацию по рядам; IAccessor — обеспечивает представление информации о формате рядов, содержащихся в буфере набора рядов; IRowsetinfo — позволяет получить информацию о наборах рядов (например, число рядов или число обновленных рядов); Icoiumnsinfo — позволяет получить информацию о колонках рядов (наименование, тип данных, возможность обновления и т. д.);  IconvertType — содержит единственный метод canConvert, позволяющий определить возможность преобразования типов данных в наборе рядов.

Примечание

В отличие от привычной практики разработки интерфейсов в рамках модели СОМ, интерфейсы OLE DB часто имеют всего один-два метода. В результате большая группа интерфейсов реализует несколько вполне стандартных функций.

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

 IRowsetchange — выполняет изменения в наборе рядов (вносит изменения, добавляет новые ряды, удаляет ряды и т. д.);  IRowsetidentity — позволяет сравнивать ряды разных рядов;  IRowsetindex — обеспечивает использование индексов;  IRowsetLocate — выполняет поиск в наборе рядов;  IRowsetupdate — реализует механизм кэширования изменений.

 

Команды


Программные средства ADO были бы неполными, если бы не имели возможности использовать для работы с данными язык SQL. Операторы DML и DDL, ряд специальных операторов ADO носят общее название текстовых команд.

Объект-команда инкапсулирует саму текстовую команду и механизм обработки и передачи команды. Объект команды выполняет следующие операции:

 разбор текста команды;  связывание команды с источником данных;  оптимизацию команды; передачу команды источнику данных.

Главный интерфейс объекта команды icommand имеет три метода:

 function Cancel: HResult; stdcall;

отменяет выполнение команды;

 function Execute(const punkOuter: lUnknown; const riid: TGUID; var pParams: DBPARAMS; pcRowsAffected: PInteger; ppRowset: PlUnknown): HResult; stdcall;

исполняет команду;

 function GetDBSession(const riid: TGUID; out ppSession: lUnknown): HResult; stdcall;

возвращает ссылку на интерфейс сессии, вызвавший данную команду.

Помимо основного, объект команды обеспечивает доступ к дополнительным интерфейсам:

 ICommandPrepare — содержит два метода (Prepare И Unprepare) для подготовки команды;  icommandProperties — задает для команды свойства, которые должны поддерживаться возвращаемым командой набором данных;  iCommandText — управляет текстом команды (этот интерфейс обязателен для объекта команды);  icommandwithParameters — обеспечивает работу с параметрами команды.

 

Провайдеры ADO


Провайдеры ADO обеспечивают соединение приложения, использующего данные через ADO, с источником данных (сервером SQL, локальной СУБД, файловой системой и т. д.). Для каждого типа хранилища данных должен существовать провайдер ADO.

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

Список установленных в данной операционной системе провайдеров доступен для выбора при установке соединения через компонент TADOConnection.

При инсталляции Microsoft ActiveX Data Objects в операционной системе устанавливаются следующие стандартные провайдеры.

 Microsoft Jet OLE DB Provider обеспечивает соединение с данными СУБД Access при посредстве технологии ОАО.  Microsoft OLE DB Provider for Microsoft Indexing Service обеспечивает доступ только для чтения к файлам и Internet-ресурсам Microsoft Indexing Service.  Microsoft OLE DB Provider for Microsoft Active Directory Service обеспечивает доступ к ресурсам службы каталогов (Active Directory Service).  Microsoft OLE DB Provider for Internet Publishing позволяет использовать ресурсы, предоставляемые Microsoft FrontPage, Microsoft Internet Information Server, HTTP-файлы.  Microsoft Data Shaping Service for OLE DB позволяет использовать иерархические наборы данных.  Microsoft OLE DB Simple Provider предназначен для организации доступа к источникам данных, поддерживающим только базисные возможности OLE DB.  Microsoft OLE DB Provider for ODBC drivers обеспечивает доступ к данным, которые уже "прописаны" при помощи драйверов ODBC. Однако реальное использование столь экзотичных вариантов соединений представляется проблематичным. Драйверы ODBC и так славятся своей медлительностью, поэтому дополнительный слой сервисов здесь ни к чему.  Microsoft OLE DB Provider for Oracle обеспечивает соединение с сервером Oracle.  Microsoft OLE DB Provider for SQL Server обеспечивает соединение с сервером Microsoft SQL Server.

 

Реализация ADO в Delphi


Механизм доступа к данным через ADO и многочисленные объекты и интерфейсы реализованы в VCL Delphi в виде набора компонентов, расположенных на странице ADO. Все необходимые интерфейсы, обеспечивающие работу компонентов, объявлены и описаны в файлах OleDB.pas и ADODB.pas в папке \Delphi7\Source\Vcl.

 

Компоненты ADO


Компонент TADOConnection вобрал возможности перечислителя, источника данных и сессии с возможностями обслуживания транзакций.

Текстовые команды ADO реализованы в компоненте TADOCommand.

Наборы рядов (нотация Microsoft) можно получить при помощи компонентов TADOTable, TADOQuery, TAOostoredProc. Каждый из них реализует способ доступа к конкретному типу представления данных в хранилище. Далее по тексту, применительно к компонентам Delphi, совокупность возвращаемых из хранилища данных строк будем называть набором записей, что соответствует документации Inprise (см. www.borland.com или www.borland.ru) и стилю изложения предыдущих глав.

Набор свойств и методов компонентов ADO обеспечивает реализацию всех необходимых приложению БД функций. Способы использования компонентов ADO немногим отличаются от стандартных компонентов VCL доступа к данным (см. гл. 11).

Однако при необходимости разработчик может использовать все возможности интерфейсов ADO, обращаясь к ним через соответствующие объекты ADO. Ссылки на объекты имеются в компонентах (см. ниже).

 

Механизм соединения с хранилищем данных ADO


Компоненты доступа к данным ADO могут использовать два варианта подключения к хранилищу данных. Это стандартный метод ADO и стандартный метод Delphi.

В первом случае компоненты используют свойство connectionstring для прямого обращения к хранилищу данных. Во втором случае используется специальный компонент TADOConnection, который обеспечивает расширенное управление соединением и позволяет обращаться к данным нескольким компонентам одновременно.

Свойство connectionstring предназначено для хранения информации о соединении с объектом ADO. В нем через точку с запятой перечисляются все необходимые параметры. Как минимум, это должны быть имена провайдера соединения или удаленного сервера:

Connectionstring:='Remote Server=ServerName;Provider=ProviderName';

 При необходимости указываются путь к удаленному провайдеру:

Connectionstring:='Remote Provider=ProviderName';

и параметры, необходимые провайдеру:

'User Name=User_Name;Password=Password';

Каждый компонент, обращающийся к хранилищу данных ADO самостоятельно, задавая параметры соединения в свойстве Connectionstring, открывает собственное соединение. Чем больше приложение содержит компонентов ADO, тем больше соединений может быть открыто одновременно.

Поэтому целесообразно реализовать механизм соединения ADO через специальный компонент — TADOConnection. Этот компонент открывает соединение, также заданное свойством Connectionstring (см. выше), и предоставляет разработчику дополнительные средства управления соединением.

Компоненты, работающие с хранилищем данных ADO через данное соединение, подключаются к компоненту TADOConnection при помощи свойства

property Connection: TADOConnection;

которое имеет каждый компонент, инкапсулирующий набор данных ADO.

 

Компонент TADOConnection


Компонент TADOConnection предназначен для управления соединением с объектами хранилища данных ADO. Он обеспечивает доступ к хранилищу данных компонентам ADO, инкапсулирующим набор данных (см. ниже).

Применение этого компонента дает разработчику ряд преимуществ:

 все компоненты доступа к данным ADO обращаются к хранилищу данных через одно соединение;  возможность напрямую задать объект провайдера соединения;  доступ к объекту соединения ADO;  возможность выполнять команды ADO;  выполнение транзакций;  расширенное управление соединением при помощи методов-обработчиков событий.

 

Настройка соединения


Перед открытием соединения необходимо задать его параметры. Для этого предназначено свойство

property ConnectionString: WideString;

которое подробно рассматривалось в разд. "Компонент TADOConnection". Добавим лишь, что набор параметров изменяется в зависимости от типа провайдера и может настраиваться как вручную, так и при помощи специального редактора параметров соединения, который вызывается двойным щелчком на компоненте TADOConnection, перенесенным на форму, или щелчком на кнопке в поле редактирования свойства ConnectionString в Инспекторе объектов (рис. 19.2).

Рис. 19.2.Редактор настройки соединения ADO

Здесь можно настроить соединение через свойство ConnectionString (радиокнопка Use Connection String) или загрузить параметры соединения из файла с расширением udl (радиокнопка Use Data Link File).

Файл UDL (листинг 19.1) представляет собой обычный текстовый файл, в котором указывается название параметра и через знак равенства его значение. Параметры разделяются точкой с запятой.

Листинг 19.1  Демонстрационный файл DBDEMOS.UDL

[oledb]

Everything after this line is an OLE DB initstring

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Program Files\Common Files\Borland Shared\Data\DBDEMOS.mdb

Если файл параметров соединения отсутствует, настройку придется осуществлять вручную. Для этого следует нажать кнопку Build. В результате появляется диалоговое окно Data Link Properties, в котором можно настроить параметры соединения вручную. Оно представляет собой четырехстраничный блокнот, позволяющий вам этап за этапом задать все необходимые параметры (рис. 19.3).

Рис. 19.3. Диалоговое окно настройки параметров соединения на странице выбора провайдера

Первая страница Provider позволяет выбрать провайдер OLE DB для конкретного типа источника данных из числа провайдеров, установленных в системе. Здесь вы видите провайдеры не только для серверов БД, но и служб, установленных в операционной системе. Состав элементов управления следующих страниц зависит от типа источника данных, но различается не так уж сильно. Далее практически везде необходимо задать источник данных (имя сервера, базу данных, файл и т. д.), режим аутентификации пользователя, а также определить имя и пароль пользователя.

Рассмотрим процесс настройки на примере провайдера OLE DB для сервера Microsoft SQL Server.

Рис. 19.4. Диалоговое окно настройки параметров соединения на странице выбора источника данных

Следующая страница Connection (рис. 19.4) настраивает источник данных.

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

Второй этап определяет режим аутентификации пользователя. Это либо система безопасности Windows, либо собственная система аутентификации сервера. Здесь же надо определить имя и пароль пользователя.

Третий этап предназначен для выбора базы данных сервера.

По окончании настройки источника данных вы можете проверить соединение, нажав кнопку Test Connection.

Теперь перейдем на следующую страницу.

Страница Advanced (рис. 19.5) задает дополнительные параметры соединения. В зависимости от типа хранилища данных некоторые элементы этой страницы могут быть недоступны.

Рис. 19.5. Диалоговое окно настройки параметров соединения на странице дополнительных параметров

Список Impersonation level определяет возможности клиентов при подключении в соответствии с полномочиями их ролей. В списке могут быть выбраны следующие значения:

 Anonymous — роль клиента недоступна серверу;  Identify — роль клиента опознается сервером, но доступ к системным объектам заблокирован;  Impersonate — процесс сервера может быть представлен защищенным контекстом клиента;  Delegate — процесс сервера может быть представлен защищенным контекстом клиента, при этом сервер может осуществлять другие подключения.

Список Protection level позволяет задать уровень защиты данных. В списке могут быть выбраны следующие значения:

 None — подтверждение не требуется;  Connect — подтверждение необходимо только при подключении;    Call — подтверждение источника данных при каждом запросе;   Pkt — подтверждение получения от клиента всех данных;  Pkt Integrity — подтверждение получения от клиента всех данных с соблюдением целостности;  Pkt Privacy — подтверждение получения от клиента всех данных с соблюдением целостности и защита шифрованием.

В поле Connect timeout можно задать время ожидания соединения в секундах. По истечении этого времени процесс прерывается.

При необходимости список Access permissions задает права доступа к отдельным видам выполняемых операций. В списке можно выбрать следующие значения:

Read — только чтение; ReadWrite — чтение и запись;  Share Deny None — полный доступ всем на чтение и запись;   Share Deny Read — чтение запрещено всем;  Share Deny Write — запись запрещена всем;  Share Exclusive — чтение и запись запрещена всем;  Write — только запись.

Последняя страница All (рис. 19.6) позволяет просмотреть и при необходимости изменить все сделанные настройки (для этого предназначена кнопка Edit Value...) для выбранного провайдера.

Рис. 19.6. Диалоговое окно настройки параметров соединения на странице просмотра настроек

После подтверждения сделанных в диалоге настроек из них формируется значение свойства Connectionstring.

 

Управление соединением


Соединение с хранилищем данных ADO открывается и закрывается при помощи свойства

property Connected: Boolean;

или методов

procedure Open; overload;

procedure Openfconst UserlD: WideString; const Password: WideString); overload;

и

procedure Close;

Метод open является перегружаемым при необходимости использования удаленного или локального соединения. Для удаленного соединения применяется вариант с параметрами UserID и Password.

До и после открытия и закрытия соединения разработчик может использовать соответствующие стандартные методы-обработчики событий:

property BeforeConnect: TNotifyEvent;

property BeforeDisconnect: TNotifyEvent; 

property AfterConnect: TNotifyEvent; 

property AfterDisconnect: TNotifyEvent;

Кроме этого, компонент TADOConnection имеет дополнительные методы-обработчики. После получения подтверждения от провайдера о том, что соединение будет открыто, перед его реальным открытием вызывается метод

TWillConnectEvent = procedure(Connection: TADOConnection; var Connectionstring, UserlD, Password: WideString; var ConnectOptions: TConnectOption; 

var EventStatus: TEventStatus) of object;

 property OnWillConnect: TWillConnectEvent;

Параметр Connection содержит указатель на вызвавший обработчик компонент.

Параметры Connectionstring, userID и Password определяют строку параметров, имя и пароль пользователя.

Соединение может быть синхронным или асинхронным, что и определяется параметром ConnectOptions типа TConnectOption:

type TConnectOption = (coConnectUnspecified, coAsyncConnect);

coConnectunspecified — синхронное соединение всегда ожидает результат последнего запроса;

coAsyncConnect — асинхронное соединение может выполнять новые запросы, не дожидаясь ответа от предыдущих запросов.

Наконец, параметр Eventstatus позволяет определить успешность выполнения посланного запроса на соединение:

type

TEventStatus = (esOK, esErrorsOccured, esCantDeny, esCancel, esUnwantedEvent);

esOK — запрос на соединение выполнен успешно;

esErrorsOccured — в процессе выполнения запроса возникла ошибка;

esCantDeny — соединение не может быть прервано;

esCancel — соединение было прервано до открытия;

esUnwantedEvent — внутренний флаг ADO.

Например, в случае успешного соединения можно выбрать синхронный режим работы компонента:

procedure TForml.ADOConnectionWillConnect(Connection: TADOConnection;

 var ConnectionString, UserlD, Password: WideString;

 var ConnectOptions: TConnectOption;

 var Eventstatus: TEventStatus); 

begin if Eventstatus = esOK

then ConnectOptions := coConnectunspecified; 

end;

Кстати, параметр синхронности/асинхронности можно также задать при помощи свойства

ConnectOptions property ConnectOptions: TConnectOption;

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

TConnectErrorEvent = procedure(Connection:

  TADOConnection; Error: Error;

 var Eventstatus: TEventStatus) of object; 

property OnConnectComplete: TConnectErrorEvent;

Здесь, если в процессе открытия соединения возникла ошибка, параметр Eventstatus будет равен esErrorsOccured, а параметр Error содержит объект ошибки ADO.

Теперь перейдем к вспомогательным свойствам и методам компонента TADOConnection, обеспечивающим соединение.

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

property ConnectionTimeout: Integer;

задающее время ожидания открытия соединения в секундах. По умолчанию оно равно 15 сек.

Также можно определить реакцию компонента на неиспользуемое соединение. Если через соединение не подключен ни один активный компонент, свойство

property KeepConnection: Boolean;

в значении True сохраняет соединение открытым. Иначе, после закрытия последнего связанного компонента ADO, соединение закрывается.

При необходимости провайдер соединения ADO определяется напрямую свойством

property Provider: WideString;

Имя источника данных по умолчанию задается свойством

property DefaultDatabase: WideString;

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

При необходимости прямой доступ к объекту соединения OLE DB обеспечивает свойство

property ConnectionObject: _Connection;

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

property LoginPrompt: Boolean;

Без этого диалога для задания данных параметров можно использовать свойство Connectionstring, метод open (см. выше) или метод-обработчик

type TLoginEvent = procedure(Sender:TObject; 

Username, Password: string)

of object;

property OnLogin: TLoginEvent;

Свойство

type TConnectMode = (cmUnknown, cmRead, cmWrite, cinReadWrite, cmShareDenyRead, cmShareDenyWrite, cmShareExclusive, cmShareDenyNone);

 property Mode: TConnectMode;

задает доступные для соединения операции:

cmUnknown — разрешение неизвестно или не может быть установлено; cmRead — разрешение на чтение; cmwrite — разрешение на запись; cmReadWrite — разрешение на чтение и запись; cmshareDenyRead — разрешение на чтение для других соединений запрещено; cmshareoenywrite — разрешение на запись для других соединений запрещено; cmShareExciusive — разрешение на открытие для других соединений запрещено; cmshareDenyNone — открытие других соединений с разрешениями запрещено.

 

Доступ к связанным наборам данных и командам ADO


Компонент TADOconnection обеспечивает доступ ко всем компонентам, которые используют его для доступа к хранилищу данных ADO. Все открытые таким образом наборы данных доступны через индексированное свойство

property DataSets[Index: Integer]: TCustomADODataSet;

Каждый элемент этого списка содержит дескриптор компонента доступа к данным ADO (тип TCustomADODataSet). Общее число связанных компонентов с наборами данных возвращается свойством

property DataSetCount: Integer;

Для этих компонентов можно централизованно установить тип используемого курсора при помощи свойства

type TCursorLocation = (clUseServer, clUseClient); property CursorLocation: TCursorLocation;

Значение clUseClient задает локальный курсор на стороне клиента, что позволяет выполнять любые операции с данными, в том числе не поддерживаемые сервером.

Значение cIUseServer задает курсор на сервере, который реализует только возможности сервера, но обеспечивает быструю обработку больших массивов данных.

Например:

for i := 0 to ADOConnection.DataSetCount — 1 do

 begin

if ADOConnection.DataSets[i].Active = True then ADOConnection.DataSets[i].Close;

ADOConnection.DataSets[i].CursorLocation := clUseClient; end;

Помимо наборов данных компонент TADOConnection обеспечивает выполнение команд ADO. Команду ADO инкапсулирует специальный компонент TADOCommand, который рассматривается ниже. Все команды ADO, работающие с хранилищем данных через это соединение, доступны для управления через индексированное свойство

property Commands[Index: Integer]: TADOCommand

Каждый элемент этого списка представляет собой экземпляр класса

TADOCommand.

Общее число доступных команд возвращается свойством

property CommandCount: Integer

Например, сразу после открытия соединения можно выполнить все связанные команды ADO, реализовав таким образом нечто вроде скрипта:

procedure TForml.ADOConnectionConnectComplete(Connection: TADOConnection; 

const Error: Error; var EventStatus: TEventStatus); 

var i, ErrorCnt: Integer;

 begin

if EventStatus = esOK then

for i := 0 to ADOConnection.CommandCount — 1 do

 try if ADOConnection.Commands[i].CommandText <> 

then ADOConnection.Commands[i].Execute; except

on E: Exception do Inc(ErrorCnt);

  end; 

end;

Однако компонент TADOConnection может выполнять команды ADO самостоятельно, без помощи других компонентов. Для этого используется перегружаемый метод

function Execute(const CommandText: WideString; ExecuteOptions:

TExecuteOptions = []): _RecordSet; overload;

procedure Execute(const CommandText: WideString; 

var RecordsAffected:

Integer; ExecuteOptions: TExecuteOptions = [eoExecuteNoRecords]);

overload;

Выполнение команды осуществляется процедурой Execute (если команда не возвращает набор записей) или одноименной функцией Execute (если команда возвращает набор записей).

Параметр commandText должен содержать текст команды. Параметр RecordsAffected возвращает число обработанных командой записей (если они есть). Параметр

type

TExecuteOption = (eoAsyncExecute, eoAsyncFetch, eoAsyncFetchNonBlocking, eoExecuteNoRecords);

TExecuteOptions = set of TExecuteOption;

задает условия выполнения команды:

 eoAsyncExecute — команда выполняется асинхронно (соединение не будет ожидать окончания выполнения команды, а продолжит работу, обработав сигнал о завершении команды, когда он поступит);  eoAsyncFetch — команда получает необходимые записи также асинхронно;  eoAsyncFetchNonBlocking — команда получает необходимые записи также асинхронно, но при этом созданная нить не блокируется;  eoExecuteNoRecords — команда не должна возвращать записи.

Если источник данных принял команду для выполнения и сообщил об этом соединению, вызывается метод-обработчик

TWillExecuteEvent = procedure(Connection: TADOConnection;

var CommandText: WideString; var CursorType: TCursorType; var LockType:

TADOLockType; var ExecuteOptions: TExecuteOptions;

 var EventStatus:

TEventStatus; const Command: _Command;

 const Recordset: _Recordset)

of object;

property OnWillExecute: TWillExecuteEvent;

После выполнения команды вызывается метод-обработчик

TExecuteCompleteEvent = procedure(Connection: TADOConnection; RecordsAffected: Integer; 

const Error: Error; var EventStatus: TEventStatus; 

const Command: _Command;

 const Recordset: _Recordset) of object;

 property OnExecuteComplete: TExecuteCompleteEvent;

 

Объект ошибок ADO


Все ошибки времени выполнения, возникающие при открытом соединении, сохраняются в специальном объекте ADO, инкапсулирующем коллекцию сообщений об ошибках. Доступ к объекту возможен через свойство

property Errors: Errors;

Подробнее об объекте ошибок ADO см. ниже.

 

Транзакции


Компонент TADOconnection позволяет выполнять транзакции. Методы

function BeginTrans: Integer;

 procedure CommitTrans; 

procedure RollbackTrans;

обеспечивают начало, фиксацию и откат транзакции соответственно. Методы-обработчики

TBeginTransCompleteEvent = procedure(Connection: TADOConnection;

TransactionLevel: Integer; 

const Error: Error; 

var EventStatus:

TEventStatus) of object;

property OnBeginTransComplete: TBeginTransCompleteEvent;

TConnectErrorEvent = procedure(Connection: TADOConnection; 

Error: Error;

var EventStatus: TEventStatus) of object;

property OnCornmitTransComplete: TConnectErrorEvent;

вызываются после начала и фиксации транзакции. Свойство

type TIsolationLevel = (ilUnspecified, ilChaos, ilReadUncommitted, ilBrowse, ilCursorStability, ilReadCorranitted, ilRepeatableRead, ilSerializable, illsolated); 

property IsolationLevel: TIsolationLevel;

позволяет задать уровень изоляции транзакции:

 IlUnspecif led — уровень изоляции не задается; Iichaos — изменения более защищенных транзакций не перезаписываются данной транзакцией; IlReadUncommitted — незафиксированные изменения других транзакций видимы;  IlBrowse — незафиксированные изменения других транзакций видимы; IlCursorStability — изменения других транзакций видимы только после фиксации;  IlReadCommitted — изменения других транзакций видимы только после фиксации; IlRepeatableRead — изменения других транзакций не видимы, но доступны при обновлении данных; ISerializable — транзакция выполняется изолированно от других транзакций; Ilisolated — транзакция выполняется изолированно от других транзакций.

Свойство

TXactAttribute = (xaCommitRetaining, xaAbortRetaining); property Attributes: TXactAttributes;

задает способ управления транзакциями при их фиксации и откате:

 xaCommitRetaining — после фиксации очередной транзакции автоматически начинается выполнение новой;  xaAbortRetaining — после отката очередной транзакции автоматически начинается выполнение новой.

 

Наборы данных ADO


На странице ADO Палитры компонентов Delphi, кроме компонентов соединения есть стандартные компоненты, инкапсулирующие набор данных и адаптированные для работы с хранилищем данных ADO (рис. 19.7). Это компоненты:

 TADODataSet — универсальный набор данных;  TАоотаblе — таблица БД;  TADOQuery — запрос SQL;  TAoostoredProc — хранимая процедура.

Как и положено для компонентов, инкапсулирующих набор данных, их общим предком является класс TDataSet, предоставляющий базовые функции управления набором данных (см. гл. II).

Рис. 19.7. Иерархия классов наборов данных ADO

Компоненты ADO обладают обычным набором свойств и методов, а необходимый для доступа к данным через ADO механизм наследуют от своего общего предка — класса TCustomADODataSet. Кроме этого, класс TCustomADODataSet содержит ряд общих для всех потомков свойств и методов, рассмотреть которые будет очень полезно. Поэтому сначала мы изучим класс TCustomADODataSet и только потом перейдем к компонентам ADO.

 

Класс TCustomADODataSet


Класс TCustomADODataSet инкапсулирует механизм доступа к хранилищу данных через ADO. Этот класс наполняет абстрактные методы общего предка TDataSet функциями конкретного механизма доступа к данным.

Поэтому здесь мы рассмотрим только уникальные свойства и методы класса TCustomADODataSet, обеспечивающие работу с ADO.

Соединение набора данных с хранилищем данных ADO осуществляется через компонент TADOConnection (свойство connection) или путем задания параметров соединения через свойство connectionstring (см. выше).

Набор данных

Перед открытием набора данных необходимо установить тип используемой при редактировании записей блокировки. Для этого применяется свойство

type TADOLockType = (ItUnspecified, ItReadOnly, ItPessimistic, ItOptimistic, ItBatchOptimistic); property LockType: TADOLockType;

ItUnspecified — блокировка задается источником данных, а не компонентом; 

ItReadOnly — набор данных откроется в режиме только для чтения;

ItPessimistic — редактируемая запись блокируется на все время редактирования до момента сохранения в хранилище данных;

ItOptimistic — запись блокируется только на время сохранения изменений в хранилище данных;

ItBatchOptimistic — запись блокируется на время сохранения в хранилище данных при вызове метода updateBatch.

 Примечание 

Для того чтобы установка блокировки сработала, свойство LockType должно быть обязательно модифицировано до открытия набора данных.

Набор данных открывается методом Open и закрывается методом close. Также можно использовать свойство

property Active: Boolean;

Текущее состояние набора данных можно определить свойством

type

TObjectState = (stClosed, stOpen, stConnecting, stExecuting, stretching);

TObjectStates = set of TObjectState;

property RecordsetState: TObjectStates;

Набор данных в компонентах ADO основан на использовании объекта набора записей ADO, прямой доступ к этому объекту возможен при помощи свойства

property Recordset: _Recordset;

Но поскольку все основные методы интерфейсов объекта набора записей ADO перекрыты методами класса, в обычных случаях прямой доступ к объекту вам не понадобится. После обновления набора данных вызывается метод-обработчик

TRecordsetEvent = procedure(DataSet: TCustomADODataSet; const Error: Error;

 var EventStatus: TEventStatus) of object; property OnFetchComplete: TRecordsetEvent;

где Error — ссылка на объект ошибки ADO, если она возникла.

Если же набор данных работает в асинхронном режиме, при обновлении вызывается метод-обработчик

TFetchProgressEvent = procedure(DataSet: TCustomADODataSet;

Progress, MaxProgress: Integer;

var EventStatus: TEventStatus) of object; 

property OnFetchProgress: TFetchProgressEvent;

где параметр Progress показывает долю выполнения операции.

Курсор набора данных

Для набора данных ADO в зависимости от его назначения можно выбрать тип и местоположение используемого курсора. Местоположение курсора задается свойством

type TCursorLocation = (clUseServer, clUseClient); property CursorLocation: TCursorLocation;

Курсор может находиться на сервере (CIUseServer) или на клиенте (CIUseClient).

 Серверный курсор используется при работе с большими наборами данных, которые нецелесообразно пересылать клиенту целиком. При этом несколько снижается скорость работы клиентского набора данных.  Клиентский курсор обеспечивает передачу набора данных клиенту. Это положительно сказывается на быстродействии, но такой курсор разумно использовать только для небольших наборов данных, не загружающих канал связи с сервером.

При использовании клиентского курсора необходимо дополнительно установить свойство

TMarshalOption = (moMarshalAll, moMarshalModifiedOnly); property MarshalOptions: TmarshalOption

которое управляет обменом данных клиента с сервером. Если соединение с сервером быстрое, можно использовать значение moMarshalAll, разрешающее возврат серверу всех записей набора данных. В противном случае для ускорения работы компонента можно применить свойство moMarshalModifiedOnly, обеспечивающее возврат только модифицированных клиентом записей.

Тип курсора определяется свойством

TCursorType = (ctUnspecified, CtOpenForwardOnly, ctKeyset, ctDynamic,

ctStatic);

property CursorType: TCursorType;

ctunspecified — курсор не задан, тип курсора определяется возможностями источника данных;

ctOpenForwardOnly — однонаправленный курсор, допускающий перемещение только вперед; используется при необходимости быстрого одиночного прохода по всем записям набора данных;

ctKeyset — двунаправленный локальный курсор, не обеспечивающий просмотр добавленных и удаленных другими пользователями записей;

 ctDynamic — двунаправленный курсор, отображает все изменения, требует наибольших затрат ресурсов;

ctStatic — двунаправленный курсор, полностью игнорирует изменения, внесенные другими пользователями.

Примечание

Если курсор расположен на клиенте (CursorType = ciusedient), то для него доступен только один тип — ctStatic.

Соответственно до и после каждого перемещения курсора в наборе данных вызываются методы - обработчики:

TRecordsetReasonEvent = procedure(DataSet: TCustomADODataSet;

const Reason: TEventReason; 

var EventStatus: TEventStatus) of object;

property OnWillMove: TRecordsetReasonEvent;

и

TP.ecordsetErrorEvent = procedure(DataSet: TCustomADODataSet; const --eason: TEventReason;

const Error: Error; var EventStatus: TEventStatus) if object; 

property OnMoveComplete: TRecordsetErrorEvent;

где параметр Reason позволяет узнать, какой метод вызвал это перемещение.

Локальный буфер

После передачи клиенту записи набора данных размещаются в локальном буфере, размер которого определяется свойством

property CacheSize: Integer;

Значение свойства есть число записей, помещаемых в локальный буфер, и оно не может быть меньше 1. Очевидно, что при достаточно большом размере буфера компонент будет обращаться к источнику данных не так часто, но при этом большой буфер заметно замедлит открытие набора данных. Кроме этого, при выборе размера локального буфера необходимо учитывать доступную компоненту память. Это можно сделать путем несложных вычислений:

"acheSizelnMem := ADODataSet.CacheSize * ADODataSet.RecordSize;

где RecordSize — свойство

 property RecordSize: Word;

возвращающее размер одной записи в байтах.

Как видите, компоненты ADO не избежали общей проблемы клиентских данных — при плохом качестве соединения работа приложения замедляется. Однако кое-что все-таки сделать можно. Если при навигации по записям вам не требуется отображать данные в визуальных компонентах пользовательского интерфейса, свойство property BlockReadSize: Integer;

позволяет организовать блочную пересылку данных. Оно задает число записей, помещаемых в один блок. При этом набор данных переходит в состояние dsBlockRead. По умолчанию блочная пересылка не используется и значение свойства равно 0. Также можно ограничить максимальный размер набора данных. Свойство

property MaxRecords: Integer

задает максимальное число записей набора данных. По умолчанию свойство имеет значение 0 и набор данных не ограничен.

Общее число записей набора данных на этот момент возвращает свойство только для чтения

property RecordCount: Integer;

При достижении последней записи набора данных вызывается метод-обработчик

TEndOfRecordsetEvent = procedure (DataSet: TCustomADODataSet;

var MoreData: WordBool;

var EventStatus: TEventStatus) of object;

property OnEndOfRecordset: TEndOfRecordsetEvent;

При этом параметр MoreData показывает, действительно ли достигнут конец набора данных. Если MoreData = True, то это означает, что в хранилище данных еще имеются записи, не переданные клиенту.

Состояние записи

Класс TCustomADODataSet обладает дополнительными возможностями, которые позволяют отслеживать состояние каждой записи.

Для текущей записи набора данных можно определить ее состояние. Для этого предназначено свойство

TRecordStatus = (rsOK, rsNew, rsModified, rsDeleted, rsUnmodified, rslnvalid, rsMultipleChanges, rsPendingChanges, rsCanceled, rsCantRelease, rsConcurrencyViolation, rsIntegrityViolation, rsMaxChangesExceeded, rsObjectOpen, rsOutOfMemory, rsPermissionDenied, rsSchemaViolation, rsDBDeleted); property RecordStatus: TRecordStatusSet;

где rsOK — запись успешно сохранена; rsNew — запись добавлена; rsModified — запись была изменена; rsDeleted — запись удалена; rsUnmodified — запись без изменений; rslnvalid — запись не может быть сохранена из-за неверной закладки; rsMultipleChanges — запись не может быть сохранена из-за множественных изменений; rsPendingChanges — запись не может быть сохранена из-за ссылки на несохраненные изменения; rsCanceled — операция с записью была отменена; rsCantRelease — запись заблокирована; rsConcurrencyViolation — запись не может быть сохранена из-за типа блокировки; rsintegrityvioiation — нарушена ссылочная целостность; rsMaxChangesExceeded — слишком много изменений; rsObjectOpen — конфликт с объектом базы данных; rsoutofMemory — недостаток памяти, rsPermissionDenied — нет доступа; rsSchemaViolation — нарушение структуры данных; rsDBDeleted — запись удалена в БД.

Как видите, благодаря этому свойству состояние отдельной записи может быть определено очень точно.

Кроме этого, метод

type

TUpdateStatus = (usUnmodified, usModifled, uslnserted, usDeleted);

 function UpdateStatus: TUpdateStatus; override;

возвращает информацию о состоянии текущей записи.

Соответственно до и после изменения записи вызываются методы-обработчики

TWillChangeRecordEvent = procedure(DataSet: TCustomADODataSet; const Reason: TEventReason; 

const RecordCount: Integer; 

var EventStatus: TEventStatus) of object;

 property OnWillChangeRecord: TWillChangeRecordEvent;

И

TRecordChangeCompleteEvent = procedure(DataSet: TCustomADODataSet; const Reason: TEventReason;

const RecordCount: Integer; const Error: Error; 

var EventStatus: TEventStatus) of object;

property OnRecordChangeComplete:

TrecordChangeCompleteEvent;

где параметр Reason позволяет узнать, какой метод изменил записи, а параметр RecordCount возвращает число измененных записей.

Фильтрация

Помимо обычной фильтрации , основанной на свойствах Filter, Filtered и методе-обработчике onFilterRecord, класс TCustomADODataSet предоставляет разработчику дополнительные возможности.

Свойство

TFilterGroup = (fgUnassigned, fgNone, fgPendingRecords, fgAffectedRecords, fgFetchedRecords, fgPredicate, fgConflictingRecords); 

property FilterGroup: TFilterGroup;

задает групповой фильтр для записей, основываясь на информации о состоянии каждой записи набора данных, подобно рассмотренному выше свойству RecordStatus.

Фильтрация возможна по следующим параметрам:

 fgUnassigned.— фильтр не задан;  fgNone — все ограничения, заданные фильтром, снимаются, отображаются все записи набора данных;  fgPendingRecords — отображаются измененные записи, несохраненные в хранилище данных при вызове метода updateBatch или cancelBatch;  fgAffectedRecords — показываются записи, обработанные при последнем сохранении в хранилище данных;  fgFetchedRecords — имеем записи, полученные при последнем обновлении из источника данных;  fgPredicate — видны только удаленные записи;  fgConfiictingRecords — отображаются модифицированные записи, при сохранении которых в хранилище данных возникла ошибка.

Для того чтобы групповая фильтрация заработала, необходимы два дополнительных условия. Во-первых, фильтрация должна быть включена — свойство Filtered должно иметь Значение True. 

Во- вторых , свойство LockType должно иметь значение ItBatchOptimistic.

with ADODataSet do begin

Close;

LockType := ItbatchOptimistic;

Filtered := True;

FilterGroup := fgFetchedRecords;

Open;

  end;

Метод

procedure FilterOnBookmarks(Bookmarks: array of const);

включает фильтрацию по существующим закладкам. Для этого предварительно необходимо при помощи метода GetBookmark установить закладки на интересующих записях. При вызове метода FilterOnBookmarks автоматически очищается свойство Filter, а свойству FilterGroup присваивается значение gUnassigned.

 

Поиск

Быстрый и гибкий поиск по полям текущего индекса набора данных обеспечивает метод

SeekOption = (soFirstEQ, soLastEQ, soAfterEQ, soAfter, soBeforeEQ, soBefore);

function Seek(const KeyValues: Variant; SeekOption: TSeekOption = soFirstEQ): Boolean;

В параметре KeyValues должны быть перечислены необходимые значения полей индекса. Параметр SeekOption управляет процессом поиска:

 soFirstEQ — курсор устанавливается на первую найденную запись;  soLastEQ — курсор устанавливается на последнюю найденную запись;  soAfterEQ — курсор устанавливается на найденную запись или, если запись не найдена, сразу после того места, где она могла находиться;  soAfter — курсор устанавливается сразу после найденной записи;  soBeforeEQ — курсор устанавливается на найденную запись или, если запись не найдена, перед тем местом, где она могла находиться;  soBefore — курсор устанавливается перед найденной записью.

Сортировка

Свойство

property Sort: WideString;

предоставляет простой способ сортировки по произвольному сочетанию полей. Оно должно включать через запятую имена нужных полей и признак прямого или обратного порядка сортировки:

ADODataSet.Sort := 'FirstField DESC';

Если порядок сортировки не указан, по умолчанию задается прямой порядок.

 

Для выполнения запросов к источнику данных любой компонент ADO инкапсулирует специальный объект команды ADO.

При использовании компонентов-потомков класса TCustomADODataSet обычно нет необходимости применять объект команды напрямую. И хотя все реальное взаимодействие объекта набора данных ADO с источником данных осуществляется через объект команды, настройка и выполнение команды скрыты в свойствах и методах компонентов ADO. Тем не менее в классе TCustomADODataSet доступ к объекту команды можно получить при помощи свойства 

property Command: TADOCommand;

 

Примечание 

При необходимости выполнить команду ADO, не связанную с конкретным набором данных, разработчик может использовать отдельный компонент TADOCommand, также расположенный на странице ADO Палитры компонентов.

Тип команды задается свойством

type

TCommandType = (cmdUnknown, cmdText, cmdTable, cmdStoredProc, cmdFile,

cmdTableDirect);

 property CommandType: TCommandType;

cmdunknown — тип команды неизвестен и будет определен источником данных;

cmdText — текстовая команда, интерпретируемая источником данных (например запрос SQL); текст должен быть составлен с учетом правил для конкретного источника данных;

cmdTable — команда на получение набора данных таблицы из хранилища данных;

cmdstoredProc — команда на выполнение хранимой процедуры;

cmdFile — команда на получение набора данных, сохраненного в файле в формате, используемым конкретным источником данных;

cmdTableoirect — команда на получение набора данных таблицы напрямую, например из файла таблицы.

Текст команды, представленный свойством

property CommandText: WideString;

обязательно должен быть согласован с ее типом.

Для ограничения времени ожидания выполнения команды используется свойство

property CommandTimeout: Integer;

В компонентах наборов данных ADO команды выполняется при выполнении следующих операций:

 открытие и закрытие набора данных;  выполнение запросов и хранимых процедур;  обновление набора данных;  сохранение изменений;  групповые операции.

Разработчик может повлиять на способ выполнения команды. Для этого он может изменить свойство

type

TExecuteOption = (eoAsyncExecute, eoAsyncFetch, eoAsyncFetchNonBlocking, eoExecuteNoRecords);

TExecuteOptions = set of TExecuteOption;

 property ExecuteOptions: TExecuteOptions;

eoAsyncExecute — асинхронное выполнение команды;

eoAsyncFetch — асинхронное выполнение команды на обновление набора данных;

eoAsyncFetchNonBlocking — асинхронное выполнение команды на обновление набора данных без установки блокировки;

eoExecuteNoRecords — выполнение команды не требует возвращения набора данных.

Групповые операции

Как уже рассказывалось выше, наборы данных ADO используют на клиентской стороне локальный кэш для хранения данных и сделанных изменений. Благодаря наличию этого кэша и появилась возможность реализовать групповые операции. В этом режиме все сделанные изменения не передаются немедленно источнику данных, а накапливаются в локальном кэше. Это повышает скорость работы и позволяет сохранять сразу группу модифицированных записей.

Из отрицательных сторон этого метода стоит отметить, что пока изменения находятся на клиенте, они недоступны другим пользователям. В данной ситуации могут возникать потери данных.

Для перевода набора данных в режим групповых операций необходимо выполнить следующие условия.

 Набор данных должен использовать клиентский курсор:

ADODataSet.CursorLocation := clUseClient;

 Курсор должен иметь тип ctstatic:

ADODataSet.CursorType := ctstatic;

Блокировка должна иметь значение itBatchoptimistic:

ADODataSet.LockType := ItBatchOptimistic;

Для передачи сделанных изменений в хранилище данных в компонентах ADO используется метод

procedure UpdateBatch(AffectRecords: TAffectRecords = arAll);

Для отмены всех сделанных, но не сохраненных методом UpdateBatch изменений применяется метод

procedure CancelBatch(AffectRecords: TAffectRecords = arAll);

Используемый в методах тип TAffectRecords позволяет задать тип записей, к которым применяется данная операция:

TAffectRecords = (arCurrent, arFiltered, arAll, arAHChapters);

arcurrent — операция выполняется только для текущей записи;

 arFiltered — операция выполняется для записей из работающего фильтра;

rAll — операция выполняется для всех записей;

arAllchapters — операция выполняется для всех записей текущего набора данных (включая невидимые из-за включенного фильтра), а также для всех вложенных наборов данных.

 

Параметры


Многие компоненты ADO, инкапсулирующие набор записей, должны обеспечивать применение параметров запросов. Для этого в них используется специальный класс TParameters.

Для каждого параметра из коллекции класса TParameters создается отдельный класс TParameter.

Этот класс является наследником класса коллекции TCollection и инкапсулирует индексированный список отдельных параметров (см. ниже). Напомним, что для работы с параметрами обычных запросов в компонентах запросов и хранимых процедур используется класс TParams (например в компонентах dbExpress), также происходящий от класса коллекции.

Методы этих двух классов совпадают, а свойства имеют некоторые отличия. Для представления параметров команд в ADO имеется специальный объект параметров, который активно используется в процессе работы компонентов АDO, инкапсулирующих набор данных.

Поэтому для компонентов ADO в VCL был создан собственный класс параметров.

Класс TParameters

Главное, для чего предназначен класс TParameters, — содержать список параметров. Индексированный список параметров представлен свойством property Items[Index: Integer]: TParameter; Текущие значения параметров можно получить из индексированного свойства

property ParamValues[const ParamName: String]: Variant;

При этом доступ к конкретному значению осуществляется по имени параметра:

Editl.Text := ADODataSet.Parameters.ParamValues['ParamOne'];

Список параметров можно обновлять при помощи методов

function AddParameter: TParameter; 

и

function CreateParameter(const Name: WideString; DataType: TDataType;

  Direction: TParameterDirection; Size: Integer;

  Value: OleVariant): TParameter;

Первый метод просто создает новый объект параметра и добавляет его к списку. Затем необходимо задать все свойства нового параметра:

var NewParam: TParameter;

NewParam := ADODataSet.Parameters.AddParameter; NewParam.Name := 'ParamTwo';

NewParam.DataType := ftlnteger; 

NewParam.Direction := pdlnput; NewParam.Value := 0;

Метод CreateParameter создает новый параметр и определяет его свойства: 

 Name — имя параметра;  DataType — тип данных параметра, соответствующий типу поля таблицы БД (тип TFieldType);  Direction — тип параметра, в дополнение к стандартным типам dUnknown, pdlnput, pdOutput, pdlnputOutput,тип TParameterDirection имеет дополнительный тип pdReturnValue, определяющий любое возвращаемое значение;  size — максимальный размер значения параметра;   value — значение параметра.

При работе с параметрами полезно вызывать их, используя имена, а не абсолютные индексы в списке. Для этого можно использовать метод

function ParamByName(const Value: WideString): TParameter;

Список параметров всегда должен соответствовать запросу или хранимой процедуре. Для обновления списка используется метод

procedure Refresh;

Также вы можете создать список параметров для не связанного с данным объектом параметров запроса. Для этого используется метод

function ParseSQL(SQL: String; DoCreate: Boolean): String;

где DoCreate определяет, удалять ли перед анализом существующие параметры.

 

Класс TParameter

Класс TParameter инкапсулирует отдельный параметр. Имя параметра определяется свойством

property Name: WideString;

Тип данных, которому должно соответствовать его значение, задается свойством

TDataType = TFieldType; property DataType: TDataType;

И так как параметры взаимодействуют с полями таблиц БД, то тип данных параметров совпадает с типами данных полей. От типа данных зависит размер параметра

property Size: Integer;

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

Само значение параметра содержится в свойстве

property Value: OleVariant; 

А свойство

type

TParameterAttribute = (paSigned, paNullable, paLong);

TParameterAttributes = set of TParameterAttribute; property Attributes: TParameterAttributes;

контролирует значение, присваиваемое параметру:

 paSigned — значение может быть символьным;  paNullable — значение параметра может быть пустым;  paLong — значение может содержать данные типа BLOB.

Тип параметра задается свойством

type TParameterDirection = (pdUnknown, pdlnput, pdOutput, pdlnputOutput, pdReturnValue);

property Direction: TParameterDirection;

pdUnknown — неизвестный тип, источник данных попытается определить его самостоятельно;

pdinput — входной параметр, используется в запросах и хранимых процедурах;

pdOutput — выходной параметр, используется в хранимых процедурах;

pdlnputOutput — входной и выходной параметр одновременно, используется в хранимых процедурах;

pdReturnValue — параметр для передачи значения.

Если параметр должен передавать большие бинарные массивы (например, изображения или файлы), то значение параметра можно загрузить, используя методы

procedure LoadFromFile(const FileName: String; DataType: TDataType);

И

procedure LoadFromStream(Stream: TStream; DataType: TDataType);

 

Компонент TADODataSet


Компонент TADODataSet предназначен для представления набора данных из хранилища данных ADO. Он прост в использовании, имея только несколько собственных свойств и методов, и применяет функции своего предка —класса TCustomADODataSet.

Это единственный компонент ADO, инкапсулирующий набор данных, для которого опубликованы свойства, позволяющие управлять командой ADO. Это свойства (см. выше)

property CommandText: WideString;

И

property CommandType: TCommandType;

В результате компонент представляет собой гибкий инструмент, который позволяет (в зависимости от типа команды и ее текста) получать данные из таблиц, запросов SQL, хранимых процедур, файлов и т. д. Например, вы выбираете нужное значение свойства CommandType = cmdText и заносите в свойство CommandText текст запроса SQL из редактора:

ADODataSet,CommandType = cmdText;

ADODataSet. CommandText := Memol.Lines.Text;

И запрос SQL готов к выполнению.

 Примечание 

Для запросов SQL можно применять только язык Data Manipulation Language (использовать только SELECT).

Соединение с базой данных задается свойством Connectionstring или Connection (см. выше).

Набор данных открывается и закрывается свойством Active или методами Open и Close.

В приложениях компонент можно применять как все обычные компоненты доступа к данным, связывая инкапсулированный в нем набор данных с визуальными компонентами отображения данных через компонент TDataSource.

 

Компонент TADOTable


Компонент ТАDOTаblе обеспечивает использование в приложениях Delphi таблиц БД, подключенных через провайдеры OLE DB. По своим функциональным возможностям и применению он подобен стандартному табличному компоненту (см. гл. 11).

Как вы уже знаете, в основе компонента лежит использование команды ADO, но ее свойства настроены заранее и изменению не подлежат.

Имя таблицы БД задается свойством

property TableName: WideString;

Другие свойства и методы компонента обеспечивают применение индексов (этой возможности лишен любой компонент запроса).

Так как не все провайдеры ADO обеспечивают прямое использование таблиц БД, то для доступа к ним может понадобиться запрос SQL. Если свойство

property TableDirect: Boolean;

имеет значение True, осуществляется прямой доступ к таблице. В противном случае компонент генерирует соответствующий запрос.

Свойство

property Readonly: Boolean;

позволяет включить или отключить для таблицы режим "только для чтения".

 

Компонент TADOQuery


Компонент TADOQuery обеспечивает применение запросов SQL при работе с данными через ADO. По своей функциональности он подобен стандартному компоненту запроса (см. гл. 11).

Текст запроса задается свойством

property SQL: TStrings;

Параметры запроса определяются свойством

property Parameters: TParameters;

Если запрос должен возвращать набор данных, для его открытия используется свойство

property Active: Boolean;

или метод

procedure Open;

В противном случае достаточно использовать метод

function ExecSQL: Integer; ExecSQL

Число обработанных запросом записей возвращает свойство

property RowsAffected: Integer;

 

Компонент TADOStoredProc


Компонент TADOStoredProc позволяет использовать в приложениях Delphi, обращающихся к данным через ADO, хранимые процедуры. Он подобен стандартному компоненту хранимой процедуры (см. гл. Л).

Имя хранимой процедуры определяется свойством

property ProcedureName: WideString;

Для определения входных и выходных параметров используется свойство

property Parameters: TParameters;

Если процедура будет применяться без изменений многократно, имеет смысл заранее подготовить ее выполнение на сервере. Для этого свойству

property Prepared: Boolean;

присваивается значение True.

 

Команды ADO


Команде ADO, которой мы уделяли так много внимания в этой главе в VCL Delphi, соответствует компонент TADOCormand. Методы этого компонента во многом совпадают с методами класса TCustomADODataSet, хотя этот класс не является предком компонента (рис. 19.8). Он предназначен для выполнения команд, которые не возвращают наборы данных.

Рис. 19.8. Иерархия классов компонента TADOCommand

Так как компоненту TADOCommand нет необходимости обеспечивать работу набора записей, его непосредственным предком является класс TComponent. К его функциональности просто добавлен механизм соединения с БД через ADO и средства представления команды.

Команда передается в хранилище данных ADO через собственное соединение или через компонент TAOocormection, аналогично другим компонентам ADO (см. выше).

Текстовое представление выполняемой команды должно содержаться в свойстве

property CommandText: WideString;

Однако команду можно задать и другим способом. Прямая ссылка на нужный объект команды ADO может быть задана свойством

property CommandObject: _Command;

Тип команды определяется свойством

type TCommandType = (cmdUnknown, cmdText, cmdTable, cmdStoredProc, cmdFile, cmdTableDirect);

property CommandType: TCommandType;

Так как тип TCommandType также используется в классе TCustomADODataSet, где необходимо представлять все возможные виды команд, по отношению к компоненту TADOcommand этот тип обладает избыточностью. Здесь нельзя установить значения cmdTable, cmdFile, cmdTableDirect, а тип cmdStoredProc должен обозначать только те хранимые процедуры, которые не возвращают набор данных.

Если команда должна содержать текст запроса SQL, свойство CommandType должно иметь значение cmdText.

Для вызова хранимой процедуры необходимо задать тип cmdStoredProc, a в свойстве CommandText ввести имя процедуры.

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

property Parameters: TParameters;

Выполнение команды осуществляется методом Execute:

function Execute: _RecordSet; overload;

function Execute(const Parameters: OleVariant): _Recordset; 

overload;

 function Execute(var RecordsAffected: Integer; var Parameters: OleVariant; ExecuteOptions: TExecuteOptions = []): _RecordSet; overload;

Разработчик может использовать любую из представленных нотаций перегружаемого метода:

 параметр RecordsAffected возвращает число обработанных записей;  параметр Parameters задает параметры команды;  параметр ExecuteOptions определяет условия выполнения команды:

TExecuteOption = (eoAsyncExecute, eoAsyncFetch, eoAsyncFetchNonBlocking, eoExecuteNoRecords); TExecuteOptions = set of TExecuteOption;

eoAsyncExecute — асинхронное выполнение команды; 

eoAsyncFetch — асинхронная передача данных;

eoAsyncFetchNonBlocking — асинхронная передача данных без блокирования потока;

eoExecuteNoRecords — если команда возвращает набор записей, то они не передаются в компонент.

При работе с компонентом TADOConnection желательно использовать опцию eoExecuteNoRecords.

Для прерывания выполнения команды используется метод

procedure Cancel;

Текущее состояние команды можно определить свойством

type

TObjectState = (stClosed, stOpen, stConnecting, stExecuting, stFetching);

TObjectStates = set of TObjectState; property States: TObjectStates;

 

Объект ошибок ADO


При рассказе о компонентах ADO в данной главе мы довольно часто упоминали об объектах ошибок ADO. Эти объекты содержат информацию об ошибке, возникшей при выполнении операции каким-либо объектом ADO.

В Delphi для объекта ошибки не предусмотрен специальный тип, но разработчик может использовать его методы интерфейса Error, предоставляемого многими методами других объектов ADO. Например, тип

TRecordsetEvent = procedure(DataSet: TCustomADODataSet; const Error: Error;

var EventStatus: TEventStatus) of object;

используемый для метода-обработчика, вызываемого после обновления набора данных, содержит параметр Error, дающий нам искомую ссылку.

Рассмотрим полезные свойства объекта ошибок ADO.

Свойство

property Description: WideString read Get_Description;

возвращает описание ошибки, переданное из объекта, в котором ошибка произошла.

Свойство

property SQLState: WideString read Get_SQLState;

содержит текст команды, вызвавшей ошибку. Свойство

property NativeError: Integer read Get_NativeError;

возвращает код ошибки, переданный из объекта, в котором ошибка произошла.

 

Пример приложения ADO


Теперь попробуем применить на практике представленную в этой главе информацию о реализации ADO в Delphi. В качестве примера создадим простое приложение ADO Demo, которое "умеет" отображать пару таблиц БД, сохранять изменения при помощи групповых операций, сортировать записи и устанавливать фильтры на выбранные записи (рис. 19.9).

Рис. 19.9. Главное окно приложения ADO Demo

В качестве источника данных выберем файлы dBase, имеющиеся в демонстрационной базе данных Delphi \Program Files\Common Files\Borland Shared \Data. Для использования в приложении выберем две таблицы: INDUSTRY и MASTER. Они связаны между собой внешним ключом по полям IND_CODE и INDUSTRY соответственно.

Таблицу INDUSTRY можно редактировать, она инкапсулирована в компоненте tbiIndustry типа TADOTable и отображается в левом компоненте TDBGrid. А таблица MASTER инкапсулирована в компоненте tbIMaster, предназначена только для просмотра. Эти два компонента связаны отношением "ОДИН-КО-МНОГИМ" При помощи свойств MasterSource И MasterFields.

Листинг 19.2. Секция implementation модуля uMain приложения ADO Demo 

implementation

uses IniFiles, FileCtrl;

const slniFileName: String = 'ADODemo.ini';

sEmptyDefDB: String = 'Database path is empty';

sEmptyFilter: String = 'Records for filter is not selected';

{$R *.dfm}

procedure TfmMain.FormShow(Sender: TObject);

 begin

with TIniFile.Create(slniFileName) do

 try

DefDBStr := ReaDString('DefDB', 'DefDBStr1, "); 

edDefDB.Text := DefDBStr;

 finally

Free; end;

SetLength(Bookmarks, 0);

  end;

procedure TfmMain.FormClose(Sender: TObject; var Action: TCloseAction);

 begin

with TIniFile.Create(slniFileName) do

 try

WriteStringCDefDB', 'DefDBStr', edDefDB.Text); 

finally

Free ;

  end; 

end;

procedure TfmMain.sbDefDBClick(Sender: TObject);

 begin if SelectDirectory(DefDBStr, [], 0)

then edDefDB.Text := DefDBStr;

  end;

procedure TfmMain.tbConnectClick(Sender: TObject); 

begin

ADOConn.Close;

ADOConn.DefaultDatabase := ''; if DefDBStr = '' then

 begin

MessageDlg(sEmptyDefDB, mtError, [mbOK], 0);

Abort;

 end 

else 

begin

ADOConn.DefaultDatabase := DefDBStr;

ADOConn.Open; 

end;

  end;

procedure TfmMain.tbSaveClick(Sender: TObject);

 begin

tbllndustry.UpdateBatch(); 

end;

procedure TfmMain.tbFilterClick(Sender: TObject); 

var i: Integer;

 begin

if dbglndustry.SelectedRows.Count > 0 then

 begin

SetLength(Bookmarks, dbglndustry.SelectedRows.Count);

 for i := 0 to dbglndustry.SelectedRows.Count — 1 do

 begin

Bookmarks[i].VType := vtPointer;

Bookmarks[i].VPointer := pointer(dbglndustry.SelectedRows[i]);

  end;

tbllndustry.FilterOnBookmarks(Bookmarks); 

end else

MessageDlgtsEmptyFilter, mtWarning, [mbOK], 0); 

end;

procedure TfmMain.tbUnFilterClick(Sender: TObject); 

begin

tbllndustry.Filtered := False;

dbglndustry.SelectedRows.Clear;

  end;

procedure TfmMain.dbglndustryTitleClick(Column: TColumn); 

begin if tbllndustry.Active then

if (Pos(Column.FieldName, tbllndustry.Sort) > 0

}and(Pos('ASC', tbllndustry.Sort) > 0)

then tbllndustry.Sort := Column.FieldName + ' DESC' else tbllndustry.Sort := Column.FieldName + ' ASC';

  end;

procedure TfmMain.ADOConnAfterConnect(Sender: TObject);

var i: Integer;

begin

for i := 0 to adoConn.DataSetCount - 1 do ADOConn.DataSets [i] .Open/end;

procedure TfmMain.ADOConnBeforeDisconnect(Sender: TObject);

var i: Integer;

begin

for i := 0 to adoConn.DataSetCount — 1 do ADOConn.DataSets[i].Close;

  end;

end.

 

Соединение с источником данных


Для связывания приложения с источником данных используем компонент TADOConnection и настроим соединения, щелкнув на кнопке свойства connectionstring в Инспекторе объектов.

Перейдя в редактор Data Link Properties, выберем провайдер Microsoft OLE DB Provider for OLE DB Drivers (см. рис. 19.3). Как правило, он имеется в операционной системе, если вы не предпринимали специальных усилий по его удалению.

Далее, на странице Connection (см. рис. 19.4) выберем радиокнопку Use data source name и в списке — файлы dBase. Для создания соединения с провайдером ODBC этого вполне достаточно.

Прокомментируем другие свойства компонента соединения ADO.

Свойство LoginPrompt должно иметь значение False, чтобы запретить показ диалога авторизации пользователя, ненужный для файлов dBase.

Свойство DefaultDatabase пока останется пустым. Мы применим его для указания пути к файлам базы данных, используя элементы пользовательского интерфейса приложения.

Свойство CursorLocation имеет значение ciuseclient, чтобы обеспечить использование курсоров наборов данных на стороне клиента.

Свойство ConnectOptions имеет значение по умолчанию coConnectUnspecified.

Это означает, что все команды будут выполняться синхронно — соединение будет ожидать ответ на каждую команду.

Для свойства Mode установим значение cmShareDenyNone, что запрещает другим соединениям устанавливать любые ограничения — ведь в данном случае мы не планируем многопользовательскую работу с источником данных.

Для открытия соединения после запуска приложения необходимо задать путь к хранилищу данных. Для этого предназначена кнопка и однострочный редактор на Панели управления. После выбора пути его значение заносится в переменную DefDBStr и в текст редактора edoefDB. Переменная используется для установления соединения. Для включения соединения необходимо нажать кнопку tbconnect. Ее метод-обработчик проверяет состояние переменной DefDBStr и заполняет свойство DefaultDatabase компонента соединения.

Примечание 

Так как во время настройки соединения выше мы не задавали путь к хранилищу данных, то свойство DefauitDatabase сработает. Иначе его значение будет перекрыто настройками свойства connectionString.

Открытие наборов данных ADO в приложении выполняется в методе-обработчике ADOConnAfterConnect, который вызывается после полного открытия соединения. Аналогичным образом наборы данных закрываются перед закрытием соединения в методе-обработчике ADOconnBeforeDisconnect.

Текущее значение пути к хранилищу данных сохраняется в файле DemoADO.ini и загружается при открытии приложения.

 

Групповые операции


Компонент tbiindustry предназначен для выполнения групповых операций. Поэтому его свойство LociType имеет значение itBatchOptimistic. Для свойства CursorLocation установлено значение ciuseclient, чтобы обеспечить использование набора данных на клиенте. Тип курсора (свойство CursorType) должен быть ctstatic.

Сохранение изменений в хранилище данных обеспечивает метод updateBatch в методе-обработчике нажатия кнопки tbsave.

 

Фильтрация


Для фильтрации записей в наборе данных tbiindustry используется метод FiiterOnBookmark. Пользователь должен выбрать интересующие его записи в компоненте dbgindustry (он работает в режиме dgMuitiSelect). Затем, при нажатии кнопки tbFilter, созданные в свойстве SelectedRows компонента dbgindustry закладки передаются в массив Bookmarks типа TVarRec, который потом передается в качестве параметра метода FilterOnBookmark для фильтрации.

Массив Bookmarks служит здесь лишь промежуточным звеном для приведения типа закладок компонента dbgindustry к параметру метода FilterOnBookmark.

 

Сортировка


Сортировка создана также для набора данных tbiindustry. При щелчке на заголовке колонки компонента dbgindustry вызывается метод-обработчик dbgindustryTitieClick. В нем, в зависимости от текущего состояния свойства сортировки tbiindustry.sort (какое поле сортируется и в каком порядке), задается новое значение свойства sort.

 

Технология ADO обеспечивает универсальный способ




Технология ADO обеспечивает универсальный способ доступа к гетерогенным источникам данных. Благодаря тому, что функции ADO реализованы на основе интерфейсов OLE DB и СОМ, приложению для доступа к данным не требуется дополнительных библиотек, кроме инсталлированного ADO.
Компонент TADOConnection обеспечивает соединение с источниками данных через провайдеры OLE DB. Компоненты TADODataSet, TADOTable, TADOQuery, TADOStoredProc обеспечивают использование наборов записей в приложении. Свойства и методы компонентов позволяют создавать полнофункциональные приложения.
Компонент TADOCommand инкапсулирует текстовую команду ADO. В дополнение к стандартным возможностям работы с данными, из компонентов можно напрямую обращаться к необходимым объектам и интерфейсам ADO.