Таким образом, содержимое, например, TDBGrid в вашем приложении будет отличаться от реального состояния таблицы на сервере. Поэтому мы рекомендуем вам все-таки заново открывать запросы, изменения которых были отменены при помощи RollbackRetaining.
Использование генераторов для автоинкрементных полей
Чаще всего автоинкрементные поля используются для поддержки суррогатных первичных ключей. В таблице заводится поле, значения для которого генерируются при помощи доступных технических средств, гарантирующих уникальность получаемых значений.
Существует несколько способов получения таких значений, мы же остановимся на том, который рекомендуется для большинства баз данных в InterBase. Как вы знаете, в InterBase существуют специальные объекты - генераторы, которые гарантируют получение уникальных возрастающих значений, независимых от контекста транзакций.
В IBDataSet существует специальное свойство, которое позволяет нам удобно использовать генераторы для создания уникальных значений первичного ключа, а именно GeneratorField. У этого свойства существует специальный редактор, доступный в design-time (рис. 2.11).
Рис 2.11. Настройка автоинкрементного поля
Укажите название генератора, имя поля в таблице, значения для которого будут генерироваться, шаг увеличения счетчика (в большинстве случаев это 1), а также опцию, указывающую, когда будет генерироваться значение поля: сразу при вставке новой записи (On New Record), при завершении вставки (On Post) или при помощи триггера (On Server). В последнем случае вы не увидите значения сгенерированного поля, пока не переоткроете весь запрос, однако данная опция все равно может оказаться полезной. Поскольку наше поле EMP_NO входит в первичный ключ, то его значение не может формально быть незаданным (NULL). Если не указывать свойство GeneratorField, то DBDataSetl будет требовать от нас указывать значение поля EMP_NO в обязательном порядке и мы не сможем "переложить ответственность" на триггер.
Указав же явным образом, что значение поля будет получено именно в триггере, мы обойдем это ограничение. Тем не менее мы рекомендуем вам использовать опции On New Record или On Post, поскольку они лучше укладываются в общую идеологию применения IBX.
Если мы получаем значение первичного ключа до отправки на сервер, то IBDataSetl сумеет корректно выполнить RefreshSQL. Если же мы будем использовать для генерации триггер, то IBDataSetl не сможет подставить правильное значение в RefreshSQL, поскольку значения полей в условии WHERE еще неизвестны.
Механизм master-detail
Механизм мастер-деталь часто используется в приложениях для работы с базами данных, поскольку именно он позволяет нам легко связывать данные из разных таблиц, полученных в результате нормализации базы.
Добавим на нашу форму новые компоненты (рис. 2.12).
IBDataSet2: TIBDataSet;
DataSource2: TdataSource;
DBGrid2: TDBGrid;
Рис 2.12. Разработка связи мастер-деталь
Свяжем DataSource2 с IBDataSet2, a DBGrid2 с DataSource2. Укажем следующие запросы для IBDataSet2:
SelectSQL:
SELECT BUDGET, DEPARTMENT, DEPT_NO, HEAD_DEPT, LOCATION,
MNGR_NO, PHONE_NO
FROM DEPARTMENT
InsertSQL:
INSERT INTO DEPARTMENT
(BUDGET, DEPARTMENT, DEPT_NO, HEAD_DEPT, LOCATION, MNGR_NO,
PHONE_NO)
VALUES
(:BUDGET, :DEPARTMENT, :DEPT_NO, :HEAD_DEPT, :LOCATION,
:MNGR_NO, :PHONE_NO)
DeleteSQL:
DELETE FROM DEPARTMENT
WHERE DEPT_NO = :OLD_DEPT_NO
ModifySQL:
UPDATE DEPARTMENT SET
BUDGET = :BUDGET,
DEPARTMENT = :DEPARTMENT,
DEPT_NO = :DEPT_NO,
HEAD_DEPT = :HEAD_DEPT,
LOCATION = :LOCATION,
MNGR_NO = :MNGR_NO,
PHONE_NO = :PHONE_NO WHERE
DEPT_NO = :OLD_DEPT_NO
RefreshSQL:
SELECT
DEPT_NO,
DEPARTMENT,
HEAD_DEPT,
MNGR_NO,
BUDGET,
LOCATION,
PHONE_NO,
DEPT_NO1 FROM DEPARTMENT WHERE
DEPT_NO = :DEPT_NO
Очевидно, что их можно сгенерировать при помощи DataSet Editor, как это было описано выше
Теперь необходимо внести изменения в IBDataSetl. Во-первых, нужно задать свойство IBDataSetl.DataSource равным DataSource2. Во-вторых, надо изменить IBDataSetl. SelectSQL:
SELECT DEPT_NO, EMP_NO, FIRST_NAME, FULL_NAME, HIRE_DATE,
JOB_CODE, JOB_COUNTRY, JOB_GRADE, LAST_NAME, PHONE_EXT, SALARY
FROM EMPLOYEE
WHERE DEPT_NO = :DEPT_NO
Значение параметра :DEPT_NO будет автоматически браться из одноименного поля IBDataSet2: DEPT_NO.
Запустите приложение, и вы увидите, что при перемещении по верхней таблице в нижней будут отображаться только сотрудники текущего отдела (рис. 2.13).
Еще одна особенность заключается в том, чтобы настроить IBDataSetl таким образом, что при добавлении новой записи о работнике автоматически подставлялся номер текущего отдела из IBDataSet2. Для этого напишем обработчик OnNewRecord у IBDataSetl:
procedure TForml.IBDataSetlNewRecord(DataSet: TDataSet);
begin
DataSetl.FieldByName('DEPT_NO').Aslnteger :=
IBDataSet2.FieldByName('DEPT_NO').Aslnteger;
end;
Puc 2.13. Запущенное приложение со связью мастер—деталь
Теперь мы можем смело запускать приложение и редактировать записи в обеих таблицах.