Лабораторная работа №4
Программирование
приложения для работы с базами данных
Цель работы: Изучить основные свойства
и методы полей и наборов данных. Получить навыки при программировании работы с
БД. Клиентские наборы данных. Обработка
исключительных ситуаций.
Основные
теоретические сведения
Выполнение предыдущей лабораторной работы
при разработке приложения для работы с БД практически не требовало
программирования. Все сводилось к размещению на форме компонентов и заданию их
свойств. Но более изощренные приложения все-таки требуют программирования и
написания различных обработчиков событий.
Разработчик приложений БД в Delphi может
использовать ряд полезных механизмов набора данных, которые реализованы для
компонентов всех технологий доступа к данным.
К этим механизмам относятся методы быстрого
поиска и перехода к найденным записям; связывания наборов данных по
индексированным полям; метод дополнительной фильтрации записей набора данных.
Изменение состояния набора данных при
выполнении стандартных операций
В процессе своего функционирования (от
открытия методом Open и
до закрытия методом Close)
набор данных может выполнять самые разнообразные операции.
Закрытый набор данных всегда имеет
неактивное состояние dsInactive
(для перевода в это состояние используется метод Close). При открытии набор данных переходит в
состояние просмотра данных dsBrowse. В этом состоянии по записям набора данных
можно перемещаться и просматривать их содержимое, но редактировать данные
нельзя. Это основное состояние открытого набора данных, из него можно перейти в
другие состояния, но любое изменение состояния происходит через просмотр НД
(рис. 1).
При необходимости редактирования данных
набор должен быть переведен в состояние редактирования dsEdit (метод Edit). После
выполнения метода можно изменять значения полей для текущей записи. При
перемещении на следующую запись набор данных автоматически переходит в
состояние просмотра.
Метод Insert переводит набор данных в состояние вставки dsInsert и добавляет на месте текущего курсора
новую пустую запись. При переходе на другую запись, после проверки на
уникальность первичного ключа (если он есть) набор данных возвращается в
состояние просмотра.
Состояние установки ключа dsSetKey
используется только в табличных компонентах при необходимости поиска методами
FindKey и FindNext.

Рис. 1. Схема изменения состояний набора данных
Наиболее
важные свойства и методы компонента TDataSet
приводятся в табл.1.
Таблица 1. Свойства и методы
компонента TDataSet
|
Открытие/ закрытие НД |
|
|
property
Active: Boolean; |
Открывает набор данных(True) или закрывает(False) |
|
procedure
Open; |
Открывает набор данных |
|
procedure
Close; |
Закрывает набор данных |
|
Редактирование
записей |
|
|
Procedure
Edit; |
Переводит набор данных в режим
редактирования |
|
Procedure
Append; |
Добавляет пустую запись в конец набор
данных |
|
Procedure AppendRecord (const
Values:array of const); |
Добавляет пустую запись и наполняет ее
поля значениями Values |
|
Procedure
Insert; |
Переводит набор данных в режим вставки
записей |
|
Procedure InsertRecord(const
Values:array of const); |
Создает пустую запись, наполняет ее поля
значениями Values
и вставляет ее в набор данных |
|
Procedure
Delete; |
Удаляет текущую запись |
|
Procedure
Post;
Virtual |
Сохраняет вставленную или
отредактированную запись в таблице БД |
|
Перемещение по записям НД. |
|
|
procedure Next; |
Перемещает курсор на одну запись вперед |
|
procedure FindNext:Boolean; |
Пытается установить курсор на следующую запись и возвращает True в случае успеха |
|
procedure Prior; |
Перемещает курсор на одну запись назад |
|
procedure
First; |
Устанавливает курсор на первую запись в НД |
|
procedure
Last; |
Устанавливает курсор на последнюю запись в НД |
|
procedure
FindFirst:Boolean; |
Пытается установить курсор на первую запись и возвращает True в случае успеха |
|
procedure
FindLast:Boolean; |
Пытается установить курсор на последнюю запись и возвращает True в случае успеха |
|
procedure FindPrior:Boolean; |
Пытается установить курсор на предыдущую запись и возвращает True в случае успеха |
|
property
Eof: Boolean; |
Если достигнута последняя запись набора
данных, то возвращает True |
|
property
Bof: Boolean; |
Если достигнута первая запись набора
данных, то возвращает True |
|
function MoveBy(Distance: Integer):
Integer; |
Перемещение вперед и назад на заданное
число записей. Distance определяет
число записей. Если параметр отрицательный — перемещение осуществляется к
началу набора данных, иначе — к концу |
|
procedure
DisableControls |
В целях улучшения производительности при
навигации по НД временно запрещает смену данных в визуальных компонентах |
|
procedure
EnableControls |
Операция, обратная предыдущей |
|
Информация
о состоянии НД |
|
|
property
RecordCount: Integer |
Возвращает общее число записей в текущем
наборе данных |
|
function
IsEmpty: Boolean |
Возвращает True, если в НД нет записей |
|
property
RecNo: Integer |
Определяет номер текущей записи |
Программный доступ к записям НД и
редактирование записей НД
Для просмотра, вставки, редактирования и удаления
записей обычно используются соответствующие визуальные компоненты (TDBGrid, TDBEdit, TDBNavigator и т. д.),
которые автоматически вызывают нужные методы НД. В некоторых случаях может
понадобиться программный доступ.
При программном доступе, прежде всего,
необходимо убедиться в том, что НД содержит записи. Для этого необходимо
вызвать метод IsEmpty,
который возвращает False,
если в НД есть хотя бы одна запись. После использования или редактирования
текущей записи переход к следующей записи реализуется методом Next или FindNext,
например:
Способ 1: Table1.Open;
If not Table1.IsEmpty Then
Repeat
// Используем информацию из текущей записи
или редактируем ее
Until not Table1.FindNext; //Переходим к следующей
записи
Table1.Close;
Способ 2: Table1.Open;
While not Table1.EOF Do
Begin
// Используем информацию из текущей записи
или редактируем ее
Table1.Next; //Переходим к следующей записи
End;
Table1.Close;
Перед редактированием текущей записи, НД
необходимо перевести в состояние dsEdit методом Edit, а после редактирования сохранить сделанные
изменения в соответствующей таблице БД, вызвав метод Post:
Table1.Edit;
Table1[‘Number’]:=123456;
Table1.Post;
По такой же схеме осуществляется вставка или
добавление новой записи, при этом вместо Edit вызывается метод Insert (вставка записи) или Append (добавление записи). Если НД не
индексирован, вставленная запись помещается на место текущей записи, а текущая
запись становится следующей за ней; добавленная запись помещается в конец НД. Если
НД индексирован, оба метода вставляют запись в позицию, определяемую текущим
индексом.
Table1.Insert; // или Table1.Append;
Table1[‘Number’]:=123456;
Table1[‘Name’]:=’Петров П. П.’;
Table1[‘Children’]:=False;
Table1.Post;
Или же, вместо всех этих операторов может
быть использован единственный оператор, в котором элементы должны идти в
строгом соответствии с порядком следования полей в НД и содержать допустимые для каждого поля выражения:
Table1.InsertRecord([123456,’Петров П. П.’,False]);
Редактирование, удаление и вставка записей
возможно только для НД, свойство CanModify которых содержит значение True. Для компонента TTable это условие выполняется, если свойство ReadOnly содержит заданное по умолчанию значение
False.
Поля и типы данных
В наборе данных приложения баз данных Delphi
каждому полю соответствует собственный объект. Основой объектов полей является
класс TField. Назначение этого
класса, как базового класса поля, во-первых, заключается в умении
взаимодействовать с компонентом отображения данных для обеспечения правильной
визуализации данных. Например, объект поля хранит способ выравнивания,
параметры шрифта, текст заголовка и т. д. Во-вторых, с точки зрения набора
данных объект поля является хранилищем текущего значения этого поля (а не всего
столбца данных).
Тип данных однозначно связан с конкретным полем таблицы базы данных. Без этого
поля само понятие типа данных не имеет практического смысла. Класс TField
инкапсулирует свойства абстрактного поля, которое не имеет заранее
определенного типа данных. Уже от этого класса порождено целое семейство
классов для типизированных полей , каждый из которых умеет обращаться со своим
типом данных.
Весь список доступных типов данных
содержится в типе TFieldType:
type TFieldType = (ftUnknown,
ftString, ftSmallint, ftInteger, ftWord, ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate,
ftTime, ftDateTime, ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic,
ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar,
ftWideString, ftLargeInt,
ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob, ftVariant,
ftlnterface, ftlDispatch, ftGuid, ftTimeStamp, ftFMTBcd);
Статические и динамические поля
Объекты класса TField и производные от него классы (например, TStringField, TBooleanField, TSmallIntField и т. п.) могут создаваться тремя способами:
1) Автоматически генерироваться для каждого
компонента набора данных (Table и др.) в момент открытия базы данных, если эти объекты не создаются
другими способами. Объекты генерируются для всех полей таблицы, и это может
быть избыточно для конкретного приложения. Такие поля называются динамическими.
2) Статические поля создаются в процессе
проектирования с помощью Редактора полей (РП), что исключает автоматическое
создание объектов полей. Таким образом, все поля, объекты которых не созданы с
помощью РП, недоступны для приложения. Использование РП очень удобно, так как
позволяет задать в процессе проектирования множество полезных свойств для
каждого поля.
3) Создаваться программно в процессе
выполнения приложения (используется сравнительно редко).
Компонент набора данных после подключения к
таблице БД без дополнительных настроек использует только динамические поля. К
свойствам и методам динамических полей можно обратиться программно, для этого
следует использовать индексированное свойство Fields компонента доступа к
данным, которое объединяет все поля набора данных или метод FieldByName.
Динамические поля используются в
случаях, когда заданные характеристики полей в таблице базы данных полностью
удовлетворяют разработчика, и нет необходимости рассматривать какое-либо поле
вне набора данных.
Статические поля создаются программистом на
этапе разработки, их свойства доступны в Инспекторе объектов, а их названия
можно выбрать из списка объектов активной формы в верхней части Инспектора
объектов. Название статического объекта поля обычно складывается из названий
таблицы и поля.
Как только для набора данных создан хотя бы один статический объект поля, считается, что набор данных содержит только те поля, которые имеются в списке статических полей. Эту особенность можно использовать для искусственного ограничения структуры данных таблиц. Все поля набора данных расположены в том порядке, как это указано в списке Редактора полей, независимо от их реального положения в таблице БД.
Наиболее
важные свойства и методы компонента TField приведены в табл. 2.
Таблица 2. Свойства и методы
компонента TField
|
property
Fields: TFields |
Совокупность полей набора данных |
|
property
FieldDefs: TFieldDefs |
Все необходимые параметры полей |
|
property
FieldCount: Integer |
Общее число полей набора данных |
|
property FieldValues[const FieldName:
string]: Variant; default |
Доступ к значениям полей текущей записи,
где в параметре FieldName задается имя поля |
|
function FieldByName (const FieldName:
string): TField |
Обращается к полям набора данных по имени |
|
procedure GetFieldNames(List: TStrings) |
Возвращает в параметр List полный список имен полей набора данных |
Доступ к объектам полей возможен тремя
способами:
1) по
порядковому индексу объекта. Осуществляется через свойство Fields[i:integer], где i – индекс объекта, индексация начинается с
0. Например, Table1.Fields[0] – это первый объект поля таблицы Table1.
2)по имени поля. Осуществляется с помощью
метода FieldByName(‘<имя>’).
Например, Table1.FieldByName(‘Fam’) – это объект, связанный с полем Fam.
3)по имени объекта. Возможен только к
объектам, созданным с помощью РП. По умолчанию
эти имена формируются из имени таблицы и имени поля – Table1Fam.
Автоматически создаваемые объекты имени не
имеют – их свойство Name
пусто. Поэтому для них обращение по имени невозможно.
Контроль
ввода данных
Контроль вводимых в поля
набора данных в Delphi возложен на объекты полей, а не на компоненты
отображения данных. С некоторыми приемами ограничений ввода данных вы
познакомились в предыдущей лабораторной работе.
Еще один мощный инструмент контроля данных
предоставляет свойство EditMask, которое позволяет создавать шаблоны ввода
данных, облегчая тем самым работу пользователя и уменьшая возможность ошибки.
Шаблон состоит из трех частей. Доступные для
создания шаблона символы приведены в табл. 3.
Таблица 3. Управляющие
символы шаблона
|
Символ |
Описание |
|
! |
Подавляет вывод всех начальных
пробелов |
|
> |
Все символы после этого преобразуются в
заглавные |
|
< |
Все символы после этого преобразуются в
строчные |
|
<> |
Все символы после этого остаются в том
регистре, как это было задано пользователем |
|
\ |
Символ, следующий за этим, считается
алфавитным, а не управляющим |
|
L |
В позиции этого символа обязательно должен
находиться только алфавитный символ |
|
I |
В позиции этого символа может находиться
алфавитный символ |
|
А |
В позиции этого символа обязательно должен
находиться алфавитный символ или цифра |
|
а |
В позиции этого символа может находиться
алфавитный символ или цифра |
|
C |
В позиции этого символа обязательно должен
находиться знак препинания |
|
с |
В позиции этого символа может находиться
знак препинания |
|
0 |
В позиции этого символа обязательно должна
находиться цифра |
|
9 |
В позиции этого символа может находиться
цифра |
|
# |
В позиции этого символа может находиться
цифра, плюс или минус |
|
: |
Символ разделения часов, минут и секунд
(зависит от системных установок) |
|
/ |
Символ разделения дней, месяцев, годов
(зависит от системных установок) |
|
; |
Символ разделения частей шаблона |
|
- |
Символ автоматического ввода в текст
пробела |
В первую часть шаблона можно включать любые
алфавитные символы (для создания поясняющих надписей, слов и сокращений), если
их нет среди управляющих символов. Также можно использовать в качестве
алфавитных и управляющие символы, для этого перед ними нужно помещать символ
"\".
Вторая часть состоит из одного символа и
определяет, могут ли не арифметические символы быть частью вводимого текста.
Если здесь расположен ноль, то можно вводить только цифры, если любой другой
символ — можно использовать и алфавитные символы.
В третьей части содержится символ,
используемый для обозначения мест, запрещенных для ввода.
Части шаблона разделяются точкой с запятой.
Например, шаблон для ввода телефонного
номера выглядит следующим образом:
!\{999\)000-0000;1;_
Связанные
таблицы
В рамках одного проекта таблицы БД можно
связывать отношениями "один- ко- многим" и "многие- ко-
многим", при этом отношения обязательно устанавливаются между
индексированными полями двух таблиц.
При создании отношений, в качестве главной
таблицы можно использовать любой компонент, инкапсулирующий набор данных. Для
задания подчиненной таблицы можно использовать только табличные компоненты.
Для установления отношения "один-
ко- многим" в наборе данных предназначены два свойства — MasterSource и MasterFields, которые задаются для подчиненной
таблицы. Набор данных главной таблицы не требует никаких дополнительных настроек,
и заданная связь будет работать только при перемещениях по записям главной
таблицы.
Свойство MasterSource определяет компонент TDataSource, который связан с главной таблицей.
Затем при помощи свойства MasterFields необходимо установить отношения между
полями главной и подчиненной таблицы. В нем содержится имя индексированного
поля, по которому устанавливается связь. Если таких полей несколько, их имена
разделяются точкой с запятой. При этом не все поля, входящие в индекс, обязаны участвовать
в создании отношения.
Для задания свойства MasterFields можно использовать Редактор связей полей (Field
Link Designer), который
вызывается щелчком на кнопке в поле редактирования этого свойства в Инспекторе
объектов.
Здесь в разворачивающемся списке Available
Indexes выбирается требуемый индекс для подчиненной таблицы.
После этого в списке Detail Fields появляются
имена всех полей, входящих в этот индекс. В списке Master Fields отображаются
все поля главной таблицы.
Теперь требуется создать связи между полями.
Для этого в левом списке выбирается поле подчиненной таблицы, а затем
соответствующее ему поле главной таблицы в правом списке. После этого
активизируется кнопка Add, щелчок на которой создает
отношение по двум полям главной и подчиненной таблиц. Созданная связь
отображается в списке Joined Fields.
После
создания связи по индексированным полям данный индекс становится текущим для
набора данных. После
создания связей между полями отношение "один- ко- многим" считается
установленным. Теперь достаточно открыть оба набора данных, чтобы увидеть
работу отношения. В результате, при перемещении по записям главной таблицы, в
таблице подчиненной будут показаны только те записи, которые относятся к
текущей записи главной таблицы.
Отношение "многие- ко- многим" отличается тем, что подчиненная таблица еще
раз связывается в качестве главной с другой подчиненной таблицей аналогичной
последовательностью действий, как и в отношении "один- ко- многим".
Основные приемы работы с наборами данных
Поиск
данных
В наборе данных реализованы два способа
поиска записей по заданным значениям полей. Один способ основан на
использовании индексов и является более быстрым, но поиск проводится только по
индексированным полям. Второй способ применяет специальные методы классов
наборов данных и позволяет проводить поиск по любому сочетанию полей, но он
более медленный.
Для организации индексного поиска к
набору данных должен быть подключен индекс (свойства IndexName или IndexFieldNames).
Метод FindKey
проводит поиск записи по заданным в параметре значениям ключевых полей текущего
индекса набора данных. В случае успеха курсор набора данных устанавливается на
найденной записи, а метод возвращает значение True, в противном случае — False. Если индекс состоит из нескольких полей,
значения для поиска записываются в виде множества.
Рассмотрим пример, в котором реализован
поиск по вторичному индексу в таблице CUSTOLY.DB демонстрационной базы данных
DBDEMOS. Индекс основан на полях Last_Name и First_Name (рис.
1).
В компоненте Tаblе1, помимо стандартных настроек на
таблицу, при помощи свойства IndexName задан и вторичный индекс (его имя
Names). Значения для поиска задаются в компонентах Edit1 и Edit2.

Рис. 1. Главная форма проекта DemoFind
Пример работы с индексами
implementation
{$R
*.DFM}
procedure
TForml.FormShow(Sender: TObject);
begin
try
Table1.Open;
except
on
E: EDBEngineError do ShowMessage('Ошибка при открытии таблицы');
end;
end;
procedure
TForml.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Table1.Close;
end;
procedure
TForml.FindBtnClick(Sender: TObject);
begin
try
if
not Table1.FindKey([Editl.Text, Edit2.Text])
then ShowMessage('Запись не
найдена');
except
on E: EDatabaseError
do
ShowMessage('Ошибка поиска');
end;
end;
end.
Набор данных открывается в
методе-обработчике FormShow при открытии формы и закрывается в
методе-обработчике FormClose.
При щелчке на кнопке FindBtn в метод FindKey
передаются значения для поиска из компонентов Edit1 и Edit2.
Данный пример демонстрирует также и приемы
обработки исключительных ситуаций двух классов исключений: EDatabaseError (ошибка работы с базой данных) и EDBEngineError (ошибка в BDE). Эти классы имеют дополнительные поля
(свойства), уточняющие вид ошибки. Переменная Е, используемая в операторе on, используется для того, чтобы можно было
сослаться на поле по имени объекта исключения. Эта переменная носит сугубо
локальный характер и нигде ранее не должна определятся.
Для поиска по произвольной выборке полей
можно использовать методы Locate и Lookup: function Locate (const KeyFields: string; const KeyValues: Variant; Options; TLocateOptions): Boolean;
function Lookup (const
KeyFields: string; const KeyValues: Variant; const ResultFields: string):
Variant;
В метод Locate необходимо передать список
полей, по которым будет идти поиск (параметр KeyFields, имена полей разделяются
точкой с запятой), их требуемые значения (параметр KeyValues, значения
разделяются запятой) и настройки поиска (параметр options). В настройках можно
задать опцию loCaseinsensitive, которая отключает проверку на регистр символов,
и опцию loPartialKey, которая включает поиск с минимальными
отличиями. В случае успеха поиска курсор набора данных устанавливается на
найденной записи, а метод возвращает значение True.
Tablel.Locate('Last__Name;First_Name',
VarArrayOf(['Editl.Text',
'Edit2.Text']), []};
В метод Lookup передается список полей для
поиска (параметр KeyFields, имена полей разделяются точкой с запятой) и их
требуемые значения (параметр KeyValues, значения разделяются запятой). В случае
успешного поиска функция возвращает массив значений типа вариант для полей,
названия которых содержатся в параметре ResultFields.
Tablel.Lookup('Last_Name;First_Name', VarArrayOf(['Editl.Text',
1Edit2.Text']), 'Last_Name;First_Name');
Оба эти метода автоматически используют
быстрый индексный поиск в случае, если в параметре KeyFields задать поля
индекса.
Фильтры
Наиболее эффективным способом отбора записей
в набор данных (особенно из больших таблиц) является создание и выполнение
соответствующего запроса SQL. Но если набор данных функционирует на базе
табличного компонента, то может быть использован встроенный в набор данных
механизм фильтрации.
Применение фильтра основано на двух основных
свойствах и одном вспомогательном. Текст фильтра должен содержаться в свойстве
Filter, a свойство Filtered включает и выключает фильтр. Параметры фильтра
определяются свойством FilterOptions.
Компонент
TQuery также может использовать фильтры. Эта возможность подчас позволяет легко
и изящно решать довольно сложные проблемы, которые иначе требуют изменения
текста запроса или создания нового компонента запроса.
При использовании фильтра его текст
транслируется в синтаксис SQL и передается для выполнения на сервер или через
соответствующий драйвер в локальную СУБД.
Для создания более сложных фильтров с
применением всех возможных средств языка программирования применяется
метод-обработчик набора данных OnFilterRecord.
Фильтры можно разделить на статические и
динамические. Статические фильтры создаются во время разработки
приложения и могут использовать как свойство Filter, так и метод OnFilterRecord. Динамические фильтры можно создавать
и редактировать во время выполнения приложения, для них используется только
свойство Filter.
При создании текста фильтра для свойства
Filter используются имена полей соответствующей таблицы БД, а для задания
отношений применяются все операторы сравнения (>, >=, <, <=, =,
<>) и логические операторы (AND, OR, NOT):
Fieldl>100 AND Field2=20
Сравнивать между собой два поля нельзя.
Для строковых полей в фильтрах можно
производить отбор по частям строк, для этого используется символ звездочка: ItemName='A*'
При создании динамических фильтров можно
изменять как выражение фильтра целиком, так и его части. Например,
ограничивающее значение для поля можно задавать при помощи элементов управления
формы, что позволяет пользователю приложения управлять фильтрацией набора
данных:
procedure
TForml.EditlChange(Sender: TObject);
begin
with
Tablel do begin
Filtered
:= False;
Filter
:= 'Fieldl>=' + TEdit(Sender).Text; Filtered := True;
end;
end;
Фильтр начинает работать только после того,
как свойству Filtered присваивается истинное значение. Перед изменением текста
динамического фильтра или для отключения фильтра свойству Filtered
присваивается значение False.
Параметры фильтра определяются свойством FilterOptions: параметр foCaseInsensitive, будучи включенным в свойстве,
отключает сравнение строковых значений с учетом регистра символов; параметр
foNoPartialCompare отключает отбор строковых значений по части строки.
Метод-обработчик onFilterRecord имеет следующее объявление:
type
TFilterRecordEvent = procedure(DataSet: TDataSet; var Accept: Boolean) of
object;
property
OnFiiterRecord: TFilterRecordEvent;
Если этот метод создан для набора данных, то
он вызывается для каждой его записи. Программный код метода должен присваивать
параметру Accept истинное или ложное значение. В результате запись передается в
набор данных или отсекается:
procedure
TForml.TablelFilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
Accept
:= ArchOrdersArchDat.AsString >= DateEditl.Text;
end;
Важнейшее преимущество метода
onFilterRecord, по сравнению со свойством Filter, заключается в том, что в этом
методе-обработчике можно сравнивать поля и производить вычисления над их
значениями.
Быстрый
переход к помеченным записям
Закладки, как инструмент работы с записями
набора данных, позволяют осуществлять быстрое перемещение на нужную запись.
Набор данных может содержать неограниченное число закладок, каждая из которых
представляет собой указатель. Закладку можно создать только для текущей записи
набора данных.
При работе с закладками используются три
основных метода:
· метод GetBookmark
создает новую закладку для текущей записи;
· метод GotoBookmark
осуществляет переход к закладке, переданной в параметре;
· метод FreeBookmark
удаляет закладку, переданную в параметре.
Кроме этого, можно использовать метод Bookmarkvalid, который проверяет, указывает ли закладка
на реально существующую запись. Метод compareBookmark позволяет сравнить между
собой две закладки:
var Bookmarkl, Bookmark2: TBookmark;
...
if
Tablel.CompareBookmark(Bookmarkl, Bookmark2) = 1
then ShowMessage (' Закладки
одинаковы') ;
В наборе данных имеется свойство Bookmark,
которое содержит название текущей закладки.
Рассмотрим небольшой пример, где право
управлять закладками предоставлено пользователю. На форме, вид которой приведен
на рис. 2, имеются две кнопки: кнопка startBookmark (помечает текущую запись), и
кнопка stopBookmark (с помощью которой происходит переход к закладке, а затем ее
уничтожение).

Рис. 2. Главная форма проекта DemoBookmark
Пример использования закладок .
implementation
{$R
*.DFM}
var
SaveRecPos: TBookMark;
procedure
TMainForm.FormShow(Sender: TObject);
begin
Table1.Open;
BookmarkControl.Brush.Color
:= clBtnFace;
end;
end;
procedure
TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Table1.Close;
end;
procedure
TMainForm.StartBookmarkClick(Sender: TObject);
begin
if
Not Table1.BookmarkValid(SaveRecPos)
then
SaveRecPos := Table1.GetBookmark;
BookmarkControl.Brush.Color
:= cILime
end;
procedure
TMainForm.StopBookmarkClick(Sender: TObject);
begin
with
Table1 do begin if Not BookmarkValid(SaveRecPos)
then
Exit;
GotoBookmark(SaveRecPos);
FreeBookmark(SaveRecPos);
SaveRecPos
:= Nil;
end;
BookmarkControl.Brush.Color
:= clBtnFace;
end;
end.
Использование метода Bookmarkvaiid позволяет
корректно переопределять закладку, если она уже установлена, и избежать ошибок
при произвольных нажатиях кнопок. Компонент BookmarkControl типа TShape
сигнализирует о том, что закладка установлена или удалена.
Бизнес – правила
Бизнес – правила определяют реакцию системы на добавление, изменение или удаление данных, обеспечивая непротиворечивость и ссылочную целостность БД. Если, например, вы удалите запись из списка книг какой – либо накладной, программа должна автоматически изменить сумму в соответствующей накладной, количество книг на складе и сальдо партнера, в противном случае мы получим недостоверную накладную, неверное количество книг и ошибочное сальдо. Такая реакция программы и есть бизнес-правило удаления записи из списка книг.
Бизнес –
правила разрабатываются на основе тщательного изучения автоматизируемой области
человеческой деятельности. Для реализации каскадных изменений и бизнес – правил
в файл – серверных БД обычно используются обработчики событий AfterXXXX и BeforeXXXX компонентов-наборов данных TTable или
TQuery. Эти две группы
событий связаны с изменениями НД (вставка, удаление или редактирование записи)
и отличаются тем, что событие BeforeXXXX непосредственно перед изменением НД, а события AfterXXXX – сразу после
изменения. К ним относятся:
- property After/BeforeCansel – возникает после/до
отмены изменений в текущей записи;
- property After/BeforeClose – возникает после/до
закрытия НД;
- property After/BeforeDelete – возникает после/до
удаления текущей записи;
- property After/BeforeEditl – возникает после/до
редактирования текущей записи;
- property After/BeforeInsert – возникает после/до
вставки новой записи;
- property After/BeforeOpen – возникает после/до
открытия НД;
- property After/BeforePost – возникает после/до
выполнения метода Post;
- property After/BeforeScroll – возникает после/до
перехода к новой записи.
В клиент-серверных системах эти действия, как правило, реализуются на сервере БД с помощью триггеров (процедур, которые автоматически запускаются при вставке, изменении или удалении записей).
Помимо рассмотренных ранее методов
модификации записей компонент Table имеет также ряд методов, позволяющих модифицировать таблицы, основные
из которых приводятся в табл. 4.
Таблица 4. методы для работы с таблицами
|
CreateTable |
Метод создает новую таблицу, исходя из установок компонента Table, содержащихся в свойствах Fields или FieldDefs. Если таблица с именем, указанным в свойстве TableName уже имеется, она будет переписана. Применение этого метода позволяет,
например, взять структуру существующей таблицы, как-то изменить ее, затем
изменить свойство TableName на имя новой таблицы и создать эту таблицу |
|
DeleteTable |
Метод уничтожает существующую таблицу,
которая задана свойствами DataBaseName и TableName. Таблицу надо предварительно закрыть. |
|
RenameTable(s) |
Метод переименовывает существующую
таблицу, присваивая ей новое имя, содержащееся в s. Одновременно переименовываются все
сопутствующие таблице файлы. |
Пример использования методов и свойств
компонента Table
для создания таблицы.
procedure TForm1.FormCreate(Sender: TObject);
begin
// Компонент
table1
делается неактивным
table1.Active:=false;
table1.TableName:='dep2.db';
with table1 do begin
tableType:=ttParadox;
// Описываются поля таблицы
with
table1.FieldDefs do begin
clear;
with
addfieldDef do begin
name:='Pole1';
dataType:=ftString;
size:=20;
Required:=true;
end;
with table1.FieldDefs.AddFieldDef do begin
name:='Pole2';
datatype:=ftBoolean;
end;
end;
//
Создание индекса без имени – первичный ключ
with IndexDefs do begin
clear;
with AddIndexDef do begin
Name:='';
Fields:='Pole1';
Options:=[ixPrimary];
end;
end;
CreateTable;
table1.active:=true;
end;
end;
Индексы
в наборе данных
Важнейшей проблемой для любой БД является достижение
максимальной производительности и ее сохранение при дальнейшем увеличении
объемов хранимых данных. Использование индексов позволяет решить эту задачу.
Индекс представляет собой часть базы данных, в которой содержится информация об
организации данных в таблицах БД.
В отличие от ключей, которые просто
идентифицируют отдельные записи, индексы занимают дополнительные объемы памяти
(довольно значительные) и могут храниться как совместно с таблицами, так и в
виде отдельных файлов. Индексы создаются вместе со своей таблицей и обновляются
при модификации данных. При этом работа по обновлению индекса для большой
таблицы может отнимать много ресурсов, поэтому имеет смысл ограничить число
индексов для таких таблиц, где происходит частое обновление данных.
Индекс содержит в себе уникальные
идентификаторы записей и дополнительную информацию об организации данных.
Поэтому если при выполнении запроса сервер или локальная СУБД обращается для
отбора записи к индексу, то это занимает значительно меньше времени, т. к.
понятно, что идентификатор гораздо меньше самой записи. Кроме этого, индекс
"знает", как организованы данные и может ускорять обработку за счет
группирования записей по сходным значениям параметров.
Создание для БД эффективного набора индексов
является нетривиальной задачей.
Во-первых, нужно верно определить
оптимальное число индексов для каждой таблицы. Во-вторых, каждый индекс должен
содержать только необходимые поля, при этом большую роль играет их
упорядочивание.
Свойства и методы для работы с индексами
присутствуют только в табличных компонентах, т. к. в компонентах запросов
работа с индексами осуществляется средствами SQL.
Для смены текущего
индекса предназначены свойства IndexName
и IndexFieldNames.
В первое из них нужно поместить имя индекса, во второе – список индексных
полей, входящий в состав индекса. Эти свойства взаимоисключающие: установка
значения в одно из них очищает другое. Если НД открыт, смена текущего индекса
приводит к немедленному изменению порядка следования записей в таблице.
Добавление
нового индекса осуществляется методом:
Procedure AddIndex (const Name, Fields: String; Options: TIndexOptions)
и происходит в режиме эксклюзивного доступа к ТБД, переход в который
осуществляется установкой свойства Exclusive
в значение True.
Этот доступ означает, что никто, кроме вашей программы, не только не может
вносить изменения в ТБД, но вообще не имеет к ней доступа. Установить
эксклюзивный доступ можно только тогда, когда ТБД не открыта.
Параметр
Name определяет имя индекса, а параметр
Fields
– список индексных полей. Параметр Options
является множеством, которое содержит
значения, определяющие свойства индекса: ixCaseInsensitive
– индекс чувствителен к регистру букв; ixDescending
– индексные поля сортируются по убыванию значений; ixPrimary
– создается первичный ключ; ixUnique
– значения полей в индексе должны однозначно определять запись.
Удаление
существующего индекса
Происходит
в режиме эксклюзивного доступа к ТБД (свойство Exclusive
= True)
и осуществляется методом: Procedure DeleteIndex (const Name: String)
Задание к лабораторной работе:
Построить пример, использующий различные способы
программирования работы с базой данных.
Требования
к выполнению лабораторной работы
1) Приложение должно быть разработано для
работы с базой данных, созданной в лабораторной работе №2.
2) Одна из таблиц БД должна быть создана
методом CreateTable.
3) На отдельной форме отобразить набор данных
одной из таблиц и создать группу кнопок, запрограммировав за ними действия,
имитирующие работу навигатора.
4) Реализовать на уровне приложения ограничения
ссылочной целостности таблиц (каскадное удаление и изменение записей в
связанных таблицах).
5) Установить связи между таблицами с помощью
Инспектора объектов. На отдельной форме приложения выполнить демонстрационный
пример для отображения работы связанных между собой таблиц (использовать
стандартные компоненты визуализации данных).
6) Реализовать на уровне приложения возможности
пользователя по созданию, смене и удалению индексов. Предусмотреть возможность
демонстрации изменения порядка следования записей при смене индекса.
7) Реализовать на уровне приложения возможности
пользователя по созданию и редактированию фильтров. Предусмотреть возможность
демонстрации работы фильтров.
8) Продумать механизм бизнес – правил для
корректной работы приложения и реализовать его путем программирования.
9) Реализовать в приложении инструмент контроля
для ввода данных в поля, используя при этом различные возможности ограничения
ввода данных и шаблоны ввода.
10) Продемонстрировать в разрабатываемом
приложении различные приемы программирования работы с базой данных для поиска
записей в наборе данных и установки закладок,
обработку исключительных ситуаций.
Контрольные вопросы:
1. Перечислите
механизмы набора данных, которые
реализованы для компонентов всех технологий доступа к данным.
2. В каких состояниях может находиться набор данных при выполнении стандартных операций?
3.
Какие визуальные компоненты используются для просмотра, вставки, редактирования и
удаления записей?
4.
Какие методы
используются для перехода к следующей записи?
5.
Что необходимо
сделать перед тем, как редактировать, добавлять или удалять записи?
6.
Какой класс
является основой объектов полей?
7.
Опишите
взаимосвязь типа данных и поля таблицы БД.
8.
Перечислите
способы создания объектов класса TField и производных от него
классов.
9.
Что такое
статические поля?
10. Что такое динамические поля?
11. Перечислите способы доступа к объектам полей.
12. Опишите инструмент контроля ввода данных EditMask (шаблон ввода данных).
13. Каким образом осуществляется поиск данных в приложении?
14. Для чего и каким образом используются фильтры?
15. Как осуществляется быстрый переход к помеченным записям?
16. Что такое бизнес-правила? Для чего они предназначены? Как реализуются основные механизмы?
17. Какие методы модификации таблиц содержит компонент TTable?
18. Что
такое индекс? В чем отличие между индексом и ключом?