Опубликован: 07.05.2010 | Уровень: специалист | Доступ: свободно
Лекция 5:

Таблицы Paradox в ADO

< Лекция 4 || Лекция 5 || Лекция 6 >
Аннотация: На этой лекции вы создадите небольшое приложение с двумя таблицами Paradox. Это приложение нам понадобится в дальнейшем, для изучения свойств полей. В приложении для доступа к этим таблицам используем механизм ADO, для чего нам потребуется создать и настроить поставщика данных ODBC. Для таблиц будет использоваться связь один-ко-многим для создания подстановочного lookup поля.

Подключение таблиц Paradox 7 к приложению через ADO

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

Прежде всего, определимся с таблицами. Таблицы будем создавать в формате Paradox 7, описание типов полей которого подробно рассматривалось на лекции №30 курса "Введение в программирование на Delphi ". Для доступа к данным этих таблиц используем механизм ADO. Создавать таблицы удобней с помощью утилиты Database Desktop, входящей в состав Delphi.

Пусть главная таблица называется Food, ее поля описаны в таблице 5.1:

Таблица 5.1. Поля таблицы Food
Имя поля: Тип Описание
FKey Auto increment (+) Ключевое поле, служит счетчиком блюд.
FName Alpha (A) Текстовое поле размером 30, название блюда.
FType Long Integer (I) Поле служит для связи с подчиненной таблицей, в которой хранятся названия типов (супы, напитки, салаты и т.п.)
FVeget Logical (L) Логическое поле - вегетарианская еда, или нет. Потребуется для изучения свойств логических полей.
FCena Money ($) Поле денежного типа. Стоимость блюда.

Подчиненная таблица будет еще проще:

Таблица 5.2. Поля таблицы Tips
Имя поля: Тип Описание
TKey Auto increment (+) Ключевое поле, служит счетчиком типов.
TName Alpha (A) Текстовое поле размером 20, название типов.

Итак, начнем. Откройте утилиту Database Desktop. Чтобы облегчить работу и не искать каждый раз нужный каталог, укажем сразу рабочую папку, которую нужно вначале создать средствами Windows:

C:\Menu

Для этого выберите команду меню "File -> Working Directory ". В открывшемся окне нажмите кнопку " Browse " и найдите эту директорию на диске. Когда вы выберите ее, нажмите кнопку " OK ". Теперь эта папка стала папкой "по умолчанию". При попытке открыть или создать таблицу в утилите Database Desktop, эта папка всегда будет текущей (если в дальнейшем вы не смените рабочую папку).

Далее выбираем команду "File -> New -> Table". Оставьте тип Paradox 7, нажмите "ОК".

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

Как только вы ввели названия, типы и размеры (размер есть только у текстового поля), в списке " Table properties " выберите команду " Table Language " и нажмите кнопку " Modify ". В выпадающем списке выберите язык, как на рисунке 5.1:

Выбор языкового драйвера для таблицы Paradox

Рис. 5.1. Выбор языкового драйвера для таблицы Paradox

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

Далее нажмите кнопку " Save as " и укажите имя таблицы: Food. Таким же образом сделайте таблицу Tips, руководствуясь таблицей 5.2.

После этого вы можете закрыть утилиту Database Desktop, она больше не нужна.

Пойдем дальше. Поскольку мы собираемся подключаться к таблицам Paradox с помощью механизма доступа к данным ADO, нам потребуется установить на компьютере нужный драйвер ODBC. Для этого откройте Панель управления (Пуск -> Настройка -> Панель управления). Если вы используете Windows 2000, XP или более новую, вам придется еще выбрать команду "Администрирование". Далее открываем " Источники данных ODBC ". Нажимаем кнопку "Добавить", выбираем драйвер " Microsoft Paradox Driver (*.db) " и нажимаем кнопку "Готово". Далее в поле "Имя источника данных" укажите MenuParadox, этот источник мы будем использовать только для этой нашей программы. Затем уберите галочку "Использовать текущий каталог" и нажмите кнопку "Выбор каталога". В открывшемся окне выберите нашу папку C:\Menu:

Установка драйвера ODBC

Рис. 5.2. Установка драйвера ODBC

Нажимаете "ОК", и драйвер готов. Теперь можете закрыть все остальные окна, они больше не нужны.

Загружаете Delphi. Свойству Name главной формы присвойте имя fMain, сохраните модуль формы как Main, а проект в целом как MyMenu. В свойстве Caption формы напишите "Изучение свойств полей". На форму бросьте компонент Panel с вкладки Standard, свойство Align установите в alTop. Ниже с вкладки Data Controls установите компонент DBGrid, в свойстве Align которого выберите alClient, чтобы заполнить оставшееся пространство. Затем на панель установите простую кнопку, в свойстве Caption которой напишите "Типы блюд". У вас должна получиться такая форма:

Главная форма проекта

Рис. 5.3. Главная форма проекта

Раз у нас еще будет форма с типами блюд, следовательно, понадобится и модуль данных, общий для всех форм. Выберите команду File -> New -> Data Module. В свойстве Name модуля укажите fDM и сохраните модуль под именем DM.

Теперь с вкладки ADO устанавливаем компонент ADOConnection. Сразу свойство Name для краткости обращения переименуйте в Con1. Займемся подключением. Дважды щелкните по компоненту, чтобы открыть редактор подключений. Нажмите кнопку " Build ". На вкладке "Поставщик данных" по умолчанию должен быть " Microsoft OLE DB Provider for ODBC Drivers ". Нам нужен именно этот поставщик. Переходим на вкладку "Подключение" (для этого можете просто нажать кнопку "Далее"). В выпадающем списке "Использовать имя источника данных" нам нужно выбрать MenuParadox, то подключение, которое мы создали ранее. К слову сказать, мы могли и не указывать адрес данных, могли оставить галочку "Использовать текущий каталог". В этом случае таблицы нужно было бы расположить там же, где и программа, или создавать подключение при работающей программе. Так удобно делать при использовании локальной базы данных. Способ, которым мы воспользовались сейчас, более удобен для многопользовательских файл-серверных БД. Если бы базы лежали где-то на сетевом диске, тогда мы могли бы в качестве папки указать сетевой путь, например:

\\myserver\Menu

но тогда эта папка должна быть открыта в сети как общий ресурс.

Нажмите кнопку "Проверить подключение". Если вышло сообщение "Проверка подключения выполнена", значит, вы все сделали правильно, и ошибок нет.

Нажимаем кнопку "ОК", чтобы подтвердить подключение, и еще раз "ОК", чтобы закрыть окно подключений. Сразу же свойство LoginPrompt компонента Con1 переводим в False, чтобы каждый раз при подключении программа не запрашивала имя пользователя и пароль. Затем в свойстве Connected устанавливаем True. Подключение произошло.

Далее с вкладки ADO устанавливаем два компонента ADOTable. Выделите оба компонента, и в их свойстве Connection выберите наш Con1. Займемся вначале первой таблицей. В свойстве TableName выберите таблицу Food, свойство Name переименуйте в FoodT, а свойство Active переведите в True. Для второго компонента ADOTable выберите таблицу Tips, а компонент переименуйте в TipsT. Также переведите Active в True. Далее рядом с таблицами установите два компонента DataSource с вкладки Data Access. Первый переименуйте в FoodDS, второй - в TipsDS. В свойстве DataSet каждого выберите соответствующую таблицу. Не забудьте сохранить проект.

Перейдите на главную форму. Командой File -> Use unit подключитесь к созданному модулю данных. В свойстве DataSource сетки DBGrid выберите fDM.FoodDS. На сетке должны появиться столбцы с данными. Нажмите кнопку Run на панели инструментов или горячую клавишу F9. Проект компилируется, запускается, и… выходит ошибка:

Ошибка при компиляции

Рис. 5.4. Ошибка при компиляции

В чем дело? Вроде бы, мы все делали правильно, иначе на сетке DBGrid не появились бы нужные столбцы? Просто мы добрались до проблем с полями. Нажмите кнопку "ОК", затем выберите Run -> Program reset, чтобы закрыть повисшую программу. Теперь перейдите на окно модуля данных, щелкните дважды по компоненту FoodT, чтобы вызвать редактор полей. Затем щелкните по окну редактора правой кнопкой и выберите команду Add all fields (Добавить все поля). То же проделайте и со второй таблицей. Снова сохраните проект, скомпилируйте его и запустите - теперь полный порядок, программа запускается и выполняется нормально.

Дело в том, что, используя драйверы ODBC с "неродными" форматами баз данных, такими как dBase или Paradox, приходится точно указывать, какие у нас поля, а не надеяться на авто-определение. Вообще, создавать поля для каждого набора данных считается хорошим тоном в программировании.

Далее создадим еще одну форму, для редактирования типов блюд:

Форма редактора типов блюд

Рис. 5.5. Форма редактора типов блюд

Форму назовите fMyTypes, сохраните модуль как MyTypes. Чтобы убрать из окна лишние кнопки системной строки и не позволять пользователю менять размеры окна, в свойстве BorderStyle формы выберите значение bsDialog. Не забудьте подключить к нему модуль данных DM. На форме две простых кнопки, поле DBEdit с вкладки Data Controls и сетка DBGrid с этой же вкладки. В свойстве DataSource и сетки, и поля выберите fDM.TipsDS. У поля DBEdit, кроме того, в свойстве DataField выберите поле TName. Обратите внимание, что я назвал форму и модуль как MyTypes, а не просто как Types. Слово Types (типы) довольно распространенное в языках программирования и может вызвать конфликт названий. Попробуйте, если не верите!

Дважды нажимаем на верхнюю кнопку, и в обработчике пишем код:

//добавляем запись:
  fDM.TipsT.Append;
  //переводим фокус:
  DBEdit1.SetFocus;

В коде обработки нижней кнопки просто закрываем окно:

Close;

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

{если изменения есть, спросим что с ними делать. если пользователь
   не желает их сохранять, отменяем изменения. иначе сохраняем: }
  if fDM.TipsT.Modified then
    if Application.MessageBox('Данные изменены! Сохранить?',
       'Внимание!', MB_YESNO+MB_ICONQUESTION) <> IDYES then
         fDM.TipsT.Cancel
    else fDM.TipsT.Post;

Далее переходим на главную форму, командой File -> Use Unit подключаем модуль MyTypes, дважды щелкаем по кнопке "Типы блюд" и в сгенерированном событии вызываем новый модуль:

fMyTypes.ShowModal;

Сохраните проект, скомпилируйте его и впишите 5-10 типов блюд, например, "Напитки", "Супы", "Салаты" и т.п. Это нам будет нужно для подстановочного поля. Если в поле автоинкремента у вас будут выходить нули, не обращайте внимания - после сохранения таблицы там окажутся правильные цифры. Вы сможете убедиться в этом, закрыв программу и загрузив ее еще раз.

Теперь займемся формой для редактирования основной таблицы. Командой File -> New -> Form или аналогичной кнопкой на панели инструментов создайте новую форму. В свойство Caption этой формы впишите "Редактирование блюда", в свойстве Name укажите fEditor, а модуль сохраните как Editor. Сразу же командой File -> Use Unit подключите к этой форме модуль данных DM. Форма будет выглядеть так:

 Форма редактора блюда

Рис. 5.6. Форма редактора блюда

Как видно из рисунка, на форме присутствуют поясняющие компоненты Label, три компонента DBEdit, один DBLookupComboBox, один DBNavigator и кнопка BitBtn, в свойстве Kind которой выбрано значение bkClose.

Компонент DBLookupComboBox немного сложней остальных. Это подстановочный компонент. Из основной таблицы Food он будет брать целое число - значение поля FType. А из дочерней таблицы Tips этот компонент будет просматривать все значения поля TName. Когда пользователь выберет какой-нибудь тип блюда, целое число, соответствующее ключевому полю TKey, попадет в поле FType главной таблицы. Другими словами, у нас получилась связь один-ко-многим (многие блюда основной таблицы могут иметь одинаковый тип):

Связь между таблицами

Рис. 5.7. Связь между таблицами

Выделите все компоненты, относящиеся к редактированию данных или перемещению по ним (начинающиеся на DB …), и в их свойстве DataSource выберите fDM.FoodDS. Затем с помощью свойства DataField подключите все DBEdit к соответствующему полю таблицы.

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

Таблица 5.3 . Значения свойств компонента DBLookupComboBox
Свойство Значение
DataSource fDM.FoodDS
DataField FType
ListSource fDM.TipsDS
KeyField TKey
ListField TName

Как видно из таблицы, компонент DBLookupComboBox имеет такие важные свойства:

  • DataSource - свойство содержит ссылку на компонент TDataSource, связанный с основной таблицей.
  • DataField - свойство указывает на имя ссылочного поля основной таблицы. В это поле после выбора значения из списка DBLookupComboBox попадает значение ключевого поля подстановочной таблицы.
  • ListSource - свойство содержит ссылку на компонент TDataSource, связанный с подстановочной (дочерней) таблицей.
  • KeyField - свойство содержит имя ключевого поля подстановочной таблицы. По этому полю ищется нужная подстановочная запись.
  • ListField - свойство содержит имя поля подстановочной таблицы, по которому формируется список значений DBLookupComboBox. Эти значения также можно подставлять в основную таблицу в виде lookup (подстановочного) поля.

Если говорить еще проще, то при открытии основного и подстановочного наборов данных, компонент DBLookupComboBox соединяется с подстановочной таблицей, указанной в свойстве ListSource, и формирует список значений из поля, указанного в ListField. Далее, при редактировании основной таблицы, пользователь выбирает из списка DBLookupComboBox одно из значений. При этом DBLookupComboBox смотрит, какое значение выбранной записи в подстановочной таблице имеет ключевое поле KeyField. Как правило, это целое число. Это число DBLookupComboBox и заносит в ссылочное поле основной таблицы DataField. Похожим образом действует и компонент DBLookupListBox, разумеется, учитывая специфику компонента.

Форма fEditor предназначена для редактирования имеющегося блюда или добавления нового, в зависимости от того, каким способом вызвали форму. Поэтому здесь нам нужно лишь создать код для события закрытия формы onClose, куда пропишем:

if fDM.FoodT.Modified then
    if Application.MessageBox('Данные изменены! Сохранить?',
       'Внимание!', MB_YESNO+MB_ICONQUESTION) <> IDYES then
         fDM.FoodT.Cancel
    else fDM.FoodT.Post;

Перейдем на главную форму. Командой File -> Use Unit добавим к главной форме новое окно. Пользователь должен иметь возможность редактировать имеющуюся запись, поэтому сгенерируем событие onDblClick для сетки DBGrid1, и пропишем туда следующий код:

fEditor.ShowModal;

Рядом с кнопкой "Типы блюд" добавим еще одну кнопку "Добавить блюдо". Сгенерируйте событие нажатия на эту кнопку и пропишите такой код:

fDM.FoodT.Append;
  fEditor.ShowModal;

Как видите, отличие кода заключается лишь в том, что при нажатии на кнопку добавляется новая запись, открывается редактор и пользователь редактирует ее. А если он дважды щелкнет по записи на сетке, то откроется тот же редактор, в который будет загружена текущая запись. Впрочем, благодаря навигатору DBNavigator, пользователь и там имеет возможность перемещаться по записям, добавлять или удалять записи. Мы, собственно, получили далекую от идеала, но вполне работоспособную программу. Сохраните проект, скомпилируйте и введите для примера несколько блюд:

Программа в действии

Рис. 5.8. Программа в действии

Как видите, программа имеет множество недостатков: пользователь видит совершенно ненужные ему ключевые поля, в сетке он видит лишь номер типа блюда, но не видит название этого типа. В поле FVeget ему вручную приходится писать True или False вместо привычных Да/Нет. Еще недостаток: названия полей в сетке соответствуют названиям полей в таблице, а поле " FType " или " FVeget " мало что скажет пользователю. Исправлением этих недостатков займемся в следующей лекции, вместе с изучением свойств полей.

< Лекция 4 || Лекция 5 || Лекция 6 >
Евгений Медведев
Евгений Медведев

В лекции №2 вставляю модуль данных. При попытке заменить name на  fDM выдает ошибку: "The project already contains a form or module named fDM!". Что делать? 

Анна Зеленина
Анна Зеленина

При вводе типов успешно сохраняется только 1я строчка. При попытке ввести второй тип вылезает сообщение об ошибке "project mymenu.exe raised exception class EOleException with message 'Microsoft Драйвер ODBC Paradox В операции должен использоваться обновляемый запрос'.