Первые шаги в построении платформы
Итак, мы имеем некоторый задел, чтобы решить самую первую задачу настройщика - создать таблицу клиентов. Однако, прежде чем создавать таблицы, научимся сначала загружать системную базу данных и просматривать состав пользовательской базы данных, для чего создадим несложную форму для отображения информации о пользовательской базе данных. Когда я попробовал изложить идею задач загрузки и сохранения информации при работе в режиме конфигуратора, делая "вырезки" из существующей платформы, то после целого дня работы убедился, что это невозможно, - слишком велик программный код, и читатель сразу бы запутался. А если бы я попробовал при этом еще все объяснить, то никогда бы эту статью не закончил. Поэтому пришлось избрать путь создания небольших приложений сугубо для учебных целей. Первое из них мы назовем конфигуратором.
Наш конфигуратор будет делать очень простую работу: загружать урезанную информацию о таблицах пользовательской базы данных и давать возможность просматривать структуру таблиц. Тем самым не будем пока решать довольно непростую задачу обеспечения целостности базы данных, возникающую при ее реконструкциях. Тем не менее, позже мы "научим" простой конфигуратор создавать поля и таблицы, и, если хватит духу, то проводить модификацию, т.е. реконструкцию базы данных. Последнее, впрочем не обязательно делать, т.к. читатель это сам легко сможет реализовать, если поймет, как делаются первые две задачи. Далее перейдем к составлению запросов, сохраняемых в системной базе данных, что завершит основной цикл функциональности платформы.
Работоспособный проект для Delphi 7 приведен в архиве DPlatform.zip. В этом архиве, в папке DbBackup расположен архив базы данных для MSSQL Server 2000. Для запуска приложения нужно этот архив базы данных развернуть на доступном MS SQL Server 2000 и затем на главной форме приложения (файлы F_Configurator.dfm и F_Configurator.pas) компоненту Database подключить к этой базе данных, создав соответсвующий псевдоним BDE. Детали этого процесса пояснять ну буду, скажу лишь, что база данных создана на SQL Server 2000 с кодировкой 1251 и сортировкой, чувствительной к регистру, что очень важно иметь ввиду при установке базы данных. Ясно, что ваш сервер баз данных должен позволять восстанавливать базу данных из приведенного архива, т.е. иметь соответствующие кодировку и сортировку. Поясним, как работает наш конфигуратор, главная форма которого называется ConfiguratorFr.
После запуска приложение ничего не делает: оно ждет нажатия кнопки DbInterface, после чего происходят главные события, показанные в листинге 4.
Листинг 4. Создание экземпляра TDbInterface и загрузка информации в память.
procedure TConfiguratorFr.Button2Click(Sender: TObject); Var k, i : Integer; wTabSheet : TTabSheet; wListBox : TListBox; wpTTableInfo : pTTableInfo; wpTInfoCategory : pTInfoCategory; wTFbTypeGroup : TFbTypeGroup; begin // Создать объект FDbInterface FDbInterface := TDbInterface.Create(nil); // Загрузить информацию из системной БД FDbInterface.DatabaseName := Database.DatabaseName; // Список категорий информации TbDbTypeComboBox.Items.Clear; TbDbTypeComboBox.Items.Assign(FDbInterface.FbDbTypeList); TbDbTypeComboBox.Sorted := True; // Настройка списка групп данных TypeGroupCmBox.Items.Clear; for wTFbTypeGroup := Low(TFbTypeGroup) to High(TFbTypeGroup) do TypeGroupCmBox.Items.AddObject(apTypeGroupNames[wTFbTypeGroup], TObject(wTFbTypeGroup)); // Показать состав загруженной информации for k:=0 to FDbInterface.InfoCategoryList.Count-1 do begin wpTInfoCategory := pTInfoCategory(FDbInterface.InfoCategoryList[k]); if wpTInfoCategory.sTFbDbType in [icAll, icNoCateg, icVirtual] then Continue; wTabSheet := TTabSheet.Create(FPageControl); wTabSheet.PageControl := FPageControl; wTabSheet.Caption := wpTInfoCategory.sInfoDescr; // Запомним ссылку для последующего использования wTabSheet.Tag := Integer(wpTInfoCategory); wListBox := TListBox.Create(Self); wListBox.Parent := wTabSheet; wListBox.Align := alClient; wListBox.OnClick := ListBoxClick; for i:=0 to FDbInterface.TablesList.Count-1 do begin wpTTableInfo := pTTableInfo(FDbInterface.TablesList[i]); if wpTTableInfo.spTInfoCategory <> wpTInfoCategory then Continue; wListBox.Items.AddObject(wpTTableInfo.sTableAttr.Values['sTableCaption'], TObject(wpTTableInfo)); end; end; Button2.Enabled := FDbInterface = nil; Button3.Enabled := FDbInterface <> nil; end; |
Сначала создается объект FDbInterface FDbInterface := TDbInterface.Create(nil).
В конструкторе этого объекта процедура CreateFbObjects обеспечивает создание всех необходимых списков, а также выполняется инициализация типов данных. Инициализация базовых типов проводится функцией Init_TFbFieldArray, которая заполняет массив FFbFieldArray информацией в соответствии с тем, как разработчик установил список поддерживаемых типов. Сначала производится стандартное заполнение информации в каждом элементе массива FFbFieldArray:
for wTFieldType := Low(TFieldType) to High(TFieldType) do begin FFbFieldArray[wTFieldType].sType := wTFieldType; FFbFieldArray[wTFieldType].sSize := 0; FFbFieldArray[wTFieldType].sBytes := capAllTypes[wTFieldType].sBytes; FFbFieldArray[wTFieldType].sInc := 0; FFbFieldArray[wTFieldType].sDescr := capAllTypes[wTFieldType].sDescr; // Эти типы включены в систему if wTFieldType in [ftAutoInc, ftString, ftMemo, ftBlob, ftInteger, ftFloat, ftDateTime, ftUnknown] then FFbFieldArray[wTFieldType].sInc := 1; // ..а для этих типов - особые условия нужны // apDATE_TIME - признак разделения данных типа ДАТА и ВРЕМЯ with FFbFieldArray[wTFieldType] do case wTFieldType of ftDate : begin if apDATE_TIME then begin sInc := 1; sDescr := 'Дата'; end; sBytes := SizeOf(TDateTime); end; ftTime : begin if apDATE_TIME then begin sInc := 1; sDescr := 'Время'; end; sBytes := SizeOf(TDateTime); end; end; end; |
Кроме того, платформа обеспечивает поддержку раздельного учета типов ftDateTime, ftDate, ftTime непосредственно в приложении, т.к. MS SQL Server такое разделение не поддерживает. Применять или нет разделение этих типов, - определяется глобальной переменной булевского типа apDATE_TIME. При желании этот параметр может быть включен в число настроек, что и сделано в штатной версии описываемой платформы. После инициализации базовых типов формируется список FFbFldGroupList, содержащий ссылки на структуры TFbCommonType, причем эти структуры создаются только для тех типов базовой группы данных, которые реально поддерживает платформа, для чего анализируется поле sInc конкретного элемента массива FFbFieldArray. Эта работа выполняется функцией Init_FbFldGroupList:
for wTFieldType := Low(TFieldType) to High(TFieldType) do begin if FFbFieldArray[wTFieldType].sInc <> 1 then Continue; New(wpTFbCommonType); New(wpTFbBaseType); wpTFbCommonType.FbTypeGroup := FldGroup; wpTFbBaseType^ := FFbFieldArray[wTFieldType]; wpTFbCommonType.FbFld := wpTFbBaseType; FFbFldGroupList.AddObject(wpTFbCommonType.FbFld.sDescr, TObject(wpTFbCommonType)); end; |
Вернемся, однако, к работе обработчика TConfiguratorFr.Button2Click.
Следующим шагом является подключение интерфейса FDbInterface к серверу базы данных, что выполняется программным кодом: // Загрузить информацию из системной БД FDbInterface.DatabaseName := Database.DatabaseName При этом срабатывает внутренняя процедура компоненты TDbInterface
Procedure TDbInterface.Set_DatabaseName(Value : String), где через параметр Value передается имя псевдонима базы данных приложения. В этой процедуре сначала производится загрузка в память информации из системной базы данных процедурой LoadSystemDatabaseInfo, а затем завершается процесс инициализации всех типов системы последовательным выполнением процедур Get_PickTypes_From_Database, Init_FbRefGroupList, Init_FbLUpGroupList.
Обратите внимание, что во всех этих процедурах используется обращение к процедуре Update_FbCommonTypeList, реализующей обновление списка FFbCommonTypeList комбинированных типов. Надо признать, что процедуру Update_FbCommonTypeList следовало бы использовать только один раз, после завершения формирования всех частных списков для отдельных групп данных. Но так сделано для того, чтобы обеспечить целостность списков комбинированных типов при манипуляции со структурой пользовательской базы данных в конфигураторе. Вероятно, есть более изящное решение этой задачи, которое могут использовать те читатели. Описание работы процедуры LoadSystemDatabaseInfo мы пока отложим, а работа остальных процедур (Get_PickTypes_From_Database, Init_FbRefGroupList, Init_FbLUpGroupList) очень проста.
- В процедуре Get_PickTypes_From_Database производится чтение информации из системной таблицы T_PickTypes и формирование списка FFbPicGroupList, содержащего ссылки на структуры списочного типа.
- В процедуре Init_FbRefGroupList создается список ссылок на структуры ссылочных типов FFbRefGroupList просматривая список структур таблиц FTablesList.
- В процедуре Init_FbLUpGroupList создается список ссылок на структуры следящих типов FFbLUpGroupList, просматривая список структур полей для всех элементов списка структур таблиц FTablesList. Как уже было замечено, процедура Update_FbCommonTypeList обеспечивает формирование списка обобщенных структур. Как может заметить внимательный читатель, тут налицо избыточность списков для частных групп данных и списка FFbCommonTypeList, хотя затраты ресурсов памяти для этого несущественны. Таких неоптимальных решений в описываемой платформе будет встречаться довольно много, за что просил бы не ругать аммирования платформы протекал при крайне жестких сроках, что называется «с листа», и не было времени заранее обдумать решения.
Так как на главной форме нашего конфигуратора предусмотрено отображение информации из выбранных структур таблицы и поля, то производится заполнение следующих списков: выпадающего списка категорий информации TbDbTypeComboBox, заполняется на основании списка FDbInterface.FbDbTypeList, выпадающего списка групп данных TypeGroupCmBox, заполняется на основании списка apTypeGroupNames, имеющего в приложении как массив-константа.
Затем производится создание страниц объекта FPageControl. Их количество определяется количеством категорий информации платформы. В данном случае плафторма создает 6 страниц, согласно списку TFbDbType, причем для категорий icVirtual, icAll и icNoCateg страницы не создаются. На каждой странице размещается объект TListBox, в который заносится список названий таблиц соответствующей категории информации, причем в TListBox запоминаются также ссылки на соответствующие структуры TTableInfo, созданные при загрузке приложения.
Итак, наш конфигуратор готов к работе.
Рассмотрим, как он выполняет типовые операции, для которого создавался.