Глава 17
Поля
Каждая таблица БД и, следовательно, каждый набор данных приложения имеет собственную структуру, которая определяется совокупностью полей. Каждое поле представляет собой описание типа данных, которому должно соответствовать значение, находящееся в записи на определенном месте. Иначе, полем можно назвать совокупность ячеек с данными определенного типа, расположенные в одном и том же месте в каждой записи набора данных, или попросту — это столбец в таблице.
В наборе данных приложения баз данных Delphi каждому полю соответствует собственный объект. Основой объектов полей является класс TField, который инкапсулирует основные свойства абстрактного поля, не зависящего от типа данных. От этого базового класса порождены другие классы, обеспечивающие функционирование реальных объектов полей, зависящих от типа данных.
Программист, грамотно использующий возможности полей, может решать существенно более сложные задачи и создавать эффективные и гибкие приложения баз данных.
Эта глава посвящена изучению объектов полей набора данных и приемов работы с ними. В главе рассматриваются следующие вопросы.
Объекты полей
Объекты полей инкапсулируют свойства и методы полей различных типов данных. Они функционируют совместно с набором данных и очень тесно связаны с ним. Например, для того чтобы получить значения полей из текущей записи набора данных, разработчик должен создать примерно такой код:
Editl.Text := Tablе1.Fields[О].AsString;
Свойство Fields представляет собой индексированный список объектов полей набора данных. Если программист не изменяет порядок следования полей в наборе данных, то расположение объектов полей соответствует структуре таблицы базы данных.
Каждый объект поля хранит ряд параметров, определяющих поле. Например, в наборе данных к объекту поля можно обратиться, зная только название поля:
Editl.TexL := Tablel-FieldByName('SomeField')-AsString;
Для того чтобы присвоить значение полю в текущей записи, можно воспользоваться приведенными выше способами или, если тип данных поля Неизвестен, свойством FieldValues:
Tablel.FieldValues['SomeField'] :- Editi.Text;
Знание имени поля дает самый простой способ обращения к текущему значению поля:
Tablel['SomeField'] := Editl.Text;
Editl.Text := Tablel['SomeField'];
При присваивании значений полям набора данных необходимо контролировать состояние, в котором находится набор данных (гл. 16).
В основе классов, описывающих иерархию типизированных полей, лежит класс TField. От него порождены другие классы, обеспечивающие работу целых групп полей, объединенных по типам данных.
Что же такое объект поля, и какие возможности он предоставляет программисту?
Во-первых, назначение класса TField как базового класса поля заключается в умении взаимодействовать с компонентом отображения данных для обеспечения правильной визуализации данных. Например, объект поля хранит способ выравнивания, параметры шрифта, текст заголовка и т. д.
Во-вторых, с точки зрения набора данных, объект поля является хранилищем текущего значения этого поля (а не всего столбца данных, как это можно себе представить по названию).
Компоненты отображения данных (гл. 18) при работе с набором данных взаимодействуют именно с полями (вспомните о роли свойства DataFieid в компонентах отображения данных). Колонки компонента TDBGrid при отсутствии дополнительных настроек соответствуют расположению объектов полей в связанном наборе данных.
В Delphi предусмотрено два способа создания объектов полей.
Динамические поля используются программой в случае, если разработчик не создал для них объекты явным образом на этапе разработки. Каждый не заданный явно объект поля автоматически создается при открытии набора данных в соответствии со структурой связанной таблицы БД. Любой объект поля является прямым наследником класса TFieid, а его конкретный тип зависит от типа данных поля таблицы. При этом свойства динамического поля устанавливаются в соответствии с параметрами поля таблицы базы данных.
Компонент доступа к данным после подключения к таблице БД без дополнительных настроек использует только динамические поля. К свойствам и методам динамических полей можно обратиться программно, для этого нужно использовать индексированное свойство Fields компонента доступа к данным, которое объединяет все поля набора данных (см. выше).
Динамические поля используются в случаях, когда заданные характеристики полей в таблице базы данных полностью удовлетворяют разработчика и нет необходимости рассматривать какое-либо поле вне набора данных.
Статические поля создаются программистом на этапе разработки, их свойства доступны в Инспекторе объектов, а их названия можно выбрать из списка объектов активной формы в верхней части Инспектора объектов. Название статического объекта поля обычно складывается из названий таблицы и поля ,например, OrdersCustNo.
Создаются статические объекты полей при помощи специализированного Редактора полей (рис. 17.1), который вызывается двойным щелчком на компоненте набора данных на форме или командой Fields Editor из всплывающего меню этого компонента.
Рис. 17.1. Редактор полей с отдельным списком агрегатных полей
Редактор полей представляет собой простой список уже созданных статических полей, все управление осуществляется командами из всплывающего меню. В верхней части окна Редактора расположены кнопки навигатора для перемещения по набору данных, которые активны только при открытом наборе данных. Если набор данных имеет агрегатные поля данных (не вычисляемые и не синхронного просмотра), то они размещаются в отдельном списке в нижней части окна Редактора полей.
Добавить к списку статических полей новое поле можно при помощи команды Add fields из всплывающего меню Редактора. Удаление элемента из списка осуществляется клавишей <Delete>, Перетаскиванием элементов списка при помощи мыши можно изменить их расположение. Таким образом, можно создавать произвольные комбинации статических полей.
Как только для набора данных создан хотя бы один статический объект поля, считается, что набор данных содержит только те поля, которые имеются в списке статических полей. Эту особенность можно использовать для искусственного ограничения структуры данных таблиц. Все поля набора данных расположены в том порядке, как это указано в списке Редактора полей, независимо от их реального расположения в таблице базы данных.
Команда New Held из всплывающего меню Редактора полей позволяет создать статическое поле, которое реально не существует в структуре данных таблицы (рис. 17.2). Для выбора типа поля используется группа переключателей Field type: Data — поле данных, Calculated — вычисляемое поле, Lookup — поле синхронного просмотра.
Рис. 17.2. Диалог создания нового статического поля Редактора полей набора данных
Реже создаются поля данных, которые обязательно должны базироваться на реальных полях таблицы. Если для такого объекта соответствующее поле в таблице будет удалено или его тип данных будет изменен, то при открытии набора данных генерируется исключительная ситуация.
Для клиентских наборов данных многоуровневых приложений диалог создания нового поля позволяет выбрать два дополнительных типа поля — это агрегатные поля (переключатель Aggregate) и внутренние вычисляемые поля (переключатель InternalCalc).
Класс TField
Как уже говорилось выше, в большой иерархии классов для полей различных типов данных класс TField является базовым, он инкапсулирует свойства и методы абстрактного поля данных. Именно от него происходят все классы типизированных полей. В реальной работе класс TField не используется, но его значение трудно переоценить. Практически все основные свойства классов типизированных полей унаследованы от класса TField без каких-либо изменений, а дополнительные свойства и методы обеспечивают работу конкретного типа данных.
Что касается методов-обработчиков событий, то четыре метода, определенные в классе TField, наследуются всеми потомками без изменения и дополнения (табл. 17.1).
Класс TField обеспечивает выполнение следующих основных задач. П Хранение текущих значений полей открытого набора данных D Определение параметров визуализации данных поля D Контроль правильности вводимых значений D Преобразование типов данных Ниже приведены свойства и методы класса TField.
Таблица 17.1. Свойства и методы класса TField
Объявление |
Тип |
Описание |
Свойства |
||
property Alignment: TAlignment; |
Pb |
Задает способ выравнивания данных при их показе в компоненте отображения данных |
property AsBoolean: Boolean; |
Pu |
Содержит значение поля в логическом формате |
property AsCurrency: Currency; |
Pu |
Содержит значение поля в денежном формате |
property AsDateTime: TDateTime; |
Pu |
Содержит значение поля в календарном формате |
property AsFloat: Double; |
Pu |
Содержит значение поля в вещественном формате |
property Aslnteger Integer; |
Pu |
Содержит значение поля в целочисленном формате |
property AsString: string; |
Pu |
Содержит значение поля в строковом формате |
property AsVariant: Variant; |
Pu |
Представляет значение поля как вариант |
property AttributeSet: string; |
Pu |
Содержит имя связанного с полем набора атрибутов из словаря данных |
Property AutoGenerateValue: TAutoRefreshFlag;
|
Pb
|
Определяет порядок генерации значения поля сервером БД для новой записи. (Применяется как простой способ создания первичных ключей и т. д.) Тип TAutoRefreshFlag имеет следующие значения: arNone — свойство не используется; arAutoinc — поле работает как автоинкрементное; arDefault — в качестве нового значения используется значение по умолчанию. |
property Calculated: Boolean; |
Pu |
Определяет, является ли поле вычисляемым |
property CanModify: Boolean; |
Ro |
Определяет, можно ли изменять значения в поле |
Property ConstraintErrorMessage: strings; |
Pb
|
Содержит текст сообщения, которое появляется при вводе ошибочного значения |
property CurValue: Variant;
|
Ro |
Содержит текущее значение поля с учетом изменений, вносимых в многопользовательском режиме работы |
Property CustomConstraint: strings; |
Pb |
Задает дополнительные ограничения на вводимые значения. Описание ограничения должно соответствовать правилам языка SQL |
property DataSet: TDataSet; |
Pu |
Определяет набор данных, которому принадлежит поле |
property DataSize: Word; |
Ro |
Возвращает размер памяти для хранения текущего значения поля |
property DataType: TFieldType; |
Ro |
Возвращает тип данных поля |
property DefaultExpression: stringy- |
Pb |
Содержит значение по умолчанию, которое используется при отсутствии реального значения поля. Для задания свойства используются правила языка SQL |
property DisplayLabel: string; |
Pb |
Задает текст заголовка колонки поля в компоненте TDBGrid |
property DisplayName: string; |
Ro |
Содержит имя поля, которое используется при выводе сообщений об ошибках |
property DisplayText: stringy- |
Ro |
Возвращает текущее значение поля в компоненте отображения данных в строковом формате |
property DisplayWidth: Integer; |
Pb |
Определяет число символов для представления данных в поле |
property EditMask: string; |
Pu |
Задает шаблон ввода данных |
property EditMaskPtr: stringy- |
Ro |
Шаблон поля, используется только для просмотра |
type TFieldKind = (fkData, fkCalculated, fkLookup, fkInternalCalc, fkAggregate); property FieldKind; TFieldKind; |
Pb
|
Определяет тип поля: поле данных, вычисляемое или поле синхронного просмотра
|
property FieldName: string; |
Pb |
Содержит имя физического поля в таблице БД |
property FieldNo: Integer; |
Ro |
Возвращает порядковый номер физического поля в таблице БД |
property FullName: string; |
Ro |
Используется при наличии родительского поля. Содержит имя данного поля и родительского ПОЛЯ |
property HasConstraints: Boolean- |
Pb, Ro |
В значении True говорит о том, что поле имеет ограничение, заданное свойствами (CustomConstraint, DefaultExpression, ImportedConstraint) |
property ImportedConstraint: string; |
Pb |
Содержит ограничения данных поля, хранящиеся на сервере |
property Index: Integer; |
Pb |
Порядковый номер поля в наборе данных |
property IsIndexField: Boolean; |
Ro |
Определяет, является ли поле индексным в таблице БД |
property IsNull: Boolean; |
Ro |
Возвращает True, если текущее значение поля пустое |
property KeyFields: string; |
Pb |
Задает поле (поля) набора данных, для которых будет организован синхронный просмотр |
property Lookup: Boolean; |
Pu |
Определяет, является ли поле полем синхронного просмотра |
property LookupCache: Boolean; |
Pb |
Включает или отключает использование кэша при синхронном просмотре |
property LookupDataSet: TDataSet; |
Pb |
Задает набор данных синхронного просмотра |
property LookupKeyFields: string; |
Pb |
Задает поле (поля) в таблице синхронного просмотра, по которому осуществляется связь |
property LookupList: TLookupList; |
Ro |
Содержит значения поля синхронного просмотра из набора данных синхронного просмотра, которые использованы в данном поле |
property LookupResultField: string; |
Pb |
Определяет поле из набора данных синхронного просмотра, значения которого передаются в данное поле |
property NewValue: Variant; |
Pu |
Содержит текущее значение поля с учетом отмененных изменений |
property Offset: word; |
Ro |
Возвращает число байтов, добавленных в конец буфера текущей записи набора данных для обеспечения кэширования значений поля |
property OldValue: Variant; |
Ro |
Содержит первоначальное значение поля |
'property Origin: strings- |
Pb |
Содержит первоначальное имя поля в наборе данных, (Например, имя поля может быть изменено на псевдоним в запросе SQL) |
property ParentField: TObjectField; |
Pu |
Возвращает указатель на объект поля, от которого порожден данный объект |
type TProviderFIag = (pfInUpdate, |
Pb |
Определяет реакцию на запись значения поля при удаленном доступе. Работает только в пакетном режиме передачи |
pfInWhere, pfInKey, pfHidden) ; TProviderFlags = set of TProviderFlag; property ProviderFlags: TProviderFlags; |
|
pfInUpdate — новое значение не записывается pfinwhere — поле не используется при просмотре записываемых записей pflnKey — поле используется после ошибки записи pfHidden — новое значение записывается |
property Readonly: Boolean; |
Pb |
Переводит поле в режим "только для чтения" |
property Required: Boolean; |
Pb |
Задает ограничение на обязательное наличие значения поля |
property Size: Word; |
Pu |
Определяет размер для значений поля, в случае, если это необходимо для типа данных поля |
property Text: string; |
Pu |
Содержит значение поля в строковом формате при переводе компонента отображения данных в режим редактирования |
type TFieldChars = set of Chart; property ValidChars: TFieldChars; |
Pu
|
Содержит символы, которые можно использовать при задании значений поля
|
property Value: Variant |
Pu |
Содержит значение поля |
property Visible: Boolean; |
Pb |
Определяет видимость поля в компоненте TDBGrid |
Методы |
||
procedure Assign (Source: TPersistent) ; |
Pu |
Присваивает данному полю текущее значение поля source. При этом должна соблюдаться совместимость по типу данных |
procedure AssignValue (const Value: TVarRec) ; |
Pu |
Присваивает полю значение Value. Заменяет свойства AsFloat, As Integer и т. д. |
procedure Clear; |
Pu |
Присваивает полю значение Null |
function FocusControl; |
Pu |
Передает фокус формы связанному с полем компоненту отображения данных, который стоит первым в списке компонентов формы |
function GetData (Buffer: Pointer): Boolean; |
Pu |
Передает в Buffer текущее значение поля в неформатированном виде. Размер буфера должен соответствовать типу данных поля |
function GetParentComponent: TComponent; override; |
Pu |
Возвращает экземпляр объекта, который загружает текущее поле. Это может быть набор данных или родительское поле |
function HasParent: Boolean; override; |
Pu |
Значение True говорит о том, что поле является частью более сложного компонента |
class function IsBlob: Boolean; virtual; |
Pu |
Сигнализирует о том, что данные в поле имеют тип BLOB |
function IsValidChar (InputChar: Char): Boolean; virtual; |
Pu |
Определяет, можно ли использовать в поле символ InputChar |
procedure RefreshLookupList; |
Pu |
Заполняет свойство LookupCache значениями из таблицы синхронного просмотра |
procedure SetData (Buffer: Pointer); |
Pu |
Передает в поле неформатированные данные из Buffer |
procedure SetFieldType (Value: TFieldType); |
Pu |
Задает тип данных |
procedure Validate (Buffer: Pointer) ; |
Pu |
Передает в объект поля буфер с текущим значением. После этого вызывается метод-обработчик OnValidate |
Методы-обработчики событий |
||
type TFieldNotifyEvent = procedure (Sender: TField) of object; property OnChange: TFieldNotifyEvent; |
Pu |
Вызывается сразу после успешного сохранения текущего значения в буфере (см. главу 14) текущей записи набора данных |
type TFieldGetTextEvent = procedure (Sender: TField; var Text: string; DisplayText: Boolean) of object; property OnGetText: TFieldGetTextEvent; |
Pu |
Вызывается при любом чтении из свойств DisplayText или Text. Параметр Text. содержит форматированный текст. Если параметр DisplayText имеет значение True, то форматированный текст используется только для показа данных, иначе — и для редактирования |
type TFieldSetTextEvent = procedure (Sender: TField; const Text: string) of object; |
Pu |
Вызывается при записи измененного значения поля в свойство Text |
property OnSetText: TFieldSetTextEvent; |
Pu |
|
Методы-обработчики событий |
||
type TFieldNotifyEvent Pu = procedure (Sender: TField) of object; property OnValidate: TFieldNotifyEvent; |
Pu |
Вызывается перед сохранением значения поля в буфере текущей записи набора данных. При этом проводится проверка правильности значения в соответствии с установленными ограничениями |
Функциональное назначение поля определяется свойством FieldKind. В большинстве случаев его значение определяется автоматически в момент создания объекта поля. Да и впоследствии вряд ли возникнет необходимость, например, сделать реальное поле данных вычисляемым. Обычно попытка изменить значение свойства FieldKind вызывает ошибку. Рассмотрим возможные значения этого свойства:
Одной из важнейших задач класса TField является обеспечение доступа к текущему значению поля. В этом случае класс взаимодействует с буфером текущей записи набора данных, а значение можно получить при помощи нескольких свойств.
Свойство value всегда содержит значение, которое сохранено после последнего выполнения метода post набора данных. Именно это свойство чаще всего используется при создании программного кода:
with Tablel do beqin Open;
while Not EOF do begin
if Fields[0].Value > 10 then Fields[1].Value := Fields[1].Value*2;
Next ;
end;
Close;
end;
В этом примере при помощи метода Next осуществляется перебор всех записей набора данных. Если значение первого поля больше 10, то значение второго поля удваивается. Для этого используется свойство value объектов полей набора данных.
Для преобразования текущего значения поля к необходимому виду можно использовать целую группу свойств As..., которые преобразуют значение в определенный тип данных. Чаще всего используется свойство AsString, оно применяется для представления числовых значений полей в элементах управления:
Editl.Text := Tablе1.Fields[0].AsString;
При работе со статическими объектами полей при передаче значений желательно использовать свойства из группы As..., так как неявное задание типа свойством value может привести к ошибке преобразования данных типа Variant.
Свойство DisplayText содержит значение поля в строковом формате до начала редактирования. Свойство Text предназначено для использования компонентами отображения данных при редактировании. Поэтому эти два свойства могут иметь разные значения в случае, если значение поля в строковом формате при редактировании и просмотре различно. У классов-наследников TField для этого достаточно задать шаблон отображения данных для поля (свойство DispiayFormat) и шаблон редактирования данных (свойство EditFormat). Например, вещественное число при просмотре может иметь разделители тысяч, а при редактировании нет. В этом случае рассматриваемые свойства будут иметь следующий вид:
DisplayText = '1 452,32' Text = 4452,32'
Свойства Text и DisplayText влияют на использование метода-обработчика onGetText (см. табл. 17.1). Если параметр DisplayText имеет значение True, то параметр Text содержит значение свойства DisplayText, в противном случае в метод передается значение поля в строковом формате.
Если поле не имеет значения, то при помощи свойства DefaultExpression можно задать некоторое постоянное значение, которое будет появляться в компоненте отображения данных при пустом поле. Если постоянное значение содержит какие-либо символы кроме цифр, то все выражение нужно обязательно брать в кавычки.
В случае возникновения исключительных ситуаций во время использования поля BDE генерирует соответствующее сообщение, в котором в качестве имени поля используется значение свойства DisplayName. Если задано свойство DispiayLabel, то DispiayName приравнивается к нему, в противном случае для задания свойства DispiayName используется свойство FieldName. Другим способом задать значение свойства DispiayName невозможно.
Типы полей
Теперь рассмотрим классификацию полей набора данных в зависимости от их функционального назначения. Самыми распространенными полями являются поля данных, базирующиеся на реальных полях таблицы БД. Свойства объектов таких полей устанавливаются в соответствии с параметрами полей таблицы БД.
Кроме этого, в практике программирования часто применяются поля синхронного просмотра и вычисляемые поля.
Ниже мы рассмотрим только поля синхронного просмотра и вычисляемые поля, так как поля данных не содержат каких-либо существенных особенностей в применении.
С точки зрения набора данных, большой разницы между этими двумя видами полей нет. Однако значения для всех полей синхронного просмотра рассчитываются раньше, чем для вычисляемых полей. Поэтому вы можете использовать поля синхронного просмотра в выражениях вычисляемых полей и не можете сделать наоборот.
Поля синхронного просмотра
При создании для исходного набора данных нового поля синхронного просмотра необходимо использовать следующие свойства.
Свойство LookupDataSet задает набор данных синхронного просмотра. Свойство LookupResuitField представляет поле синхронного просмотра из набора данных LookupDataSet, данные из которого будут появляться в созданном поле. Свойство LookupKeyFields содержит поле (или поля) из набора данных синхронного просмотра, по значению которого выбирается значение из поля LookupResuitField. Свойство KeyFields определяет поле (или поля) из исходного набора данных, для которого создается поле синхронного просмотра.
Для просмотра данных из поля синхронного просмотра можно использовать любые компоненты отображения данных, но естественно будет применить специальные компоненты синхронного просмотра, о которых рассказывается в следующей главе.
Кроме этого, очень удобно использовать поля синхронного просмотра в компоненте TDBGrid. Если такое поле связать с одной из колонок компонента, то для него автоматически заполняется список синхронного просмотра. Его элементы хранятся в свойстве pickList, которое, как известно, имеется в любой колонке. Теперь пользователю достаточно выбрать нужную колонку в сетке и, щелкнув на появившейся в текущей ячейке кнопке, получить возможные значения для замены. Одновременно с изменением поля синхронного просмотра изменяется и ключевое поле (свойство KeyFields) исходного набора данных.
При использовании полей синхронного просмотра в компоненте TDBGrid открывать набор данных синхронного просмотра необязательно. При этом свойство LookupCache, о котором речь пойдет ниже, обязательно должно иметь значение False.
Для идентификации полей синхронного просмотра можно использовать булевское свойство Lookup базового класса TField, которое принимает истинное значение для таких полей.
Свойство LookupCache определяет режим использования специального буфера значений синхронного просмотра. Если это свойство истинно, то буфер работает.
Буфер основан на свойстве LookupList. При открытии исходного набора данных каждое поле синхронного просмотра получает свое значение, одновременно с этим в соответствии со всеми имеющимися в исходном наборе данных значениями ключевого поля заполняется и буфер синхронного просмотра. Впоследствии при перемещении на другую запись значение синхронного просмотра берется не из набора данных синхронного просмотра, а из буфера. Этот механизм при небольших объемах значений синхронного просмотра позволяет увеличить скорость работы с исходным набором данных особенно в режиме удаленного доступа при низкоскоростных сетях.
При изменениях в наборе данных синхронного просмотра можно использовать метод RefreshLookupList, который обновляет текущее значение поля и список значений в буфере.
Специально для разработчиков в базовый класс TField включено свойство offset, которое возвращает размер буфера в байтах.
Для создания поля синхронного просмотра удобнее всего воспользоваться Редактором полей компонента доступа к данным. После выбора команды New field из всплывающего меню в одноименном диалоговом окне (см. рис. 17.2) помимо обычных действий, соответствующих созданию поля данных, необходимо задать значения свойств в группе Lookup definition. Элементы управления группы становятся доступны после выбора типа поля (переключатель Lookup в группе Field type).
В списке Dataset представлены все доступные в модуле наборы данных, из которых нужно выбрать набор данных синхронного просмотра (свойство Lookup DataSet). Список Result Field позволяет выбрать поле синхронного просмотра (свойство LookupResuitField). Список Lookup Keys задает ключевое поле в наборе данных синхронного просмотра (свойство LookupKeyFields). Список Key Fields определяет ключевое поле исходного набора данных (свойство KeyFieids).
Вычисляемые поля
Вычисляемые поля существенно облегчают разработку приложений баз данных, так как позволяют получать новые данные на основе существующих, не изменяя при этом структуру таблиц БД. Выражения для получения значений вычисляемых полей программист должен разместить в методе-обработчике OnCalcFields набора данных. Здесь можно использовать любые арифметические и логические операции и функции, любые операторы языка, свойства и методы любых компонентов, в том числе запросы SQL:
procedure TFormI.TablelCalcFields(DataSet: TDataSet) ;
begin with Table1 do TablelCalcFieidl. Value := Fields [0] .Value + Fjei.d?ni.Vfli.!:
with Queryl do begin Params[0].As Integer := Tablel.Fields[0].Aslnteger;
Open;
TablelCalcFieidl.Value := Fields[0].AsString;
Close;
end;
end;
Метод OnCalcFields выполняется при открытии набора данных, при переходе в режим редактирования, при передаче фокуса между компонентами отображения данных или колонок сетки, при удалении записи. Но для этого нужно, чтобы свойство AutoCalcFields набора данных было равно True.
Необходимо учитывать, что сложные вычисляемые поля могут существенно замедлить работу набора данных (особенно при использовании запросов SQL). Кроме того, в процессе редактирования набора данных (при изменении значения поля, сохранении изменений и переходе на следующую запись) вычисляемые поля рассчитываются несколько раз подряд. Для уменьшения числа автоматических обращений к методу OnCalcFields нужно использовать свойство AutoCalcFields := False.
Для создания вычисляемого поля достаточно в диалоге создания нового поля Редактора полей в качестве типа поля задать Вычисляемое, в остальном процесс совпадает с созданием поля данных.
В выражениях вычисляемых полей можно использовать другие вычисляемые поля, но они обязательно должны быть уже определены в методе onCalcFields.
Типы данных
В среде разработки Delphi можно создавать приложения для работы с самыми разными базами данных. Такая универсальность подразумевает необходимость применения средств, которые обеспечили бы возможность работы со многими типами данных, которые используются в этих базах данных.
Естественно, что существует большая группа типов данных, конкретная реализация которых практически не отличается от платформы к платформе. Это, например, строки, символы, целые и вещественные числа и т. д.
Есть типы данных, которые реализованы далеко не на каждой платформе.
Есть, наконец, просто уникальные типы данных, но зато СУБД, на которых этот тип реализован, достаточно широко распространены.
Для удовлетворения потребностей разработчиков в Delphi применен следующий способ работы с типами данных.
Тип данных однозначно связан с конкретным полем таблицы базы данных. Без этого поля само понятие типа данных не имеет практического смысла. В Delphi свойства абстрактного поля инкапсулирует класс TFieid, который не имеет заранее определенного типа данных. От этого класса порождено целое семейство классов для типизированных полей, каждый из которых умеет обращаться со своим типом данных.
В классе TFieid имеется свойство DaiaType, которое отвечает за тип данных, но оно не может быть изменено.
Весь список доступных типов данных содержится в типе TFileldType:
type TFieldType = (ftUnknown, ftString, ftSmallint, ftlnteger, ftWord, ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ft-DBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString, ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftdraBlob, ftOraClob, ftVariant, ftlnterface, ftIDispatch, ftGuid) ;
Ниже рассматриваются все типы данных, которые можно использовать при разработке приложений для работы с базами данных (табл. 17.2).
Таблица 17.2. Типы данных
Тип |
Класс |
Описание |
|
Абстрактный (ftADT) |
TADTField |
Произвольный тип данных,создаваемый пользователем на сервере БД и используемый в приложении |
|
Строковый (ftString) |
TStringField |
Строка длиной до 8192 символов |
|
Расширенный строковый (ftWideString) |
TWideStringField |
Динамически выделяемая строка 16-битных символов в кодировке Unicode |
|
Фиксированный символьный (ftFixedChar) |
TStringField |
Строка символов с нулевым символом в конце |
|
Фиксированный буфер (ftBytes) |
TBytesField |
Набор байтов фиксированного размера. Для работы с этим типом требуется выделять и освобождать память (методы GetMem И FreeMem) |
|
Переменный буфер (ftVarBytes) |
TVarBytesField |
Набор байтов переменного размера. Текущий размер буфера хранится в первых двух байтах. Для работы с этим типом требуется выделять и освобождать память (методы GetMem и FreeMem) |
|
Целый короткий (ftSmallint) |
TSmallIntField |
16-битное целое в диапазоне от-32768 до 32767 |
|
Целый (ftlnteger) |
TIntegerField |
32-битное целое в диапазоне от -2147483648 до 2147483647 |
|
Целый большой (ftLargeint) |
TLargeIntField |
64-битное целое число |
|
Целый положительный (ftWord) |
TWordField |
16-битное целое в диапазоне от 0 до 65535 |
|
Типизированный двоичный (ftTypedBinary) |
нет |
Типизированный двоичный |
|
Логический (ftBoolean) |
TBooleanField |
Значения True и False |
|
Вещественный (ftFloat) |
TFloatField |
Вещественные положительные и отрицательные числа с точностью 15 цифр после запятой с модулем в диапазоне от 5,0х10"324 до 1,7х10308 |
Денежный (ftCurrency) |
TCurrencyFieid |
Вещественные положительные и отрицательные числа с точностью 15 цифр после запятой с модулем в диапазоне от 5,0х10'324 до 1,7х10308. Дополнительно вставляется символ валюты |
|
Десятичный с двоичным кодированием (ftBCD) |
TBCDField |
Вещественные числа с повышенной точностью (до 4 знаков перед запятой и до 20 знаков после запятой). Могут храниться в двоичном и десятичном форматах |
|
Дата (ftDate) |
TDateField |
Дата |
|
Время (ftTime) |
TDateTimeField |
Время |
|
Календарный (ftDateTime) |
TDateTimeField |
Комбинированный формат с одновременным хранением даты и времени |
|
Автоинкрементный (ftAutoInc) |
TAutoIncField |
Значение поля в каждой новой записи автоматически увеличивается на 1. Целое число в диапазоне от-2147483648 до 2147483647. Применяется для обеспечения уникальности значений ключей |
|
BLOB (ftBlob) |
TBLOBField |
Большой двоичный массив. Используется для хранения любых данных, которые можно преобразовать в цифровой массив (Memo, Graphic). В базах данных такие данные хранятся в отдельных файлах, а поле содержит лишь ссылки на них |
|
Memo (ftMemo) |
TMemoField |
Набор строк произвольной длины |
|
Графический (ftGraphic) |
TGraphicField |
Формат для хранения изображений |
|
Форматированный Memo (ftFmtMemo) |
нет |
Форматированный набор строк произвольной длины |
|
OLE Paradox (ftParadoxOle) |
нет |
Поле OLE для таблиц Paradox |
|
OLE dBASE (ftDBaseOle) |
нет |
Поле OLE для таблиц dBASE |
|
Курсор Oracle (ftCursor) |
нет |
Курсор для хранимых процедур сервера Oracle |
|
CLOB Oracle 8 (ftOraClob) |
нет |
Тип CLOB для сервера Oracle 8 |
|
BLOB Oracle 8 (ftOraBlob) |
нет |
Тип BLOB для сервера Oracle 8 |
|
Вариант (ftVariant) |
TVariantField |
Вариант |
|
Интерфейс (ftlnterface) |
TInterfасе Field |
Ссылка на интерфейс (потомок от lunknown) |
|
Ссылка на интерфейс Idispatch (ftIDispatch) |
TIDispatchField
|
Сссылка на интерфейс (потомок от Idispatch)
|
|
Глобальный идентификатор (ftGuid) |
TGuidField |
Глобальный идентификатор GUID |
|
Массив (ftArray) |
TArrayField |
Массив полей любого типа, кроме TarrayField |
|
Ссылочный (ftReference) |
TReferenceField |
Указатель на объект, содержащийся в другой таблице |
|
Набор данных (ftDataSet) |
TDataSetField |
Содержит набор данных, интегрированный в текущий набор данных |
В Delphi тип данных BCD (Binary Coded Decimal) напрямую не поддерживается. Его использование обеспечивает денежный тип (ftCurrencv). Поэтому точность BCD ограничена 20 цифрами после запятой.
Как видно из таблицы, наряду с традиционными типами данных в Delphi 5 имеются и специальные типы, использование которых значительно расширяет функциональность приложений. В частности, типы ftTnterface, ftIDispatch, ftGuid позволяют создавать полноценные приложения БД для СОМ и OLE DB (часть IV).
Практически во всех серверах БД пользователь имеет возможность создавать собственные типы данных. Для их использования в приложениях Delphi имеется абстрактный тип данных и класс TADTField. Абстрактный тип может включать любой скалярный тип данных (числа, даты, ссылки, массивы, наборы данных).
Автоинкрементный тип данных давно используется в СУБД. Поле автоинкрементного типа для каждой новой записи автоматически увеличивает свое значение на единицу. Тем самым, каждая запись имеет собственный уникальный идентификатор, который очень часто используется в качестве первичного ключа.
Данные типа BLOB (Binary Large OBject) представляют собой двоичные массивы произвольной длины. В самом поле содержится лишь ссылка на отдельный файл базы данных, в котором хранится двоичный массив. Таким образом, поля типа BLOB являются универсальным носителем любых данных, которые имеют скалярную и нескалярную структуру и которые можно преобразовать в двоичное представление.
Тип Memo представляет собой набор строк произвольной длины (его разновидность — форматированный Memo), основан на формате BLOB. Используется при необходимости сохранить текст из компонента тмето или из текстового редактора.
Графический тип данных используется для хранения в базе данных изображений, основан на формате BLOB. Поле TGraphicFieid непосредственно взаимодействует с компонентом отображения данных (например, тпвттаде). Изображения должны храниться в формате BMP.
Типы данных ParadoxOle и dBaseOle разработаны специально для использования возможностей СУБД Paradox и dBASE по работе с данными OLE. В Delphi эти типы данных основаны на формате BLOB.
Специально для работы с сервером Oracle 8 предназначены типы CLOB и BLOB.
Тип ftArray организует массив из данных любой структуры, за исключением таких же массивов. Для каждого элемента массива может создаваться собственный объект TField. Для управления этим механизмом используется свойство SparseArrays в классе TDataSet.
В качестве отдельного поля в набор данных можно включить и любой другой произвольный набор данных. Для этого используется специальный тип данных и класс TDataSetFieid. Причем каждым полем из интегрированного набора данных также можно управлять.
Ссылочный тип данных также использует внешние наборы данных, но в данном случае можно подключать и использовать только отдельные поля.
Ограничения
Контроль значений, вводимых в поля набора данных, в Delphi возложен на объекты полей, а не на компоненты отображения данных. Именно в рамках этого мы рассмотрим работу методов-обработчиков событий базового класса TField.
Перед сохранением значения поля в БД всегда вызывается метод-обработчик onValidate, при этом автоматически проводится проверка на выполнение задаваемых ограничений и ограничений типа данных. Кроме этого, здесь можно предусмотреть свою дополнительную обработку:
procedure TForml.TablelSomeFieldValidate(Sender: TField);
begin
if (Sender as TField).Value < 0 then begin ShowMessage('Значение поля не может быть отрицательным');
(Sender as TField).Value := Null;
end;
end;
Различают контроль значения в целом и посимвольный контроль. Метод OnValidate проверяет значение поля целиком. Если при проверке обнаружена ошибка, то выдается сообщение (эту функцию выполняет BDE) и фокус формы устанавливается на соответствующем компоненте отображения данных.
Если метод OnValidate не вызвал исключительной ситуации, то при сохранении значения поля в БД вызывается обработчик события oncnange. В принципе, можно предусмотреть операции по контролю данных и в этом методе, но тогда в случае ошибки возникает нежелательная исключительная ситуация, которая может привести к серьезным сбоям в работе приложения.
Проверить текущее значение поля перед его появлением в компоненте отображения данных можно в методе-обработчике onGetText. Если параметр DisplayText принимает истинное значение, то в параметре Text передается значение свойства DisplayText (значение в строковом формате в таком виде, как оно будет показано в компоненте отображения данных — с символами форматирования). В противном случае в параметре Text передается текущее значение в строковом формате.
При использовании метода-обработчика OnGetText на программиста ложится обязанность самостоятельно предусмотреть передачу значения в компонент отображения данных, в противном случае компонент останется незаполненным.
В методе-обработчике OnSetText можно осуществлять текущий контроль значения в строковом формате в том виде, как оно представлено в компоненте отображения данных. Напомним, что этот обработчик вызывается при каждом изменении свойства Text класса TField.
Рассмотренные методы-обработчики удобнее всего использовать для проверки текущего значения с точки зрения программной логики. Например, чтобы отпускная цена была выше закупочной, или чтобы остаток был не больше первоначального количества товара в партии. Для проверки правильности самого значения класс TField имеет несколько полезных свойств.
Если на сервере БД задано ограничение на некоторое поле, его можно использовать в приложении Delphi при помощи свойства importedconstraint.
Для создания собственного ограничения можно использовать свойство customConstraint, в котором используется синтаксис SQL:
Value>10
или
OutputPrice>InputPrice*l.25
При возникновении ошибки будет совсем не лишним, если программа выдаст некое осмысленное сообщение, которое поможет пользователю исправить оплошность. При работе с методами-обработчиками это можно предусмотреть в программном коде.
Для встроенного контроля предусмотрено специальное свойство — ConstraintErrorMessage, которое выводится в виде сообщения при возникновении ошибки. Согласитесь, что это гораздо проще, чем исправлять и перекомпилировать соответствующие файлы ресурсов. Если приложение работает с сервером БД, и возникла ошибка ограничения поля, то выводится сообщение, определяемое сервером, а не этим свойством.
Если для поля заданы ограничения, то свойство HasConstraints принимает истинное значение.
Посимвольный контроль данных осуществляется свойством vaiidchars, в котором можно определить допустимые в строковом представлении значения поля символы и методом isvalidchar, который определяет допустимость использования переданного в параметре символа.
Еще один мощный инструмент контроля данных предоставляет свойство EditMask, которое позволяет создавать шаблоны ввода данных, облегчая тем самым работу пользователя и уменьшая возможность ошибки. Рассмотрим правила создания шаблонов.
Шаблон состоит из трех частей.
Первая часть содержит управляющие символы собственно шаблона. Доступные для создания шаблона символы приведены в табл. 17.3.
Таблица 17.3. Управляющие символы шаблона
Символ |
Описание |
> |
Все символы после данного преобразуются в прописные |
|
< |
Все символы после данного преобразуются в строчные |
|
<> |
Все символы после данного остаются в том регистре, какой был задан пользователем |
|
\ |
Символ, следующий за этим, считается алфавитным, а не управляющим |
|
L |
В позиции этого символа обязательно должен находиться только алфавитный символ |
|
| |
В позиции этого символа может находиться алфавитный символ |
|
А |
В позиции этого символа обязательно должен находиться алфавитный символ или цифра |
|
а |
В позиции этого символа может находиться алфавитный символ или цифра |
|
С |
В позиции этого символа обязательно должен находиться знак препинания |
|
с |
В позиции этого символа может находиться знак препинания |
|
0 |
В позиции этого символа обязательно должна находиться цифра |
|
9 |
В позиции этого символа может находиться цифра |
|
# |
В позиции этого символа может находиться цифра, плюс или минус |
|
: |
Символ разделения часов, минут и секунд (зависит от системных установок) |
|
/ |
Символ разделения дней, месяцев, годов (зависит от системных установок) |
|
; |
Символ разделения частей шаблона |
|
- |
Символ автоматического ввода в текст пробела |
|
! |
Символ определяет подавление пробелов: если он присутствует в маске (в любом месте), в выходном тексте удаляются пробелы перед текстом; в противном случае - идущие после текста |
В первую часть шаблона можно включать любые алфавитные символы (для создания поясняющих надписей, слов и сокращений), если их нет среди управляющих символов. Также можно использовать в качестве алфавитных и управляющие символы, для этого перед ними нужно помещать символ " \".
Вторая часть состоит из одного символа и определяет, могут ли неарифметические символы быть частью вводимого текста. Если здесь расположен ноль, то можно вводить только цифры, если любой другой символ — то можно использовать и алфавитные символы.
В третьей части содержится символ, используемый для обозначения мест, запрещенных для ввода.
Части шаблона разделяются точкой с запятой.
Например, шаблон для ввода телефонного номера выглядит следующим образом:
!\(999\)000-0000;1;_
Резюме
Работа с полями является важным этапом в процессе разработки приложения баз данных. В нем используются специальные объекты, которые инкапсулируют возможности полей таблицы БД. В Delphi имеется целая иерархия классов, обеспечивающих применение полей самых различных типов. В основе этой иерархии лежит класс TFieid.
Объекты полей играют важную роль в работе наборов данных. С их помощью можно получить доступ к текущим значениям, задать ограничения на вводимые величины и проверить их правильность.