36.1. Размещение бизнес-правил
Бизнес-правила
(БП) задают ограничения на значения данных в БД. Они также определяют механизмы, согласно которым при изменении одних данных изменяются и связанные с ними данные в той же или других таблицах БД.Таким образом, бизнес-правила определяют условия поддержания БД в целостном состоянии.
Идеология архитектуры "клиент-сервер" требует переноса максимально возможного числа БП на сервер. К преимуществам такого подхода относятся:
• гарантия целостности БД, поскольку БП сосредоточены в едином месте (в базе данных);
• автоматическое применение БП, определенных на сервере БД, для любых приложений;
• отсутствие различных реализации БП в разнотипных клиентских приложениях, работающих с БД;
• быстрое срабатывание БП, поскольку они реализуются на сервере и, следовательно, нет необходимости посылать данные клиенту, увеличивая при этом сетевой трафик;
• доступность изменений, внесенных в БП на сервере, для всех клиентских приложений, работающих с настоящей БД и отсутствие необходимости повторного распространения измененных приложений клиентов среди пользователей.
К недостаткам хранения бизнес-правил на сервере можно отнести:
• отсутствие у клиентских приложений возможности реагировать на некоторые ошибочные ситуации, возникающие на сервере при реализации БП (например, игнорирование приложениями, написанными на Delphi, ошибок при выполнении хранимых процедур на сервере);
• ограниченность возможностей SQL и языка хранимых процедур и триггеров для реализации всех возникающих потребностей определения БП.
На практике в клиентских приложениях реализуют лишь такие бизнес-правила, которые тяжело или невозможно реализовать с применением средств сервера. Все остальные БП переносятся на сервер.
36.2. Реализация бизнес-правил на сервере
36.2.1. Ограничения значения столбца записи
Ограничение первичного ключа
Задает требование уникальности значения поля (столбца) или группы полей (столбцов), входящих в первичный ключ, по отношению к другим записям таблицы Например,
CREATE TABLE SAL_HIST (QUORTER INTEGER NOT NULL, PRIMARY KEY (QUORTER));
Ограничение уникального ключа
Задает требование уникальности значения поля (столбца) или группы полей (столбцов), входящих в уникальный ключ, по отношению к другим записям таблицы. Например,
CREATE TABLE VLADLIM (... NAZVVLAD VARCHAR(50) NOT NULL, UNIQUE (NAZVVLAD) );
Ограничение ссылочной целостности
Задает требование, согласно которому для каждой записи в дочерней таблице должны иметься записи в родительской таблице. При этом изменение значения столбца связи в записи родителя при наличии дочерних записей блокируется, равно как и удаление родительской записи (запрет каскадных изменений и удалений). Для разрешения каскадных воздействий следует отменить ограничение ссылочной целостности и реализовать каскадные воздействия по отношению к дочерним записям (изменение поля связи, удаление) в триггерах. Например,
CREATE TABLE SPR_TOVAR(
TOVAR VARCHAR(20) NOT NULL COLLATE PXW_CYRL, ........ PRIMARY KEY(TOVAR));
CREATE TABLE PRIHOD(
ID_PRIHOD INTEGER NOT NULL PRIMARY KEY,
.............
TOVAR VARCHAR(20) NOT NULL COLLATE PXW_CYRL,
FOREIGN KEY(TOVAR) REFERENCES SPR_TOVAR);
Ограничение требуемого значения
Определяет, что в поле не может хранится пустое значение (NULL). Например,
CREATE TABLE TOVAR {............ OSTATOK INTEGER NOT NULL, ................. };
Значения по умолчанию
Столбцу может быть присвоено значение по умолчанию. Это значение будет актуально в том случае, если пользователь не введет в столбец никакого иного значения. Например,
CREATE TABLE SAL_HIST (QUORTER INTEGER DEFAULT 1 , .......);
Требование соответствия одному значению из списка
К столбцу может быть предъявлено ограничение, согласно которому значение столбца должно содержать одну из величин, объявленных в списке, и никакое другое. Например,
CREATE DOMAIN POL_TYPE AS CHAR(3) CHECK(VALUE IN ("Муж","Жен")) ;
Ограничение диапазона возможных значений
Такое требование определяет, что значение поля должно ограничиваться указанным диапазоном. Например,
CREATE TABLE TBL(CHECK(STOLBEZ BETWEEN 100 AND 200); ) ;
Ограничение максимума или минимума
Указывает, что поле должно превышать какое-либо значение (минимум) и/ или не превышать какое-либо значение (максимум). В приводимом ниже примере для столбца
STOLBEZ в качестве минимального значения указано 100.CREATE TABLE TBL(CHECK(STOLBEZ >= 100); );
Алгоритм вычисления значений
Для столбца, чье значение вычисляется по значениям других столбцов, может быть установлен алгоритм вычисления значения. Например,
CREATE TABLE SAL_HIST (LAST_YEAR INTEGER, THIS_YEAR INTEGER,
GROWTH COMPUTED BY ( THIS_YEAR - LAST_YEAR), ...);
Ограничение отношения между полями (столбцами) записи
Такое ограничение определяет некоторое отношение (больше, меньше, равно и т.д.) между значениями двух полей одной и той же записи. Например,
CREATE TABLE PERSON_PARAMS(
HEIGHT INTEGER NOT NULL, WIEGHT INTEGER NOT NULL CHECK(HEIGHT > WIEGHT));
Ограничение формата значения
Указывает, что в значение столбца должна входить группа символов. В приводимом ниже примере значение поля STOLBEZ должно оканчиваться символами 'USD', независимо от того, какие символы и сколько расположены перед ними.
CREATE TABLE TBL (... CHECK (STOLBEZ LIKE "%USD") ; ...);
Требование вхождения символов в значение
Устанавливает, что в значение столбца должна входить группа символов (с неопределенной позиции). В приводимом ниже примере значение поля STOLBEZ должно содержать вхождение символов 'USD', независимо от того, какие символы и сколько расположены перед ними и после них.
CREATE TABLE TBL( ... CHECK (STOLBEZ CONTAINING "USD") ; ... );
Требования присутствия ведущих символов
Устанавливает, что значение столбца должно начинаться с определенной группы символов. В приводимом ниже примере значение поля STOLBEZ должно начинаться с символов 'USD'.
CREATE TABLE TBL( ... CHECK (STOLBEZ STARTING WITH "USD") ; ... );
Требование отношения со значением в другой таблице
Устанавливает, что значение столбца находится в некотором отношении (=, >, < и т.д.) со значением, получаемым путем выполнения запроса к другой таблице. В приводимом ниже примере значение в столбце KOLVO таблицы RASHOD не должно превышать значения столбца OSTATOK из записи таблицы TOVAR, причем поле TOVAR у обеих сравниваемых записей должно иметь одинаковое значение.
CREATE TABLE RASHOD (
ID_RS INTEGER NOT NULL,
TOVAR VARCHAR(20) CHARACTER SET WIN1251 NOT NULL,
COLLATE PXW_CYRL,
KOLVO_R INTEGER NOT NULL,
...
CONSTRAINT PO_OSTATKU
CHECK(KOLVO_R <= (SELECT TOVAR.OSTATOK
FROM TOVAR WHERE TOVAR.TOVAR = RASHOD.TOVAR) )
);
Требование отношения значения столбца со всеми или некоторыми значениями в другой таблице
Устанавливает, что значение столбца находится в отношении (=, >, < и т.д.) со значением столбца всех (ALL) или некоторых (SOME) записей, получаемых путем выполнения запроса к другой таблице.
В приводимом ниже примере значение столбца DATE_RASH (таблица RASHOD) должно быть больше значений всех полей DATE_PRIH в таблице PRIHOD. При этом сравниваемые записи с PRIHOD и RASHOD должны иметь одинаковое значение столбца TOVAR.
CREATE TABLE RASHOD (ID_RS INTEGER NOT NULL,
TOVAR VARCHAR(20) CHARACTER SET WIN1251 NOT NULL,
COLLATE PXW_CYRL,
DATE_RASH DATE NOT NULL,
...
CONSTRAINT RASH_TOVAR
FOREIGN KEY (TOVAR) REFERENCES TOVAR(TOVAR),
CONSTRAINT PO_DATE_RASH
CHECK ( DATE_RASH > ALL
(SELECT DATE_PRIH FROM PRIHOD WHERE PRIHOD.TOVAR = RASHOD.TOVAR) ) ) ;
Требование существования хотя бы одной записи в другой таблице
Устанавливает, что в другой таблице должна существовать хотя бы одна запись, удовлетворяющая некоторому условию.
В приводимом ниже примере обязательно существование хотя бы одной записи в таблице PRIHOD с таким же значением столбца TOVAR, что и в поле TOVAR записи таблицы RASHOD.
CREATE TABLE RASHOD (ID_RS INTEGER NOT NULL,
TOVAR VARCHAR(20) CHARACTER SET WIN1251 NOT NULL
COLLATE PXW_CYRL,
DATE_RASH DATE NOT NULL,
...
CONSTRAINT RASH_TOVAR
FOREIGN KEY (TOVAR) REFERENCES TOVAR(TOVAR),
CONSTRAINT PO_DATE_RASH
CHECK (EXISTS (SELECT TOVAR FROM PRIHOD WHERE PRIHOD.TOVAR = RASHOD.TOVAR)));
Требование существования единственной записи в другой таблице
Устанавливает, что в другой таблице должна существовать только одна запись, удовлетворяющая некоторому условию
В приводимом ниже примере обязательно существование единственной записи в таблице PRIHOD с таким же значением столбца TOVAR, что и в столбце TOVAR записи таблицы RASHOD
CREATE TABLE RASHOD (ID_RS INTEGER NOT NULL,
TOVAR VARCHAR(20) CHARACTER SET WIN1251 NOT NULL
COLLATE PXW_CYRL,
DATE_RASH DATE NOT NULL,
CONSTRAINT RASH_TOVAR
FOREIGN KEY (TOVAR) REFERENCES TOVAR(TOVAR),
CONSTRAINT PO_DATE_RASH
CHECK (SINGULAR(SELECT TOVAR FROM PRIHOD WHERE PRIHOD.TOVAR = RASHOD.TOVAR)));
36.2.2. Запрет добавления записей в просмотре
Для просмотра
VIEW может быть включен режим WITH CHECK OPTION, предотвращающий добавление записей, не удовлетворяющих условию WHERE оператора SELECT данного просмотра Например, для приведенного ниже обзора будет отвергаться попытка добавления записи со значением поля KOLVO, меньшим 1000CREATE VIEW RASH_1000_CHECK
AS
SELECT * FROM RASHOD WHERE KOLVO > 1000 WITH CHECK OPTION;
36.2.3. Использование триггеров для поддержания ссылочной целостности
Для поддержания ссылочной целостности на сервере могут использоваться триггеры. Они автоматически запускаются при выполнении любого изменения таблицы БД (добавление новой записи, корректировка или удаление существующей). Время запуска - до или после события - определяется в заголовке триггера. Большим преимуществом триггеров является возможность обращения (при изменении записи) к старому (
OLD) и новому (NEW) значению столбца.Триггеры могут использоваться для:
реализации каскадных воздействий в дочерних таблицах при изменении значения столбца связи в записи родительской таблицы или при удалении записи родительской таблицы;
• внесения уникального значения в столбец, по которому построен уникальный или первичный ключ;
внесения изменений в семантически связанные таблицы;
• ведения журнала изменений БД.
Пример. Приводимые ниже триггеры выполняют каскадные обновления в дочерней таблице
RASHOD после изменения значения столбца связи в записи в родительской таблице TOVARY:CREATE TRIGGER BU_TOVARY FOR TOVARY
ACTIVE
BEFORE UPDATE
AS
BEGIN
IF (OLD.TOVAR 0 NEW.TOVAR) THEN
UPDATE RASHOD SET TOVAR = NEW.TOVAR WHERE TOVAR = OLD.TOVAR;
END
CREATE TRIGGER AD_TOVARY FOR TOVARY
ACTIVE
AFTER DELETE
AS
BEGIN
DELETE FROM RASHOD WHERE RASHOD.TOVAR = TOVARY.TOVAR;
END
Пример.
Приводимый ниже триггер при добавлении записи в таблицу RASHOD присваивает столбцу N_RASH уникальное значение, для чего используется генератор RASHOD_N_RASH.CREATE TRIGGER BI_RASHOD_GEN FOR RASHOD ACTIVE
BEFORE INSERT
BEGIN
NEW.N_RASH = GEN_ID(RASHOD_N_RASH,1) ;
END
Пример.
Приводимый ниже триггер при добавлении новой записи в таблицу RASHOD (расход товара) прибавляет значение поля KOLVO (количество) вновь введенной записи к полю KOLVO в таблице STAT_TOVARY для записи с той же датой (DAT_RASH) и названием товара (TOVAR). Если в таблице STAT_TOVARY такая запись отсутствует, она создается.CREATE TRIGGER AI_RASHOD FOR RASHOD
ACTIVE
AFTER INSERT
AS
DECLARE VARIABLE CNT INTEGER;
DECLARE VARIABLE OLD_KOLVO_VAL INTEGER; BEGIN
/* выбрать число записей в таблице
STAT_TOVARY по данному товару за дату расхода */SELECT COUNT(*)
FROM STAT_TOVARY
WHERE (STAT_TOVARY.DAT_RASH = NEW.DAT_RASH) AND
(STAT_TOVARY.TOVAR = NEW.TOVAR)
INTO :CNT;
/* если число записей = 0, добавить запись в таблицу
STAT_TOVARY по данному товару и дате */IF (:CNT = 0) THEN INSERT INTO STAT_TOVARY (DAT_RASH, TOVAR, KOLVO)
VALUES(NEW.DAT_RASH, NEW.TOVAR, NEW.KOLVO);
ELSE
/* иначе добавить новое количество товара в уже существующей записи для нового товара */
/* и новой даты в
STAT_TOVARY */BEGIN
SELECT KOLVO FROM STAT_TOVARY
WHERE (STAT_TOVARY.DAT_RASH = NEW.DAT_RASH) AND (STAT_TOVARY.TOVAR = NEW.TOVAR)
INTO :OLD_KOLVO_VAL;
UPDATE STAT_TOVARY
SET KOLVO = :OLD_KOLVO_VAL + NEW.KOLVO
WHERE (STAT_TOVARY.DAT_RASH = NEW.DAT_RASH) AND STAT_TOVARY.TOVAR = NEW.TOVAR) ;
END
END
Пример
. Триггер реализует автоматическую фиксацию в таблице TOVARY_LOG добавлений, внесенных в таблицу ТО VARY.CREATE TRIGGER TOVARY_ADD_LOG FOR TOVARY
ACTIVE
AFTER INSERT
AS
BEGIN
INSERT INTO TOVARY_LOG(DAT_IZM, DEISTV, OLD_TOVAR,NEW_TOVAR)
VALUES ("NOW","ADD","".NEW.TOVAR) ;
END
36.3. Реализация бизнес-правил в приложении клиента
Для реализации бизнес-правил в приложении клиента могут использоваться различные компоненты. В первую очередь это компоненты типа "набор данных" (
TTable, TQuery, TStoredProc) и компоненты TField. Интерфейсные компоненты (например, TEdit и TButton), могут использоваться для контроля за вводом пользователя и могут блокировать закрытие формы, если он ввел неправильные значения.36.3.1. Реализация бизнес-правил в компонентах типа "набор данных"
Компоненты типа "набор данных" позволяют реализовывать бизнес-правила в следующих обработчиках событий:
OnNewRecord -
происходит при добавлении новой записи сразу после перехода НД в состояние dslnsert из состояния dsBrowse, но перед выдачей полей новой записи пользователю для ввода значений; используется для присваивания полям значений по умолчанию;BeforeOpen, BeforeClose, BeforeCancel, BeforeEdit, Beforelnsert -
происходят до выполнения соответствующего метода;After Post, AfterDelete, AfterOpen, AfterClose, AfterCancel, AfterEdit, Afterlnsert •
происходят после выполнения соответствующего метода; не возникают, если при выполнении соответствующего метода произошел сбой;OnCalcFields -
наступает при необходимости заполнения вычисляемых полей; применяется для задания алгоритмов расчета значений вычисляемых полей;OnDeleteError -
наступает при ошибке удаления записи;OnUpdateError -
происходит при ошибке редактирования записи;OnPostError -
происходит при возникновения ошибки в ходе выполнения метода Post.Определение алгоритма вычисления значений вычисляемых полей
Обработчик события OnCalcFields применяется для определения алгоритма расчета значения вычисляемых полей. Например,
procedure SomeTableCalcFields(DataSet: TDataSet);
begin
SomeTableVychPole.Value := SomeTablePolel.Value / SomeTablePolel.Value;
end;
Присваивание значений полей по умолчанию
В обработчике события OnNewRecord можно присвоить полям вновь добавляемой записи значения по умолчанию. Эти значения останутся актуальными, если пользователь перед добавлением записи не изменит их. Например,
procedure SomeTableNewRecord(DataSet: TDataSet);
begin
WITH Some do begin
FieldByName('polel').AsInteger := ...;
FieldByName('poleN').AsInteger := ...;
END;//with
end;
Автоматическое присваивание значения полям связи
При добавлении новой записи в дочерний НД может понадобиться присвоить соответствующие значения полям связи с родительской таблицей. В дальнейшем поля связи обычно не предоставляют пользователю для редактирования.
Например, при добавлении новой записи в таблицу ChildTable устанавливается значение поля связи, равное значению поля 'cod_parent' текущей записи родительской таблицы ParentTable:
procedure ChildTableNewRecord(DataSet: TDataSet);
begin
ChildTable.FieldByName('cod_parenf).AsInteger :=
ParentTable.FieldByName('cod_parenf).AsInteger;
end;
Назначение уникального значения столбцу таблицы
Если столбцу таблицы при добавлении новой записи должно присваиваться уникальное значение, можно выполнить отдельный запрос к той же таблице, получить максимальное значение уникального поля и увеличить его на 1.
Например, при добавлении новой записи в RashodTable выполняется формируемый запрос (компонент WorkQuery, тип TQuery), возвращающий максимальное значение поля 'cod_unique'. Будучи увеличено на 1, оно присваивается полю 'cod_unique' вновь добавляемой записи:
procedure RashodTableNewRecord(DataSet: TDataSet);
var Max_cod_unique : Integer;
begin
WITH WorkQuery do begin
SQL.Clear;
SQL.ADD('SELECT MAX(COD_UNIQUE)') ;
SQL.ADD('FROM RASHOD');
Open;
Max_cod_unique := WorkQuery.Fields[0].Aslnteger + 1;
Close;
END;//with
ChildTable.FieldByName('cod_unique').Aslnteger : =Max cod unique;
end;
Назначение полю уникального значения удобнее производить в хранимой процедуре при помощи генератора. Например, если в БД определен генератор
CREATE GENERATOR X;
SET GENERATOR X TO 1;
и
определена процедураCREATE PROCEDURE GET_UNIQUE_VALUE
RETURNS(UV INTEGER) AS
BEGIN
UV = GEN_ID(X,1) ;
END
то в приложении достаточно определить компонент
TStoredProc и связать его с хранимой процедурой GET_UNIQUE_VALUE. Затем в приложении, например в обработчике события OnNewRecord, нужно произвести вызов процедуры и присвоить полю уникальное значение, возвращаемое в качестве выходного параметра процедуры:procedure RashodTableNewRecord(DataSet: TDataSet);
begin
StoredProc1.ExecProc;
ChildTable.FieldByName('cod_unique').Aslnteger := StoredProc1.ParamByName('UV).Aslnteger;
end;
Внесение изменений в связанную таблицу БД
Обычно изменения в связанные таблицы вносятся в обработчиках событий AfterPost, AfterDelete.
Например,
уменьшить значение поля 'Kolvo' таблицы StatTable на значение поля 'Kolvo' из удаленной записи таблицы RashodTable:procedure RashodTableAfterDelete(DataSet: TDataSet);
begin
WITH StatTable do begin
Edit;
FieldByName('Kolvo').Value := FieldByName('Kolvo').Value - DeletedRashodValue;
Post;
END;//with
end;
Внесение признака изменения таблицы в глобальную переменную
Для того чтобы сигнализировать клиентскому приложению об изменении записи в какой-либо таблице, устанавливают в нужное значение какую-либо глобальную переменную приложения. Анализ этой глобальной переменной может впоследствии производиться приложением со значительными временными задержками. Например,
procedure TDM.SomeTableAfterDelete(DataSet: TDataSet);
begin
IzmSomeTable := True;
end;
Свойство Constrained (компонент TQuery)Предотвратить ввод записей, не удовлетворяющих условиям, перечисленным в предложении
WHERE оператора SELECT, можно путем установки в True значения свойстваproperty Constrained: Boolean;
Например, для НД, возвращенного оператором
SELECT * FROM RASHOD WHERE KOLVO > 1000
при Constrained, содержащим True, будут блокироваться попытки запоминания записей со значением поля KOLVO, меньшим 1000. ConstraintsСвойство набора данных
property Constraints: TCheckConstraints;
является коллекцией ограничений на значения столбцов (полей) НД. Указание ограничения производится в SQL-подобном синтаксисе (рис. 36.1). В случае, если значение поля не удовлетворяет наложенным на него ограничениям, при попытке запоминания записи возбуждается исключение.
36.3.4. Реализация бизнес-правил в компоненте
TFieldАнализ правильности введенного в поле значения
Проверку правильности введенного в поле значения можно осуществить в обработчиках событий On Validate, OnSetText, OnChange.
Пример.
Значение поля Company не должно содержать символ '@':procedure TForm1.Table1CompanyValidate(Sender: TField);
begin
IF POS('@',Table1Company.AsString) > 0 THEN raise Exception.Create('
Неверное значение');end;
Пример.
Значение поля PUR_PRICE не должно превышать 100:procedure TForm1.Table1PUR_PRICESetText(Sender: TField;
const Text: String);
var Tmp : Real;
begin
Tmp := StrToFloat(Text) ;
IF Tmp > 100 THEN ShowMessage('
Ошибочное значение')ELSE Table1PUR_PRICE.Value := Tmp;
end;
или
procedure TForm1.Table1PUR_PRICEChange(Sender: TField);
begin
IF Table1PUR_PRICE.Value > 100 THEN raise Exception.Create('
Ошибочное значение');end;
Форматирование содержимого поля при показе и редактировании
Значения могут храниться в виде, отличном от того, в котором они показываются пользователю. Преобразование значения поля к виду, пригодному для показа пользователю, осуществляется свойствами EditMask (маска значения при редактировании записи) и DisplayFormat (маска значения при показе в визуальных компонентах), и обработчиком события OnGetText, который применяется, если возможностей DisplayFormat для преобразования значения недостаточно.
Пример. Значение поля Company при редактировании должно показываться в кавычках, при просмотре - с заменой всех букв на заглавные:
procedure TForm1.Table1CompanyGetText(Sender: TField; var Text: OpenString;
DisplayText: Boolean);
begin
IF DisplayText THEN Text := AnsiUpperCase(Table1Company.AsString)
ELSE Text := ' " ' + Table1Company.AsString + ' " ';
end;
Свойства
CustomConstraint и ConstraintErrorMessageСвойство
поляproperty CustomConstraint: string;
позволяет наложить ограничения на значения поля (при помощи SQL-подобного синтаксиса). Если в поле занесено значение, не отвечающее указанному ограничению, возбуждается исключение с сообщением, определяемым свойством
property ConstraintErrorMessage: string;
36.3.5. Реализация бизнес-правил в иных компонентах
Бизнес-правила могут реализоваться и при помощи иных компонентов приложения. Обычно при попытке выполнения некоторого действия проверяются значения каких-либо переменных, которые затем станут значениями полей (столбцов) таблицы БД, или - при вводе пользователем неверных значений -блокируется кнопка подтверждения изменений или выхода из формы.
Проверка правильности значения
Приводимый ниже пример проверяет значение в
Edit 1.Text на предмет соответствия формату даты. В дальнейшем это значение, приведенное к формату даты, планируется записать в поле типа даты таблицы БД. Проверка производится в обработчике события OnButtonClick (компонент TButton):procedure TToObrForm.GoButtonClick(Sender: TObject);
var Tmp: TDateTime;
begin
TRY
Tmp := StrToDate(Editl.Text);
EXCEPT
ShowMessage('
Неверная дата') ;Editl.SetFocus;
Exit;
END;//try
end;
Запрет подтверждения изменений в БД
В случае невыполнения какого-либо условия в форме может быть запрещена кнопка, реализующая запоминание внесенных изменения в БД:
procedure TToObrForm.GoButtonClick(Sender: TObject);
var Tmp: TDateTime;
begin
TRY
Tmp := StrToDate(Editl.Text);
EXCEPT
ShowMessage('
Неверная дата');Edit1.SetFocus;
PostButton.Enabled := False;
Exit;
END;//try
end;
Отмена выхода из формы до осуществления каких-либо действий
В случае изменения какого-либо значения и до выполнения какого-либо условия в форме может быть запрещена кнопка, реализующая выход из модальной формы:
procedure TForm1.DBEditlChange(Sender: TObject);
begin
ExitButton.Enabled := False;
end;
procedure TForm1.Table1AfterPost(DataSet: TDataSet) ;
begin
ExitButton.Enabled := True;
end;
Заметим, что из такой формы можно выйти при помощи системного меню и комбинации клавиш
Alt + F4. Поэтому нужно отменить для формы системное меню и при попытке выхода в обработчике события OnCloseQuery формы такую попытку блокировать.Выполнение действий при переходе на другую запись
Событие
OnDataChange компонента TDataSource происходит при изменении текущей записи в НД (в режиме dsBrowse} или при изменении какого-либо поля записи (режим dsEdit).В приводимом примере подразумевается, что для текущей записи в
ParentTable по запросу (нажатие кнопки SelectChildRecordsButton) в НД ChildTable фильтруются дочерние записи.При переходе на новую запись в
ParentTable содержимое ChildTable до нового нажатия кнопки SelectChildRecordsButton становится неактуальным. Поэтому таблица TDBGrid, показывающая дочерние записи, окрашивается в серый цвет и визуализируется кнопка SelectChildRecordsButton:procedure TForm1.ParentDataSourceDataChange(Sender: TObject;
Field: TField) ;
begin
IF ParentTable.State <> dsBrowse THEN Exit;
ChildDBGrid.Enadbled := True;
ChildDBGrid.Color := cISilver;
SelectChildRecordsButton.Visible := True;
end;
Те же действия можно реализовать в обработчике события
AfterScroll набора данных, возникающем после перехода на новую запись в НД.36.4. Использование словаря данных для определения атрибутов полей
В том случае, когда для какого-либо поля (столбца) таблицы БД требуется определить характеристики его визуального представления, используют словарь данных.
Словарь данных позволяет:
сформировать поименованный набор атрибутов поля без привязки его к конкретному полю (столбцу) конкретной таблицы БД;
по мере необходимости ставить поименованный набор атрибутов в соответствие конкретным полям (столбцам) конкретных ТБД.
В состав атрибутов, которые можно определить в поименованном наборе атрибутов, входят: выравнивание
(Alignment), заголовок поля при показе (Display Label), ширина показа (Display Width}, только для чтения (Read Only), требующее обязательного ввода значения (Required}, видимое (Visible), маска при редактировании (Edit Mask), маска при показе (Display Mask) и другие.Пусть конкретному полю (столбцу) ставится в соответствие какой-либо набор атрибутов Тогда при добавлении этого поля в приложение в качестве компонента
Tfield происходит следующее соответствующие свойства компонента принимают значение одноименных атрибутов поляНаличие такой возможности, как назначение полям (столбцам) набора атрибутов, преследует вполне конкретную цель однажды определив в словаре данных некий абстрактный набор атрибутов, мы затем можем ставить его в соответствие полям в одной или нескольких БД и, таким образом:
• устраняем повторное определение визуальных атрибутов поля (столбца) в любом приложении, его использующем,
• придаем полю (столбцу) единообразные характеристики во всех приложениях, с ним работающих.
Рассмотрим процесс назначения атрибутов полю и затем импорт их в приложение на следующем примере.
Пусть существует таблица БД
'zpa_knig.db' (записная книжка), в состав которой входят поля NN (порядковый номер, автоинкрементное поле), FIO (фамилия, имя, отчество, символьное поле), Tel (телефон, символьное поле), Prim (примечание, символьное поле).Пусть поле
Tel должно вводится в формате (ххх) ххх-хх-хх и показываться с заголовком "Телефон" При этом известно, что поле будет использовано более чем в одном приложении и потому его визуальному представлению желательно придать определенное единообразиеПроцесс назначения полю набора атрибутов в словаре данных и последующего импорта атрибутов в приложение состоит из ряда этапов.
1 Выберем в главном меню
Delphi элемент Database \ Explore2
В появившемся окне утилиты SQL Explorer выберем закладку Dictionary3 Импортируем в словарь базу данных, содержащую таблицу '
zpa_knig db' (псевдоним БД book) Для этого в меню SQL Explorer выберем элемент Dictionary | Import from Database и затем в появившемся окне запроса выберем с помощью выпадающего меню псевдоним book, после чего нажмем кнопку Ok4 Создадим новый набор атрибутов. Для этого установим мышь на элемент дерева
AttributeSets, нажмем правую кнопку мыши и в появившемся меню выберем элемент New5 В правом окне для вновь созданного набора атрибутов введем имя
Tel_attribute_set
В левом окне установим значения атрибутов DisplayLabel, Edit Mask
и Display Mask (рис 36 2)6. Подтвердим внесенные для набора атрибутов
Tel_attribute_set изменения,вызвав правой кнопкой мыши меню и выбрав элемент
Apply7 Раскроем ветвь для БД
book, раскроем ветвь Tables, выберем zap_knig, раскроем ее ветвь Fields и выберем поле Те/ (правое окно) В левом окне для данного поля установим значение A ttribute set в Te/_attr;bute_scf при помощи выпадающего списка (рис 36 3) 8 Подтвердим внесение изменений в набор атрибутов для поля Tel, для чего вызовем правой кнопкой мыши всплывающее меню и выберем элемент Apply9
В приложении Delphi разместим в форме компонент TTable, связанный с ТБД zap_kmg, а также компоненты TDataSource и TDBGrid, связанные между собой стандартным образом Как видно из рис 36 4, характеристики поля Tel пока не отражают набора атрибутов, установленных этому полю в словаре данных10 Используя редактор полей, явно определим для
Table1 компоненты TField Как видно из рис 36.5, свойства поля Tel (компонент Table1Tel, тип TStrmgField) отражают атрибуты, установленные этому полю в словаре данных.11. В режиме выполнения приложения поле
Tel использует маску ввода и маску показа, установленные в словаре данных (рис 36.6).Рис 36.6 В режиме выполнения приложения поле
Tel использует маску вводи и меню показа, установленные в словаре данных