Глава 16
Набор данных
Любое приложение баз данных должно уметь выполнять как минимум две операции. Во-первых, иметь информацию о местонахождении базы данных, подключаться к ней и считывать имеющуюся в таблицах БД информацию. Приложения баз данных Delphi используют для этих целей возможности процессора БД BDE (гл. 14).
Во-вторых, обеспечивать представление и использование полученных данных. Множество записей одной или нескольких таблиц, переданные в приложение в результате выполнения запроса, будем называть набором данных. Понятно, что в объектно-ориентированной среде для представления какой-либо группы записей приложение должно использовать возможности некоторого класса. Этот класс должен инкапсулировать набор данных и обладать методами для управления записями и полями.
Таким образом, сам набор данных и класс набора данных является той осью, вокруг которой вращается любая деятельность приложения.
Пользователь просматривает на экране данные — это результат использования набора данных.
Пользователь решил изменить какую-то цифру — он изменит содержимое ячейки набора данных.
При закрытии приложение сохраняет все изменения — это набор данных приложения передается в базу данных для сохранения.
Поэтому не удивительно, что разработчики VCL уделили особое внимание созданию максимально эффективной иерархии классов, обеспечивающих использование наборов данных (рис. 16.1).
Класс TDataSet является базовым классом иерархии, он инкапсулирует абстрактный набор данных и реализует максимально общие методы работы с ним. В него можно передать записи из таблицы базы данных или строки из обычного текстового файла — набор данных будет функционировать одинаково хорошо.
Класс TBDEDataSet обеспечивает использование функций BDE при работе с набором данных. Именно этот класс наполняет реальным содержанием методы, реализующие операции управления записями набора данных как частью базы данных.
Класс TDBDataSet обеспечивает доступ к таблицам БД через BDE, получение и передачу данных.
Рис. 16.1. Иерархия классов, обеспечивающих функционирование набора данных
На основе этих классов реализованы специальные компоненты VCL, которые позволяют разработчику легко и просто конструировать приложения баз данных. Это компоненты TTable и TQuery.
К очень важным классам TClientDataSet, TIBCustomDataSet, TCustomADODataSet мы обратимся в следующей части. В этой главе рассматриваются следующие вопросы.
Абстрактный набор данных
В основе иерархии классов, обеспечивающих функционирование наборов данных в приложениях баз данных Delphi, лежит класс TDataSet. Хотя он почти не содержит методов, реально обеспечивающих работоспособность основных механизмов набора данных, тем не менее, его значение трудно переоценить.
Этот класс задает структурную основу функционирования набора данных. Другими словами, это скелет набора данных, к методам которого необходимо лишь добавить требуемые вызовы соответствующих функций BDE. Рассмотрим небольшой пример.
Следующий исходный код описывает метод, отвечающий за сохранение : базе данных изменений, сделанных в наборе данных (модуль db. раs):
procedure TDataSet.Post;
begin UpdateRecord;
case State of daEdit, dslnsert:
begin DataEvent(deCheckBrowseMode, 0);
CheckRequiredFields;
DoBeforePost;
CheckOperation(Internalpost, FOnPostError);
FreeFieldBuffers;
SetState(dsBrowse);
Resync([]);
DoAfterPost;
end;
end;
end;
Метод UpdateRecord обеспечивает передачу в набор данных последних изменений, сделанных в компонентах отображения данных приложения. Если состояние набора данных (см. ниже) позволяет выполнять операцию, осуществляется проверка на значения полей, которые обязательно должны быть заполнены, и вызывается метод-обработчик события BeforePost.
После этого выполняется метод internalpost, который и должен осуществлять передачу изменений в базу данных. В классе TDataSet этот метод является абстрактным. Конкретный механизм фиксации изменений имеется в перекрытом методе internalpost класса TBDEDataSet.
На завершающем этапе осуществляются действия по контролю выполнения операции и синхронизации набора данных с таблицей базы данных после внесения изменений.
Таким образом, для каждой конкретной операции в классе определен алгоритм выполнения последовательности необходимых действий, реализованных в базовых методах. А совокупность свойств и методов класса обеспечивает работу набора данных в целом.
При решении наиболее распространенных задач программирования в процессе создания приложений баз данных класс TDataSet не нужен. Тем не менее, знание основных принципов работы набора данных всегда полезно. Кроме этого, класс TDataSet может использоваться разработчиками в качестве основы для создания собственных компонентов. Поэтому рассмотрим основные механизмы, реализованные в наборе данных.
Поля
Как известно, набор данных представляет собой двумерную таблицу данных. Строки соответствуют записям, столбцы — полям таблицы БД. При этом поля составляют структуру данных таблицы и играют важнейшую роль в работе набора данных.
Для представления полей в наборе данных используется очень важное свойство Fields. Именно к этому свойству обращается разработчик при необходимости прочитать или записать значение какого-либо поля. Это переменная объектного типа. Для инкапсуляции списка полей набора данных предназначен специальный класс TFieid (гл. 17).
Для каждого поля в наборе данных хранится текущее значение, так как ввиду больших размеров таблиц БД размещение в адресном пространстве всей информации из таблицы не представляется возможным. Для хранения текущих значений полей используется специальный буфер. Все текущие значения полей составляют текущую запись набора данных.
Каждому полю соответствует специальный объект TFieidDef, в котором содержится описание основных параметров поля.
Редактирование данных
Механизм редактирования данных использует описанный выше буфер текущих значений полей и ряд методов, выполняющих основные операции с текущими значениями полей и текущей записью целиком.
Для отдельного поля значение можно изменить, обнулить или восстановить.
Записи можно добавлять в конец набора данных, вставлять на место текущей записи (при этом старая запись смешается на одну позицию к концу набора данных), удалять.
Применительно к любым операциям редактирования данных могут выполняться методы post и cancel, post обеспечивает передачу сделанных в текущей записи изменений в таблицу БД. Cancel отменяет все сделанные с момента последнего сохранения изменения и восстанавливает первоначальные значения полей текущей записи.
Метод post может вызываться разработчиком при необходимости и вызывается автоматически при переходе к другой записи.
Навигация по набору данных
Перемещение по записям набора данных осуществляется при помощи группы специальных методов. Физически переход на новую запись означает очистку буфера текущей записи (см. выше) и загрузку в него новых значений полей из требуемой записи. С текущей записью набора данных связано понятие курсора набора данных.
В основе методов навигации по набору данных лежит метод MoveBy, который обеспечивает перемещение на число записей, переданных в параметре.
function TDataSet.MoveBy(Distance: Integer): Integer;
Положительное число означает перемещение к концу набора данных, отрицательное — к началу. Например, перемещение на одну запись назад осуществляется методом prior:
procedure TDataSet.Prior;
begin
MoveBy(-l);
end;
Перемещение на одну запись вперед и назад, переход на первую и последнюю записи являются стандартными операциями навигации по набору данных. Работа компонента TDBNavigator основана на использовании этих методов набора данных.
Состояния набора данных
В зависимости от выполняемой операции, набор данных может находиться в одном из нескольких состояний. Например, при перемещении по записям набор данных находится в состоянии просмотра. Подробно состояния обсуждаются ниже. Сейчас остановимся только на механизме определения состояния, который реализован в классе TDataSet.
Практически в каждом методе класса проводится первоначальная проверка на текущее состояние набора данных. Для этого используется процедура CheckBrowseMode или свойство State типа TDataSetState (см. исходный код
метода post выше). А после выполнения операции, при необходимости, значение свойства State изменяется в соответствии с выполненными действиями.
Поиск данных
Поиск записей одного или нескольких полей, которые содержат требуемые значения, является одной из самых распространенных операций, выполняемых в наборе данных. Для поиска записей в классе созданы заготовки для методов Lookup и Locate. Реальный механизм поиска реализован при помощи функций BDE в классах-потомках.
При этом поиск может осуществляться как по полям вторичного индекса, что обеспечивает быстрый поиск, так и по произвольным полям, что обеспечивает более гибкий критерий отбора.
Фильтры
Механизм фильтрации позволяет разработчику использовать при работе с набором данных возможности запросов SQL, отбирая в набор -только те записи, которые соответствуют заданному критерию.
Текст фильтра задается свойством Filter, синтаксис фильтра похож на запрос SQL, но обладает меньшими возможностями, позволяя отбирать записи только по конкретным значениям полей.
Закладки
Закладки позволяют отмечать необходимые записи набора данных для быстрого возвращения к ним в дальнейшем. В классе TDataSet этот механизм доведен до высокой степени детализации, единственное, что пока не создано — это поиск отмеченной закладкой записи (он осуществляется средствами BDE).
Класс TDataSet
В завершение обсуждения абстрактного набора данных приведем справочную информацию об основных свойствах и методах класса TDataSet. Этот класс происходит напрямую от класса TComponent и описывает независимый от BDE реляционный набор данных.
Так как класс TDataSet является самым первым в иерархии (см. рис. 16.1), то его основное назначение — создать базовую платформу свойств и методов для дальнейшего наследования. Поэтому многие методы в нем являются виртуальными или абстрактными (табл. 16.1).
Таблица 16.1. Свойства и методы класса TDataSet
Объявление |
Тип |
Описание |
Свойства |
||
property Active: Boolean; |
Pu |
Открывает набор данных. Значение свойства можно изменять, но лучше использовать для этого методы Ореn и Сlose |
property AggFields: Tfields; |
Pu |
Содержит список агрегатных полей набора данных (подробнее о таких полях см. главу 18) |
property AutoCalcFields: Boolean; |
Pu |
Сигнализирует о выполнении метода-обработчика OnCalcFields |
property BlockReadSize: Integer; |
Pu
|
Определяет размер буфера записей. Если значение свойства больше нуля, то при навигации по набору данных перемещение курсора осуществляется сразу на столько записей, сколько перемещается в буфер. В этом режиме данные не отображаются в элементах управления |
property Bof: Boolean; |
Pu, Ro
|
При значении True сообщает о том, что курсор расположен на первой записи набора данных |
type TBookniarkStr: string; |
Pu |
Содержит текущую закладку на произвольную запись набора данных |
property Bookmark: TBookmarkStr; |
|
|
property CanModify: Boolean; |
Ro |
При значении True говорит о том, что набор данных можно редактировать и изменения будут сохранены в базе данных |
property DataSetField: TDataSetField; |
Pu |
Поле, которое представляет собой присоединенный набор данных |
property DataSource: TDataSource; |
Pu, Ro |
Содержит указатель на связанный с набором данных компонент TdataSource |
property DefaultFields: Boolean; |
Pu, Ro |
Значение True говорит о том, что набор данных использует динамические объекты полей. При значении False объекты полей являются статическими |
property Designer: TdataSetDesigner; |
Pu, Ro |
Экземпляр класса, который представляет собой редактор полей. Существование экземпляра говорит о том, что структура набора данных редактируется |
property Eof: Boolean; |
Pu, Ro |
При значении True сообщает о том, что курсор расположен на последней записи набора данных |
property FieldCount: Integers- |
Pu, Ro |
Возвращает число полей в структуре набора данных |
property FieldDefLi5t: TFieldDefList; |
Pu, Ro |
Указатель на экземпляр класса, представляющего собой список параметров полей набора данных |
property FieldDefs: TFieldDefs; |
Pu |
Набор параметров полей набора данных |
property FieldList: TFieldList; |
Pu, Ro |
Указатель на экземпляр класса, представляющего собой список объектов полей набора данных |
property Fields: TFields; |
Pu, Ro |
Список полей набора данных. Тип списка (последовательный или иерархический) зависит от значения свойства objectView |
property FieldValues [const FieldName: string]: Variant; |
Pu |
Дает доступ на чтение и запись к значениям всех полей текущей записи набора данных. Идентификация поля осуществляется по его имени, передаваемого в параметре FieldName |
property Filter: string; |
Pu |
Содержит строку фильтра для набора данных (гл. 18) |
property Filtered: Boolean; |
Pu |
Разрешает или запрещает использование фильтра для набора данных |
property FilterOptions: TFilterOptions; |
Pu |
Задает параметры фильтрации набора данных |
property Found: Boolean; |
Pu, Ro |
Возвращает True или False в зависимости от результата выполнения методов поиска по набору данных(FindFirst,FindLast, FindNext, FindPrior) |
property Modified: Boolean; |
Pu, Ro |
При значении True сообщает о том, что текущая запись была изменена |
property ObjectView: Boolean- |
Pu |
При значении True поля в свойстве Fields структурированы иерархически, в противном случае — последовательно (гл. 17) |
property RecordCount: Integer; |
Pu |
Возвращает общее число записей в наборе данных |
property RecNo: Integer; |
Pu |
Возвращает порядковый номер текущей записи |
property RecordSize: Word; |
Pu |
Возвращает размер одной записи |
property SparseArrays: Boolean; |
Ro |
При значении True определяет, что для каждого поля в массиве полей (тип TArrayField) создается отдельный объект |
type TdataSetState = (dslnactive, dsBrowse, dsEdit, dslnsert, dsSetKey, dsCalcFields, dsFilter, dsNewValue, dsOldValue, dsCurValue, dsBlockRead, dsInternalCalc); property State: |
Pu,Ro
|
Содержит информацию о текущем состоянии набора данных
|
Методы unction CompareBookmarks (Bookmarki, Bookmark2: TBookmark): Integer; virtual; |
Pu |
Возвращает 0, если закладки указывают на одну запись. Возвращает -1, если закладка Bookmarki, расположена раньше закладки Bookmark2. Возвращает 1, если закладка Bookmarki расположена позже закладки Bookmark2 |
procedure Close; |
Pu |
Закрывает набор данных. Приводит к изменению свойства Active на False |
procedure ClearFields; |
Pu |
Обнуляет значения полей текущей записи |
procedure CheckBrowseMode; |
Pu |
Осуществляет фиксацию или отмену изменений при смене текущей строки |
procedure Cancel; virtual; |
Pu |
Отменяет любые изменения в текущей записи, если они не были переданы в базу данных |
Function BookmarkValid (Bookmark: TBookmark): Boolean; virtual; |
Pu |
Проверяет существование экземпляра закладки, передаваемого в параметре |
Procedure AppendRecord (const Values: array of const) ; |
Pu |
Добавляет новую запись в конец набора данных. Значения для полей записи передаются в параметрах метода в виде массива констант. Добавленная запись становится текущей |
procedure Append; |
Pu |
Добавляет новую пустую запись в конец набора данных. Добавленная запись становится текущей. |
function ActiveBuffer: PChar; |
Pu |
Возвращает ссылку на буфер активной записи |
function Controls Disabled: Boolean; |
Pu |
Возвращает True, если элементы управления, связанные с набором данных, неактивны. Эту функцию можно использовать при необходимости отключить показ данных при перемещениях по большим массивам записей |
type TBIobStreamMode = (binRead, bmWrite, bmReadWrite) ; function CreateBlobStream (Field: TField; Mode: TBIobStreamMode): TStream; virtual; |
Pu |
Создает специальный поток для работы с данными полей типа BLOB. Перекрывается в классах-потомках, поэтому в TDataSet всегда возвращает Nil. Параметр Mode определяет режим работы потока |
procedure Delete; |
Pu |
Удаляет текущую запись и курсор устанавливается на следующую |
procedure DisableControls; |
Pu |
Переводит элементы управления, связанные с набором данных, в неактивное состояние. Этот метод можно использовать для отключения показа данных при перемещениях по большим массивам записей |
procedure Edit; |
Pu |
Переводит набор данных в режим редактирования |
procedure EnableControls; |
Pu |
Переводит элементы управления, связанные с набором данных, в активное состояние |
function FieldByName (const PieldName: string): TField; |
Pu |
Возвращает экземпляр объекта поля по переданному в параметре FieldName имени поля |
function FindField (const FieldName: string): TField; |
Pu |
Возвращает экземпляр объекта поля по переданному в параметре FieldName имени поля. Используется для проверки существования объекта для нужного поля перед вызовом других методов, обращающихся к объекту поля |
function FindFirst: Boolean; |
Pu |
Устанавливает курсор на первую запись отфильтрованного набора данных. В случае успеха возвращает True |
function FindLast: Boolean; |
Pu |
Устанавливает курсор на последнюю запись отфильтрованного набора данных. В случае успеха возвращает True |
function FindNext: Boolean; |
Pu |
Устанавливает курсор на следующую запись отфильтрованного набора данных. В случае успеха возвращает True |
function FindPrior: Boolean; |
Pu |
Устанавливает курсор на предыдущую запись отфильтрованного набора данных. В случае успеха возвращает True |
procedure First; |
Pu |
Устанавливает курсор на первую запись набора данных |
procedure FreeBookmark (Bookmark: TBookmark); virtual; |
Pu |
Уничтожает экземпляр закладки, передаваемый в параметре Bookmark |
function GetBlobFieldData (FieldNo: Integer; var Buffer: TBIobByteData): Integer; virtual; |
Pu |
Передает данные типа BLOB из поля, определенного порядковым номером FieldNo, в двоичный буфер Buffer. Функция возвращает размер буфера |
function GetBookmark: TBookmark; virtual; |
Pu |
Возвращает экземпляр объекта текущей закладки |
function GetCurrentRecord (Buffer: PChar): Boolean; virtual; |
Pu |
Помещает в двоичный буфер текущую запись набора данных. Этот абстрактный метод всегда возвращает False. Перекрывается в классах-потомках |
procedure GetDetailDataSets (List: TList) ; |
Pu |
Возвращает в параметре List список наборов данных, связанных с данным набором отношением "один ко многим" |
procedure GetDetailLinkFields (MasterFields, DetailFields: TList); virtual; |
Pu |
Заполняет списки MasterFields и DetailFields параметрами полей набора данных, связанных отношением главный-подчиненный |
function GetFieldData (FieldNo: Integer-Buffer: Pointer): Boolean; overload; virtual; |
Pu |
Возвращает значение поля Field в буфер Buffer в том же виде, как оно хранится в буфере текущей записи. |
function GetFieldData (Field: TField; Buffer: Pointer) : Boolean; overload; virtual; |
Pu |
Первое объявление перекрывается в классе TClientDataSet, второе — в классе TBDEDataSet |
procedure GetFieldList (List: TList; const FieldNames: string); |
Pu |
Передает в список List совокупность объектов полей, имена которых заданы параметром FieldNames |
procedure GetFieldNames (Lj,st: TStrings); |
Pu |
Передает в список List все экземпляры объектов полей набора данных |
procedure GotoBookmark (Bookmark: TBookmark); |
Pu |
Устанавливает курсор на запись, на которую указывает закладка, передаваемая в метод параметром Bookmark |
procedure Insert; |
Pu |
Вставляет новую пустую запись на месте курсора |
procedure InsertRecord (const Values: array . of const) ; |
Pu |
Вставляет новую запись на месте курсора. Значения полей передаются в параметре |
function IsEmpty: Boolean; |
Pu |
Возвращает значение True, если набор данных пуст |
function IsLinkedTo (DataSource: TDataSource): Boolean; |
Pu |
Возвращает значение True, если набор данных связан с компонентом, заданным параметром DataSource |
function IsSequenced: Boolean; virtual; |
Pu |
Определяет, поддерживает ли таблица БД нумерацию последовательности записей. В классе TDataSet всегда возвращает True, так как абстрактный набор данных свободен от конкретной реализации БД и всегда нумерует записи |
procedure Last; |
Pu |
Перемещает курсор на последнюю запись набора данных |
function Locate (const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions): Boolean; virtual; |
Pu |
Абстрактный метод, перекрыт в классах-потомках. Осуществляет поиск записи по значениям полей. Возвращает False |
function Lookup (const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant; virtual; |
Pu |
Абстрактный метод, перекрыт в классах-потомках. Осуществляет поиск записи по значениям полей. Возвращает False. В классах-потомках возвращаемое методом значения типа вариант содержит значения полей найденной записи. Поля, значения которых необходимо просмотреть, перечисляются через запятую в параметре ResultFields |
function MoveBy (Distance: Integer): Integers- |
Pu |
Перемещает курсор на Distance записей по набору данных. Если передаваемый параметр больше нуля, то производится перемещение к концу набора данных, если меньше нуля — к началу. Возвращает число записей, на которые был перемещен курвор |
procedure Next; |
Pu |
Передвигает курсор на следующую запись в наборе данных |
procedure Open; |
Pu |
Открывает набор данных. После этого свойство Active устанавливается равным True |
procedure Post;virtual; |
Pu |
Передает все изменения в текущей записи в базу данных |
procedure Prior; |
Pu |
Перемещает курсор на одну запись назад, к началу набора данных |
procedure Refresh; |
Pu |
Восстанавливает изменения в текущей строке, которые были сделаны со времени последнего выполнения метода Post |
procedure SetFields (const Values: array of const) ; |
Pu |
Изменяет значения всех полей в текущей записи. Значения передаются в параметре Values в порядке следования полей в структуре набора данных |
function Translate (Src/ Dest: PChar; ToOem: Boolean): Integer; virtual; |
Pu |
Переносит строку данных из Src в Dest. Если параметр ToOem имеет значение True, то при переносе выполняется преобразование строки из формата ANSI в OEM |
TUpdateStatus = (usUnmodified/ usModified/ usinserted/ usDeleted); |
Pu |
Возвращает состояние текущей строки. Используется в режиме CachedUpdates набора данных |
function UpdateStatus: TUpdateStatus; virtual; |
Pu
|
|
Методы-обработчики событий |
||
property AfterCancel: TdataSetNotifyEvent; |
Pu |
Вызывается после отмены изменений в текущей записи |
property AfterClose: TdataSetNotifyEvent; |
Pu |
Вызывается после закрытия набора данных |
property AfterDelete: TdataSetNotifyEvent; |
Pu |
Вызывается после удаления записи |
property AfterEdit: TdataSetNotifyEvent; |
Pu |
Вызывается после начала редактирования данных |
property Afterlnsert: TdataSetNotifyEvent; |
Pu |
Вызывается после вставки новой записи |
property AfterPost: TdataSetNotifyEvent; |
Pu |
Вызывается после записи сделанных изменений в базу данных |
property AfterRefresh: TDataSetNotifyEvent; |
Pu |
Выполняется после обновления набора данных (после вызова метода Refresh) |
property AfterScroll: TDataSetNotifyEvent; |
Pu |
Вызывается после перемещения курсора по набору данных |
property BeforeCancel: TDataSetNotifyEvent; |
Pu |
Вызывается перед отменой изменений в текущей записи |
property BeforeClose: TDataSetNotifyEvent; |
Pu |
Вызывается перед закрытием набора данных |
property BeforeDelete: TDataSetNotifyEvent; |
Pu |
Вызывается перед удалением записи |
property BeforeEdit: TDataSetNotifyEvent; |
Pu |
Вызывается перед началом редактирования данных |
property Beforelnsert: TDataSetNotifyEvent; |
Pu |
Вызывается перед вставкой новой записи |
property BeforeOpen: TDataSetNotifyEvent; |
Pu |
Вызывается перед открытием набора данных |
property BeforePost: TDataSetNotifyEvent; |
Pu |
Вызывается перед записью сделанных изменений в базу данных |
property BeforeScroll: TDataSetNotifyEvent; |
Pu |
Вызывается перед перемещением курсора по набору данных |
property OnCalcFields: TDataSetNotifyEvent; |
Pu |
Используется для расчета значений вычисляемых полей (гл. 17) |
type TDataAction = (daFail, daAbort, daRetry) ; |
Pu |
Вызывается при возникновении ошибки удаления записи |
TDataSetErrorEvent = procedure (DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction) of object; |
|
|
property OnDeleteError: TDataSetErrorEvent; |
|
|
type TDataAction = (daFail, daAbort, daRetry) ; |
Pu |
Вызывается при возникновении ошибки редактирования данных |
TDataSetErrorEvent = procedure (DataSet: TDataSet; E: EDatabaseError;var Action: TdataAction) of object; |
|
|
property OnEditError: TDataSetErrorEvent ; |
|
|
type TFilterRecordEvent = procedure (DataSet: TDataSet; var Accept: Boolean) of object- |
Pu |
Вызывается при попадании курсора на запись, соответствующую текущему фильтру набора данных |
property OnFilterRecord: |
|
|
TfilterRecordEvent; |
|
|
property OnNewRecord: TdataSetNotifyEvent; |
Pu |
Вызывается при создании новой записи |
type TDataAction = (daFail, daAbort, daRetry) ; |
Pu |
Вызывается при возникновении ошибки во время записи в базу данных |
TdataSetErrorEvent = procedure (DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction) of object; |
|
|
property OnPostError: TDataSetErrorEvent; |
|
|
Набор данных, связанный с BDE
На следующих после класса TDataSet уровнях иерархии классов, обеспечивающих доступ к данным (рис. 16.1), находятся классы TBDEDataSet и TDBDataSet. Они обеспечивают реальное взаимодействие набора данных с таблицей БД. Те методы, которые в классе TDataSet являются абстрактными или пустыми, в рассматриваемых здесь классах реализуют свою функциональность за счет обращения к возможностям процессора баз данных BDE.
Классы TBDEDataSet и TDBDataSet обеспечивают работоспособность основных компонентов доступа. К данным — TTable, TQuery, TStoredProc.
Класс TBDEDataSet
Этот класс является потомком класса TDataSet, его значение трудно переоценить: именно TBDEDataSet обеспечивает работоспособность важнейших механизмов набора данных за счет обращения к функциям BDE. В частности, класс TBDEDataSet перекрывает абстрактные методы своего предка TDataSet, отвечающие за такие важнейшие операции, как чтение данных и сохранение изменений в базе данных, навигация по записям набора данных, фильтрация (табл. 16.2).
Напомним, что все эти механизмы не созданы с нуля, а только дополнены обращениями к функциям BDE в необходимых местах методов, изначально описанных в классе TDataSet. Например, для обеспечения фильтрации записей набора данных к классу добавлено новое свойство:
type
TFilterOption = (foCaseInsensitive, foNoPartialCompare);
TFilterOptions = set of TFilterOption;
property FilterOptions: TFilterOptions;
Оно определяет дополнительные параметры отбора записей по фильтру (чувствительность к регистру символов и отбор по текстовому шаблону).
Дополнительно к существовавшим добавлен механизм кэширования изменений. Теперь все вносимые пользователем изменения могут накапливаться в специальном буфере, а их передачей в базу данных можно управлять.
Эта возможность очень полезна при создании клиентских приложений в архитектуре клиент/сервер и играет ключевую роль при обеспечении возможности редактирования наборов данных сложных запросов SQL.
Дополнительно к методам работы с полями класса TDataSet добавлены функции использования полей в формате BLOB и включения ограничений по значениям (гл. 17).
Для обеспечения использования функций API BDE на программном уровне добавлено свойство, содержащее дескриптор курсора, соответствующего текущей записи набора данных.
Также класс обеспечивает возможность программного управления вторичными индексами набора данных в зависимости от типа таблицы базы данных.
Таблица 16.2. Свойства и методы класса TBDEDataSet
Объявление |
Тип |
Описание |
Свойства |
||
property BlockReadSize: Integer; |
Pu |
Определяет размер буфера при блочном чтении данных. Такой режим используется при необходимости быстрого перемещения по большим массивам данных. Если значение свойства больше нуля, навигация по набору данных осуществляется без изменения состояния компонентов отображения данных и вызова методов-обработчиков событий |
property CacheBlobs: Boolean; |
Pu |
Разрешает использование буфера в памяти для данных BLOB |
property CachedUpdates: Boolean; |
Pb |
Включает или отключает режим кэширования изменений в наборе данных. Часто используется в клиентских приложениях архитектуры клиент/сервер |
property CanModify: Boolean; |
Pu, Ro |
Если набор данных позволяет делать изменения, свойство возвращает True, иначе возвращается False |
property Explndex: Boolean; |
Pu, Ro |
Показывает, используются ли в наборе данных индексы dBASE |
property Filter: string; |
Pb, Ro |
Содержит выражение для фильтра набора данных |
property Filtered: Boolean; |
Pb, Ro |
Управляет включением фильтра набора данных |
TFilterOption = (foCaseInsensitive, foNoPartialCompare); property FilterOptions: TFilterOptions;
|
Pb |
Определяет параметры фильтра. FoCaseInsensitive — строковые значения фильтруются с учетом регистра FoNoPartialCompare — при фильтрации символ "*" рассматривается как обычный символ, иначе он означает, что на этом месте может находиться произвольное подмножество любых символов |
type HDBICur: Longint; property Handle: HDBICur; |
Pu, RO |
Указатель на курсор BDE, связанный с текущей записью набора данных |
property KeySize: Word; |
Pu, Ro |
Содержит размер ключа для текущего индекса набора данных |
type TLocale: Pointer; property Locale: TLocale; |
Pu, Ro |
Указатель на языковый драйвер BDE |
property RecNo: Longint; |
Pu |
Номер текущей записи набора данных |
property RecordCount: Longint; |
Ro |
Содержит число записей в наборе данных |
property RecordSize: Word; |
Ro |
Содержит размер одной записи набора данных |
property UpdateObject: TDataSetUpdateObject; |
Pu |
Экземпляр объекта TUpdateObject, используемого при кэшировании изменений |
type TUpdateRecordTypes = set of (rtModified, rtlnserted, rtDeleted, rtUnmodified) ;
|
Pu
|
Определяет видимость записей в режиме кэширования изменений в зависимости от их состояния. RtUnmodified — доступны не модифицированные записи. |
property UpdatesPending: Boolean; |
Ro |
Значение True говорит о том, что буфер изменений при кэшировании содержит не сохраненные на сервере изменения |
Методы |
||
procedure ApplyUpdates; |
Pu |
Записывает изменения из буфера в базу данных в режиме кэширования |
function BookmarkValid (Bookmark: TBookmark): Boolean; override; |
Pu |
Проверяет существование экземпляра закладки, передаваемого в параметре Bookmark |
procedure Cancel; |
Pu |
Отменяет все изменения, сделанные в текущей записи |
procedure CancelUpdates; |
Pu |
Отменяет все изменения, сделанные с момента последней записи в базу данных и очищает буфер в режиме кэширования |
procedure CommitUpdates ; |
Pu |
Очищает буфер изменений в режиме кэширования |
function CompareBookmarks (Bookmarki, Bookmark2: TBookmark): Integer; |
Pu |
Проверяет идентичность закладок, указанных в параметрах Bookmarki и Bookmark2. Значение 1 сигнализирует о наличии отличий в двух закладках |
property UpdateRecordTypes: TUpdateRecordTypes; |
RtModified — доступны измененные записи Rtlnserted — доступны добавленные записи RtDeleted — доступны удаленные записи |
|
function ConstraintCallBack (Req: DsInfoReq; var ADataSources: DataSources): DBIResult; stdcall; |
Pu |
Обеспечивает доступ к функциям ограничения данных API BDE |
Function Constraints Disabled: Boolean; |
Pu |
Показывает, включены или отключены ограничения данных |
function CreateBlobStream (Field: TField; Mode: TbIobStreamMode): TStream; override; |
Pu |
Создает поток для чтения/записи данных BLOB |
Procedure DisableConstraints; |
Pu |
Отключает ограничения данных |
procedure |
Pu |
Включает ограничения данных |
EnableConstraints; |
|
|
procedure FetchAll; |
Pu |
Переносит все изменения из буфера и восстанавливает все записи от текущей позиции до конца набора данных |
procedure FlushBuffers; |
Pu |
Передает в базу данных все изменения из буфера записи |
Function GetBlobFieldData (FieldNo: Integer; var Buffer: TbIobByteData): Integer; override; |
Pu |
Читает все данные BLOB из поля FieldNo в буфер Buffer |
function GetCurrentRecord (Buffer: PChar),: Boolean; |
Pu |
Помещает текущую строку в буфер |
function IsSequenced: Boolean; override; |
Pu |
Определяет, поддерживает ли таблица БД нумерацию последовательности записей. В классе TDataSei всегда возвращает True, так как абстрактный набор данных свободен от конкретной реализации БД и всегда нумерует записи |
function Locate (const KeyFields: string; const KeyValues: Variant; Options: TlocateOptions): Boolean; |
Pu |
Осуществляет поиск в наборе данных. Параметр KeyFields содержит список полей, по которым ведется поиск. Параметр KeyValues содержит значения полей для поиска. Параметр Options определяет условия поиска. Если запись найдена, курсор набора данных устанавливается на эту запись и возвращается True |
function Lookup (const Pu KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant; |
Pu |
Осуществляет поиск в наборе данных. Возвращает массив значений требуемых полей найденной записи. Параметры аналогичны методу Locate |
procedure Post; override; |
Pu |
Пересылает сделанные в текущей записи изменения в базу данных |
procedure RevertRecord; |
Pu |
Отменяет все изменения в текущей строке при работающем буфере изменений |
procedure Translate (Src, Dest: PChar; ToOem: Boolean) ;overrider; |
Pu |
Форматирует текст. Если параметр ToOem =True, текст Src в формате ANSI переводится в текст Dest в формате OEM и наоборот |
function UpdateStatus: TUpdateStatus; |
Pu |
Возвращает тип сохраняемых в буфере из менений данных (табл. 16.1) |
Методы-обработчики событий |
||
TUpdateAction = Pu(uaFail, uaAbort, uaSkip, uaRetry, uaApplied) ; |
Pu |
Вызывается при возникновении ошибки переноса кэшированных в буфере изменений в таблицу базы данных |
TUpdateErrorEvent = procedure (DataSet: TDataSet; E: EDatabaseError; UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction) of Object; |
||
property OnUpdateError: TUpdateErrorEvent; |
Класс TDBDataSet
Класс TDBDataSet является непосредственным предком основных компонентов доступа к данным TTable, TQuery и TStoredProc. Новые свойства и методы класса обеспечивают соединение набора данных с базой данных и используют функции BDE (табл. 16.3).
В процессе соединения важнейшую роль играет свойство DatabaseName, которое должно содержать псевдоним или полный путь к файлам БД. Для управления отдельным соединением с базой данных можно применять специальный компонент TDatabase. Указатель на экземпляр такого компонента содержится в свойстве Database.
Многие функции API BDE используют в своей работе дескриптор специальной структуры, описывающей подключенную базу данных. Доступ к этому дескриптору можно получить через свойство DBHandle.
Приложение баз данных одновременно может использовать несколько наборов данных, каждый из которых подключен к собственной базе данных. Совокупность соединений управляется в рамках сеанса работы, который инкапсулируется компонентом TSession. Указатель на экземпляр такого компонента можно использовать в наборе данных при помощи свойства DBSession.
Для работы с удаленными серверами в класс введено свойство provider, обеспечивающее доступ к интерфейсу iprovider.
Таблица 16.3 Свойства и методу класса TDBDataSet
Объявление |
Тип |
Описание |
Свойства |
||
property AutoRefresh: Boolean; |
Pb |
При значении True все автоматически создаваемые значения полей (автоинкрементные, значения по умолчанию) обновляются автоматически |
property Database: TDatabase; |
Pu, Ro |
Указатель связанного с набором данных компонента TDatabase |
property DatabaseName: string; |
Pu, Pb |
Псевдоним базы данных |
type HDBISES: Longint; |
Pu, |
Дескриптор базы данных. Используется при |
property DBHandle: HDBISES; |
Ro |
работе с API BDE |
type TLocale: Pointer; property DBLocale: TLocale; |
Pu, Ro |
Идентифицирует языковый драйвер API BDE |
property DBSession: TSession |
Pu, Ro |
Указатель для компонента TSession, с которым работает набор данных |
property Provider: IProvider; |
Pu, Ro |
Идентифицирует интерфейс IProvider |
property SessionName: string; |
Pu, Ro |
Содержит имя компонента сеанса, в котором работает набор данных |
Методы |
||
function CheckOpen (Status: DBIResult): Boolean; |
Pu |
Возвращает результат вызова BDE. Используется для тестирования соединения |
procedure CloseDatabase (Database: TDatabase); |
Pu |
Закрывает связь с базой данных, определяемой параметром Database |
Procedure Get ProviderAttributes (List: TList) ; override; |
Pu |
Возвращает в списке List параметры языкового драйвера |
function OpenDatabase: TDatabase; |
Pu |
Открывает связь с базой данных, определяемой свойством DatabaseName |
Компонент ТТаblе
Компонент TTable является конечным звеном в иерархии классов доступа к данным, его свойства и методы описывают таблицу реляционной базы данных, причем независимо от типа базы данных. Для доступа к данным компонент использует функции BDE (см. выше).
Необходимая для работы база данных задается свойством DatabaseName, в котором можно указать зарегистрированный в BDE псевдоним БД или полный путь к файлам БД (табл. 16.4).
Таблица БД, на основе которой создается набор данных, определяется свойством TableName. При необходимости тип таблицы задается свойством TableType, хотя обычно это свойство имеет значение ttDefault (табл. 16.4), которое включает автоматическое определение типа таблицы по расширению файла.
Свойство TabieType работает только в локальных БД. Обратите внимание, что возможные значения свойства соответствуют основным типам локальных драйверов BDE.
При помощи методов Open и close набор данных открывается и закрывается. О его состоянии можно судить по значению свойства Active. Более подробно о состоянии набора данных расскажет свойство State (см. ниже).
Открывать и закрывать набор данных можно и простым присваиванием значения свойству Active. Но, согласно негласным правилам ООП, любые действия должны выполняться методами.
Записи в набор данных можно отбирать при помощи свойств Filter, Filtered, FilterOptions, создающих фильтр, ограничивающий набор данных по значениям данных в одном или нескольких полях.
Методы.
SetRangeStart, SetRangeEnd, SetRange, ApplyRange,
EditRangeStart, EditRange
End
создают специальный диапазон включаемых в набор данных записей, отбор в диапазон проводится по задаваемым граничным значениям любых полей набора данных.
Поиск нужной записи можно осуществлять методами Lookup или Locate (достаточно просто, но не очень быстро) или используя существующие в таблице базы данных индексы — методом FindKey (сложнее, но очень быстро).
От предков компонент унаследовал инструменты для работы с закладками. Это свойство Bookmark и методы GetBookmark, FreeBookmark, GotoBookmark.
Работа с полями осуществляется целой группой свойств и методов, среди которых особое место занимает свойство Fields, (см. таблицу 16.1) представляющее собой индексированный список всех полей набора данных. Это свойство удобно использовать в процессе разработки для организации доступа к полям.
Использование индексов обеспечено свойствами indexName, indexFields, IndexFieldNames,IndexFiles.
Свойства MasterSource, MasterField, IndexName дают возможность установить отношение типа главный-подчиненный с другой таблицей.
Очень полезны в практическом использовании методы и свойства для работы С буфером изменений (свойства CachedUpdates, PendingUpdates, UpdateRecordTypes, методы ApplyUpdates, CancelUpdates, CommitUpdate, RevertRecord). Буфер используется в клиентских приложениях многоуровневых систем доступа к данным.
От классов TDataSet и TBDEDataSet унаследован обширный набор методов-обработчиков событий, позволяющий решать любые задачи по управлению набором данных.
Ниже приведена справочная информация о свойствах и методах компонента TTable. После этого рассматриваются подробности применения основных механизмов набора данных.
Таблица 16.4. Свойства и методы класса TTable
Объявление |
Тип |
Описание |
|||
Свойства |
|||||
property DataSource: TDataSource; |
Pu, Ro |
Ссылается на компонент TDataSource главного набора данных в отношении главный/подчиненный |
|||
property Defaultlndex: Boolean; |
Pb |
Управляет сортировкой данных. При значении True записи упорядочиваются по первичному ключу. При значении False упорядочивание не производится |
|||
property Exclusive: Boolean; |
Pb |
Ограничивает доступ к таблице. При значении True с таблицей может работать только одно приложение. Это свойство важно при одновременной работе нескольких приложений с данными в локальной сети |
|||
property Exists: Boolean; |
Pu, Ro |
Значение True говорит о том, что связанная с компонентом таблица базы данных существует |
|||
property IndexDefs: TindexDefs; |
Pb |
Содержит информацию об индексах таблицы |
|||
property IndexFieldCount: Integer; |
Pu, Ro |
Возвращает число полей в текущем индексе таблицы |
|||
property IndexFieldNames: string; |
Pb |
Разделенный запятыми список названий полей, составляющих текущий индекс. Используется для таблиц серверов SQL |
|||
property IndexFields: [Index: Integer]: TField; |
Pu |
Индексированный список полей текущего индекса |
|||
property IndexFiles: TStrings; |
Pb |
Список индексных файлов для таблиц dBASE |
|||
property IndexName: string; |
Pb |
Определяет вторичный индекс для таблицы. Используется для таблиц локальных СУБД |
|||
property KeyExclusive: Boolean; |
Pu |
Управляет границами диапазона, задаваемого методом setRange. При значении True крайние записи в диапазон не включаются |
|||
property KeyFieldCount: Integer; |
Pu |
Содержит число полей ключа, используемых при поиске. При значении 0 используется только первое поле, при значении 1 используются два первых поля и т. д. По умолчанию устанавливается полное число полей ключа |
|||
property MasterFields: string; |
Pb |
Список имен полей главной таблицы, разделенных запятой, используемых при создании отношения главный/подчиненный |
|||
property MasterSource: TdataSource; |
Pb |
Содержит имя компонента TDataSource, связанного с набором данных, который является главным в отношении главный/ подчиненный |
|||
Property Readonly: Boolean; |
Pb |
Включает и отключает режим "только для чтения". В некоторых случаях набор данных можно открыть только в этом режиме |
|||
Property StoreDefs: Boolean; |
Pb |
При значении True все сведения об индексах и структуре таблицы хранятся вместе с формой или модулем данных. В этом случае при создании набора данных одновременно создаются поля, индексы, ограничения |
|||
Property TableLevel: Integer; |
Pu |
Содержит значение уровня таблицы, используемого в драйвере BDE |
|||
Property TableName: TfileName; |
Pb |
Определяет имя таблицы |
|||
type TtableType = (ttDefault, ttParadox, ttDBase, ttASCII, ttFoxPro) ; |
Pb |
Определяет тип таблицы для стандартного драйвера BDE. Значение ttDefault означает, что тип таблицы определяется по расширению файла |
|||
property TableType: TTableType; |
|
|
|||
procedure Addlndex (const Name, Fields: |
Pu |
Создает новый индекс. Параметр Name определяет имя нового индекса, параметр |
|||
string; Options: TIndexOptions) ; |
|
Fields — список полей индекса через запятую, параметр Options задает тип индекса |
|||
procedure ApplyRange; |
Pu |
Включает в работу границы диапазона, заданные методами SetRangeStart, SetRangeEnd или EditRangeStart, EditRangeEn |
|||
type TBatchMode = (batAppend, batUpdate, batAppendUpdate, batDelete, batCopy) ; function BatchMove (ASource: TBDEDataSet; AMode: TBatchMode): Longint; |
Pu
|
Переносит записи из таблицы ASource в набор данных. Тип операции задается параметром AMode. Возвращает число обработанных записей
|
|||
procedure CancelRange; |
Pu |
Удаляет текущий диапазон |
|||
procedure CloseIndexFile (const IndexFileName: string); |
Pu
|
Закрывает индексный файл для таблиц dBASE
|
|||
procedure CreateTable;
|
Pu |
Создает новую таблицу, основываясь на данных о структуре таблицы, содержащихся в свойствах FieldDefs и indexDefs. Если свойство FieldDefs пустое, используется свойство Fields. Структура и данные существующей таблицы перезаписываются |
|||
procedure Deletelndex (const Name: strings; |
Pu |
Удаляет вторичный индекс |
|||
procedure DeleteTable; |
Pu |
Уничтожает таблицу базы данных. Набор данных при этом должен быть закрыт |
|||
procedure EditKey; |
Pu |
Переводит набор данных в режим редактирования буфера поиска. После использования этого метода можно изменять значения полей, используемые для поиска записей |
|||
procedure EditRangeEnd; |
Pu |
Разрешает редактирование нижней границы диапазона |
|||
Procedure EditRangeStart ; |
Pu |
Разрешает редактирование верхней границы диапазона |
|||
procedure EmptyTable; |
Pu |
Удаляет все записи из набора данных |
|||
function FindKey (const KeyValues: array of const): Boolean; |
Pu |
Проводит поиск записи, значения полей которой удовлетворяют условиям, заданными параметром KeyValues. Значения разделяются запятыми. Для поиска можно использовать только поля, входящие в текущий индекс. Для локальных стандартных таблиц BDE это, поля, определяемые свойством indexName. Для таблиц серверов SQL индекс можно задать свойством IndexFieldNames. При успешном поиске функция возвращает True |
|||
procedure FindNearest (const KeyValues: array of const) ;
|
Pu |
Проводит поиск записи, значения полей которой, заданные параметром KeyValues, в минимальной степени отличаются от требуемых в большую сторону. Значения для поиска разделяются запятыми. Для поиска можно использовать только поля, входящие в текущий индекс. Для локальных стандартных таблиц BDE это поля, определяемые свойством indexName. Для таблиц серверов SQL индекс можно задать свойством IndexFieidNames. При успешном поиске функция возвращает True |
|||
procedure GetIndexNames (List: TStrings); |
Pu |
Возвращает список индексов таблицы |
|||
procedure GotoCurrent (Table: TTable); |
Pu |
Синхронизирует курсор набора данных с курсором таблицы, заданной параметром Table |
|||
function GotoKey: Boolean; |
Pu |
Устанавливает курсор на запись, соответствующую значениям полей,заданным при последнем применении методов SetKey или EditKey |
|||
procedure GotoNearest; |
Pu |
Устанавливает курсор на запись, точно соответствующую значениям полей, заданным при последнем применении методов SetKey или EditKey, или ближайшую к ним по значениям в большую сторону |
|||
type TLockType = (ItReadLock, ItWriteLock); procedure LockTable (LockType: TLockType); |
Pu |
Закрывает доступ к таблице Paradox или dBASE из других приложений |
|||
procedure OpenIndexFile (const IndexFileName: string) ; |
Pu |
Открывает индексный файл таблицы dBASE |
|||
procedure RenameTable (const NewTableName: string) ; |
Pu |
Переименовывает таблицу Paradox или dBASE |
|||
procedure SetKey; |
Pu |
Очищает буфер поиска. После использования этого метода можно изменять значения полей, используемые для поиска записей |
|||
procedure SetRange (const StartValues, EndValues: array of const) ; |
Pu |
Задает диапазон отбора записей. Параметр StartValues определяет значения полей для верхней границы диапазона. Параметр EndValues определяет значения полей для нижней границы диапазона. Значения диапазона задаются для полей текущего индекса |
|||
procedure SetRangeEnd; |
Pu |
Задает нижнюю границу диапазона. После этого метода необходимо задать значения для полей текущего индекса, которые и будут нижней границей |
|||
procedure SetRangeStart; |
Pu |
Задает верхнюю границу диапазона. После этого метода необходимо задать значения для полей текущего индекса, которые и будут верхней границей |
|||
type TLockType = (ItReadLock, ItWriteLock) ; procedure UnlockTable (LockType: TLockType); |
Pu |
Разблокирует таблицу Paradox или dBASE для доступа из других приложений |
Индексы в наборе данных
Важнейшей проблемой для любой БД является достижение максимальной производительности и ее сохранение при дальнейшем увеличении объемов хранимых данных. Использование индексов позволяет решить эту задачу. Индекс представляет собой часть базы данных, в которой содержится информация об организации данных в таблицах БД.
В отличие от ключей, которые просто идентифицируют отдельные записи, индексы занимают дополнительные объемы памяти (довольно значительные) и могут храниться как совместно с таблицами, так и в виде отдельных файлов.
Индексы создаются вместе со своей таблицей и обновляются при модификации данных. При этом работа по обновлению индекса для большой таблицы может отнимать много ресурсов, поэтому имеет смысл ограничить число индексов для таких таблиц, где происходит частое обновление данных.
Индекс содержит в себе уникальные идентификаторы записей и дополнительную информацию об организации данных. Поэтому если при выполнении запроса сервер или локальная СУБД обращается для отбора записи к индексу, то это занимает значительно меньше времени, так как идентификатор, очевидно, гораздо меньше самой записи. Кроме этого, индекс "знает", как организованы данные, и может ускорять обработку за счет группирования записей по сходным значениям параметров.
Создание для БД эффективного набора индексов является нетривиальной задачей. Во-первых, нужно верно определить оптимальное число индексов для каждой таблицы. Во-вторых, каждый индекс должен содержать только необходимые поля, при этом большую роль играет их упорядочение.
В подавляющем большинстве СУБД при создании индексов требуется только задать поля и название индекса, вся остальная работа выполняется автоматически.
Естественно, что в компонентах доступа данных VCL Delphi используются все возможности такого мощного инструмента, как индексы. Причем, свойства и методы для работы с индексами присутствуют только в классе TTable, так как в компоненте TQuery работа с индексами осуществляется средствами SQL. В ближайшем общем предке — классе TBDEDataSet — возможности для использования индексов отсутствуют.
Набор данных на основе компонента TTable может работать и без применения индексов, но для этого соответствующая таблица БД не должна иметь первичного ключа — случай довольно редкий. Поэтому по умолчанию в наборе данных используется первичный индекс. При открытии набора данных все записи отсортированы в соответствии с первичным ключом, но только в том случае, если свойство Defaultindex имеет значение True.
Для того чтобы подключить к набору данных вторичный индекс, необходимо присвоить свойству indexName название индекса. Если свойство не имеет значения, то в наборе данных используется первичный индекс. Это свойство используется при задании индексов для таблиц Paradox и dBASE.
Альтернативный способ задания индекса заключается в использовании свойства indexFieldNames, в котором задается перечень имен полей необходимого индекса, разделенных точкой с запятой. В Инспекторе объектов для этого свойства список полей для существующих индексов создается автоматически, разработчику остается только сделать выбор. Такое автоматическое создание используется при задании индексов для таблиц серверов SQL.
Список имен всех индексов можно получить при помощи метода GetIndexNames.
Изменение текущего индекса можно осуществлять без отключения набора данных, поэтому в приложениях очень удобно делать сортировку данных по индексам. Такой метод смены индексов называется индексацией "на лету".
После установки индекса количество полей в индексе передается в свойство
IndexFieldCount.
Информация об индексах набора данных содержится в свойстве indexDefs. В нем для каждого индекса создается структура TindexDef, ее свойства представлены в табл. 16.5. Свойство indexDefs представляет собой экземпляр класса TindexDefs (табл. 16.6), в котором доступ к информации об индексах осуществляется через свойство items, являющееся списком объектов TindexDef.
Таблица 16.5. Свойства и методы класса TindexDefs
Объявление |
Описание |
Свойства |
|
property Items [Index; Integer]: TindexDef; default; |
Список объектов описаний всех индексов набора данных |
Методы |
|
procedure Add (const Name, Fields: string; Options: TIndexOptions) ; |
Создает и добавляет к списку новый объект описания. Метод используется для обратной совместимости версий. Для создания новых описаний рекомендуется использовать метод AddFieldDef |
function AddIndexDef: TindexDef; |
Создает и добавляет к списку новый объект описания индекса |
function Find (const Name: string): TindexDef; |
Возвращает экземпляр объекта описания по имени индекса, переданному в параметре |
function FindIndexForFields (const Fields: string): TindexDef; |
Возвращает экземпляр объекта описания по списку полей индекса, переданному в параметре. Если индекс не найден, ищется первый индекс, начинающийся с указанных полей. Если индекс не найден, генерируется исключительная ситуация EdatabaseError |
function GetIndexForFields (const Fields: string; Case Insensitive: Boolean): TindexDef; |
Возвращает экземпляр объекта описания по списку полей индекса, переданному в параметре. Если индекс не найден, ищется первый индекс, начинающийся с указанных полей. Если индекс не найден, функция возвращает Nil |
procedure Update; reintroduce; |
Обновляет описания индексов в соответствии с текущим состоянием таблицы |
Объявление |
Тип |
Описание |
Свойства |
||
property CaseInsFields: string; |
Pb |
Содержит список всех полей индекса, которые нечувствительны к регистру символов |
property DescFields: string; |
Pb |
Содержит список всех полей индекса, которые упорядочены в обратном порядке |
property Expression: string; |
Pb |
Выражение для упорядочивания записей по индексу для таблиц dBASE |
property FieldExpression: string; |
Ro |
Список, содержащий имена полей и выражения для них одновременно |
property Fields: string; |
Pb |
Список полей, входящих в индекс |
property GroupingLevel: Integer; |
Pb |
Уровень группировки индекса |
property Name: string; |
Pb |
Имя индекса |
property Options: TIndexOptions; |
Pb |
Набор параметров для индекса . |
property Source: string; |
Pb |
Имя индексного файла для dBASE |
Методы |
||
procedure Assign(ASource: TPersistent); override; |
Pu |
Копирует описание ASource в текущее описание |
Параметры описания индекса определяются свойством Options, имеющим тип TIndexOptions. Для индекса возможны сочетания следующих параметров:
Свойство GroupingLevel позволяет ограничить область применения индекса. Если значение этого свойства равно нулю, индекс упорядочивает все записи набора данных. В противном случае действие индекса распространяется на группы записей, имеюших одинаковые значения для того числа полей, которое задано свойством.
Описания индексов, наряду с описаниями полей (гл. 17), также используются при создании новых таблиц БД. Для каждого планируемого индекса перед вызовом метода createTable необходимо создать или скопировать из существующего набора данных соответствующее описание. Тогда при создании таблицы индексы будут добавлены автоматически:
with Tablel do
begin
DatabaseName := 'DBDEMOS';
TableType := ttParadox;
TableName := 'DemoTable';
{Создание описаний полей}
with IndexDefs do begin Clear;
AddIndexDef;
with Items [0] do begin Name : = ";
Fields := 'Fieldl';
Options := [ixPrimary, ixUnique];
end;
AddIndexDef;
with Items[I] do
begin
Name := 'Secondlndex';
Fields := 'Field1;Field2';
Options := [ixCaseInsensitive];
end;
end;
CreateTable;
end;
При создании описаний индексов использован метод AddIndexDef, который при каждом вызове добавляет к списку items объекта TindexDefs новый объект TindexDef. Таким образом, сначала создается первичный индекс (в таблицах Paradox он не имеет имени), затем вторичный индекс secondIndex. Для каждого описания обязательно определяются составляющие индекс поля и параметры индекса (свойства Fields и options).
Состояния набора данных
В процессе своего функционирования, от открытия методом open и до закрытия методом close, набор данных может выполнять самые разнообразные операции. Можно просто перемещаться по записям, можно редактировать данные и удалять записи, можно проводить поиск по различным параметрам и т. д. При этом желательно, чтобы все операции выполнялись как можно быстрее и эффективнее.
Разнообразие функций и эффективность работы обеспечивает сложный механизм доступа к данным, основой которого является BDE. В нем все операции объединены в группы по функциональному признаку. Например, перемещение на первую и последнюю запись, перемещение на следующую и предыдущую запись, перемещение на N записей вперед или назад объединены в группу операций навигации по набору данных.
Для каждой группы операций BDE выполняет ряд подготовительных действий по предварительной настройке набора данных. При этом состояние набора данных изменяется, причем, иногда довольно сильно. Например, сразу после перемещения по записям нельзя приступить к редактированию данных или к удалению строк.
Из сказанного выше можно заключить, что набор данных в любой момент времени находится в некотором состоянии, то есть подготовлен к выполнению действий строго определенного рода.
Все состояния набора данных делятся на две группы.
К первой группе относятся состояния, в которые набор данных переходит автоматически, к ним относятся непродолжительные по времени состояния, сопровождающие оперирование с полями набора данных (табл. 16.7).
Во вторую группу входят состояния, которыми можно управлять из приложения, например, перевод набора данных в режим редактирования (табл. 16.8).
Базовый класс TDataset, инкапсулирующий свойства набора данных, позволяет изменять состояние, а также проверять текущее состояние набора данных.
Текущее состояние набора данных передается в свойство State, имеющее ТИП TDataSetState:
type TDataSetState = (dslnactive, dsBrowse, dsEdit, d5lnsert, dsSetKey, dsCalcFields, dsFilter, dsNewValue, dsOldValue, dsCurValue, dsBlockRead, dsInternalCalc);
Для управления состояниями набора данных используются методы open,
Close, Edit,In5ert.
Таблица 16.7. Автоматические состояния набора данных
Константа состояния |
Описание |
DsNewVaiue |
Включается при обращении к свойству NewValue поля набора данных |
DsOldValue |
Включается при обращении к свойству oldvalue поля набора данных |
DsCurValue |
Включается при обращении к свойству CurValue поля набора данных |
DsInternalCalc |
Включается при расчете значений полей, для которых FindKind = fkInternaiCalc |
DsCalcFields |
Включается при выполнении метода OnCalcFieids |
DsBlockRead |
Включается механизм ускоренного перемещения по набору данных. |
DsOpening |
Включается при открытии набора данных методом Open или свойством Active |
DsPilter |
Включается при выполнении метода OnFilterRecord |
Таблица 16.8. Управляемые состояния набора данных
Константа состояния |
Метод |
Описание |
dslnactive |
Close |
Набор данных закрыт |
dsBrowse |
Open |
Данные доступны для просмотра, но недоступны для редактирования |
dsEdit |
Edit |
Данные можно редактировать |
dslnsert |
Insert |
К набору данных можно добавлять новые записи |
DsSetKey |
SetKey |
Включается механизм поиска по ключу. Также могут использоваться диапазоны |
Рассмотрим, как изменяется состояние набора данных при выполнении стандартных операций.
Закрытый набор данных всегда имеет неактивное состояние dslnactive.
При открытии набор данных переходит в состояние просмотра данных dsBrowse. В этом состоянии по записям набора данных можно перемещаться и просматривать их содержимое, но редактировать данные нельзя. Это основное состояние открытого набора данных, из него можно перейти в
другие состояния, но любое изменение состояния происходит через просмотр данных (рис. 16.2).
Рис. 16.2. Схема изменения состояний набора данных
При необходимости редактирования данных набор должен быть переведен в состояние редактирования dsEdit, для этого используется метод Edit. После выполнения метода можно изменять значения полей текущей записи. При перемещении на следующую запись набор данных автоматически переходит в состояние просмотра.
Для того чтобы вставить в набор данных новую запись, необходимо использовать состояние вставки dsInsert. Метод insert переводит набор данных в это состояние и добавляет на место текущего курсора новую пустую запись. При переходе на другую запись после проверки на уникальность первичного ключа (если он есть) набор данных возвращается в состояние просмотра.
Состояние установки ключа dsSetKey используется только в компоненте TTable при необходимости поиска методами FindKey и FindMext, а также при использовании диапазонов (метод SetRange). Это состояние сохраняется до момента вызова одного из методов поиска по ключу или метода отмены диапазона. После этого набор даннь1х возвращается в состояние просмотра.
Состояние просмотра по блокам dsBiockRead используется набором данных при реализации быстрого перемещения по большим массивам записей без показа промежуточных записей в компонентах отображения данных и без вызова обработчика события перемещения по записям. Для реализации быстрого перемещения по набору данных можно использовать методы DisableControls и EnableControls.
Резюме
Набор данных является прообразом таблицы базы данных в приложении. Он содержит ipynny записей и обеспечивает их использование. Классы иерархичной структуры, инкапсулирующие набор данных, реализуют большое число разнообразных механизмов для работы с данными.
В основе функционирования набора данных лежат методы классов-предков. Эти методы основаны на функциях процессора БД Borland Database Engine. Поэтому для понимания возможностей рассматриваемых здесь компонентов желательно просмотреть главы 14 "Процессор баз данных Borland Database Engine" и 15 "Этапы создания приложения баз данных".
На материале этой главы основываются следующие главы: