Глава 21
Механизм удаленного доступа
Архитектура клиент/сервер реализует многопользовательский доступ к данным и их распределенную обработку. Мощность современных промышленных серверов БД позволяет организовать одновременную работу с данными сотен и тысяч пользователей. При создании подобных систем требуется решить ряд сложных технических проблем.
Во-первых, необходимо обеспечить соединение клиентского приложения с сервером БД таким образом, чтобы пользователь имел доступ к необходимым таблицам и отправлял запросы и получал их результаты в рамках одной сессии соединения сервера и клиента. В этом случае говорят о доступе к серверу на уровне соединения.
Во-вторых, клиентское приложение должно иметь достоверную информацию о структуре данных, запросы клиента должны быть понятны серверу, а ответы сервера должны быть понятны клиенту. В этом случае говорят о доступе к данным сервера на уровне логики приложения.
Первая задача решается, в основном, средствами стандартного ПО сервера и клиента.
Вторая задача решается при помощи инструментов, предоставляемых средой разработки Delphi.
В этой главе описываются инструменты Delphi, предназначенные для управления соединениями с БД и сеансами связи.
Ниже рассматриваются следующие вопросы.
Сервер
Важнейшей частью приложений в архитектуре клиент/сервер является сервер. Сервер базы данных представляет собой мощную систему управления базой данных, основанную на использовании SQL. Бизнес по разработке и эксплуатации промышленных серверов БД ежегодно имеет оборот в десятки миллиардов долларов. Подавляющее большинство крупных и средних компаний имеют собственные корпоративные базы данных, с которыми работают многоуровневые приложения на самых разных платформах. Серверы Oracle, Informix, Sybase управляют базами данных объемом десятки и сотни гигабайт. Для управления меньшими корпоративными базами данных применяются программные продукты MS SQL Server и InterBase.
Сервер как часть приложения, предназначенная для обеспечения работы клиентов с БД в целом, решает три основных задачи.
Связь между клиентскими приложениями и сервером осуществляется на двух уровнях.
Во-первых, должна существовать связь между операционными системами компьютеров клиентов и сервера. Это вопрос создания и администрирования компьютерных сетей.
Во-вторых, части многоуровневого приложения должны уметь взаимодействовать между собой. Сервер имеет встроенные программные средства, обеспечивающие взаимодействие с клиентами на уровне данных. На клиентской стороне устанавливается специальное ПО клиента.
При формировании запросов в многоуровневых приложениях используется язык SQL. Каждый тип сервера работает на собственном диалекте языка, который в большей или меньшей степени соответствует стандарту ANSI 92. Сервер InterBase имеют немного дополнений. А диалекты серверов Sybase и MS SQL отличаются от стандарта довольно сильно.
Организация обработки данных в приложениях клиент/сервер имеет ряд. особенностей.
Поля таблиц базы данных сервера создаются с использованием не стандартных типов данных, а доменов — создаваемых пользователем типов данных с дополнительными ограничениями.
Все операции с данными должны выполняться сервером по заявке клиента. Для этого используются хранимые процедуры — наиболее распространенные запросы, которые хранятся на сервере и выполняются по требованию клиентов. Хранимые процедуры оптимизированы для выполнения на сервере.
Некоторые хранимые процедуры должны выполняться автоматически при возникновении той или иной ситуации. В частности, они используются для поддержания ссылочной целостности данных. Такие запросы называются триггерами.
Объединяя данные из различных таблиц, можно создавать совокупности столбцов из разных таблиц, которые называются просмотрами. Структуру просмотра можно сохранить и использовать в дальнейшем.
В процессе выполнения запросов сервер может самостоятельно создавать временные таблицы.
При работе с наборами данных сервер использует курсоры — указатель на текущую запись таблицы (аналог текущей записи в наборе данных Delphi). Курсоры бывают однонаправленными и двунаправленными. Это связано с особенностями индексирования записей.
Механизм собственных курсоров ВОЕ позволяет разработчику абстрагироваться от курсоров сервера.
Выполнение наборов операций выполняется сервером при помощи механизма транзакций. Транзакция — это группа операторов SQL, все команды которой должны быть успешно выполнены. В этом случае изменения фиксируются на сервере. В противном случае все команды отменяются. Транзакции обеспечивают сохранение целостности данных при работе с сервером многих клиентов одновременно.
Так как с базой данных сервера одновременно работает множество клиентов, поэтому весьма высока вероятность возникновения конфликтов при попытках модификации одних и тех же данных. Чтобы обеспечить целостность данных, серверы вынуждены осуществлять блокировку данных не отдельными записями, как в локальных СУБД, а страницами или даже таблицами. Размер страниц зависит от типа сервера. Табличная блокировка применяется при выполнении запросов модификации данных к целым таблицам (без использования оператора where).
Организация удаленного доступа
Чаще всего клиентское приложение обращается к серверу при помощи запросов SQL. Однако для этого между клиентом и сервером должно существовать устойчивое соединение. На компьютере клиентского приложения должно быть инсталлировано специальное программное обеспечение для связи с сервером. Для многих типов серверов это ПО можно настраивать на конкретный протокол, который используется для соединения сервера с компьютером клиента.
Каждый сервер БД имеет собственное клиентское ПО, имеющее особенности инсталляции. Поэтому имеет смысл изучать возможности только того сервера, который будет использоваться в конкретной работе.
Ниже мы все же рассмотрим процесс установки клиентского ПО на примере сервера InterBase.
Созданные в Delphi многоуровневые приложения (гл. 20) связываются с сервером БД при помощи BDE. Для этого используются драйверы системы SQL Links.
Для всех распространенных серверов БД в составе BDE поставляются соответствующие драйверы. Их можно выбрать при инсталляции Delphi или установить впоследствии. Каждый драйвер имеет собственные настройки, которые можно изучить в главе 14.
Драйвер обеспечивает трансляцию команд, генерируемых BDE при его взаимодействии с клиентским приложением, в формат, понятный серверу БД. Он же производит обратное преобразование.
При подключении к серверу БД также необходимо создать новый псевдоним BDE, для которого выбирается драйвер нужного сервера.
Итак, для организации доступа из клиентского приложения к данным на сервере SQL необходимо, в первую очередь, выполнить следующие действия.
Установка клиентского ПО сервера InterBase
Опуская вопросы инсталляции сервера (это не относится к теме книги), рассмотрим процесс установки клиентского программного обеспечения на рабочей станции пользователя.
В начале необходимо сделать некоторые вводные замечания относительно сервера InterBase.
Так как этот сервер выпускается компанией Inprise, разработчики Delphi всегда уделяли вопросам улучшения взаимодействия Delphi и InterBase повышенное внимание.
Ниже в этой части (гл. 24) рассматриваются специально разработанные компоненты доступа к данным, предназначенные для организации взаимодействия с БД на сервере InterBase.
Разработчику доступен специально облегченный вариант сервера — локальный InterBase, обеспечивающий работу клиентских приложений с сервером на одном компьютере. Если читатель решил использовать локальный вариант (для учебных целей он предпочтительнее), то клиентское ПО ставить не нужно. Установка локального сервера практически полностью автоматизирована, при этом сервер сам подключается к BDE, обеспечивая настройку своего драйвера в системе SQL Links. Для дальнейшей успешной работы, после инсталляции локального сервера достаточно создать базу данных (см. ниже) и псевдоним BDE.
Процесс инсталляции клиентского ПО для работы с настоящим, удаленным сервером, полностью автоматизирован. Некоторых усилий требует лишь процесс настройки соединения с компьютером сервера.
Для этого необходимо задать в системном файле HOSTS адрес TCP/IP компьютера сервера и его имя, например:
192.196.10.12 WHServer
Если планируется работа с несколькими серверами, то их адреса также нужно указать в этом файле.
Кроме этого, нужно проверить, чтобы в файле SERVICES была следующая строка:
Gds_db 3050/tcp
Естественно, необходимо, чтобы между сервером и клиентским компьютером существовала связь, а для этого на них должны быть установлены соответствующие сетевые протоколы.
На клиентском компьютере не помешает установить комплект вспомогательных программ InterBase, в частности — утилиту Windows ISQL, которая обеспечива-г ет создание баз данных на сервере и соединение с ними. С ее помощью можно проверить работоспособность связи с сервером, подключив, например, демонстрационную базу данных EMPLOYEE.GDB.
Настройка BDE клиента
Установленный на клиентском компьютере экземпляр BDE должен иметь инсталлированный драйвер SQL Links для InterBase (о настройках драйвера см. главу 14).
После этого создается новый псевдоним BDE, в котором требуется задать путь к файлу базы данных на компьютере сервера. Путь, например, может выглядеть так:
Ошибка! Закладка не определена.
В данном примере IBServer — это сетевое имя компьютера сервера, а путь задается без указания логического диска. Папка с файлом базы данных должна быть доступна для совместного использования в сети.
Для локального сервера в настройках псевдонима достаточно указать полный путь к файлу базы данных.
Для удобства работы желательно, но не обязательно настроить параметр user name, который задает имя пользователя по умолчанию в диалоговом окне регистрации пользователя на сервере.
При создании базы данных InterBase предлагает использовать стандартного пользователя SYSDBA с паролем masterkey.
Обратите внимание, что уже на этом этапе вы сталкиваетесь с работой сервера, который в ответ на вызов клиента предлагает ему ввести свое имя и пароль. Естественно, что для этого сервер должен быть запущен на своем компьютере, иначе возникает ошибка опознавания базы данных.
Сеанс связи с сервером
Теперь рассмотрим вопросы модернизации программной логики приложения для обеспечения обмена данными с сервером.
Для управления сеансом связи приложения с базой данных, используется компонент TSession. Этот объект создается во время выполнения приложения автоматически, независимо от желания разработчика. Программист лишь может задать параметры сеанса явным образом, перенеся во время разработки компонент TSession в приложение со страницы Data Access Палитры компонентов. В рамках одного сеанса связи с сервером приложение может работать с несколькими базами данных, то есть использовать несколько компонентов TDatabase (см. ниже). Все они управляются одним компонентом TSession.
Свойства и методы компонента TSession позволяют управлять сеансом и получать о нем исчерпывающую информацию.
Клиентское приложение может держать открытыми несколько наборов данных от различных серверов. Для каждого такого соединения создается собственный экземпляр компонента TSession. Для доступа к любому из этих экземпляров используется компонент TSessionList, который инкапсулирует индексированный список указателей на экземпляры компонента TSession.
Приложение может иметь несколько экземпляров компонента TSession, но только один из них является активным в данный момент.
Экземпляр класса TSessionList создается в единственном числе в каждом приложении. Для доступа к свойствам и методам экземпляра класса TSessionList в приложении используется глобальная переменная из модуля
DBTables:
Sessions: TSessionList;
Если в приложении отсутствуют определенные разработчиком компоненты TSession, то доступ к созданному автоматически экземпляру также осуществляется при помощи глобальной переменной из модуля DBTables:
Session: TSession;
В случае если приложение работает с несколькими сеансами, эта переменная всегда указывает на экземпляр активного в настоящий момент компонента.
Теперь остановимся более подробно на свойствах и методах классов, обеспечивающих управления сеансами в приложении.
Класс TSessionList
Класс TSessionList представляет собой индексированный список указателей на экземпляры всех созданных в приложении компонентов TSession. Используя его свойство sessions, можно получить доступ к любому объекту сессии. Так как в данный момент может быть активной только одна сессия, то существует свойство CurrentSession, которое обеспечивает доступ к текущей сессии:
with Sessions do for i := 0 to Count do begin
ShowMessage('В настоящее время работает'
+IntToStr(CurrentSession.
DatabaseCount)+'соединений');
if Sessions[i].Active then Sessions[i].Close;
end;
Этот исходный код вполне может работать при закрытии приложения. Обращение к экземпляру класса TSessionList осуществляется при помощи глобальной переменной sessions.
Свойства и методы класса TSessionList представлены в табл. 21.1.
При запуске приложения автоматически создается единственный экземпляр этого объекта. Обратиться к нему можно при помощи глобальной переменной Sessions, объявленной в модуле DBTables.
Таблица 21.1. Свойства и методы класса TSessionList
Объявление |
Тип |
Описание |
property Count: Integer; |
Ro |
Возвращает число доступных в приложении сеансов |
property CurrentSession: TSession |
Pu |
Возвращает указатель на экземпляр активного компонента сеанса |
property List [const SessionName: string]: TSession; |
Ro |
Возвращает указатель на компонент сеанса с именем SessionName |
property Sessions [Index: Integer]: TSession; |
Ro |
Индексированный список указателей на все компоненты сеансов приложения |
Методы |
||
function FindSession (const SessionName: string): TSession; |
Pu |
Возвращает указатель на компонент сеанса по имени SessionName |
procedure GetSessionNames (List: TStrings) ; |
Pu |
Возвращает в параметре List список имен всех сеансов приложения |
function OpenSession (const SessionName: string): TSession; |
Pu |
Активизирует компонент сеанса с именем SessionName. Если такого сеанса в списке нет, то создается и активизируется новый |
Компонент TSession
При создании приложений баз данных довольно часто требуется обеспечить управление соединением с базой данных. При этом одно приложение может одновременно работать с несколькими базами данных. Для управления всеми соединениями с базами данных в рамках одного сеанса используется компонент TSession.
Экземпляр класса TSession создается автоматически при запуске приложения и обеспечивает управление всеми соединениями с базой данных. Доступ к свойствам и методам этого экземпляра осуществляется через глобальную переменную session.
Разработчик может использовать свойства и методы компонента TSession во время разработки. Для этого достаточно перенести этот компонент на форму из Палитры компонентов (страница Data Access).
В табл. 21.2 представлены свойства и методы компонента TSession. Их описание соответствует поведению компонента TSession, перенесенного из Палитры компонентов (в некоторых случаях поведение автоматически создаваемого экземпляра класса отличается).
Таблица 21.2. Свойства и методы компонента TSession
Объявление |
Тип |
Описание |
|
Свойства |
|||
type Tlocale: Pointer; property Locale: TLocale; |
Pu
|
Указатель на языковый драйвер BDE
|
|
Property NetFileDir: string; |
Pb |
Определяет каталог, в котором расположен файл PDOXUSER.NET, который обеспечивает работу баз данных Paradox в сетевом режиме |
|
Property PrivateDir: string/ |
Pb
|
Определяет каталог для хранения вре менных файлов BDE |
|
Property SessionName: String; |
Pb
|
Содержит уникальное имя сессии
|
|
Property SQLHourGlass: Boolean; |
Pb |
При значении True во время выполнения операций BDE курсор заменяется на песочные часы |
|
Type TtraceFlag = (tfQPrepare, tfQExecute, tfError, tfStmt, .tfConnect, tfTransact, tfBlob, r.fMisc, tfVendor, tfDataIn, tfDataOut) ; TtraceFlags = set of TtraceFlag; Property TraceFlags: TtraceFlags; |
Pu
|
Определяет типы операций,отображающиеся в утилите SQL Monitor во время выполнения приложения
|
|
Методы |
|||
Procedure AddAlias (const Name, Driver: string; List: Tstrings); |
Pu |
Добавляет новый псевдоним для использования в сеансе. Параметр Name определяет имя, Driver задает драйвер SQL Links. Параметр List содержит список нестандартных значений свойств драйвера |
|
Procedure AddDriver (const Name: .string; List: TStrings) ; |
Pu |
Добавляет для использования в сеансе новый драйвер. Параметр Name задает имя драйвера, параметр List — список значений свойств |
|
procedure AddPassword (const Password: string); |
Pu |
Добавляет для использования в сеансе новый пароль для таблиц Paradox |
Методы |
|
|
|
procedure AddStandardAlias (const Name, Path, DefaultDriver: string); |
Pu |
Добавляет для использования в сеансе стандартный псевдоним BDE. Параметр Name задает имя псевдонима, параметр Path определяет нахождение базы данных. Параметр DefaultDriver определяет тип драйвера |
|
procedure Close; |
Pu |
Закрывает все соединения и сеанс |
|
procedure CloseDatabase (Database: TDatabase); |
Pu |
Закрывает соединение, управляемое компонентом Database |
|
procedure DeleteAlias (const Name: string); |
Pu |
Удаляет псевдоним BDE из сеанса |
|
procedure DeleteDriver (const Name: string); |
Pu |
Удаляет драйвер из сеанса |
|
procedure DropConnections; |
Pu |
Удаляет из сеанса все соединения, которые в этот момент не активны |
|
function FindDatabase (const DatabaseName: string): TDatabase; |
Pu |
Проводит поиск соединения, представленного компонентом TDatabase |
|
function GetAliasDriverName (const AliasName: string): string; |
Pu |
Возвращает имя драйвера в сеансе |
|
procedure GetAliasNames (List: TStrings) ; |
Pu |
Возвращает в списке List имена псевдонимов BDE данного сеанса |
|
procedure GetAliasParams (const AliasName: string; List: TStrings); |
Pu |
Возвращает в списке List параметры псевдонима AliasName |
|
procedure GetConfigParams (const Path, Section: string; List: TStrings); |
Pu |
Возвращает в списке List параметры того раздела конфигурации BDE, который задан параметрами Path и Section |
|
procedure GetDatabaseNames (List: TStrings) ; |
Pu |
Возвращает в списке List имена доступных в сеансе псевдонимов BDE и компонентов TDatabase |
|
procedure GetDr.iverNames (List: TStrings) ; |
Pu |
Возвращает в списке List имена всех драйверов BDE |
|
procedure GetDriverParams (const DriverName: string; List: TStrings); |
Pu |
Возвращает в списке List параметры драйвера BDE с именем DriverName |
Методы |
|||
function GetPassword: Boolean; |
Pu |
Вызывает метод-обработчик OnPassword компонента Tsession. Если он не определен, вызывается стандартное диалоговое окно ввода пароля для таблиц Paradox. Метод возвращает True, если пользователь щелкнул на кнопке ОК |
|
procedure GetStoredProcNames (const DatabaseName: string; List: TStrings) ; |
Pu |
Возвращает в списке List имена всех хранимых процедур сервера, имя которого представлено параметром DatabaseName |
|
procedure GetTableNames (const DatabaseName, Pattern: string; Extensions, SystemTables: Boolean; List: TStrings); |
Pu |
Возвращает в списке List имена таблиц базы данных DatabaseName. Параметр Extensions управляет отображением расширением файлов таблиц (важно для таблиц Paradox и dBASE). Параметр SystemTables управляет отображением системных таблиц (используется для серверов SQL) |
|
function IsAiias (const Name: string): Boolean; |
Pu |
Определяет, является ли строка Name именем псевдонима BDE |
|
procedure ModifyAlias (Name: string; List: TStrings); |
Pu |
Изменяет параметры псевдонима BDE по имени Name. Параметры содержатся в списке List |
|
procedure ModifyDriver (Name: string; List: TStrings) ; |
Pu |
Изменяет параметры драйвера ВОЕ по имени Name. Параметры содержатся в списке List |
|
procedure Open; |
Pu |
Открывает сеанс и делает его текущим для приложения |
|
function OpenDatabase (const DatabaseName: string): TDatabase; |
Pu |
Открывает существующую, или создает и открывает новую базу данных. Параметр DatabaseName содержит значение одноименного свойства компонента TDatabase |
|
procedure RemoveAllPasswords ; |
Pu |
Удаляет все ранее добавленные в данной сессии пароли для таблиц Paradox |
|
procedure RemovePassword "(const Password: string) ; |
Pu |
Удаляет ранее добавленный в данной сессии пароль для таблиц Paradox |
|
procedure SaveConfigFile; |
Pu |
Сохраняет текущую конфигурацию BDE |
|
Методы обработчики событий |
|||
type TPasswordEvent = procedure (Sender: TObject; var Continue: Boolean) of objects-property OnPassword: TpasswordEvent ; |
Pb |
Вызывается при открытии защищенной паролем таблицы Paradox. Параметр Continue при значении True разрешает добавление нового пароля |
|
Property OnStartup: TNotifyEvent; |
Pb |
Вызывается при активизации сессии |
При использовании компонента TSession обязательно необходимо задавать значение свойства sessionName, которое содержит уникальное имя сессии. Свойство AutoSessionName поможет создать уникальное имя сессии автоматически.
При управлении соединениями приложения с базами данных очень важную роль играет BDE. Поэтому многие методы компонента TSession предназначены для чтения и сохранения параметров BDE, драйверов и псевдонимов.
При работе с этими методами следует учитывать, что многие из них работают только в пределах текущей сессии. Например, методы Addpassword и RemovePassword манипулируют паролем для таблицы Paradox только в текущей сессии. Если в этот же момент времени к BDE на этом же компьютере обращается другое приложение, то на него выполненные операции не распространяются. Дополнительные ограничения на использование псевдонимов BDE накладывает свойство configMode.
Рассмотрим небольшой пример, представленный в листинге 21.1. В нем используется компонент Sessionl типа TSession.
Листинг 21.1. Пример программного кода по управлению соединением с базой данных DBDEMOS
var UniList: TStringLlst;
Index: Integer;
WHPath: Strings; begin UniList := TStringList.Create;
with Sessioni do begin Open;
ConfigMode := cmSessiori;
GetAliasNames (UniList) ;
if Not UniLisL.Find('DBDEMOS',Index) then AddStandd rdAlias ( ' DDDEMOS ' , ' ' , ' PARADOX ' ) ;
UniList.Clear;
UniLis L.Add(•PATH=D:\DG\DBDEMOS') ;
Uri i List.Add('ENABLE BCD=TRUE') ;
ModifyAlias ('DBDEMOS', UniList) ;
UniList.Clear;
GetAliasParams('DBDEMOS', UniList) ;
Cloae;
end;
UniList.Free;
ena;
После открытия сеанса при помощи метода open задается ограничение на
использование в сессии только созданных в ней псевдонимов (свойство
ConfigMode).
Затем метод GetAliasNames передает имена всех псевдонимов сеанса в специально созданный список UniList. Если в списке не находится псевдоним DBDEMOS, то он создается при помощи метода AddStandardAlias. При этом путь к файлам базы данных не указывается. Обратите внимание, что использованный метод пригоден для создания стандартных псевдонимов для локальных БД.
После этого в список UniList записываются параметры нового псевдонима, а сам список передается в качестве параметра в метод ModifyAlias. Во всех подобных случаях используется единый формат задания значения свойства:
<Название параметра>-<3начение параметра>
При помощи компонента TSession можно управлять доступом к закрытым паролем таблицам СУБД Paradox.
var PasswordStr: String;
procedure TForml.SessJnnlPassword(Sender: TObject;
var Continue:
Boolean);
begin
Sessioni.Add Password(PasswordStr) ;
Continue := True;
end;
Для этого используется метод-обработчик Onpassword. Он вызывается при попытке открыть защищенную паролем таблицу. При помощи метода
Addpassword можно добавлять новый пароль к сеансу. Параметр continue разрешает или запрещает добавление нового пароля в сеансе.
Соединение с сервером
Все обращения из приложения к таблицам одной базы данных на сервере осуществляются через одно соединение, на которое замыкаются все компоненты доступа к данным, имеющие соответствующие значения свойства
DatabaseName.
Все управление одиночным соединением с какой-либо базой данных осуществляется компонентом TDatabase (табл. 21.3). В общем случае применение этого компонента в приложениях баз данных необязательно. Однако при работе с серверами SQL он необходим, так как:
При обычном использовании локальных баз данных можно прекрасно обойтись и без этого компонента.
В процессе работы компонент активно использует параметры псевдонимов и драйверов BDE.
Таблица 21.3. Свойства и методы компонента тоа tabase
Объявление |
Тип |
Описание |
Свойства |
||
property AliasName: string; |
Pb |
Задает имя псевдонима BDE используемой базы данных |
property Connected: Boolean/ |
Pb |
Управляет включением соединения с базой данных |
property DdlabaseName: string; |
Pb |
Определяет имя базы данных |
property DataSetCount: Integer- |
Ro |
Возвращает число открытых наборов данных, работающих через данное соединение |
property DataSets [Index: Integer]: TDBDataSet"; |
Ro |
Индексированный список всех объектов открытых наборов данных данного соединения |
property Directory: strinq; |
Pu |
Определяет текущий каталог для баз данных Paradox и dBASE |
property DriverName: string; |
Pb |
Содержит имя драйвера базы данных |
property Exclusive: Boolean; |
Pb |
При значении True другие приложения не могут работать с базой данных одновременно с данным компонентом , |
type HDBIDB: Longint; property Handle: HDBIDB; |
Pu |
Дескриптор BDE. Используется для прямых вызовов функций API BDE |
property HandleShared: Boolean; |
Pu |
При значении True дескриптор BDE компонента доступен в компоненте TSession |
property InTransaction: Boolean |
Ro |
Показывает состояние транзакции. При значении True транзакция выполняется |
property isSQLBased: Boolean; |
Ro |
При значении True соединение работает через драйвер SQL Links |
property KeepConnection: boolean; |
Pb |
При значении True соединение продолжает оставаться активным после закрытия всех наборов данных. При значении False после закрытия последнего набора данных соединение закрывается |
type TLocale: Pointer; property Locale: TLocale; |
Ro |
Указывает на языковый драйвер BDE, используемый при работе с базой данных |
property LoginPrompt: Boolean; |
Pb |
Управляет отображением стандартного диалога регистрации пользователя при подключении к серверу |
property Params: TStrings; |
Pb |
Содержит список значений параметров псевдонима BDE, которые пользователь задает перед подключением к серверу |
property Session: TSession |
Ro |
Указывает на компонент TSession, который управляет работой данного компонента |
property SessionAlias: Boolean; |
Ro |
При значении True при подключении к БД используется псевдоним сессии |
property SessionName: string; |
Pb |
Содержит имя сеанса, который управляет работой компонента |
property Readonly; Boolean; |
Pb |
Управляет режимом доступа к данным "только для чтения" |
property Temporary: Boolean; |
Pu |
Значение True говорит о том, что экземпляр компонента создан во время выполнения |
type TTraceFlag = (tfQPrepare, tfQExecute, tfError, tfStmt, tfConnect, tfTransact, tfBlob, tfMisc, tfVendor, LfDdLaIll, LfDctLciOuL) ; TTraceFiags = set of TTraceFlag; property TraceFlags: TTraceFiags; |
Pu |
Определяет перечень операций, выполнение которых отображается в утилите SQL Monitor при выполнении приложения |
type TTranslsblation =(tiDirtyRead, riReadComniitted, tiRepeatableRead) ; property TransIsolation: TTrans Isolation; |
Pb |
Определяет уровень изоляции транзакций. TiDirtyRead — незавершенное чтение TiReadCommitted — завершенное чтение tiRepeatableRead — повторяемое чтение |
Методы |
||
procedure ApplyUpdates (const DataSets: array of TDBDataSet) ; |
Pu |
Фиксирует в базе данных все изменения в наборах данных, работающих через данное соединение |
procedure Close; |
Pu
|
Закрывает все открытые наборы данных и соединение |
procedure CloseDatasets; |
Pu |
Закрывает все открытые наборы данных, работающие через данное соединение |
procedure Commit; |
Pu |
Завершает выполнение текущей транзакции и фиксирует все изменения в базе данных |
function Execute (const SQL: string; Params: TParams = nil; Cache: Boolean - False; Cursor: phDBICur = nil) : Integer- |
Pu |
Выполняет запрос SQL без использования компонента TQuery. Текст запроса содержится в параметре sql. Параметры запроса определяются параметром Params. Режим кэширования изменений включается параметром Cache. Параметр Cursor может использоваться при работе с функциями BDE, использующими курсор набора данных (гл. 14) |
procedure FlushSchemaCache (const TableName: string); |
Pu |
Изменяет представление о структуре таблиц БД, загруженной в память |
Методы |
||
procedure Open; |
Pu |
Открывает соединение |
procedure Rollbacks- |
Pu |
Отменяет все операции текущей транзакции и завершает ее |
procedure StartTransactions- |
Pu |
Начинает выполнение транзакции |
procedure ValidateName (const Name: string); |
Pu |
Вызывает исключительную ситуацию, если база данных Name уже открыта в текущей сессии |
Методы-обработчики событий |
||
type TLoginEvent = procedure (Database: TDatabase; LoginParams: TSLrings) of object; property OnLogin: TLoginEvent; |
Pb
|
Вызывается при регистрации пользователя на сервере
|
property AfterConnect: TNotifyEvent; |
Pb |
Вызывается после подключения |
property AfterDisconnect: TNotifyEvent; |
Pb |
Вызывается после отключения |
property BeforeConnect: TNotifyEvent; |
Pb |
Вызывается перед подключением |
property AfterDisconnect: TNotifyEvent; |
Pb |
Вызывается перед отключением |
Обычно компонент TDatabase размещается в модуле данных приложения.
Для определения базы данных (сервера), с которой приложение устанавливает соединение при помощи компонента TDatabase, чаще используется СВОЙСТВО AliasName. Свойства DatabaseName И DriverName Предоставляют альтернативный способ создания соединения. если соединение задано свойством AliasName, то свойство DatabaseName можно использовать для создания временного псевдонима, который будет доступен только для компонентов доступа к данным внутри приложения. При щелчке на кнопке списка доступных псевдонимов свойства DatabaseName в Инспекторе объектов для любого компонента доступа к данным, в списке будет доступен и временный псевдоним компонента TDatabase.
Например, при переключении приложения на другую базу данных можно изменить только значение псевдонима в компоненте TDatabase. Если все компоненты наборов данных подключены к временному псевдониму компонента TDatabase, то они автоматически переключатся на новую БД.
Дополнительные возможности управления наборами данных при переключении соединения предоставляют свойства Connected и KeepConnection. Они позволяют одновременно с соединением закрыть все активные наборы данных.
Если наборы данных приложения подключены к базе данных через компонент TDatabase, то перед их открытием необходимо установить соединение с БД. Соединение с БД устанавливается при помощи метода open. Если попытаться активизировать набор данных без этого, то соединение будет установлено автоматически.
Аналогичная картина возникает при закрытии наборов данных и отключении от БД. Дополнительное средство управления в этом случае предоставляет свойство KeepConnection. Если оно равно True, то при закрытии последнего открытого набора данных соединение остается открытым. В противном случае соединение автоматически закрывается.
Это позволяет управлять соединением в различных исходных ситуациях. При большой загруженности сервера бывает необходимо прерывать соединение каждый раз. Если требуется разгрузить сетевой график, то соединение лучше оставлять включенным.
При подключении к базе данных довольно часто требуется задать значения для параметров драйвера BDE. Для этого используется свойство params, представляющее собой обычный список. В нем необходимо задавать названия изменяемых параметров и их новые значения:
USERNAMЕ=SYSDBA PASSWORD=masterkey
Значения параметров можно задавать как статически, так и динамически во время выполнения.
Компонент TDatabase может облегчить подключение к базам данных с регистрацией пользователей. При регистрации на сервере достаточно задать имя пользователя и пароль в свойстве params (см. выше) и установить для свойства Loginprompt значение False. Эта комбинация работает как во время выполнения, так и во время разработки.
Для организации доступа к защищенным паролем таблицам Paradox используется метод AddPassword компонента TSession (см. выше).
Дополнительные возможности обработки регистрации пользователя дает единственный метод-обработчик OnLogin, программный код которого выполняется вместо появления стандартного диалога ввода имени и пароля. Это позволяет разработчику создавать собственные сценарии регистрации пользователей.
Для обеспечения доступа к функциям API BDE используется свойство Handle (BDE играет важную роль при создании соединения).
Управление выполнением транзакций осуществляется при помощи методов StartTransaction, Commit И Rollback.
Управление транзакциями
При обращении клиентского приложения к серверу все команды должны выполняться в рамках специально создаваемых групп операторов — транзакций. Транзакции незаменимы при любом виде многопользовательского доступа к данным, так как позволяют эффективно разрешать возникающие в этом случае проблемы конкурентного доступа к данным и множественным изменениям данных.
Транзакция — это группа операторов SQL, объединенных логикой выполнения приложения и реализующих законченную операцию с данными сервера. После начала выполнения транзакции все сделанные операторами изменения кэшируются и записываются в базу данных только после команды, подтверждающей успешное выполнение последнего оператора в рамках транзакции. Происходит так называемая фиксация транзакции.
Если хотя бы один оператор транзакции был выполнен с ошибкой, то выполнение всех операторов транзакции отменяется. Происходит так называемый откат транзакции.
Транзакция должна удовлетворять ряду требований.
Из этих требований вытекают некоторые ограничения на применяемые в транзакциях операторы.
В многоуровневых приложениях Delphi использование транзакций обеспечивает компонент TDatabase. Любые действия с любыми наборами данных, подключенных через данное соединение, заключенные между методом начала транзакции и методами фиксации или отката транзакции, будут выполняться в рамках одной транзакции. Начало транзакции задается методом StartTransaction. Фиксация транзакции осуществляется методом Commit. Откат выполняется методом Rollback.
with Databasel do try
StartTransaction;
Tablel.Edit;
Tablel.Fields[0].Value := Null;
Tablel.Post;
{Любые другие операции}
Commit;
except
Rollback; end;
При использовании транзакции окончательная запись обнуленного поля в таблицу набора данных Tablel произойдет не после применения метода Post- (обнуленное поле все-таки появится в базе данных), как в обычных случаях, а только после выполнения метода commit. В случае отката транзакции полю будет возвращено прежнее значение.
Обратите внимание, что для выполнения отката транзакции очень удобно использовать механизм генерирования исключительных ситуаций. Если все операторы транзакции выполнены успешно, то транзакция фиксируется. Если произошла ошибка — обработка исключения позволяет применить откат.
При необходимости для обозначения транзакций разработчики могут использовать стандартные операторы SQL. Но для этого придется применить или дополнительный компонент TQuery, или включить операторы транзакции в текст запроса.
Для обеспечения защиты данных используют механизм блокировок. Блокировкой данных позволяют управлять уровни изоляции транзакций — механизм определения степени защищенности данных при многопользовательском доступе.
В Delphi уровни изоляции транзакции проще всего установить при помощи свойства Transisoiation (табл. 21.3) компонента TDatabase. Полдержива-ются три уровня изоляции транзакции.
При назначении уровня изоляции транзакций следует соблюдать баланс между надежностью данных (здесь следует учесть вероятность одновременного доступа к данным разных приложений) и скоростью доступа (каждый уровень блокирует данные в разной степени).
При малом числе пользователей можно обойтись уровнем незавершенного чтения. Возможные ошибки можно исправить при помощи нехитрых проверок ключевых значений перед записью. Зато все приложения получают неограниченный доступ к данным.
Уровень завершенного чтения дает примерное равенство возможностей неблокированного доступа к данным и надежности данных-
Уровень повторяемого чтения обеспечивает наивысшую надежность данных ценой максимально жесткой блокировки.
При организации сетевого доступа к базам данных Paradox уровень повторяемого чтения и завершенного чтения не поддерживается.
Не все серверы поддерживают все три уровня изоляции транзакций. Более подробная информация может быть получена из документации.
Механизм блокировки данных зависит от типа сервера. Большинство серверов выполняет блокировку страницами данных, размер одной страницы может задаваться администратором, минимальный размер страницы составляет примерно 1—2 Кбайт. В одной странице может храниться несколько записей или часть одной записи (зависит от структуры таблицы). При выполнении некоторых видов команд может быть заблокирована целая таблица.
Блокировка данных в локальных СУБД может осуществляться и на уровне отдельных записей -
При обработке операторов транзакций серверы БД используют журнал регистрации транзакций, который представляет собой каким-либо образом организованную информацию (например, в виде системной таблицы) обо всех выполненных сервером операциях. Любые изменения, сделанные в базе данных, сначала регистрируются в журнале, и только затем записываются в базу данных.
Журнал регистрации транзакций используется при откате транзакций или при восстановлении разрушенных данных.
При разработке клиентских приложений с использованием транзакций желательно учитывать следующие рекомендации.
Каждая транзакция должна быть открытой минимально возможное время. Если программная логика не позволяет это сделать, можно разбить транзакцию на несколько меньших.
При простом просмотре данных транзакции можно не использовать, или включать в них только операторы отбора набора данных по заданным критериям. В этом случае пользователь получит желаемый результат, а данные на сервере не будут заблокированы.
В приложении желательно использовать единый уровень изоляции транзакций. При одновременной работе нескольких-приложений это поможет избежать конфликтных ситуаций.
Основные методы модификации набора данных в Delphi автоматически распознаются сервером как отдельные транзакции. Например, следующий исходный код заставит сервер выполнить столько элементарных транзакций, сколько содержится записей в наборе данных items:
I :=0;
with Items do begin
Open ;
Edit;
While Not EOF do
begin ItemsKod.Value := IntToStr(Inc(I));
Next;
end;
Close;
end;
Запись изменений для каждой строки в базу данных воспринимается сервером как отдельная транзакция.
Это еще один довод за то, чтобы в многоуровневых приложениях использовать запросы SQL. В рассматриваемом случае адекватный запрос модификации данных позволяет выполнить весь объем модификации в одной транзакции.
Разграничение доступа
Организация разграничения доступа к данным является частью большой группы задач обеспечения безопасности данных. Обычно безопасность данных обеспечивается на двух уровнях — сервера и приложения, так как пользователь может не только посылать на сервер команды модификации данных, но и получать для дальнейшей работы наборы данных на сторону клиента.
Степень безопасности данных различается от сервера к серверу. Тем не менее, в системе защиты сервера SQL можно выделить основные общие элементы.
На уровне сервера, с точки зрения создания клиентских приложений, нас в первую очередь интересуют те меры безопасности, которые напрямую влияют на разработку программного кода приложений. К ним относится регистрация пользователей при входе на сервер.
Если приложение никак не модифицирует процедуру регистрации на сервере, то при установлении соединения с базой данных автоматически появляется диалог ввода имени пользователя и пароля. Результаты регистрации отправляются на сервер. Основные инструменты управления регистрацией сконцентрированы в компоненте TDatabase.
Появлением диалогового окна регистрации пользователя управляет свойство Loginprompt. Если диалоговое окно не должно появляться, то в параметрах соединения задаются имя пользователя и пароль.
Если при помощи свойства Loginprompt запретить отображения диалога регистрации пользователя (Loginprompt := False) и при этом не задать в параметрах соединения имя пользователя и пароль, то возникает ошибка подключения к серверу.
Рис. 21.1. Специализированный диалог регистрации пользователя
Если на стороне клиента процедура регистрации должна содержать дополнительные действия (ввод приоритета пользователя, категории данных и т. д.), то разработчик может использовать метод-обработчик onLogin. В нем можно предусмотреть вывод специальных диалогов регистрации и специфического программного кода. Если этот метод определен, то он вызывается вместо стандартного диалога автоматически:
procedure TFormI.DatabaselLogin(Database: TDatabase;
LoginParams: TStrings);
begin
if Not LoginDialog.Execute then Exit;
OsrMame := LoginDialog.NameEdit.Text;
UsrPwd := LoginDialog.PasswordEdit.Text;
UsrLevel := LoginDialog.LevelEdit.Text;
LoginParams.Add('USERNAME-'+UsrName);
LoginParams.Add('PASSWORD='+UsrPwd);
end;
В этом примере использовано специальное диалоговое окно регистрации LoginDialog, которое помимо имени (воднострочном редакторе NameEdit) и пароля (в однострочном редакторе passwordEdit) требует задать приоритет пользователя (в однострочном редакторе LevelEdit). Заданные значения сохраняются в глобальных переменных приложения usrName, usrpwd и UsrLevel. Имя пользователя и пароль должны быть переданы серверу. Для этого предназначена переменная LoginParams (см. выше), сами параметры и их значения передаются в списке в обычном формате.
Метод-обработчик OnLogin также можно использовать для передачи в приложение имени пользователя и его пароля. Пример их использования рассмотрен ниже.
На уровне приложения разграничение доступа обеспечивается контролем за выполняемыми пользователем операциями. В зависимости от приоритета пользователя, ему могут быть доступны только операции просмотра данных, редактирования данных или ввода новых данных. Разграничение доступа можно организовать включением иди отключением команд меню и кнопок на панели инструментов и элементов управления внутри рабочих форм приложения.
Приоритеты пользователей можно хранить в специальной таблице БД и получать их, используя в качестве идентификатора имя пользователя. Право редактирования такой таблицы должно быть предоставлено только системному администратору. Значение приоритета может быть использовано при отображении главной и рабочих форм приложения:
procedure TMainFom.FormShowfSender: TObject);
begin case UsrLevei of 0:;
1:begin
Buttonl.Enabled :=.False;
MainMenuIteml.Enabled := False;
end;
2:begin
Buttonl.Enabled := False;
Button2.Enabled := False;
MainMenuIteml.Enabled := False;
MainMenuItem2.Enabled := False;
end;
end;
end;
Переменная UsrLevei содержит значение приоритета пользователя. В зависимости от ее значения, главная форма MainForm имеет различный набор активных кнопок и пунктов меню.
Предложенный пример иллюстрирует только общую схему организации разграничения доступа в клиентских приложениях. На практике используются более совершенные способы защиты данных, хотя и этот способ может быть применен.
Разграничение доступа к данным при использовании запросов SQL осуществляется при помощи операторов grant и revoke, которые позволяют обеспечить пользователя дополнительными привилегиями по доступу к данным.
Резюме
Приложения клиент/сервер позволяют обеспечивать работу с данными сотен и тысяч клиентов. Для этого компьютер клиента должен быть настроен соответствующим образом. Клиентские приложения, созданные в Delphi, используют BDE для связи с сервером.
Для эффективного управления доступом к удаленным данным из приложения в Delphi реализован специальный механизм.
Одно приложение может работать с несколькими серверами БД. С каждым сервером устанавливается отдельный сеанс связи. Каждый сеанс управляется компонентом TSession. Доступ ко всем сеансам осуществляется через объект TSessionList.
В рамках одного сеанса приложение может работать с несколькими БД, заданными соответствующими псевдонимами. Соединение с одной БД устанавливается и управляется при помощи компонента TDatabase.