Разработка приложений баз данных в среде Delphi. Часть 3. Interbase: Учебно-методическое пособие

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

МИНИСТЕРСТВО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ ВОРОНЕЖСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

Разработка приложений баз данных в среде Delphi. Часть III. InterBase Учебно-методическое пособие по специальности «Прикладная математика и информатика» 010501 (010200)

Воронеж, 2005

2 Утверждено научно-методическим советом факультета ПММ протокол № 1 от 21.09.2005 г.

Составитель Рудалев В.Г.

Учебно-методическое пособие подготовлено на кафедре технической кибернетики и автоматического регулирования факультета прикладной математики, информатики и механики Воронежского государственного университета. Рекомендуется для студентов 4 курса д/о факультета ПММ.

3

Введение Данное пособие, являющееся продолжением серии методических пособий по разработке приложений баз данных, содержит сведения об особенностях использования СУБД InterBase. Для работы с пособием необходимо знание основных компонентов Delphi для работы с базами данных в рамках пособия [3] и знакомство с языком SQL. Сервер баз данных InterBase выпускается фирмой Borland для различных программно-аппаратных платформ: Novell NetWare, Windows всех версий, Linux, информационных систем на базе серверов IBM, Hewlett-Packard, SUN, SGI и др. Реально InterBase может обслуживать БД размером в 10-20 гигабайт, при размере одного файла БД 2 гигабайта. Многофайловая БД может состоять из 65535 файлов, таким образом, теоретический предел для одной базы данных 132 терабайта. InterBase – оптимальное решение для малых и средних организаций с числом рабочих станций порядка нескольких десятков. Сервер Interbase обладает достаточной производительностью, надежностью и низкими системными требованиями. Администрирование Interbase гораздо проще, чем серверов Oracle или MS SQL, но при этом обеспечивается приемлемая для многих задач степень защиты информации. Входной язык Interbase наиболее точно поддерживает стандарт ANSI SQL 92, что делает Interbase некоей «константой» среди множества несовместимых диалектов SQL других фирм. Немаловажное преимущество Interbase – отсутствие жестких лицензионных ограничений. Версии InterBase, выпускаемые фирмой Borland, не бесплатны. В то же время исходные тексты InterBase открыты для разработчиков и в рамках ряда проектов (например, FireBird, Jaffil) выпускаются свои совершенно бесплатные усовершенствованные версии сервера, совместимые с InterBase, информация о которых постоянно обновляется на сайте ibase.ru. Известный недостаток Interbase – отсутствие средств инструментальной поддержки - полностью компенсируется многочисленными утилитами и программными комплексами сторонних фирм – IBAdmin, IBManager, IBExpert и др.

4 1. Основы работы в InterBase 1.1. Установка InterBase Установка InterBase или FireBird, как правило, происходит быстро и не вызывает затруднений. Например, при установке сервера FireBird 1.5 на локальный компьютер, не подключенный к сети, необходимо запустить установочный файл Firebird-1.5.2.4731-Win32.ехе и следовать инструкциям, принимая установки по умолчанию (в частности, согласившись с запуском FireBird при старте Windows в качестве службы). FireBird предложит Вам два варианта установки – Classic и SuperServer. В варианте Classic каждый запрос клиента обрабатывается в отдельном процессе, в варианте SuperServer – отдельными потоками внутри единого процесса. Первый вариант отличается стабильностью работы, второй – меньшим потреблением системных ресурсов. При установке FireBird в сети необходимо установить сервер на одном из узлов сети, который будет играть роль сервера баз данных. На рабочих станциях сети необходимо установить клиент InterBasе. Это можно выполнить различными способами. Во-первых, запустить установочный файл, отключив опцию установки сервера, а оставив только установку клиента. Во-вторых, отметить при установке Delphi флажок Install InterBase Client. Наконец, можно скопировать файл gds32.dll в папку Windows\System32. Это единственный файл, необходимый для работы клиента. 1.2. Инструментальные средства Утилита Interbase Console (сокращенно IBConsole) – основная утилита в InterBase версий 6-7 для работы с БД. Ее функции: • Создание базы данных • Соединение с БД и выполнение SQL-запросов • Отображение информации о БД. • Администрирование БД. В целом функциональные возможности IBConsole невелики, поэтому часто пользуются утилитой SQL Explorer, входящей в состав Delphi. SQL Explorer работает через BDE и позволяет легко создавать таблицы и другие объекты БД в диалоговом режиме. Однако, по мнению автора (совсем не оригинальному), предпочтительней является свободно распространяемая утилита IBExpert, отличающаяся богатой функциональностью, простотой использования и стабильностью работы. Рассмотрим подробнее основные возможности IBExpert и методику ее использования на примере версии 2.5. Утилита поддерживает несколько языков пользовательского интерфейса, в том числе и русский (который можно выбрать через пункт меню Options – Environment Option – Interface Language), но далее мы будем из педагогических побуждений (т.е. из вредности) использовать англоязычный интерфейс.

5 1.3. Создание и регистрация базы данных Создание новой базы данных происходит в четыре этапа • Создание новой пустой БД, хранящейся в файле с расширением *.gdb (для InterBase) *.fdb (для FireBird) • Регистрация БД в IBExpert • Соединение с БД • Создание таблиц и других метаданных Предположим, что сервер установлен у Вас на локальном компьютере дома или на том же компьютере, на котором будет далее работать клиентское ПО. Убев дитесь, что сервер запущен: в Windows 98 он будет виден в виде значка правой части панели задач; в Windows 2000/XP управлять его запуском можно через список служб (Services), в котором присутствуют службы InterBase (или Firebird) Guardian и InterBase Server. Выберите в главном меню IBExpert пункт DataBase - Create DataBase. В поле Server введите Local, в поле Database – путь к новому, еще не существующему файлу:

В поле UserName – имя администратора SYSDBA, в поле Password – пароль администратора masterkey (впрочем, надеемся, что Вы этот всем известный пароль уже сменили или скоро смените). В качестве SQL Dialect укажите Dialect 3. Этот диалект языка SQL поддерживается начиная с версий InterBase 6 и FireBird и отличается расширенными возможностями. Диалект 1 используйте, если необходимо обеспечить совместимость с InterBase ранних версий. Остальные поля можно оставить без изменений. Регистрация необходима для удобства дальнейшей работы. При регистрации IBExpert запоминает путь к БД и другие параметры соединения и при последующих запусках отображает список зарегистрированных баз. Окно регистрации автоматически появляется после создания БД. Здесь необходимо указать имя и версию сервера ( в данном случае FireBird 1.5), выбрав их из списка, и алиас – сокращенное имя БД. Будьте внимательны и

6 уточните, какая версия InterBase у Вас установлена. заполнять не обязательно.

Остальные

поля

После регистрации в окне DataBase Explorer появится алиас БД – NewSklad.

Для дальнейшей работы необходимо соединиться с БД, два раза щелкнув по алиасу, после чего появится список метаданных (доменов, таблиц, просмотров, хранимых процедур, триггеров, генераторов, исключений, функций, определяемых пользователем и ролей):

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

7 учитывать при работе в сети. Имена баз данных в InterBase строятся по правилу: Server_Name:Full_FileName, где Server_Name – сетевое имя компьютера, на котором установлен сервер InterBase. В качестве сетевого имени могут выступать имя NetBios, доменное имя или IP-адрес компьютера. Full_FileName – имя файла *.gdb, обычно с указанием пути. Например: c1r214srv:d:\ib\4gr\MyBase\sklad.gdb. Направление слэша значения не имеет. Но в имени файла нельзя указывать ни имена папок общего доступа, ни отображения этих папок на логические буквы дисков. Например, недопустима запись \\c1r214srv\MyBase\sklad.gdb. Нельзя также записывать c1r214srv:H:\MyBase\sklad.gdb, если буква H указывает не на физический диск, а обозначает сетевую папку общего доступа. Например, если на сервере на диске D заведена папка общего доступа d:\ib, отображаемая на букву H. В качестве Full_FileName можно прописывать только физический путь к БД на компьютере, где установлен сервер. В приведенном примере таким путем являлся путь d:\ib\4gr\MyBase\sklad.gdb, который и надо указывать при регистрации сервера. В реальных ситуациях для выяснения допустимых путей обратитесь к администратору сети. При работе в сети окна создания и регистрации БД в IBExpert имеют несколько иной вид, где для удобства имя сервера c1r214srv записывается отдельно в верхней части окна. Разумеется, если БД используется не только для учебных целей, то администратор должен заранее создать пользователей БД с неадминистративными правами, имена и пароли которых Вы, как рядовые пользователи, указываете вместо SYSDBA и masterkey (см. п. 1.7.2).

8

Если Вам требуется перенести готовую базу данных с другого компьютера, то для регистрации ее на сервере необходимо выполнить два действия: • Скопировать файл БД на сервер, например в папку H:\4gr\MyBase. • Зарегистрировать БД, выбрав в главном меню IBExpert пункт DataBase Register DataBase и заполнив окно

Замечание. В литературе рекомендуется для корректной работы с русскими буквами настраивать InterBase специальным образом: указывать в качестве CharSet при создании БД WIN1251 и задавать атрибут COLLATE PXW_CYRL при определении строковых полей. Однако реально все это необходимо только

9 для правильной работы функции Upper языка SQL с русскими буквам. По мнению автора, гораздо рациональнее и надежнее не менять языковых настроек, а использовать функцию UpCase (и аналогичные функции) из модуля CASEUDF, который можно скачать по адресу http://www.ibase.ru/ download/caseudf.zip и установить на сервере (см. раздел 3). 1.4. Создание таблиц Для создания таблиц, генераторов, триггеров и других метаданных (как, впрочем, и для выполнения любых SQL-запросов) необходимо соединиться с уже существующей БД. Для этого после запуска IBExpert раскройте алиас БД, выделите ветку Tables, и в контекстном меню выберите пункт New Table. Сразу же измените имя таблицы на Customers (см. верхнюю часть окна). Добавьте имена полей, выбирая их тип (или домен) из списка доступных типов (или доменов). Чтобы объявить поле первичным ключом, дважды щелкните напротив него в колонке PK.

Нажатие первой кнопки в панели инструментов или Ctrl+F9 генерирует DDL оператор создания таблицы, которое затем следует подтвердить, нажав кнопку Commit в следующем окне. Заметим, что и далее все действия в IBExpert рассматриваются как транзакции и требуют явного подтверждения (Commit), что повышает надежность работы. После того как таблица была создана, ее структуру можно при необходимости модифицировать. Например, чтобы добавить новое поле, следует в контекстном меню выбрать New Field, в появившемся окне задать имя поля и его тип и в завершение нажать Ctrl+Alt+C (подтверждение транзакции):

10

IBExpert пытается корректно модифицировать структуру таблиц без потери содержащихся в них данных. Замечание: для изменения типа поля существуют два способа. В первом способе следует удалить поле и создать его заново. Второй способ предпочтительнее, но требует некоторой предусмотрительности: старайтесь явно создавать домены для всех используемых в БД типов данных. Для изменения типа достаточно будет выбрать необходимый домен из списка на первой вкладке предыдущего окна. InterBase автоматически, неявно создает домены для всех создаваемых полей, но присваивает им «неудобочитаемые» имена, например, RDB$6. Поэтому лучше создавать домены явно. Для явного создания домена выделите объект Domains в окне Database Explorer, нажмите CTRL+N и заполните окно,

указав при необходимости накладываемые на домен ограничения. Далее при создании поля указываем не тип Varchar(40) not null, а имя домена Address_Domain. 1.5. Создание генераторов, триггеров и хранимых процедур Генераторы обычно используются в InterBase для получения уникальных автоинкрементных значений. Занесение их в первичные ключевые поля может быть выполнено или триггером (автоматически), или клиентским приложением. Первый способ во многих случаях предпочтителен, так как триггер срабатывает

11 всегда, независимо от того, как происходит вставка новой записи – из IBExpert, клиентского приложения на Delphi, Web-приложений и т.п. Создать генератор проще всего сразу при объявлении первичного ключевого поля. Для этого надо перейти на вкладку AutoIncrement и пометить флаг Create Generator. Генератор будет создан автоматически, его имя и начальное значение можно при необходимости отредактировать.

На вкладке Trigger отметьте флаг Create Trigger, что приведет к аналогичному результату:

Здесь был создан триггер, выполняющийся после события вставки новой записи и заполняющий ключевое поле новой записи уникальным значением. На всякий случай сгенерируйте тем же способом и хранимую процедуру (вкладка Procedure) для передачи значения генератора во внешнюю среду, например, в клиентское приложение.

12 CREATE PROCEDURE SP_GEN_CUSTOMERS_ID RETURNS (ID INTEGER) AS BEGIN ID = GEN_ID(GEN_CUSTOMERS_ID, 1); SUSPEND; END

Генераторы, триггеры и хранимые процедуры можно также создавать и вручную (через окно Database Explorer). При создании триггеров и хранимых процедур открывается удобный редактор с синтаксической подсветкой и ниспадающими списками подсказок. Внешний вид редактора представлен на рисунке (см. ниже). Отметим, что редактируется только тело триггера. Название таблицы и событие для триггера (After Insert и т.п.) выбираются из списков.

Аналогичные действия можно выполнить для всех создаваемых таблиц. При необходимости можно создать дополнительные триггеры, например, для каскадных воздействий или ведения статистики. Заметим, что для каждого действия по созданию таблиц и других объектов можно просмотреть соответствующий оператор определения данных на закладке DDL. Действие вступает в силу после применения (кнопка с «молнией» на панели в верхней части окна). 1.6. Выполнение SQL-запросов В IBExpert существует несколько способов выполнения операторов языка SQL. Инструмент SQL Editor (клавиша F12) наиболее универсален и предназначен для выполнения произвольных операторов создания, выборки, манипулирования и управления данными.

13 Инструмент Query Builder (пункт меню Tools) удобно использовать, как следует из названия, для визуального построения запросов SELECT. Вкладка Builder предназначена для конструирования запроса, вкладка Edit – для просмотра и редактирования его текста, Result – просмотра результата. Для выполнения запроса нажмите F9, но перед этим убедитесь, что нужная база данных (NewSklad) выбрана в левом верхнем углу окна:

Инструмент Script Executive используется для редактирования и выполнения скриптов – последовательностей SQL-операторов. Например, у Вас имеется скрипт для создания базы данных, полученный при помощи системы Caseпроектирования ERWIN. Скрипт хранится в текстовом файле sklad.sql и содержит два оператора Create Table. Загрузите этот файл в Script Executive.

Запустите скрипт с помощью клавиши F9. В окне DataBase Explorer появятся две созданные скриптом таблицы, которые можно в дальнейшем модифицировать, добавив, например, ограничение ссылочной целостности.

14

1.7. Управление пользователями Каждый сервер InterBase имеет пользователя SYSDBA с административными полномочиями. Пароль - masterkey. Сначала это единственный авторизованный пользователь на сервере. И он должен авторизовать (зарегистрировать) других пользователей. Только пользователь SYSDBA имеет права на добавление, модификацию и удаление пользователей. Имена пользователей могут быть длиной до 31 символа, не являются чувствительными к смене регистра, но не должны содержать пробелов. Пароли чувствительны к регистру букв. Значение имеют только первые 8 символов пароля, но длина пароля может достигать 32 символов. Рассмотрим основные операции администрирования InterBase. 1.7.1. Смена пароля администратора Обычно это самая первая операция после установки сервера. Выберите в главном меню Tools – User Manager. В появившемся окне вводим SYSDBA и masterkey. Выделяем пользователя (пока он только один) и жмем Edit.

Вводим и подтверждаем новый пароль. Если в IBExpert уже были зарегистрированы базы данных со старым паролем, то их все придется перерегистрировать, иначе соединение не будет установлено. В этом – смысл появляющегося на экране предупреждения. Пароль не может быть пустым и состоящим из пробелов!

15 1.7.2. Добавление нового пользователя В окне User Manager нажимаем Add и добавляем информацию

Новый пользователь 4k4gr сможет создавать базы данных и регистрировать их, после чего он станет их владельцем (owner) с правом полного доступа. При соединении c базой данных 4k4gr будет указывать свои учетные данные. Владелец базы данных может предоставлять привилегии доступа к своей БД другим пользователям, разрешая или запрещая им отдельные операции. 1.7.3. Предоставление привилегий Подключение к БД не означает автоматическую возможность редактирования, и даже отображения данных. Привилегии должны быть заданы явным образом; пользователи не смогут обращаться ни к одному объекту БД, пока не получат соответствующие привилегии. Привилегии, данные специальному «пользователю» PUBLIC, применимы для всех пользователей. Сначала только создатель таблицы (owner) имеет доступ к данным, но он может назначать привилегии другим пользователям (предоставлять грант). Grant (англ.) – дар, предоставление, субсидия. Одноименный SQL-оператор GRANT назначает привилегии доступа различным субъектам безопасности - конкретным пользователям, ролям (см. п. 3.4), а также хранимым процедурам и триггерам. Объектами безопасности (охраняемыми объектам), к которым применяется GRANT, могут быть таблицы целиком, столбцы таблиц, триггеры, хранимые процедуры, просмотры. SQL-оператор REVOKE удаляет ранее предоставленные привилегии доступа. Привилегия

Разрешает пользователям …

SELECT DELETE INSERT UPDATE EXECUTE

Просматривать строки в таблице или просмотре Удалять строки Вставлять строки Изменять все или указанные столбцы Выполнять хранимые процедуры

REFERENCES

ALL

16 Создавать внешний ключ, который ссылается на указанный первичный ключ таблицы, даже если пользователь не является ее владельцем Объединяет SELECT, DELETE, INSERT, UPDATE, и REFERENCES

Привилегии можно назначать в диалоговом режиме с помощью IBExpert. Вызовите в главном меню инструмент Tools - Grant Manager. Откроется окно:

Как видим, первоначально все права имеются только у SYSDBA. У пользователей 4k4gr и PUBLIC их нет:

Назначение привилегий производится двойным щелчком либо выбором из контекстного меню или с использованием панели инструментов. Кнопки с изо-

17 бражением «ладони» означают Grant WITH GRANT OPTION. Пользователь, обладающий такими привилегиями, может предоставлять указанную привилегию другим пользователям. Привилегии могут назначаться как по отношению к таблицам, так и к отдельным колонкам (см. нижнюю часть окна). 1.7.4. Использование ролей. Роль – это аналог группы пользователей в Windows 2000/XP. Рекомендуется привилегии назначать именно ролям, а не пользователям. Действительно, роли хранятся в самой базе данных, а пользователи – в служебной базе isc4.gdb, которая у каждого сервера своя. Поэтому при переносе БД на другой сервер роли сохраняются, а пользователи – нет. Если права даны не ролям, а пользователям, то потребуется их назначать заново. При использовании же ролей достаточно будет включить в их состав новых пользователей, привилегии менять нет необходимости. Создадим в качестве примера роли Manager и Guest. Для создания ролей служит вкладка Roles инструмента User Manager.

Для включения пользователей в состав роли перейдите на вкладку Membership и отметьте соответствующий флажок.

Выбор флага With ADMIN OPTION означает, что пользователь, включенный в состав роли, будет иметь возможность назначения, отзыва и удаления роли, т.е. станет ее администратором. Назначение привилегий для ролей выполняется с помощью Grant Manager, как и для обычных пользователей. Замечание. Если пользователь имеет привилегии доступа к БД только через роль, то имя роли должно быть указано при любом соединении с базой данных,

18 например, при использовании операторов SQL, в окне регистрации IBExpert, в компонентах клиентских приложений и т.п. 1.8. Создание UDF Помимо встроенных простейших функций языка SQL, таких как min(), max(), avg(), InterBase поддерживает библиотеки внешних функций UDF (функций, определяемых пользователем). UDF существенно расширяют возможности языка SQL. Например, в состав Firebird входят UDF математических и календарных функций. UDF можно создавать с помощью любых компиляторов (С, Delphi, Fortran и т.п.). Для этого необходимо написать необходимые функции, откомпилировать их, включить в состав DLL и объявить на сервере InterBase. UDF может принять до 10 параметров, соответствующих любому типу данных InterBase, за исключением массивов. Все входные параметры передаются по ссылке. Возвращаемые значения передаются либо по ссылке (по умолчанию), либо по значению. В последнем случае необходимо учитывать соответствие типов: Тип Interbase

Тип Object Pascal

Integer

Integer

Double precision

Double

Cstring

Pchar

Date

Type IBGateTime=record Days: integer; Msec: cardinal; End;

Чтобы передать числовое значение по значению, следует при объявлении UDF в БД после выходного параметра указать служебное слово BY VALUE. После того как функции откомпилированы в библиотеку, их нужно объявить для всех БД, в которых планируется ее использование. Каждую функцию требуется объявлять отдельно, но только один раз для каждой БД. Объявление функции информирует БД о ее расположении и свойствах. Синтаксис объявления: DECLARE EXTERNAL FUNCTION name [datatype | CSTRING(длина) [,datatype | CSTRING(длина)…]] RETURNS {datatype [BY VALUE] | CSTRING(длина)} [FREE_IT] ENTRY_POINT ‘entryname’ MODULE_NAME ‘modulename’;

Аргумент Name

Описание Имя UDF для использования в SQL-операторах; может отличаться от имени, указанного после

19 ENRT_POINT

Datatype

Тип входного или возвращаемого параметра

RETURNS

Определяет возвращаемое функцией значение

Определяет, что возвращаемый результат должен передаваться по значению CSTRING(длина) Указывается для параметров строкового типа После завершения работы UDF освобождает память, FREE_IT выделенную под возвращаемое по ссылке значение, Строка в кавычках, определяющая имя функции в ‘entryname’ исходном коде как оно хранится в библиотеке; Описание файла, идентифицирующее библиотеку, в ‘modulename’ которой хранится UDF BY VALUE

Замечание: Начиная с InterBase 6, UDF должны храниться только в папке ..\InterBase\udf . Когда UDF создана и объявлена в БД, ее можно использовать в операторах SQL, в хранимых процедурах и триггерах. Для этого следует вставить ее имя в соответствующее место SQL-оператора, заключив входные параметры в круглые скобки. Продемонстрируем подключение и использование UDF на примере известной библиотеки caseudf.dll. Просмотрим исходный текст библиотеки. Как видим, это - обычная DLL, написанная на Delphi. library caseudf; { Copyright Dmitry Kuzmenko, Epsylon Technologies } } { e-mail: [email protected], web: http://ib.demo.ru { вообще о копирайте на такие функции говорить смешно, так что пользуйтесь на здоровье } uses Windows, SysUtils; function UpCase(CStr: PChar): PChar; cdecl; export; begin CharUpperBuff(CStr, Length(CStr)); Result:=CStr; end; { другие функции, а также комментарии и инструкции автора для краткости опущены } exports UpCase; begin isMultiThread:=True; end.

20 В DataBase Explorer выберем пункт UDFs – New и заполним открывшуюся форму редактирования в соответствии с ее объявлением в DLL:

Сгенерированное объявление UDF на SQL, записанное на вкладке DDL, будет иметь вид: DECLARE EXTERNAL FUNCTION UPCASE CSTRING(255) RETURNS CSTRING(255) ENTRY_POINT 'UpCase' MODULE_NAME 'CASEUDF.DLL'

Обычно авторы UDF не прилагают к файлу *.dll исходный код, а предоставляют только вышеприведенный SQL-оператор, что вполне достаточно. Пример использования UDF в SQL Builder:

2. Написание клиентских приложений Наиболее эффективным способом создания приложений являются прямые вызовы функций API клиента из приложений. Хорошим примером такого способа является сама программа IBExpert, написанная, по косвенным признакам, на Delphi. Однако в повседневной практике гораздо удобнее использовать компонентно-ориентированные технологии, основанные на стандартных технологиях доступа к данным. Для разработчика важно, что поддержка InterBase встроена во все существующие стандарты доступа к данным со стороны клиента – ADO, ADO.NET, ODBC, JDBC, BDE, DbExpress и др. В Delphi имеются компоненты для доступа через ADO, BDE и DbExpress. Правда, технология BDE в настоящее время устарела и не обеспечивает требуемой производительности. Для доступа через ADO необходимо установить в Windows ADO-провайдер для InterBase (возможные варианты см. на сайте www.ibase.ru). Универсальная

21 компонентная технология DbExpress, официально рекомендуемая Borland, позволяет создавать компактные и быстрые кроссплатформенные приложения, но имеет некоторые неудобства и ограничения. Далее мы будем рассматривать специализированные компоненты Interbase Express (IBX), поставляемые в составе Delphi и умеющие работать только с InterBase. Альтернативные специализированные компоненты FIBPlus от компании Devrace более функциональны, но они являются самостоятельным платным программным продуктом, который следует приобретать отдельно. 2.1. Структура приложения Структура и код приложения при различных технологиях доступа однотипны, так как компоненты доступа к данным в Delphi, будучи потомками класса TDataSet, имеют много общих черт со ставшими классическими BDEориентированными компонентами. Наиболее инвариантны участки кода, связанные с поиском, просмотром и редактированием данных. Необходимо учитывать следующие особенности IBX. Обязательным является компонент IBDataBase, содержащий параметры соединения с сервером (имя сервера, протокол, путь к БД, имя и пароль), которые устанавливаются в окне DataBase Component Editor, вызываемом при двойном щелчке по компоненту.

Эти значения можно ввести также программно или с помощью инспектора объектов. Например, путь к базе данных на сервере c1r214srv:D:\IB\4gr\MyBase\NewSklad.gdb хранится в свойстве DataBaseName рассматриваемого компонента.

22 На этапе отладки не забудьте установить свойство LoginPrompt в False, что лишит Вас сомнительного удовольствия многократно набирать имя и пароль при запусках программы. На завершающем этапе разработки при подготовке программы к развертыванию, наоборот, следует очистить компонент IBDataBase от параметров соединения с базой данных, предусмотрев их ввод в диалоговом режиме (см. пример в разделе 3). В противном случае при установке программы на новый компьютер, скорее всего, потребуется ее перекомпиляция. Обязательным в приложении является компонент IBTransaction, с помощью которого происходит управление транзакциями. Компонент IBDataBase связывается с объектом IBTransaction через свойство property DefaultTransaction: TIBTransaction. Кроме того, ссылку на IBTransaction надо указать в свойствах Transaction: TIBTransaction наборов данных (IBQuery, IBTable и др.). В приложении может быть несколько объектов-транзакций, поэтому каждый набор данных может организовывать свои собственные, независимые от других наборов транзакции. Стартует транзакция вызовом метода StartTransaction или установкой свойства компонента TIBTransaction Active:= True. Но запуск транзакции происходит также и неявно при активизации соединения, происходящей при открытии наборов данных, связанных с компонентом IBTransaction, например, при вызове tbCust.Open. При вызове методов Commit (подтверждение транзакции) или RollBack (откат) связанные наборы данных закрываются, а при их новом открытии запускается новая транзакция. Поэтому следует обязательно проверять, не запущена ли ранее транзакция другими способами (вложенные транзакции не допускаются!). Для этой цели служит свойство компонента IBTransaction property InTransaction: Boolean. Проверив его значение, можно также сигнализировать об активности транзакций запросами, сообщениями т.п. Опишем кратко особенности компонентов доступа к данным. 2.2. Компоненты доступа к данным 2.2.1. TIBTable Компонент TIBTable формирует виртуальную таблицу, аналог физической таблицы БД. Компонент при открытии набора данных неявно вызывает оператор SELECT * FROM Таблица, что приводит к копированию всех данных на

23 клиентский компьютер. Поэтому для больших таблиц этот компонент использовать не рекомендуется. Основные свойства и методы совпадают с TTable. Положительная особенность - пригодность для отображения не только таблиц, но и просмотров (View). Начиная с версии Delphi 7, исправлена досадная ошибка при работе с фильтрами. 2.2.2. TIBQuery Набор данных, возвращаемый TIBQuery, всегда, независимо от типа запроса, имеет значение свойства ReadOnly, равное True, что препятствует его непосредственному редактированию. Для редактирования наборов данных, возвращаемых TIBQuery, последний надо обязательно связывать с компонентом TIBUpdateSQL, независимо от того, используется кэширование изменений или нет. Компонент TIBUpdateSQL размещается в модуле данных. Имя экземпляра TIBUpdateSQL указывается в свойстве UpdateObject компонента TIBQuery. Далее в Update SQL Editor необходимо автоматически сгенерировать или вручную прописать модифицирующие SQL-запросы. 2.2.3. TIBDataSet Данный компонент объединяет функциональность IBTable, IBQuery и IBUpdateSQL, являясь наиболее «продвинутым». Текст SQL-запроса хранится в свойстве SelectSQL типа TStrings (причем, формируется он удобным редактором). Возможности IBUpdateSQL обеспечиваются редактором набора данных, вызываемом в контекстном меню и работающем так же, как в обычном UpdateSQL. SQL-операторы модификации НД записываются в соответствующие свойства InsertSQL, ModifySQL, DeleteSQL. Интересной особенностью компонентов IBQuery и IBDataSet является возможность непосредственного вызова генератора для заполнения первичного ключевого поля. Здесь используется свойство property GeneratorField: TIBGeneratorField, в которое следует записать имя ключевого поля и имя генератора (это удобнее сделать с помощью инспектор объектов). Если для данной цели определен также и триггер, то он перепишет вставленное значение. 2.2.4. TIBSQL Компонент предназначен для быстрого выполнения SQL-запросов. В отличие от рассмотренных компонентов, являющихся потомками TDataSet, он происходит непосредственно от TСomponent, не умеет отображаться в визуальных

24 компонентах и использует однонаправленный курсор. Перемещение по набору данных, возвращаемому компонентом, происходит только в одном направлении – вниз, с использованием метода Next. Выполняется запрос при вызове метода ExecQuery.

3. Пример программирования 3.1. Предметная область В качестве примера рассмотрим задачу автоматизации отпуска товара со склада. Цели данного пособия не требуют учета всех особенностей предметной области, поэтому упростим задачу до минимально возможных пределов. Предположим, что БД состоит из трех таблиц (заказчики, заказы и изделия), связанных по ключам Cust_ID и Prod_ID. Каждый заказчик может сделать несколько заказов, а каждый товар может быть продан нескольким заказчикам. Анонимные продажи не допускаются. Товар продается поштучно, причем нельзя продать товар, которого нет на складе. Логическая структура БД в нотации системы Case-проектирования Erwin приведена на рисунке. (Заметим, что в составе IBExpert имеется подобный инструмент Database Designer).

В данном случае заказ (Order) может состоять только из одного товара (Product), что является наиболее серьезным ограничением представленной модели, которое можно преодолеть введением еще одной таблицы «Детали заказа». Развернутый и более функциональный демонстрационный пример базы данных «Заказы» можно найти, например, в папке ..\Borland\Delphi7\Demos\Db\IBMastApp в установочной директории Delphi (соответствующий файл БД находится в папке ..\Borland\Borland Shared\Data\mastsql.gdb).

3.2. Структура БД Структура БД для предметной области с учетом особенностей языка SQL сервера InterBase может быть описана с помощью следующего скрипта:

25 SET

TERM

^;

/*

Устанавливает

разделитель между операторами */

/* Таблицы */ CREATE TABLE Customers ( Cust_ID INTEGER NOT NULL, CustName VARCHAR(20) NOT NULL, Address CHAR(40), PRIMARY KEY (Cust_ID)

)^ CREATE TABLE Products ( Prod_ID INTEGER NOT NULL, ProductName VARCHAR(20) NOT NULL, Price DECIMAL(10,2) NOT NULL CHECK (Price > 0), /* Остаток на складе */ Rest INTEGER NOT NULL CHECK (Rest >= 0), PRIMARY KEY (Prod_ID) )^ CREATE TABLE Orders ( Num INTEGER NOT NULL, Cust_ID INTEGER NOT NULL, Prod_ID INTEGER NOT NULL, ProdCount INTEGER NOT NULL CHECK (ProdCount > 0), Total NUMERIC(10,2) NOT NULL,/*Итого в у.е.*/ SaleDate DATE NOT NULL, PRIMARY KEY (Num), FOREIGN KEY (Cust_ID) REFERENCES Customers, FOREIGN KEY (Prod_ID) REFERENCES Products )^

/* Генераторы */ CREATE GENERATOR GEN_CUSTOMERS_ID ^ SET GENERATOR GEN_CUSTOMERS_ID TO 0 ^ CREATE GENERATOR GEN_ORDERS_ID ^ SET GENERATOR GEN_ORDERS_ID TO 0 ^ CREATE GENERATOR GEN_PRODUCTS_ID ^ SET GENERATOR GEN_PRODUCTS_ID TO 0 ^

/* Хранимые процедуры */ CREATE PROCEDURE SP_GEN_CUSTOMERS_ID RETURNS (ID INTEGER) AS BEGIN ID = GEN_ID(GEN_CUSTOMERS_ID, 1); SUSPEND; /* Возвращает значение генератора */ END ^

/* Триггеры */ CREATE TRIGGER PRODUCTS_BI FOR PRODUCTS ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.PROD_ID IS NULL) THEN NEW.PROD_ID = GEN_ID(GEN_PRODUCTS_ID,1); END ^

26 CREATE TRIGGER PRODUCTS_AD0 FOR PRODUCTS ACTIVE AFTER DELETE POSITION 0 AS begin /* Каскадное удаление заказов на удаленный товар */ delete from Orders where orders.prod_id = products.prod_id; end ^ CREATE TRIGGER ORDERS_BI FOR ORDERS ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.NUM IS NULL) THEN NEW.NUM = GEN_ID(GEN_ORDERS_ID,1); END ^ COMMIT ^ SET TERM ; ^

Для создания перечисленных объектов выполните скрипт в окне Script Executive или воспользуйтесь диалоговыми средствами IBExpert. Задание. Перепишите операцию отпуска товара с помощью триггера, автоматически уменьшающего остаток товара на складе после вставки нового заказа. 3.3. Клиентская часть 3.3.1. Настройка модуля данных После создания модуля данных (пункт меню File – New – Data Module) расположим на нем компоненты доступа к данным. При выборе имен компонентов здесь и далее будем пользоваться венгерской нотацией. Префикс ds используем для типов TDataSorce, id – TIBDataSet, iq – TIBQuery, и т.д.

1. Настройте компонент соединения idbSklad: TIBDatabase и компонент управления транзакциями itaSklad: TIBTransaction. При начальной отладке воспользуйтесь для настройки idbSklad редактором DataBase Editor

27 (см.п.2.1), в котором пользователя и пароль.

укажите

параметры соединения, включая имя

Свойство Значение DatabaseName D:\IB\4gr\MyBase\NewSklad.gdb LoginPrompt False DefaultTransacti ItaSklad on После того, как программа будет полностью отлажена, вызовите DataBase Editor и очистите в нем все поля. Они будут заполняться при запуске программы. 2. Настройте компонент iqInsert , используемый для вставки новых записей в таблицу Product, и компоненты idCust, idOrders, idProduct, связав их с базой данных: Database

IdbSklad

Transaction

itaSklad

3. Для объектов idCust, idOrders, idProduct типа TIBDataSet укажите в свойствах SelectSQL запросы вида select * from . 4. Используя DataSet Editor (контекстное меню компонентов idCust, idOrders, idProduct), определите действия по редактированию наборов данных. Для примера рассмотрим компонент idCust.

Кнопка Get Table Fields заполняет полями таблицы области на экране Key Fields и Update Fields. Во второй области указываются редактируемые поля. Как пра-

28 вило, можно оставить все поля. В области слева необходимо выбрать ключевые поля, по которым будет происходить поиск в таблице. Нажав кнопку Select Primary Keys, выберем для поиска первичный ключ. При выборе Datasets Defaults поиск будет проводиться по всем полям. Нажав кнопку Generate SQL, получим SQL-операторы для обновления НД, которые можно просмотреть и при необходимости отредактировать на вкладке SQL. Сами операторы хранятся в свойствах SelectSQL, DeleteSQL, InsertSQL, ModifySQL и имеют вид, соответственно, select * from CUSTOMERS delete from CUSTOMERS where

Cust_ID = :OLD_Cust_ID insert into CUSTOMERS (Cust_ID, CustName, Address) values (:Cust_ID, :CustName, :ADDRESS) update CUSTOMERS set Cust_ID = :Cust_ID, CustName = :CustName, ADDRESS = :ADDRESS where Cust_ID = :OLD_ Cust_ID Параметрами запросов (например, :Cust_ID) являются поля набора данных, значения которых подставляются в поля таблицы на сервере. Приставка OLD означает обращение к старому значению поля из набора данных (до редактирования), с которым сравнивается поле из таблицы на сервере. Заполните свойство GeneratorField, указав имя генератора GEN_CUSTOMERS_ID, имя ключевого поля Cust_ID и событие On New Record.

Аналогичным образом настройте компоненты idOrders, idProduct. 5. Для редактирования заказчиков включим режим кэширования изменений, установив свойство CachedUpdates компонента idCust в True. Кэширование изменений означает, что записи на сервере могут изменяться группами, которые будут переноситься из приложения на сервер отдельной командой. В данном случае можно будет добавлять сразу несколько заказчиков, но в базе данных они появятся только после вызова метода ApplyUpdates компонента idCust.

29 Для компонентов idOrders, idProduct оставим обычный режим редактирования, установив CachedUpdates равным False. 6. Для компонентов idCust, idOrders, idProduct создайте типизированные поля (например, idСustAddress: TStringField и остальные), используя редактор полей Fields Editor. Fields Editor вызывается в контекстном меню упомянутых компонентов. Добавьте в состав типизированных полей (ТП) все поля набора данных. Имена ТП строятся по принципу ИмяНД+ИмяПоля и включаются в описание модуля данных. Очевидным преимуществом данного подхода является контроль компилятора за корректностью обращений к полям. 7. Установите свойство DataSource объектов dsCust, dsOrder, dsProduct, в idCust, idOrders, idProduct, соответственно. 3.3.2. Взаимодействие с пользователем Главная форма приложения

На главной форме расположены кнопки sbAdd, sbOtp, sbArc, spQuit: TspeedButton, реализующие функции – приход (модуль AddUnit), расход (OtpUnit), архив (ArcUnit), выход. В главном меню функции кнопок продублированы. Установка соединения с БД выполняется при выборе пункта «Инструменты Соединение». Запускается форма соединения (frmConnect), имеющая на этапе разработки следующий вид

Для ввода пароля здесь используется компонент MaskEdit. Информация о соединении записывается в компоненты edtDatabase: TEdit; //Полное имя БД с указанием сервера (см.стр 6)

30 edtUser: TEdit; // Имя пользователя edtRole: TEdit; //Роль пользователя medtPassword: TMaskEdit; // Пароль В обработчик события OnClick кнопки Ok запишите: if dlgOpen.Execute then edtDatabase.Text := dlgOpen.FileName; Соединение выполняется в главной форме в обработчике пункта главного меню: procedure TMainForm.ConnectClick(Sender: TObject); begin with frmConnect, dm do if ShowModal = IDOK then begin itaSklad.Active := false; IdbSklad.Connected := false; idbSklad.DatabaseName := edtDatabase.Text; idbSklad.Params.Clear; idbSklad.Params.Add(Format('USER_NAME=%s', [edtUser.Text])); idbSklad.Params.Add(Format('PASSWORD=%s', [medtPassword.Text])); if edtRole.Text '' then idbSklad.Params.Add(Format('ROLE=%s', [edtRole.Text])); idbSklad.Connected := true; itaSklad.Active := true; end; end; Упражнения. Запрограммируйте доступность кнопок операций с БД в зависимости от наличия с ней соединения. Предусмотрите безопасное хранение параметров соединения в конфигурационном файле или системном реестре, чтобы не вызывать диалог соединения при каждом запуске программы. Форма прихода товара на склад имеет вид

31 и раскрывается при нажатии кнопки

sbAdd главного меню procedure TMainForm.sbAddClick(Sender: TObject); begin // При добавлении товара SQL-операторы with DM.iqInsert do // Добавление товара на склад if AddForm.ShowModal=mrOK then begin // Проверяем наличие на складе Close; SQL.Clear; SQL.Add('SELECT * FROM Products WHERE ProductName = :p1 and Price = :p2'); ParamByName('p1').value:=AddForm.Edit1.Text; ParamByName('p2').value:=AddForm.Edit2.Text; Open; if IsEmpty then begin //Если нет, то добавляем CLose; SQL.Clear; SQL.add('INSERT INTO Products (ProductName, Price, Rest) values (:p1, :p2, :p3)'); ParamByName('p1').value:=AddForm.Edit1.Text; ParamByName('p2').value:= StrToFloat(AddForm.Edit2.Text); ParamByName('p3').value:= StrToInt(AddForm.Edit3.Text); ExecSQL; end else // Если есть, то увеличиваем количество begin CLose; SQL.Clear; SQL.add('UPDATE Products SET Rest = Rest+ :p3 WHERE (ProductName = :p1) and (Price = :p2)'); ParamByName('p1').value:=AddForm.Edit1.Text; ParamByName('p2').value:= StrToFloat(AddForm.Edit2.Text); ParamByName('p3').value:= StrToInt(AddForm.Edit3.Text); ExecSQL; end; end; end;

Для лучшей иллюстрации материала модули AddUnit, OtpUnit, ArcUnit используют различные способы доступа к данным. Непосредственный вызов SQL-операторов, использованный выше, обеспечивает наиболее быстрый доступ. Но следует помнить, что подобный код некорректен, его следует обязательно дополнять проверкой вводимых данных (что Вы сейчас сделаете в качестве упражнения). В этом плане более удобны компоненты, используемые нами при программировании форма отпуска товаров. Форма отпуска товара имеет вид

32

и вызывается в обработчике procedure TMainForm.spOtpClick(Sender: TObject); begin OtpForm.ShowModal; end;

Компонент dblcCust: TDBLookUpComboBox служит для занесения в заказы кода заказчика, выбираемого из таблицы клиентов, и настраивается следующим образом: Свойство

Значение

Примечание

DataField DataSource

Cust_ID DM.dsOrders

В какое поле и в какой НД записывается

KeyField

Cust_ID

Что записывается

ListField

CustName;Address

Что отображается в списке

ListSource

DM.dsCust

Откуда берется

Компонент dblcProduct выполняет ту же функцию, но данные берет из таблицы изделий. Свойство

Значение

Примечание

DataField DataSource

Prod_ID DM.dsOrders

В какое поле и в какой НД записывается

KeyField

Prod_ID

Что записывается

ListField

ProdName;Price;Rest

Что отображается в списке

ListSource

DM.dsProduct

Откуда берется

Компонент deCount: TDBEdit предназначен для ввода количества товаров в связанное с ним поле БД:

33 Свойство

Значение

Примечание

DataField DataSource

ProdCount DM.dsOrders

В какое поле и в какой НД записывается

Большим преимуществом TDBEdit по сравнению с обычными TEdit является его «типизированность»: ввод символов, недопустимых для связанных с компонентом данных, блокируется. Компонент dp: TdateTimePicker используется для отображения текущей даты и занесения ее в таблицу заказов. Далее напишем простую процедуру (метод класса TOtpForm ), которая проверяет состояние НД и, в случае вставки, делает доступными элементы управления. procedure TOtpForm.Accessed; var Ins: Boolean; begin // Разрешаем кнопки в зав-ти от состояния НД Ins := dm.idOrders.State in [dsInsert]; dblcCust.Enabled := Ins; dblcProduct.Enabled := Ins; btOrder.Enabled := Ins; btCancel.Enabled := Ins; btAdd.Enabled := Not Ins; btClose.Enabled := Not ins; end; end;

В первый раз процедура вызывается при появлении формы на экране: procedure TOtpForm.FormShow(Sender: TObject); begin Accessed; end;

Обработчик нажатия кнопки «Добавить пустой»: procedure TOtpForm.btAddClick(Sender: TObject); begin with dm do begin // Переоткрываем НД, что влечет старт транзакции idCust.Close; idCust.Open; idProduct.Close; idProduct.Open; idOrders.Close; idOrders.Open; // Добавляем пустую запись idOrders.Append; end; // О старте транзакции сигнализируем цветом OtpForm.lbCount.Color := clRed;

34 Accessed; end;

При написании обработчика нажатия кнопки «В заказ!» учтем, что оформление заказа состоит из операций в двух таблицах (записи в журнал заказов и уменьшения остатка на складе) и должно рассматриваться как транзакция. procedure TOtpForm.btOrderClick(Sender: TObject); var otpusk, ostatok: integer; begin with dm do begin Otpusk := deCount.Field.Value; Ostatok := DM.idProductRest.value; if Otpusk