Devrace FIBPlus: Использование базы данных в качестве хранилища запросов и настроек клиентских приложений

Devrace FIBPlus: Использование базы данных в качестве хранилища запросов и настроек клиентских приложений

Рано или поздно большинство разработчиков приходят к тому, чтобы делать свои приложения как можно более простыми для определенных модификаций без требования перекомпиляции всей программы. Если это касается работы с базами данных, то, как правило, программисты начинают выносить запросы во внешние текстовые файлы или хотя бы сохраняют настройки пользовательского интерфейса во внешних файлах конфигурации. Этот подход, однако, имеет свой недостаток: при изменении настроек приходится копировать их на все рабочие места. Поэтому следующим этапом создания более гибких многопользовательских приложений является перенос подобных вещей в базу данных. Используя InterBase и Firebird при помощи FIBPlus, мы получаем в руки готовый механизм, называемый FIBPlus Repository.

Шаг первый: сохранение и использование визуальных настроек полей в базе данных.
Самое простое, что можно сделать при помощи FIBPlus Repository — это сохранить в базе данных такие настройки полей, как Display Label или Display Format, а потом автоматически получить эти настройки из базы данных в приложение без единой строчки дополнительного кода.

Разместим на форме следующие основные компоненты: pFIBDatabase1: TpFIBDatabase, pFIBDataSet: TpFIBDataSet, pFIB Transaction: TpFIBTransaction, DataSource1: TDataSource, DB Grid1: TDBGrid. Мы не будем останавливаться на очевидных деталях: указание параметров подключения к базе данных в pFIBDatabase1, связывание компонентов FIBPlus между собой, связывание DataSource1 и pFIBDataSet и т.д. Все эти моменты были неоднократно описаны в предыдущих статьях. Отметим только, что мы будем использовать для наших примеров стандартную базу данных Employee. gdb, которая поставляется вместе с Borland InterBase.
Укажем следующий запрос для pFIBDataSet1.SelectSQL:

SELECT
EMP.EMP_NO,
EMP.FIRST_NAME,
EMP.LAST_NAME,
EMP.PHONE_EXT,
EMP.HIRE_DATE,
EMP.DEPT_NO,
EMP.JOB_CODE,
EMP.JOB_GRADE,
EMP.JOB_COUNTRY,
EMP.SALARY,
EMP.FULL_NAME
FROM
EMPLOYEE EMP

И сгенерируем модифицирующие запросы (UpdateSQL, DeleteSQL и InsertSQL) при помощи SQL Generator, входящего в поставку FIBPlus. Этот вопрос тоже неоднократно освещался в статьях по FIBPlus. Запустим приложение:

Рис. 1. Внешний вид приложения без использования FIBPlus Repository

Как мы можем видеть, колонки в DBGrid1 называются так же, как и физические поля в базе данных. Очевидно, что это не очень удобно для пользователя, особенно если мы хотим использовать национальные слова в колонках, поскольку InterBase, несмотря на SQL Dialect 3, имеет проблемы с использованием национальных символов в названиях полей. Delphi (равно как и BCB) позволяет нам настраивать такое свойство полей, как Display-Label. Именно это свойство выводится в заголовки DBGrid1. При работе в design-time мы можем создать экземпляры полей, указать в Object Inspector значения DisplayLabel для каждого из них и получить в DBGrid1 названия колонок, которые уже будут более понятны пользователям. Однако если в процессе работы над приложением мы будем вынуждены изменить структуру базы данных, то нам придется проделывать почти всю эту работу заново: удалять в design-time старые поля в pFIBDataSet1, добавлять новые, снова указывать DisplayLabel и т.д. Мы уже не говорим о том, что если вам просто захочется переименовать одно из полей, то придется перекомпилировать приложение и заново раздавать его всем пользователям в сети. Простая операция потребует кучу совершенно тривиальной и ненужной работы!

Всего этого можно избежать. FIBPlus позволяет сохранять настройки полей в базе данных. Таким образом, когда вы исправите настройки прямо в базе, то все заново подключившиеся приложения автоматически применят их без изменения и грамма кода. Обратимся к конкретным шагам. Нажмите правой кнопкой мыши на компоненте pFIBDatabase1 и вызовите редактор полей FIBPlus Repository:

Рис. 2. Вызов редактора полей FIBPlus Repository

FIBPlus Repository создает в основной базе данных дополнительную таблицу, причем на создание такой таблицы требуется наше явное разрешение. Нажмите ОК в появившемся диалоге, чтобы разрешить FIBPlus создать дополнительную таблицу FIB$ FIELDS_INFO в нашей базе данных. После создания мы увидим диалог редактирования свойств полей:

Рис. 3. Редактор FIB$FIELDS_INFO

Видно, что FIBPlus Repository позволяет нам сохранять такие настройки полей, как Dis-playLabel, Visible, DisplayFor-mat, EditFormat, DisplayWidth. Особенное внимание следует обратить на колонку Triggered. Некоторые поля в таблицах, имеющие опцию NOT NULL, предназначены для автоматического заполнения при помощи триггеров базы данных. Компоненты FIBPlus при получении мета-данных для генерации модифицирующих запросов выставляют свойство Required у всех полей с опцией NOT NULL. Программа начинает требовать от пользователя безусловного указания значений таких полей до отправки запроса на сервер. Такое поведение оправдано во всех случаях, кроме некоторых конкретных ситуаций, когда разработчик точно знает: только триггер имеет право менять значения определенных полей. Чтобы не запутать вас, приведем пример. Предположим, у нас в таблице Employee есть два триггера, которые формируют значения поля HIRE_DATE таблицы Employee:

CREATE TABLE EMPLOYEE (
EMP_NO EMPNO NOT NULL,
FIRST_NAME FIRSTNAME NOT NULL,
LAST_NAME VARCHAR(35) NOT NULL,
PHONE_EXT VARCHAR(4) CHARACTER SET NONE,
HIRE_DATE DATE DEFAULT 'NOW' NOT NULL,
DEPT_NO DEPTNO NOT NULL,
JOB_CODE JOBCODE NOT NULL,
JOB_GRADE JOBGRADE NOT NULL,
JOB_COUNTRY COUNTRYNAME NOT NULL,
SALARY SALARY NOT NULL,
FULL_NAME COMPUTED BY (last_na-me || ', ' || first_name)
);

CREATE TRIGGER EMPLOYEE_NEW_ HIRE_DATE FOR EMPLOYEE
ACTIVE AFTER INSERT POSITION 0
AS
begin
new.hire_date = 'NOW';
end

CREATE TRIGGER EMPLOYEE_NEW_ HIRE_DATE_UPD FOR EMPLOYEE
ACTIVE AFTER UPDATE POSITION 0
AS
begin
new.hire_date = 'NOW';
end

Очевидно, что пользователю бессмысленно менять значения этого поля. Тем не менее, поскольку поле HIRE_DATE описано как NOT NULL, то наше приложение будет требовать от пользователя внести хоть какое-то значение. Мы можем избежать этого, если укажем в FIBPlus Repository, что данное поле обрабатывается триггером.
Вернемся к редактору полей FIBPlus Repository. По двойному щелчку на названии таблицы Employee в списке ее поля будут добавлены в Repository. Нам остается только указать нужные нам значения. Мы можем даже спрятать лишние поля, указав Visible равным 0:

Рис. 4. Редактирование свойств полей в FIBPlus Repository

Теперь необходимо включить опцию psApplyRepository в pFIBDataSet1.PrepareOptions и запустить наше приложение:

Рис. 5. Настройки полей из FIBPlus Repository применены к pFIBDataSet1

Видно, что все наши настройки DisplayLabel были применены. Одной из приятных особенностей данного подхода является также то, что в каких бы запросах ни участвовала таблица Employee, данные централизованные настройки полей будут автоматически применяться во всем приложении.

Шаг второй: дополнительные возможности FIBPlus Repository.
Иногда возникает нужда в каких-либо дополнительных настройках для полей таблиц. Особенность FIBPlus Reposi-tory состоит в том, что он позволяет легко оперировать с любыми дополнительными полями в FIB$FIELDS_INFO. Предположим, мы хотим хранить значения DisplayLabel на трех языках. Очевидно, что одного поля DISPLAY_LABEL нам не хватит. Добавим пару колонок:
ALTER TABLE FIB$FIELDS_INFO ADD GERMANY_LABEL VARCHAR(20);
ALTER TABLE FIB$FIELDS_INFO ADD RUSSIAN_LABEL VARCHAR(20);
Откроем редактор полей в FIBPlus Repository и укажем альтернативные названия для DisplayLabel на немецком и русском языках:

Рис. 6. Заполнение значений для DisplayLabel на немецком и русском

Добавим компонент Combo-Box1: TComboBox на форму, зададим свойство ComboBox1. Style равным csDropDownList и внесем три элемента в свойство Items:
English
German
Russian

Добавим также компонент DataSetsContainer1: TDataSets-Container. Укажем свойство Container у pFIBDataSet1 равным DataSetsContainer1. Подключим модуль pFIBDataInfo в список uses в нашем модуле:
implementation
uses pFIBDataInfo;
Напишем обработчик OnClick у ComboBox1:
procedure TForm1.ComboBox1Click (Sender: TObject);
begin
pFIBDataSet1.CloseOpen(false);
end;
Напишем также обработчик события OnDataSetEvent у DataSetsContainer1:
procedure TForm1.DataSetsContai-ner1DataSetEvent(DataSet: TDataSet;
Event: TKindDataSetEvent);
var I: integer;
Dl: string;
begin
if Event = deAfterOpen then
with DataSet do
for I := 0 to (FieldCount — 1) do begin
case ComboBox1.ItemIndex of
1 : Dl := GetOtherFieldInfo(Fields[I], 'GERMAN_LABEL');
2 : Dl := GetOtherFieldInfo(Fields[I], 'RUS-SIAN_LABEL');
else
Dl := GetOtherFieldInfo(Fields[I], 'DISPLAY_LABEL');
end;
if Dl <> '' then Fields[I].DisplayLabel := Dl;
end;
end;

Функция GetOtherFieldInfo обращается к FIBPlus Reposito-ry и возвращает значение нужного нам параметра для указаного поля. В нашем примере мы передаем по очереди все поля нашего pFIBDa-taSet1 и запрашиваем либо параметр GERMAN_LABEL, либо параметр RUSSIAN_LA-BEL в зависимости от текущего языка, выбранного в Combo-Box1. Запустите приложение и попробуйте выбрать немецкий язык в списке ComboBox1. Вы увидите, как будут заменены заголовки у столбцов в DB Grid1. Очевидно, что данный механизм может быть легко применен и для многих других настроек таблиц, которые хочется хранить в базе данных. Использование TDataSetsCon-tainer продиктовано тем, что этот компонент позволяет сосредоточить код по обработке дополнительных полей в FIB$ FIELDS_INFO в одном месте приложения. Подключая любые другие экземпляры TpFIB DataSet к DataSetsContainer1, вы тем самым автоматически получите использование дополнительных языков во всем приложении.

Шаг третий: Query Repository.
Логичным продолжением идеи о хранении настроек приложения в базе данных становится мысль о хранении в этой же базе и запросов. Это позволило бы оптимизировать запросы, вводить в них дополнительные поля или условия без перекомпиляции приложений.
В FIBPlus уже реализован механизм хранения запросов в базе данных. Обратите внимание на свойство DataSet_Id у TpFIBDataSet:

Рис. 7. Свойство DataSet_Id у TpFIBDataSet

Вызовем редактор этого свойства. Хранение запросов потребует создания в основной базе данных еще одной системной таблицы — FIB$DATA-SETS_INFO. При первом вызове редактора свойства Data-Set_ID нам будет предложено создать ее. Нажмите кнопку OK, и вы увидите диалог:

Рис. 8. Диалог Query Repository в редакторе свойства Data Set_Id у TpFIBDataSet

В таблице слева мы можем добавить необходимое количество групп запросов для приложения. Группа запросов — это серия запросов для SelectSQL, UpdateSQL, Dele-teSQL, Insert SQL и Refresh SQL, объединенных под одним номером, указанным в поле DS_ID. Поле Description содержит текстовый комментарий, который поможет вам в дальнейшем разобраться в целях той или иной группы запросов. На рисунке 8 видно, что мы можем добавить группу с номером 1 и заполнить все запросы начиная с SelectSQL. Для данного примера мы перенесем туда те SQL-тексты, которые были сгенерированы на предыдущих этапах SQL Generator'ом.

Замечание: Обратите внимание на поля Key field и Generator. В предыдущих статьях мы уже описывали, как использовать автогенерацию модифицирующих запросов и автозаполнение ключевого поля значениями генератора при помощи свойства AutoUpdate-Options в TpFIBDataSet. Репозитарий запросов позволяет вам хранить и изменять нужные параметры для AutoUp-dateOptions в базе данных наряду с запросами.

При нажатии на OK текущий выбранный DS_ID будет присвоен свойству DataSet_ID у pFIBDataSet1. Теперь мы можем смело удалять значения свойств SelectSQL, UpdateSQL, DeleteSQL, InsertSQL и RefreshSQL у pFIBDataSet1, поскольку они в любом случае будут браться из базы данных. Запустите приложение, и вы увидите, что оно работает так же, как и раньше. Разница состоит лишь в том, что в нашем коде нет ни запросов, ни настроек полей — все данные берутся из FIBPlus Repository. Мы можем взять любое средство администрирования базы данных или воспользоваться редакторами FIBPlus Repository, встроенными в FIBPlus, изменить значения в таблицах в FIB$FIELDS_INFO и FIB$ DATASETS_INFO и перезапустить приложение без перекомпиляции. Также следует отметить, что SQL Generator автоматически начинает работать с использованием FIBPlus Reposi-tory, если значение свойства DataSet_ID у TpFIBDataSet отлично от нуля. Таким образом, вы имеете целый ряд возможностей работы с FIBPlus Repository. В любом случае новые запросы и настройки будут применяться в вашем приложении автоматически.

Например, мы можем захотеть показать дополнительную информацию о сотрудниках — их должности. Для этого достаточно сделать следующий запрос:
SELECT
EMPLOYEE.EMP_NO,
EMPLOYEE.FIRST_NAME,
EMPLOYEE.LAST_NAME,
EMPLOYEE.PHONE_EXT,
EMPLOYEE.HIRE_DATE,
EMPLOYEE.DEPT_NO,
EMPLOYEE.JOB_CODE,
EMPLOYEE.JOB_GRADE,
EMPLOYEE.JOB_COUNTRY,
EMPLOYEE.SALARY,
EMPLOYEE.FULL_NAME,
JOB.JOB_TITLE
FROM
EMPLOYEE, JOB
WHERE
(EMPLOYEE.JOB_CODE = JOB. JOB_CODE) and
(EMPLOYEE.JOB_COUNTRY = JOB. JOB_COUNTRY) and
(EMPLOYEE.JOB_GRADE = JOB. JOB_GRADE)
ORDER BY LAST_NAME

Откроем редактор свойства DataSet_ID для pFIBDataSet1 (или воспользуемся SQL Generator или любым визуальным средством администрирования для InterBase и Firebird) и заменим запрос для SelectSQL с DS_ID, равным 1. Перезапустим приложение без перекомпиляции:

Рис. 9. Приложение с измененным запросом в FIBPlus Repository

Для того чтобы заголовок у поля JOB_TITLE был более удобен для пользователей, вам остается лишь настроить данные о полях таблицы JOB, как мы это делали на самом первом этапе. Мы уверены, что эту операцию вы уже сможете сделаеть самостоятельно.

Резюме
Мы показали, каким образом можно более полно использовать базу данных InterBase и Firebird при разработке приложений. При помощи FIBPlus вы сможете создавать приложения, которые будет легко адаптировать к некоторым меняющимся условиям эксплуатации. Вообще сам процесс разработки может оказаться более простым за счет централизованного хранения настроек и запросов.

Если в процессе эксплуатации вашей программной системы оказывается, что определенные запросы надо оптимизировать со знанием низкоуровневых особенностей InterBase (в частности, с указанием непосредственных планов выполнения), то такого рода оптимизация не потребует от вас изменения собственно клиентских приложений. Все операции можно будет делать независимо, напрямую работая с базой. А после изменения запросов в FIBPlus Repository эти запросы будут "подхвачены" клиентскими приложениями автоматически. Найти последнюю версию FIBPlus можно на сайтах: http://www.devrace.com или http://www.devrace.de .

Сергей Востриков, Денис Мигачев
Дополнительную информацию можно найти на сайте http://interbase-world.com



Компьютерная газета. Статья была опубликована в номере 22 за 2004 год в рубрике программирование :: разное

©1997-2024 Компьютерная газета