Построение клиент-серверных баз данных в Delphi

До настоящего момента мы рассматривали только локальные базы данных, не затрагивая многопользовательского доступа. Целью данного занятия является обсуждение особенностей, присущих удаленным базам данных.

 

Напомним, что при использовании технологии «клиент-сервер» база данных располагается на удаленном компьютере (сервере) и называется удаленной базой данных.

 

ПРИМЕЧАНИЕ: Сервером называется не только компьютер (сервер сети), но и специализированная программа, которая в нашем случае управляет базой данных.

Приложение, обеспечивающее доступ к базе данных с компьютера пользователя, называется приложением-клиентом.

Взаимодействие клиента и сервера можно описать двумя фразами (рис. 9.1): 

  • клиент формирует SQL-запрос серверу, на котором расположена база данных, и отправляет его по сети;
  • сервер выполняет запрос и возвращает клиенту запрашиваемые данные.

Преимущества такого метода работы очевидны:

  •  обработка запроса происходит на сервере, клиент же получает только необходимые данные;
  •  поскольку посылаются только необходимые данные, снижается сетевой трафик;
  • приложение-клиент не может напрямую обратиться к файлам, расположенным на сервере, — таким образом обеспечивается высокая степень защиты данных;
  •  управление базой данных производит сервер, поэтому код приложения-клиента значительно уменьшается и упрощается.
В основе взаимодействия клиента и сервера лежит язык SQL. Именно поэтому сервер базы данных называют еще и SQL-сервером, а базу данных — базой данных SQL. К SQL-серверам относятся:
  •  InterBase;
  •  Oracle;
  •  Sybase;
  •  DB2;
  •  Microsoft SQL Server.
Есть еще множество программ SQL-серверов. Каждая из них имеет свои особенности, которые необходимо учитывать при разработке приложения-клиента. Некоторые из них мы рассмотрим на следующем, десятом занятии.
Далее в книге сервер будет называться программой SQL-сервера. Установленную на компьютере-сервере базу данных будем именовать удаленной базой данных.
В изложенной простой схеме взаимодействия приложения-клиента н сервера мы еще не упомянули несколько важных функций, которые должно выполнять приложение-клиент. Вот эти функции (включая и упомянутые ранее):
  • осуществление соединения с сервером и отключения от него;
  • формулировка запроса и посылка его серверу;
  • прием результата выполнения запроса от сервера;
  • обработка полученных данных.
Удаленная база данных представляет собой совокупность взаимосвязанных таблиц. Отличие ее от локальной заключается в том, что данные связанных таблиц содержатся в одном общем файле. В остальном структуры практически идентичны.
Для управления базой данных сервер может задействовать следующие механизмы:
  •  триггеры;
  •  генераторы;
  •  хранимые процедуры;
  •  функции, определяемые пользователем;
  •  транзакции;
  •  кэшированные изменения;
  •  события.
Многие из этих механизмов зависят от установленного SQL-сервера и его языка. Далее на этом занятии мы рассмотрим некоторые из перечисленных средств на примере их использования в InterBase.

Хранимая процедура — это небольшая программа, которая расположена на сервере базы данных и которую можно вызвать из приложения-клиента.

Хранимые процедуры позволяют ускорить работу с данными на сервере базы данных благодаря следующим преимуществам:

  •  при использовании хранимых процедур вместо длинного текста SQL-запроса серверу отправляется короткое обращение к хранимой процедуре. Таким образом уменьшается сетевой график;
  •  хранимая процедура выполняется непосредственно на сервере. В результате скорость доступа к данным не зависит от производительности машины-клиента;
  •  хранимая процедура, в отличие от SQL-запроса, не фебует предварительной проверки синтаксиса;
  •  хранимые процедуры являются общими для всех приложений-клиентов и реализуют единые правила работы с базой данных.

Для выполнения хранимой процедуры в Delphi был введен компонент TStoredProc. Рассмотрим его основные свойства.

Свойство DatabaseName типа String указывает на компонент TDatabase, используемый для установления соединения с базой данных. Это свойство аналогично одноименному свойству компонентов ТTable и TQuery.

 

Свойство StoredProcName типа String определяет хранимую процедуру, которая должна вызываться. Имя хранимой процедуры выбирается н выггядяющрм списке с помощью инспектора объектов.

ПРИМЕЧАНИЕ: Если при попытке выбора хранимой процедуры соединение с базой данных отсутствует, будет выдан запрос на его установление.

После выбора хранимой процедуры можно устанавливать два других свойства. Свойство РагатэтипаТРагатзопределяет массив параметров компонента TStoredProc. 

Свойство ParamBindMode типа TParamBindMode определяет, как будет установлено соответствие между параметрамикомпонента TStoredProc и параметрами процедуры, и может принимать одно из следующих значений:

  •  pbByName — соответствие будет установлено по именам, то есть имена параметров компонента TStoredProc и соответствующих параметров процедуры должны совпадать. Данное значение принимается по умолчанию;
  •  pbByNumber — соответствие будет установлено в порядке перечисления, то есть первый параметр компонентаTStoredProc сопоставлен первому параметру процедуры и т. д.

Выполнение выбранной хранимой процедуры осуществляется с помощью вызовов методов Prepare и ЕхесРгос:

  •  метод Prepare осуществляет подготовку хранимой процедуры, которая заключается в связывании параметров процедуры п компонента TStoredProc в соответствии с установленным значением свойства ParamBindMode;
  •  метод ЕхесРгос непосредственно выполняет хранимую процедуру.

Листинг 9.1 иллюстрирует вызов хранимой процедуры из приложения при нажатии кнопки Buttonl.

Листинг 9.1. Вызов хранимой процедуры


Для написания хранимых процедур и триггеров используется специальный язык хранимых процедур. Разные серверы используют разные диалекты. Мы не будем изучать их все, а кратко рассмотрим язык хранимых процедур, использующийся сервером InterBase, который имеет много общего с языком Pascal. Язык хранимых процедур включает в себя операторы для управления ходом вычислительного процесса (ветвления, цикла), а также некоторые функциональные возможности языка SQL.

 

Хранимая процедура создается оператором

 

После имени процедуры следует необязательный список входных параметров, с помощью которых из приложения в процедуру могут передаваться исходные данные. Список выходных параметров, посредством которых в приложение возвращаются результаты выполнения процедуры, указывается после слова RETURNS. Каждый параметр описывается своим именем и типом, разделенными пробелом. Между различными параметрами должны стоять запятые. 

 

При использовании параметра в теле процедуры перед его именем необходимо ставить двоеточие.

В текст хранимой процедуры допускается вставлять комментарии. Для вставки комментариев используются комбинации символов /* и */.

Вновь созданную процедуру можно удалить или изменить. Для удаления процедуры служит оператор 

DROP PROCEDURE <Имя процедуры>

Для изменения существующей процедуры используется оператор

ALTER PROCEDURE

Его список параметров идентичен списку параметров оператора

CREATE PROCEDURE

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

Оператор объявления переменных имеет следующий вид: 

 

Переменные могут быть только того типа, который допускается в InterBase.

 

Объявленные переменные являются локальными, то есть видимы исключительно внутри процедуры, в которой они были объявлены. Приведем пример объявления переменных:

 

Оператор присваивания описывается следующим образом: <Имя переменной> <Выражение>;

Перед знаком равенства двоеточие не ставится!

Переменная и выражение должны иметь одинаковый или совместимый тип, иначе возможна ошибка.

ВНИМАНИЕ: Ошибка возникает не при создании хранимой процедуры, а во время ее выполнения.


Проиллюстрируем сказанное несколькими примерами:


Оператор ветвления имеет вид: 

Этот оператор аналогичен оператору ветвления, использующемуся в Delphi. Оператор цикла оформляется аналогично такому же оператору в Delphi:

 

Оператор выбора записи похож на инструкцию SELECT языка SQL, но дополнен следующим операндом: 

Каждое имя после двоеточия указывает переменную или выходной параметр, которому должно быть присвоено значение столбцов строки, полученной в результате выполнения команды SELECT.

 

Например:

Здесь создается хранимая процедура pSelect, в которой для сотрудников из таблицы MyTable подсчитываются общая сумма заработной платы (по полю Zarplata) и среднее значение по организации. Полученные в результате выполнения процедуры значения будут присвоены выходным параметрам opSum и opSred. Входных параметров процедура не имеет.

 

Оператор выхода из процедуры служит для досрочного выхода из процедуры и передачи управления вызывающей программе млн процедуре. Оператор выхода представляет собой ключевое слово EXIT

 

Оператор вызова процедуры применяется для вызова из одной хранимой процедуры другой хранимой процедуры и записывается в общем виде так:

 

 

Оператор вызывает хранимую процедуру с указанным именем и параметрами. Приведем пример вызова хранимой процедуры:

Оператор посылки сообщения предназначен для уведомления о событии всех приложений-клиентов, связанных с сервером. Данный оператор имеет следующий вид: 

POSTJVENT <Имя события>;

Триггер представляет собой процедуру, которая постоянно размещена на сервере базы данных (как и хранимая процедура) и вызывается автоматически при изменении записей базы данных.

 

В отличие от хранимых процедур, триггеры нельзя вызывать из приложений-клиентов, а также передавать им параметры и получать от них результаты.

 

По определению триггер похож на обработчик событий BeforeEdit, AfterEdit, Before-Delete, AfterDelete, Beforelnsert и Afterlnsert. 

Триггеры в основном используются для программной реализации бизнес-правил. С помощью триггеров накладываются различные ограничения (например, иа значения столбцов).

 

Для создания триггера применяется оператор CREATE TRIGGER, который имеет следующий вид:


Операнды ACTIVE и INACTIVE определяют, будет ли триггер активен сразу после его создания. По умолчанию ои будет активен (ACTIVE), то есть при наступлении определенного события триггер будет выполняться. Неактивный триггер (INACTIVE) при наступлении определенного события выполняться не будет. Таким образом, созданный триггер можно включать или выключать.

 

Операнды BEFORE и AFTER указывают, когда будет выполняться триггер: до наступления определенного события (BEFORE) или после (AFTER).

Операнды UPDATE, INSERT и DELETE определяют тип события, при возникновении которого будет выполняться триггер. Это такие типы событий, как изменение (UPDATE), добавление (INSERT) и удаление (DELETE) записей.

 

Для каждого события можно создать несколько триггеров, которые будут выполняться (если они активны). Порядок их выполнения определяется операндом POSITION. Триггеры будут срабатывать в порядке возрастания чисел, указанных в данном операнде.

Созданный триггер можно изменить или удалить. Удаление триггера осуществляется оператором 

DROP TRIGGER <Имя триггера>
 

Изменить уже существующий триггер можно с помощью оператора ALTER TRIGGER, который имеет такой же формат, что и оператор CREATE TRIGGER.

 

Тело триггера программируется так же, как и тело хранимой процедуры.


В таблицах InterBase отсутствует автоинкрементный тип. Для обеспечения уникальности значений ключевых столбцов совместно с триггерами используются генераторы.

Генератор применяется для создания уникальных целочисленных значений. Генератор можно создать с помощью приведенного ниже оператора: 

CREATE GENERATOR <Имя генератора>
 

Кроме непосредственного создания генератора, необходимо присвоить ему начальное значение. Для этого используется следующий оператор: 

SET GENERATOR <Имя генератора> ТО «Начальное значение>
 

Начальное значение — это число, начиная с которого будут генерироваться уникальные значения.

К созданному генератору можно обращаться с помощью функции 

GEN_ID (<Имя генератора>, <Шаг>)

 

Данная (функция возвращает значение, увеличенное на целочисленный шаг относительно последнего сгенерированного значения.

ВНИМАНИЕ: После определения начального значения и шага их изменять нельзя. В противном случае уникальность генерируемых значений может быть нарушена.

Приведем пример создания генератора:

CREATE GENERATOR MyGen: SET GENERATOR MyGen TO 2:

Здесь создается генератор с именем MyGen, начальное значение которого равно 2. Пример обращения к этому генератору может быть таким:

GENJD(MyGen. 1):

Все операции, выполняемые приложением-клиентом с данными на SQL-сервере, происходят в виде транзакций.

Транзакцией называется группа из нескольких операции над данными из таблиц удаленной базы данных.

 

Основным принципом транзакций является «либо все, либо ничего». Если во время выполнения набора действий (транзакции) па каком-то этапе невозможно произвести очередное действие, то нужно выполнить возврат базы данных к начальному состоянию (произвести откат транзакции). Таким образом (при правильном планировании транзакций) обеспечивается целостность базы данных. Далее мы расскажем, как начинать, завершать транзакции п управлять ими с помощью SQL-выражений. А также рассмотрим вопрос об использовании транзакций в приложениях, разработанных в Delphi. Вся приведенная информация относится к InterBase.

 

Для управления транзакциями в Delphi имеются три основные команды:

  •  SET TRANSACTION — начинает транзакцию и определяет се поведение;
  •  COMMIT — сохраняет изменения, внесенные транзакцией, в базе данных и завершает транзакцию;
  •  ROLLBACK — отменяет изменения, внесенные транзакцией, и завершает транзакцию. Рассмотрим эти команды более подробно.

Команда для запуска транзакции в общем виде выглядит следующим образом:

 

SET TRANSACTION [Access mode] [Lock Resolution] [Isolation Level] [Table Reservation]

 

Команда содержит четыре необязательных параметра (перечисленные в квадратных скобках). Если эти параметры опустить, то получившаяся инструкция SET TRANSACTION равносильна выражению

 

SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT

 

Перечислим параметры команды и их возможные значения.

  • Параметр Access mode (тип доступа) определяет тип доступа к данным. Он может принимать одно из двух значении:
    •  READ ONLY — транзакция может только читать данные н не в состоянии модифицировать их;
    •  READ WRITE — указывает, что транзакция может читать и модифицировать данные. Это значение устанавливается по умолчанию.
  • Параметр Isolation Level (уровень изоляции) определяет порядок взаимодействия данной транзакции с другими в рабочей базе данных. Может принимать значения:
    •  SNAPSHOT — значение по умолчанию. Внутри транзакции будут доступны данные в том состоянии, в каком они находились на момент начала транзакции. Если по ходу дела в базе данных появилисьнзменепия, внесенные другими завершенными транзакциями, то начатая транзакция их не увидит. При попытке модифицировать такие записи будет выдано сообщение о конфликте;
    •  SNAPSHOT TABLE STABILITY — предоставляет транзакции исключительный доступ к таблицам, которые она использует. Другие транзакции смогут только читать данные из этих таблиц;
    •  READ COMMlT — позволяеттранзакцин видеть текущее состояние базы данных.

ВНИМАНИЕ: Обратите особое внимание на уровень изоляции транзакции. При неправильно установленном уровне могут возникать проблемы. Эти проблемы имеют место в одном из двух случаев: транзакция пытается модифицировать запись, которая была изменена или удалена уже после ее старта; транзакция пытается модифицировать таблицу, заблокированную другой транзакцией с уровнем изоляции SNAPSHOT TABLE STABILITY.

  • Параметр Lock Resolution (разрешение блокировки) управляет интерпретацией событий при обнаружении конфликта блокировки. Может принимать одно из двух значений:
    •  WAIT — значение по умолчанию. Ожидает разблокирования требуемой записи. После этого пытается продолжить работу;
    •  NO WATT — немедленно возвращает ошибку блокировки записи и прекращает выполнение транзакции.
  • Параметр Table Reservation (уровень доступа к таблице) позволяет транзакции получить гарантированный доступ необходимого уровня к указанным таблицам. Существует четыре уровня доступа:
    •  PROTECTED READ — запрещает обновление таблицы другими транзакциями, но позволяет им выбирать данные из таблицы;
    •  PROTECTED WRITE — блокируетобновление таблицы другими транзакциями, читать данные из таблицы могут только транзакции типа SNAPSHOT пли READ COMMITTED;
    • SHARED READ — самый либеральный уровень. Читать могут все, модифицировать — транзакции типа READ WRITE;
    • SHARED WRITE - транзакции типа SNAPSHOT или READ COMMITTED и READ WRITE могут модифицировать таблицу, остальные способны только выбирать данные.

Если все действия, составляющие транзакцию, успешно выполнены или возникла ошибка, транзакция должна быть завершена, для того чтобы база данных оказалась в непротиворечивом состоянии. Для этого в Delphi есть две SQL-команды:

  •  COMMIT — сохраняет внесенные транзакцией изменения в базу данных. Это означает, что транзакция завершена успешно;
  •  ROLLBACK — откат транзакции. Транзакция завершается и никаких изменений в базу данных не вносится. Операция выполняется в случае возникновения ошибки при выполнении операции (например, при невозможности обновить запись).

Все транзакции в Delphi можно условно разделить на явные и неявные.

Явная транзакция — это транзакция, начатая и завершенная с помощью методов компонента TDataBase: StartTransaction, Commit и RollBack. После начала явной транзакции все изменения, вносимые в данные, относятся к этой транзакции.

Другого способа начать явную транзакцию, кроме использования компонента TDataBase, нет. Следовательно, в рамках одного соединения нельзя начать две транзакции.

ПРИМЕЧАНИЕ: На самом деле такая возможность есть, но это потребует обращения к функциям API InterBase. В данной книге мы не будем останавливаться на вопросах программирования на низком уровне с использованием API.

Неявная транзакция стартует при модификации данных, если в текущий момент нет явной транзакции. Неявная транзакция возникает, например, при выполнении метода Post для компонентов наборов данных ТТаblе и TQuery. Например, если вы отредактировали запись в TDBGrid и переходите на другую запись, то это влечет за собой выполнение метода Post, что, в свою очередь, приводит к началу неявной транзакции, обновлению данных внутри транзакции и ее завершению. Важно отметить, что неявная транзакция, начатая с помощью методов Post, Delete, Insert, Append и т. д., заканчивается автоматически.

������� ������ ��� dle ������� ��������� ������

Помоги проекту! Расскажи друзьям об этом сайте: