Тема 13. Создание приложений для работы с базами данных в сети

1.             Работа с базами данных в сети

2.             Основные свойства компонент Query.

3.             Основные методы компонента Query

4.             InterBase - работа на платформе клиент/сервер

5.             Доступ к базам данных через ADO

6.             Обзор компонентов наборов данных

Презентация к лекции

 

1. Работа с базами данных в сети

Управление транзакциями, компонент Database

Управления транзакциями автоматически осуществляются при каждой модификации данных. Это производится компонентом типа TDatabase, который C++Builder включает автоматически в любое приложение, работающее с базами данных. Этот компонент решает следующие задачи:

·      Создание соединения с удаленным сервером

·      Регистрация пользователя при первом обращении к серверу

·      Создание локальных псевдонимов приложений

·      Управление транзакциями

·      Определение уровня изоляции транзакции (регулирование одновременных транзакций к одним и тем же таблицам)

Для сознательного управления транзакциями нужно явным образом включить со страницы Data Access компонент Database в приложение.

Database связывается с компонентами наборов данных Table, Query и другими через имя базы данных, к которой он подключается. Имя задается в свойстве DatabaseName. Может быть задан псевдоним базы данных или полный путь к ней. Если задается база данных, имеющая псевдоним BDE, то свойства AliasName, DriverName и Params можно не задавать. В противном случае надо задать или свойство AliasName, или свойства DriverName и Params.

Значения этих свойств устанавливается в Инспекторе Объектов, или специальным редактором, который вызывается двойным щелчком на Database.

Индикатор Keep inactive connection устанавливает свойство KeepConnection.

После нажатия на ОК введенные установки заполнят значения свойств DatabaseName, AliasName, DriverName, Params и KeepConnection.

Свойство Connected (соединение) совместно со свойством KeepConnection управляют процессом соединения компонентов с базой данных. Если KeepConnection равно true, то соединение с базой данных постоянное даже при отсутствии открытых наборов данных. Если же KeepConnection равно false, то для регистрации на сервере надо устанавливать Connected в true при каждом открытии таблицы.

Свойство Translsolation определяет уровень изоляции транзакции. Это свойство может иметь значения:

 

tiDirtyRead

Позволяет читать все текущие изменения, проводимые другими транзакциями, до их фиксации.

tiReadCommit

Позволяет читать только зафиксированные изменения, проводимые другими транзакциями. Это значение принято по умолчанию.

tiRepeatableRead

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

 

Начало транзакции осуществляется методом StartTransaction компонента Database. При этом начинающаяся транзакция использует текущее значение свойства Translsolation для определения уровня изоляции.

Завершается транзакция методом Commit, фиксирующим ее результаты в базе данных.

Метод Rollback используется для «отката» назад при неудаче - этот метод отменяет все операции с базой данных, выполненные после последнего выполнения метода Commit.

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

Database1->StartTransaction();

Группа операторов изменения данных (ExecSQL и др.)

Проверка результатов: Если успешно – Database1->Commit();

Если неудача – Database1->Rollback();

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

Из общих рекомендаций по организации транзакций можно прежде всего отметить рекомендацию по возможности сокращать число транзакций. Сокращение числа транзакций возможно за счет команд SQL групповой обработки записей. Например, при необходимости переименовать подразделение во всех записях таблицы можно написать следующий код (предполагается, что мы хотим переименовать «Цех 2» в «Цех 1» ):

Tablel->First();

while (! Tablel->Eof)

{

if (Tablel->FieldByName("Dep")->AsString == "Цех 2")

{

Tablel->Edit();

Tablel->FieldByName("Dep")->AsString = "Цех 1";

}

Tablel->Next();

}

Tablel->First();

Этот код предполагает осуществление стольких транзакций, в скольких записях будут сделаны исправления. Для каждого исправления будет генерироваться своя команда Update. Если база данных большая, то это может привести к значительным затратам времени и к проблемам на сервере при регистрации транзакций. А если во время выполнения цикла возникли какие-то исключительные ситуации, то часть записей будет изменена, а часть - нет. Последнее можно исправить, поместив перед циклом оператор StartTransaction. Но неэффективность выполнения останется.

Более удачным решением в данном случае является выполнение с помощью Query команды типа:

UPDATE Pers SET Dep='Uex 1" WHERE Dер='Цех 2'

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

Число транзакций сокращается также за счет кэширования изменений.

Желательно также уменьшать длительность каждой транзакции, поскольку слишком долго выполняемая транзакция может надолго заблокировать ресурсы для других приложений. Например, если таблица Pers очень большая, то приведенную выше команду можно разбить на две, выполняемые в пределах отдельных транзакций:

UPDATE Pers SET Dер='Цех 1'  WHERE (Num BETWEEN 1 AND 10000) and (Dep='Цех 2')

UPDATE Pers SET Dep='Цех 1' WHERE (Num > 10000)and(Dep='Цех 2')

 

Управление доступом

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

C++Builder реализует более оптимальное управление доступом. Оно предполагает, что большинство обращений к базе данных - это обращения для чтения. И очень невелика вероятность того, что два приложения одновременно будут изменять одну и ту же запись и одни и те же поля в ней.

Процесс работы приложения C++Builder с базой данных выглядит следующим образом. При выполнении приложения оно получает копию записи с сервера, позволяет что-то в ней изменить, а затем посылает изменения на сервер с помощью команды Update через элемент Where, перечисляющий значения полей, совпадение которых идентифицирует изменяемую запись. Эти значения берутся из копии записи и, следовательно, не зависят от того, не изменились ли за это время поля реальной записи.

В компонентах Table и Query имеется свойство UpdateMode - режим обновления. Значения этого свойства и определяют, какие поля будут включены в элемент Where команды Update. Возможные значения:

 

upWhereAll

Where включает все поля записи (принято по умолчанию)

upWhereChanged

Where включает ключевые поля и поля, которые были изменены

upWhereKeyOnly

Where включает только ключевые поля

 

Вариант upWhereAll наиболее надежен, поскольку распознает запись по значениям всех ее полей. Наиболее трудоемок, поскольку при большом количестве полей элемент Where получается очень большим. Вариант upWhereChanged требует меньших затрат, но он и менее надежен. Если, пока шло редактирование записи в данной транзакции, другая транзакция изменила в этой записи поля, отличные от измененных данной транзакцией, то фиксироваться результаты будут уже практически для другой записи. Еще менее надежен вариант upWhereKeyOnly, хотя он наиболее быстрый.

 

2. Основные свойства компонент Query.

Компонент Query позволяет работать с таблицами, используя операторы SQL. Большинство свойств и методов Query совпадают с Table. Дополнительные преимущества Query - возможность формировать запросы на языке SQL.

Query целесообразнее использовать в серверных приложениях.

Рассмотрим пример использования Query. Поместим на форму компоненты Query, DataSource, DBGrid. Создадим цепочку связей: набор данных (Query1), источник данных (DataSource1), компонент визуализации и управления данными (DBGrid1).

Основное свойство Query - SQL типа TStrings - список строк, содержащих запросы SQL. В процессе проектирования приложения обычно необходимо сформировать в этом свойстве некоторый предварительный запрос SQL, который показал бы, с какой таблицей или таблицами будет проводиться работа. Но далее во время выполнения приложения свойство SQL может формироваться программно методами, обычными для класса TStrings: Clear - очистка, Add - добавление строки и т.д.

Настройку компонента Query в процессе проектирования можно производить вручную.

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

Свойства TableName нет, т.к. таблица, с которой ведется работа, в Query будет указываться в запросах SQL. Поэтому, прежде всего надо занести в свойство SQL запрос, содержащий имя таблицы, с которой нужно работать.

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

Запрос, заносимый в SQL в начале проектирования, может иметь, например, следующий вид:

Select * from pers

После этого система поймет, с какой таблицей будет проводиться работа, и можно будет настроить поля в Query. Если работа будет проводиться с несколькими таблицами, нужно их указать в запросе. Например:

Select * from pers, dep

После того как соответствующий запрос написан, можно установить свойство Active в true,  и  в компоненте DBGrid1 отобразится информация из запрошенных таблиц.

Приложение предоставляет возможность просматривать записи, но, не позволяет их редактировать. Это связано с тем, что запрос Select возвращает таблицу только для чтения. Это легко исправить. Достаточно установить в компоненте Query1 свойство RequestLive в true. Это позволяет возвращать как результат запроса изменяемый, «живой» набор данных, вместо таблицы только для чтения. Точнее, установка RequestLive в true делает попытку вернуть «живой» набор данных. Успешной эта попытка будет только при соблюдении ряда условий, в частности:

·      набор данных формируется обращением только к одной таблице

·      набор данных не упорядочен (в запросе не используется ORDER BY)

·      в наборе данных не используются совокупные характеристики типа Sum, Count и др.

·      набор данных не кэшируется (свойство CashedUpdates равно false)

В примере все эти условия соблюдаются, поэтому установив RequestLive в true можно редактировать данные, удалять записи, вставлять новые записи.

Для управления отображением данных, как и в компоненте Table, имеется Редактор Полей (Field Editor). Вызывается двойным щелчком на Query, или щелчком правой кнопки мыши на Query и выбором Fields Editor из всплывающего меню. В нем можно добавить имена получаемых полей (щелчок правой кнопкой мыши и выбор раздела меню Add), задать заголовки полей, отличающиеся от их имен, сделать какие-то поля невидимыми (Visible), не редактируемыми (Readonly), в логических полях можно задать высвечиваемые слова (да;нет), задать формат высвечивания чисел, создать вычисляемые поля, поля просмотра, задать диапазоны значений и многое другое.

 

Динамические запросы и параметры Query

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

Параметры задаются в запросе с двоеточием, предшествующим имени параметра:

:<имя параметра>

Например, можно записать в запросе Select элемент WHERE в виде:

WHERE Year_b <= :PYear

Здесь сравнивается год рождения не с какой-то константой, а со значением параметра PYear.

Теперь обратимся к компоненту Query. Нужно ввести в его свойство SQL запрос, содержащий параметры, например:

Select * from pers where (year_b>:PYear) and (dep=:Dep)

в Инспекторе Объектов при выборе свойства Params, откроется окно со списком объектов - указанных в запросе параметров PYear и Dep. В этом списке можно выделять по очереди параметры и в Инспекторе Объектов устанавливать их свойства. Это свойства:

 

DataType

тип данных параметра (int, string и т.п.)

Name

имя параметра

ParamType

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

Value

значение параметра по умолчанию

Type  подсвойство Value

тип значения по умолчанию

DataType

тип данных параметра (int, string и т.п.)

 

После установки свойства всех параметров, можно использовать их при программировании приложения.

Программный доступ к параметрам во время выполнения приложения осуществляется аналогично доступу к полям набора данных. Свойство Params является указателем на массив параметров типа TParam, к элементам которого можно обращаться по индексу через его свойство Items[Word Index]. Последовательность, в которой располагаются параметры в массиве, определяется последовательностью их упоминания в запросе SQL.

Значения параметров, как и значения полей, определяются такими свойствами объектов-параметров, как Value, AsString, Aslnteger и т.п. Например, оператор

for (int i = 0; i < Query1->Params->Count; i++)

if (Query1->Params->Items[i]->IsNull &&

Query1->Params->Items[i]->DataType == ftInteger)

Query1->Params->Items[i]->AsInteger = -1;

задаст значение «-1» всем целым параметрам, которым до этого не было присвоено значение. В нем использовано свойство Count - число параметров, свойство IsNull, равное true, если параметру не задано никакое значение, свойство DataType, указывающее тип параметра, и свойство AsInteger, дающее доступ к значению параметра как к целому числу. Другой пример: операторы

Query1->Params->Items[0]->AsInteger = 1950;

Query1->Params->Items[1]->AsString = "Бухгалтерия";

задают значения первому (индекс 0) и второму (индекс 1) параметрам компонента Query1, в свойстве SQL которого записан приведенный ранее оператор Select с параметрами :PYear и :Dep. Поскольку в этом операторе параметр :PYear упоминается первым, го его индекс равен 0.

У Params есть еще свойство - ParamValues. Оно представляет собой массив значений параметров типа Variant. В качестве индекса в это свойство передается имя параметра или несколько имен, разделяемых точками с запятой. Например, операторы

Query1->Params->ParamValues["PYear"] = 1950;

Query1->Params->ParamValues["Dep"] = "Бухгалтерия";

задают те же значения параметрам, что и приведенные выше. Преимуществом является то, что при записи этих операторов не надо помнить индексы параметров. Другим преимуществом свойства ParamValues является возможность задать значения сразу нескольким параметрам. Например:

Variant par[] = {1950,"Бухгалтерия");

Query1->Params->ParamValues["PYear;Dep"] = VarArrayOf(par,1);

Другой способ обращения к параметрам - использование метода ParamByName компонента Query- Например, операторы

Query1->ParamByName("PYear")->AsInteger = 1950;

Query1->ParamByName("Dep")->AsString = "Бухгалтерия";

задают параметрам с именами PYear и Dep те же значения.

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

 

Связывание таблиц

Большинство свойств Query аналогичны свойствам Table. Объекты полей создаются автоматически для тех полей, которые перечислены в операторе SQL. Программный доступ к этим полям осуществляется так же с помощью свойства Fields (например, Query1-> Fields[0]) или методом FieldByName (например, Query1->FieldByName("Dep")). Можно также создавать объекты полей с помощью Редактора Полей, вызываемого двойным щелчком на Query или из меню, всплывающего при щелчке на Query правой кнопкой мыши. В этом случае доступ к объекту поля можно осуществлять также и по его имени (например, Query1Dep).

При использовании для Query Редактора Полей надо добавлять в нем все поля, перечисленные в операторе SQL Иначе поля не добавленные в Редакторе Полей не будут доступны

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

Для доступа к значениям полей используются те же их свойства Value, AsString, Aslnteger. Точно так же, как в Table, можно осуществлять навигацию по набору данных, устанавливать фильтры, ограничивать вводимые значения полей, кэшировать изменения.

Из свойств, отличных от Table, остановимся на свойстве DataSource. Это свойство позволяет строить приложения, содержащие связанные друг с другом таблицы. Рассмотрим, как это делается, на примере. Построить приложение, включающее в себя таблицу Dep, содержащую список отделов (в поле Dep) и их характеристику, в качестве головной таблицы и таблицу персонала Pers, содержащую в поле Dep имя отдела, в котором работает каждый сотрудник. Нужно, чтобы при выборе записи в таблице Dep в таблице Pers отбирались записи, относящиеся к выбранному отделу.

Поместим на форму компоненты Query1, DataSource1, DBGrid1 и соединим их обычной цепочкой: в DBGrid1 задайте свойство DataSource равным DataSource1, а в DataSource1 задайте свойство DataSet равным Query1. Компонент Query1 настроим на таблицу Dep. Для этого установим свойство DatabaseName (например, dbP), а в свойстве SQL напишем оператор

Select * from Dep

Установим свойство Active в true и в DBGrid1 должно отобразиться содержимое таблицы Dep.

Создадим другую аналогичную цепочку, перенеся на форму компоненты Query2, DataSource2, DBGrid2, и свяжем ее с таблицей Pers запросом

Select * from Pers в компоненте Query2. Установим свойство Active компонента Query2 в true и в DBGrid2 отобразится содержимое таблицы Pers.

Пока таблицы независимы. Теперь нужно связать эти таблицы. Делается это следующим образом. Измените текст запроса в свойстве SQL вспомогательного компонента набора данных Query2 на

Select * from Pers where (Dep=:Dep)

В этом запросе указано условие отбора: значение поля Dep должно быть равно параметру :Dep. Далее в свойстве DataSource компонента Query2 надо сослаться на DataSource1 - источник данных, связанный с таблицей Dep. Это скажет приложению, что оно должно взять значения параметра :Dep из текущей записи этого источника данных. А поскольку имя параметра совпадает с именем поля в источнике данных, то в качестве значения параметра будет взято текущее значение этого поля. Таким образом, вспомогательная таблица, запрашиваемая в Query2, оказывается связанной с головной таблицей, запрашиваемой в Query1.

После изменения содержимого свойства SQL свойство Active компонента Query2 сбросится в false. Нужно установить его в true и запустить приложение. Увидим, что при перемещении по первой таблице во второй отображаются только те записи, которые относятся к отделу, указанному в текущей записи первой таблицы.

 

3. Основные методы компонента Query

 

К основным методам Query можно отнести методы открытия и закрытия соединения с базой данных.

Метод Close закрывает соединение с базой данных, переводя свойство Active в false. Этот метод надо выполнять перед изменением каких-то свойств, влияющих на выполнение запроса или на отображение данных.

Метод Open открывает соединение с базой данных и выполняет запрос, содержащийся в свойстве SQL - применим только в случае, если запрос сводится к оператору Select. Если же запрос содержит другой оператор, например, Update или Insert, то при выполнении Open будет генерироваться исключение EDatabaseError.

В некоторых случаях приложению требуется получить список имен полей таблицы, связанной с Query. Это может быть сделано методом GetFieldNames, который загружает список имен полей в любую переменную типа TStrings, передаваемую в него в качестве аргумента. Например, оператор Query1->GetFieldNames(ComboBox1->Items);

загружает в выпадающий список ComboBox1 имена полей таблицы, связанной с Query1. В дальнейшем будет приведен пример использования этого метода.

 

Кэширование изменений, совместное применение Query и UpdateSQL

Часто удобнее кэшировать изменения (хранить их временно в памяти), а после того, как все изменения и проверки сделаны, переслать их в базу данных или отменить все сделанные исправления.

Это делается так же, как и для компонента Table: устанавливается в true свойство CachedUpdates компонента Query и применяются методы ApplyUpdates для записи изменений в базу данных, метод CancelUpdates для отмены изменений и метод CommitUpdates для очистки буфера кэша.

Но режим кэширования позволяет также подключить в приложение компонент UpdateSQL. Этот компонент (страница Data Access) позволяет модифицировать наборы данных, открытые в режиме только для чтения. Это особенно важно для наборов данных, открываемых Query с запросом Select, поскольку Select создает таблицу только для чтения.

Построим приложение, демонстрирующее режим кэширования. Поместим на форму компоненты Query1, Datasource1, DBGrid1, DBNavigator, две кнопки Фиксация и Отмена, индикатор Кэширование, который  переключает режим кэширования. Кнопка Фиксация фиксирует все сделанные изменения в базе данных. Кнопка Отмена отменяет сделанные изменения. Когда приложение закрывается, надо проверить, не работало ли оно в режиме кэширования и не было ли сделано изменений, которые не зафиксированы в базе данных. Если были, то следует спросить пользователя о необходимости их сохранения и при положительном ответе зафиксировать изменения.

В свойстве SQL компонента Query1 запишите «Select * from Pers» и установите свойство CachedUpdates в true.

Запустите приложение, и увидите, что оно не работает. Отредактировать запись или вставить новую запись невозможно. А из кнопок навигатора доступны только кнопки навигации. Причина всего этого была указана ранее - Query с запросом Select создает таблицу только для чтения.

Поместите компонент UpdateSQL. Чтобы связать его с приложением, установите в компоненте Query1 свойство UpdateObject равным UpdateSQL1, выбрав из выпадающего списка этого свойства.

Далее нужно задавать следующие свойства компонента UpdateSQL1: DeleteSQL, InsertSQL и ModifySQL. Они содержат соответственно запросы, которые должны выполняться при удалении, вставке или модификации записи. Эти запросы можно записать обычным образом, вручную, или воспользоваться редактором UpdateSQL, который вызывается двойным щелчком на UpdateSQL. Первая страница Options Редактора UpdateSQLсодержит два окна: Key Fields и Update Fields.

В окне Key Fields надо выделить поля, по которым программа будет распознавать модифицируемую или удаляемую запись. Можно выделить все поля, что гарантирует максимально возможную надежность распознавания, но можно ограничиться выбором нескольких наиболее важных полей.

В окне Update Fields надо выделить поля, значения которых будут задаваться при модификации или вставке записи (кроме вычисляемых полей).

После этого нужно нажать кнопку Generate SQL. После этого отобразится вторая страница SQL редактора UpdateSQL, на которой можно просмотреть сгенерированные запросы для модификации (Modify), вставки (Insert) и удаления (Delete) записи. После щелчка на ОК эти запросы перенесутся в свойства DeleteSQL, InsertSQL и ModifySQL, где можно их дополнительно отредактировать.

При всех выделенных полях запрос ModifySQL будет иметь вид:

update Pers

set

Num =:Num, Dep =:Dep, Fam =:Fam, Nam =:Nam, Par =:Par, Year_b = :Year_b,

Sex = :Sex, Charact =:Charact, Photo = :Photo

where   

Num = :OLD_Num and Dep = :OLD_Dep and Fam = :OLD_Fam and

Nam = :OLD_Nam and Par = :OLD_ Par and Year b = :OLD_Year_b and

Sex = :OLD_Sex and Charact = :OLD_Charact and Photo  = :OLD_Photo

В нем в разделе Set указана установка всех полей в значения, задаваемые соответствующими параметрами с именами, тождественными именам полей -  включаются те поля, которые выделены в окне Update Fields редактора UpdateSQL. Заполнение этих параметров при выполнении соответствующих команд приложения вам не потребуется: все это сделают автоматически методы компонента UpdateSQL. В разделе Where содержатся условия, по которым идентифицируется модифицируемая запись. В этих условиях используются параметры с именами, тождественными именам полей, но с префиксом OLD_. Эти параметры - прежние значения соответствующих полей, которые были получены компонентом до модификации записи. В условия Where включены те поля, которые вы выделили в окне Key Fields редактора UpdateSQL.

Естественно, что в зависимости от приложения можно заменить эти автоматически сгенерированные имена параметров на другие или вообще использовать запросы без параметров.

Для того чтобы запросы компонента UpdateSQL срабатывали, все объекты полей, имена которых в них используются, должны быть или автоматически сгенерированы (т.е. без применения в компоненте Query Редактора Полей), или введены в Редакторе Полей. В противном случае при попытке выполнить запрос будет выдано сообщение: «Field ... is of an unknown type» (поле ... неизвестного типа) и запрос не выполнится.

Предупреждение. В запросах компонента UpdateSQL должны фигурировать только те поля, которые введены вами в Редакторе Полей компонента Query, или не должны пользоваться в Query Редактором Полей.

Посмотрим на приведенный выше сгенерированный запрос ModifySQL с точки зрения нашего приложения. Прежде всего очевидно, что из условия запроса where надо удалить поля Charact и Photo, так как сравнение полей такого типа бессмысленно. Вообще достаточно оставить сравнение только по полю Num, поскольку это поле обеспечивает уникальность каждой записи. Кроме того, из раздела set надо исключить поле Num, так как оно типа Autoincrement, а значения полей этого типа устанавливать нельзя: они автоматически нарастают с каждой новой записью. Имеет также смысл исключить поля Charact и Photo, так как они в приложении не отображаются. Таким образом, запрос ModifySQL имеет смысл оставить в виде:

update Pers

set

Dep =:Dep, Fam =:Fam, Nam =:Nam, Par =:Par, Year b = :Year, Sex = :Sex

 where

Num =OLD_Num

Запрос в свойстве DeleteSQL строится по такому же принципу. Его можно записать в виде

delete from Pers where

Num = :OLD_Num

Запрос InsertSQL, построенный при выделении всех полей имеет вид:

insert into Pers

(Dep, Fam, Nam, Par, Year_b, Sex) values

(:Dep, : Fam, :Nam, :Par, : Year_b, :Sex)

Тогда обработчик события OnClick кнопки Фиксация примет вид:

Query1->ApplyUpdates() ;

Query1->CommitUpdates();

Query1->Close();

Query1->Open() ;

modif = false;

 

В него добавлены операторы закрывания (Close) и открывания (Open) соединения с базой данных компонента Query1. Это желательно сделать, чтобы в Query1 отобразилось новое состояние базы данных после внесенных в нее изменений. Если не предусмотреть этого, то, например, будет невозможно вставить в сеансе работы новую запись, подтвердить изменение, а затем удалить эту запись. Дело в том, что если не обновили данные в Query1, то в этих данных отсутствует вставленная в данном сеансе работы запись. И, следовательно, ее не удастся удалить.

Запустите теперь приложение, увидите, что можно редактировать записи, удалять, вставлять новые записи, управлять кэшированием.

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

Метод Apply(Db::TUpdateKind UpdateKind) обеспечивает загрузку значений всех необходимых параметров и выполнение одного из запросов компонента UpdateSQL. Какого именно - определяется параметром UpdateKind, который может принимать значения ukModify, uklnsert или ukDelete, что соответствует выполнению запроса, хранящегося в свойствах ModifySQL, InsertSQL и DeleteSQL.

Например, оператор UpdateSQLl->Apply(ukModify) вызовет выполнение запроса, содержащегося в свойстве ModifySQL.

Если нужный запрос SQL не содержит параметров, то эффективнее вызвать метод ExecSQL компонента UpdateSQL:

ExecSQL(Db::TUpdateKind UpdateKind) который тоже обеспечивает выполнение указанного запроса, но без предварительной загрузки значений параметров.

Метод SetParams(Db::TUpdateKind UpdateKind) обеспечивает загрузку значений параметров указанного запроса SQL без его выполнения. Таким образом, метод Apply - это просто последовательное выполнение методов SetParams и ExecSQL.

 

Формирование произвольных запросов SQL

Часто в приложениях пользователю бывает необходимо формировать любые запросы к базе данных. В таких приложениях без языка SQL и, соответственно, без компонентов Query не обойтись. Рассмотрим на примере, как строить подобные приложения.

Приложение позволяет пользователю сформировать различные запросы Select к таблице Pers. Выпадающий список Поля (типа TComboBox, в программе назван CBFilds) заполнен именами полей, которые пользователь может выбирать из него при формировании запроса. Выпадающий список Операции, знаки (типа TComboBox, в программе назван СВОр) заполнен символами допустимых операций, функций и знаков, которые пользователь также может выбирать при формировании запроса. Окно, расположенное ниже кнопок, является компонентом типа ТМеmо (в программе названо MSQL). Это окно служит для отображения формируемого запроса SQL. Ниже расположена таблица DBGrid1 типа TDBGrid, которая через компонент DataSource связана с компонентом Query типа TQuery. Этот компонент и выполняет сформированные пользователем запросы.

Кнопка Новый запрос начинает формирование запроса, посылая в MSQL текст «Select ». Затем пользователь может вводить имена полей или непосредственно в текст, или, чтобы не ошибиться, выбирая их имена из Списка Поля. При вводе совокупных характеристик или вычисляемых полей пользователь может брать необходимые символы и ключевые слова из списка Операции, знаки. При формирования запроса пользователь может щелкнуть на кнопке Условие (в запрос вводится элемент WHERE), на кнопке Порядок (в запрос вводится элемент ORDER BY) или кнопке Группировка (в запрос вводится элемент GROUP BY) и сформировать условия отбора, сортировки, группирования. После того, как запрос сформирован, пользователь выполняет его щелчком на кнопке Выполнить и в окне отображаются результаты запроса.

Ниже приведен полный текст данного приложения.

// Перечислимый тип, определяющий режим работы в каждый момент времени

ermm TRegim {RNone, RFields, RWhere, ROrder, REnd) Regim;

void _fastcall TForml::ADDS(String s)

{

// Добавление в конец последней строки в Memo новой строки s

MSQL->Lines->Strings(MSQL->Line3->Count-l] =MSQL->Lines->Strings[MSQL->Lines->Count-l] + s;

}

void _fastcall TForml::CBFieldsChange(TObject *Sender)

{

if ((Regim == REnd)||(MSQL->Lines->Count < 1))

ShowMessage("Начните новый запрос или вводите оператор вручную");

else ADDS(" "+CBFields->Text);

}

void _fastcall TForml::FormCreate(TObject *Sender)

{

// Загрузка в выпадающий список имен полей таблицы

Query->GetFieldNames(CBFields->Items) ;

CBFields->Items->Insert(0, "*") ;

CBFields->ItemIndex = 0;

CBOp->ItemIndex = 0;

Regim = RNone;

}

void _fastcall TForml::BbeginClick(TObject *Sender)

{

MSQL->Clear();

MSQL->Lines->Add("Select ");

Regim = RFields;

}

void _fastcall TForml::BexecClick(TObject *Sender)

{

if (Regim == RNone)

{

ShowMessage("Вы не ввели запрос");

return;

}

 if (Regim == RFields)

ADDS(" FROM PERS");

Regim = REnd;

Query->SQL->Assign(MSQL->Lines);

Query->Open() ;

}

void _fastcall TForml::BWhereClick(TObject *Sender)

{

if (Regim == RFields)

ADDS(" FROM PERS");

ADDS(" WHERE");

Regim = RWhere;

}

void _fastcall TForml::CBOpChange(TObject *Sender)

{

if ((Regim == REnd) || (MSQL->Lines->Count < 1))

ShowMessage("Начните новый запрос или вводите оператор вручную");

else ADDS(" "+CBOp->Text);

}

void _fastcall TForml::BOrderClick(TObject *Sender)

{

if (Regim == RFields)

ADDS(" FROM PERS");

ADDS(" ORDER BY");

Regim = ROrder;

}

void _fastcall TForml::BGroupClick(TObject *Sender)

{

if (Regim == RFields)

ADDS(" FROM PERS");

ADDS(" GROUP BY");

Regim = ROrder;

}

В начале вводится переменная Regim перечислимого типа:

enum TRegim (RNone, RFields, RWhere, ROrder, REnd) Regim;

возможные значения которой определяют, в каком режиме в данный момент находится приложение. В начале значение переменной Regim задается равным RNone. Обработчики всех кнопок проверяют значение переменной Regim и в зависимости от результатов производят те или иные операции. Кроме того, они изменяют значение этой переменной, сообщая приложению, какие операции выполняет пользователь. Например, обработчик щелчка кнопки Выполнить в процедуре BexecClick проверяет Regim и, если значение этой переменной оказывается RNone, это означает, что пользователь, не нажав до этого ни одной кнопки, сразу щелкнул на кнопке Выполнить. В этом случае пользователю выдается замечание «Вы не ввели запрос» и обработка события прерывается. Соответствующие проверки режима можно увидеть и в других обработчиках событий.

Далее в приложении вводится процедура ADDS, которая добавляет заданную текстовую строку в конец текста, содержащегося в MSQL. Это сделано просто во избежание повтора входящего в. эту процедуру оператора во многих местах кода. Конечно, при этом в заголовочном файле модуля добавляется в описание класса соответствующее объявление этой процедуры: void _fastcall ADDS(String s);

В процедуре FormCreate производится загрузка списка CBFields значениями имен полей в таблице методом GetFieldNames, который загружает список имен полей в любую переменную типа TStrings, передаваемую в него в качестве аргумента.

Остальные процедуры приложения специальных комментариев не требуют

 

4. InterBase - работа на платформе клиент/сервер

Общие сведения

Рассмотрим в качестве примера работу с сервером InterBase. В C++Builder имеется локальный сервер InterBase - Borland InterBase Server. Он позволяет в локальном варианте разрабатывать программы, которые в дальнейшем будут работать на реальных системах. При этом во время разработки отпадает нужда в реальном отдельном сервере.

База данных в InterBase – это файл со стандартным расширением .gdb,в котором хранятся все таблицы базы данных, просмотры, хранимые процедуры и другие объекты.

Borland InterBase Server - сложная система, обладающая широкими возможностями построения больших корпоративных баз данных. Ограничимся только основами работы с InterBase, необходимыми для работы на платформе клиент/сервер.

Программа IBConsole

Перед началом работы с БД в Borland InterBase Server,: нужно зарегистрироваться как пользователь, так как создание и использование баз данных в InterBase требует указания имени пользователя, создавшего базу данных, и его пароля.

В InterBase C++Builder программой регистрации является IBConsole.

При первом запуске IBConsole откроется окно и в левой панели окна будет только одна вершина InterBase Servers. Здесь и нужно начать с регистрации локального сервера.

Выполните команду Server|Register или сделайте двойной щелчок на вершине InterBase Servers. Откроется окно, в котором нужно включить индикатор Local Engine - локальный сервер. В окнах редактирования нужно ввести имя администратора баз данных (User Name) и пароль (Password). Это имя - SYSDBA. Пароль администратора - masterkey.После нажатия ОК, в окне IBConsole появится вершина локального сервера.

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

Снять какой-то сервер с регистрации можно командой Server|UnRegister.

Процедура регистрации выполняется для каждого сервера только один раз. При последующих выполнениях IBConsole серверы уже будут зарегистрированы и их вершины будет появляться в левой панели окна. Для установки соединения с серверами нужно выполнить команду Server|Login или команду Login контекстного меню вершины сервера. В открывшемся окне нужно указать имя и пароль. После этого соединение с сервером установится.

После регистрации сервера надо зарегистрировать на нем требуемые базы данных с помощью команды Database|Register или соответствующей командой меню, всплывающего при щелчке на вершине Databases. Откроется окно, в окошке File нужно указать имя файла базы данных (это файл .gdb) с полным путем к нему. Если база данных регистрируется на локальном сервере, для поиска и задания файла можно воспользоваться кнопкой с многоточием. Если регистрация проводится на удаленном сервере, то путь к файлу должен включать соответствующий сетевой адрес.

В окошко Alias Name нужно занести псевдоним, под которым база данных будет отображаться в левой панели окна. По умолчанию псевдоним идентичен имени файла базы данных.

Если нужно сразу после регистрации соединиться с базой данных, надо указать имя (User Name) и пароль (Password). Тогда сразу после регистрации в левой панели окна отобразится вершина с развернутым множеством дочерних вершин. Перемещаясь по ним можно получить развернутую информацию о таблицах, просмотрах, процедурах и т.п., хранящихся в базе данных.

Если при регистрации базы данных указаны имя и пароль, или если при последующих запусках IBConsole только что соединились с сервером, вершина базы данных будет отображаться перечеркнутой. Тогда соединение с базой данных осуществляется командой Database|Connect или аналогичной командой контекстного меню, всплывающей при щелчке правой кнопкой мыши на вершине базы данных.

Чтобы зарегистрировать нового пользователя, щелкните правой кнопкой мыши на вершине Users в левой панели окна. Выберите из контекстного меню раздел Add User. В открывшемся окне введите в имя пользователя (User Name), пароль (Password), его подтверждение (Confirm Password).

Изменять пароли пользователей можно с помощью команды Server|User Sequrity.

Команда Database|Create Database или команда меню, всплывающего при щелчке правой кнопкой мыши на вершине Databases, обеспечивают создание новой базы данных. Они открывают диалоговое окно, в панели File(s) нужно записать имя файла создаваемой базы данных с полным путем к нему. Файл должен быть расположен на том же компьютере, на котором размещается сервер. Если путь к файлу указывает на другой компьютер, работа с базой данных будет осуществляться сервером, расположенным на том компьютере. Сам файл с указанным именем не должен существовать во время создания базы данных.

InterBase позволяет размещать базу данных в нескольких файлах. В этом случае в первой строке панели File(s) записывается имя первого файла, а имена вторичных файлов записываются в следующих строках. Тогда для всех файлов, кроме последнего, надо указывать их размер.

В окне Alias записывается псевдоним созданной базы данных. В панели Options можно изменить значения опций создаваемой базы данных.

 

Interactive SQL

Interactive SQL представляет собой интерфейс для выполнения запросов SQL в интерактивном режиме или из специальных файлов. Вызов Interactive SQL в C++Builder осуществляется из окна программы IBConsole командой Tools|Interactive SQL или третьей справа быстрой кнопкой. Предварительно в этом окне должна быть выделена вершина базы данных.

После вызова Interactive SQL откроется окно. В верхней панели этого окна нужно писать запросы SQL. Можно записать один запрос или сразу несколько запросов. Если пишется несколько запросов, каждый из них должен завершаться символом точки с запятой.

Когда запросы введены, выполните команду Query|Execute или щелкните на соответствующей быстрой кнопке - третья слева. Если в запросах есть синтаксические ошибки, они отобразятся в окне.

Если никаких ошибок нет, то после выполнения запросов в нижней панели окна отобразятся результаты его выполнения.

Запросы могут быть связаны как с просмотром, так и с изменением данных. В последнем случае все изменения хранятся в памяти и не заносятся в базу данных. Для переноса их в базу данных надо выполнить команду Transactions|Commit. Команда Transactions|Rollback отменяет все изменения, сделанные после последней выполненной команды Commit.

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

Можно посмотреть возможности Interactive SQL, соединившись с базой данных ib.gdb, предварительно зарегистрировав ее на локальном сервере.

Далее можно занести в верхнюю панель окна запрос на создание таблицы, например, такой:

create table Pers (Num smallint Not Null Primary Key, Dep char (15), Fam char(20) Not Null, Nam char (20) Not Null, Par char(20) Not Null, Year_b smallint DEFAULT 1950,

Sex char(l) DEFAULT 'м', Charact blob, Photo blob

create table Dep(Dep char(15) Not Null Primary Key, Proisv char(l) ) ;

Обратите внимание, что в таблице Pers поле Sex (пол) имеет символьный тип размером в 1 символ, хотя для такого поля можно было бы использовать булев тип. Но в InterBase нет булева типа, так что приходится пользоваться вместо него символьным.

После этого выполните команду Query | Execute. Если в операторе SQL нет ошибок, то таблицы будут созданы.

Теперь можно заполнить таблицы с помощью операторов Insert Into. Например:

Insert Into PERS(Num, Dep, Fam, Nam, Par, Year_b, Sex)

Values(1, "Бухгалтерия", "Иванов", "Иван", "Иванович", 1950, "м")

Этот оператор создает первую запись в таблице. Для создания следующих записей, выберите команду Query|Previous и в появившемся операторе нужно заменить содержание текстов в списке Values.

Имеется и другой вариант - работа из script-файла запросов. Это текстовый файл с расширением .sql. Запуск выполнения запросов из файла осуществляется командой Query | Load Script.

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

Connect <база данных> User <имя пользователя> Password <пароль>;

Например:

CONNECT "c:\test\lb.gdb" USER "A" PASSWORD "I";

Затем в файле могут размещаться любые запросы SQL.

 

Просмотры - Views

Просмотры (views) - это дочерние образования таблицы, в которые помещается некоторое подмножество записей, содержащих все, или только указанные поля. Пусть, например, имеется таблица персонала Pers. Из нее имеет смысл создать просмотры сотрудников, работающих в каждом подразделении, и раздать их руководителям подразделений. Тогда каждый руководитель будет иметь базу данных своих сотрудников, но не будет иметь доступа к сведениям о сотрудниках других отделов. А общая, базовая таблица имеется, например, у руководства предприятия. При этом можно сохранять конфиденциальность - в каждом отделе будут сведения только о своих сотрудниках, а какая-то конфиденциальная информация (какие-то поля) может вообще не включаться в просмотры, а храниться только в общей таблице под паролем и быть доступными только избранным представителям администрации. Все просмотры и базовая таблица, на основе которой они созданы, связаны друг с другом. Если в таблице или в каком-то просмотре проведены изменения, все они тут же отразятся во всех просмотрах, в которых есть соответствующие записи, и в базовой таблице.

Синтаксис оператора создания просмотра следующий:

CREATE VIEW <имя просмотра> AS SELECT <список полей> FROM <таблица> WHERE <условие>

Например:

CREATE VIEW DEP_1 AS SELECT Fam, Nam, Par, Year_b, Sex FROM Pers WHERE Dep = 'Цех 1'

Уничтожение ранее созданного просмотра осуществляется оператором:

DROP VIEW <имя просмотра>

Например, уничтожить просмотр, созданный в приведенном выше примере, можно оператором: DROP VIEW Dep_l

 

3 Доступ к базам данных через ADO

Соотношение между компонентами BDE и ADO

В C++реализована возможность работы с базами данных посредством разработанной в Microsoft технологии ActiveX Data Objects (ADO). ADO - это пользовательский интерфейс к любым типам данных, включая реляционные и не реляционные базы данных, электронную почту, системные, текстовые и графические файлы. Связь с данными осуществляется посредством так называемой технологии OLE DB.

Использование ADO является альтернативой BDE, обеспечивающей более эффективную работу с данными. Для использования этой возможности в системе нужноа установить систему ADO (ADO имеется в последних версиях Windows). Кроме того, нужно установить клиентскую систему доступа к данным, например, Microsoft SQL Server, а в ODBC должен иметься драйвер OLE DB для того типа баз данных, с которым нужно работать.

Для работы с ADO в C++Builder предусмотрены компоненты, расположенные на странице библиотеки - ADO. Они инкапсулируют такие объекты ADO, как Connection, Command и Recordset. Им соответствуют компоненты C++Builder ADOConnection, ADOCommand и ADODataSet.

Связь с базой данных в технологии ADO осуществляется обычной цепочкой: набор данных = источник данных (компонент DataSource), компоненты управления и отображения данных (DBGrid, DBEdit и др.). Отличие заключается только в первом звене этой цепочки, в котором используются компоненты, расположенные на странице ADO.

Большинство компонентов, предназначенных для работы с ADO, аналогичны прежним компонентам, работающим с BDE:

Ниже приведена краткая характеристика основных компонентов ADO.

 

ADOConnection

Используется для связи с набором данных ADO. Может работать с несколькими компонентами наборов данных как диспетчер выполнения их команд.

ADODataSet

Универсальный компонент связи с наборами данных, который может работать в различных режимах, заменяя связанные с BDE компоненты Table, Query, StoredProc. Может связываться с одной или множеством таблиц. Связь осуществляется непосредственно, или через ADOConnection.

ADOTable

Используется для работы с одной таблицей. Может связываться с ней непосредственно, или через ADOConnection.

ADOQuery

Используется для работы с набором данных с помощью запросов SQL, включая такие запросы языка DDL (data definition language), как CREATE TABLE. Может связываться с набором данных непосредственно, или через ADOConnection.

ADOStoredProc

Используется для выполнения процедур, хранимых на сервере. Может связываться с набором данных непосредственно, или через ADOConnection.

ADOCommand

Используется в основном для выполнения команд SQL, не возвращающих множество результатов. Может также совместно с другими компонентами использоваться для работы с таблицами. Может связываться с набором данных непосредственно, или через ADOConnection

 

В качестве общей характеристики можно сказать, что в некоторых отношениях компоненты ADO мощнее компонентов BDE, но в то же время ряд возможностей компонентов BDE в них не реализован. Например, они не могут использовать словари свойств, что приводит к лишней работе при создании приложений. Не все источники данных ADO могут работать со всеми типами полей. Например, источник данных Paradox ADO не работает с графикой.

 

Задание соединения компонентов ADO с базой данных

В отличие от компонентов BDE, в компонентах ADO доступ к базе данных осуществляется или с помощью строки соединения - свойства ConnectionString, или с помощью отдельного компонента ADOConnection, имя которого задается в свойстве Connection других компонентов.

Рассмотрим соединение с базой данных с помощью свойства ConnectionString на примере компонента ADOTable. Свойство ConnectionString представляет собой строку, содержащую параметры соединения. Отдельные параметры отделяются друг от друга точками с запятой. В C++Builder имеется специальное диалоговое окно, облегчающее работу по формированию строки соединения.

Перенесите на форму компонент ADOTable и в Инспекторе Объектов нажмите кнопку с многоточием около свойства ConnectionString. Откроется окно. Радиокнопка Use Data Link File позволяет использовать файл связи .udl. Радиокнопка Use Connection String позволяет в режиме диалога сформировать строку соединения. Включите эту радиокнопку и нажмите кнопку Build (Сформировать).

Откроется многостраничное окно задания свойств соединения. Надо отметить, что окна, открывающиеся в процессе формирования строки соединения, не имеют отношения к самой системе C++Builder. Это окна формирования источников данных ODBC. Доступ к ним и формирование соответствующих источников данных может осуществляться непосредственно из Панели Управления Windows. В окне Панели Управления или имеется непосредственно пиктограмма источников данных ODBC, или такая пиктограмма становится доступна после щелчка на пиктограмме Администрирование.

Итак, после нажатия кнопки Build, откроется окно установки свойств связи. На его странице Поставщик Данных (Provider) нужно указать провайдер OLE DB, который будем использовать для доступа к данным.

Выбрав провайдер, нужно перейти на страницу Подключение (Connection), или нажать кнопку Далее.

На странице Подключение нужно указать, как будем соединяться с ODBC. Выбрав кнопку Использовать имя источника данных (Use data source name), можно задать из выпадающего списка имя источника данных (data source name - DSN), зарегистрированного в ODBC. Впрочем, для того, чтобы использовать этот список, надо сначала создать и зарегистрировать требуемый источник данных. Кнопка Использовать строку подключения (Use connection string) позволяет задать строку соединения самостоятельно, не прибегая к зарегистрированным DSN.

Выберите кнопку Использовать строку подключения (Use connection string) и нажмите Сборка (Build). Откроется окно, в котором предоставляется одна из двух возможностей: работать с файловым источником данных - страница Файловый источник донных (File Data Source), или с источником данных компьютера - страница Источник данных компьютера (Machine Data Source). На одной из страниц ружно нажать кнопку Создать (New).

Откроются Диалоговые окна, в которых нужно указать характеристики создаваемого источника данных. При создании источника данных Paradox для базы данных dbP нужно выбрать драйвер Microsoft Paradox Driver (*.db). После этого нужно задать произвольное имя нового источника данных. Далее нужно указать базу данных, для которой создается источник, и характеристики (имя пользователя, пароль и т.п.). Далее в окне появится созданный источник данных.

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

Provider-MSDASQL.1;

Password=l; Persist Security Info=True; User ID=a;

Extended Properties="CollatingSequence=ASCII; DBQ=F:\DATABASE\DBPAR; DefaultDir=F:\DATABASE\DBPAR; Driver={Microsoft Paradox Driver (*.db )}; Driverld=538; FIL=Paradox 5.X; FILEDSN=D:\Program Files\Common Files\ODBC\Data Sources\dbP.dsn; MaxBufferSize=2048; MaxScanRows=8; PageTimeout=5; ParadoxNetPath=D:\WINDOWS\System32; ParadoxNetStyle=3.x; ParadoxUserName=admin; SafeTransactions=0; Threads=3; UID=admin; UserCommitSync=Yes;"

Как видите, сформировались строки, которые иным способом записать было бы весьма трудно.

Кнопка Проверить подключение (Test Connection) позволяет проверить правильность всей информации. При нажатии на эту кнопку происходит соединение с базой данных. Если все нормально, будет показано окно с надписью: «Test connection succeeded», нормально произведенное тестировании соединения. Если в сформированной строке соединения что-то неправильно, будут выданы сообщения об ошибках.

После завершения формирования строки соединения можно перейти на страницу Дополнительно (Advanced) и задать режимы работы с сетью. Страница Все (All) сообщает итоговую информацию о соединении и позволяет ее отредактировать.

После завершения операций формирования строки соединения, эта строка появится в свойстве ConnectionString компонента.

Описанные выше операции формирования строки соединения достаточно громоздки. Но если вы их проделали один раз, то можете в дальнейшем использовать созданные источники данных при задании соединения других компонентов.

Соединение с помощью компонента ADOConnection, управление транзакциями

Соединение компонентов с базами данных можно осуществлять также и через свойство Connection, связывающее данный компонент с компонентом ADOConnection. В компоненте ADOConnection, осуществляющем диспетчеризацию работы с набором данных, соединение задается свойством ConnectionString. А во всех прочих компонентах наборов данных достаточно установить в свойстве Connection имя компонента ADOConnection.

Соединение с базой данных компонентов наборов данных, связанных с ADOConnection, происходит, даже если в самом ADOConnection не предпринимается никаких действий для открытия базы данных. Достаточно в компоненте набора данных установить свойство Active = true, и он свяжется с набором данных. При этом свойство Connected компонента ADOConnection, показывающее наличие соединения, автоматически установится в true.

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

Во время выполнения соединение ADOConnection с базой данных осуществляется методом Open:

HIDESBASE void _fastcall Open(const WideString UserlD, const WideString Password);

Например:

ADOConnectionl->Open("A","1") ;

Параметры UserID и Password не обязательны. Они могут быть заданы в строке соединения - свойстве ConnectionString.

Закрывается соединение с базой данных методом Close. Свойство KeepConnection определяет, сохраняется ли соединение с базой данных, даже если база данных не открыта. Установка KeepConnection в true сокращает затраты времени и загрузку сети при соединениях с базой данных, но увеличивает затраты ресурсов компьютера.

Метод CloseDataSets закрывает соединение всех компонентов наборов данных, соединенных с данным компонентом ADOConnection, но не закрывает соединение самого ADOConnection.

Альтернативным способом установления и разрыва соединения с базой данных является установка соответственно в true или false свойства Connected. Если значение Connected равно true, значит соединение активно. Если значение Connected равно false и KeepConnection также равно false, то соединение неактивно.

Свойство Connected компонента ADOConnection связано со свойствами Active компонентов наборов данных, подключенных к данному ADOConnection. Если Connected = false, а в одном из подключенных компонентов набора данных свойство Active переключается в true, то Connected, автоматически устанавливается в true. Если же ADOConnection разрывает соединение методом Close или установкой Connected в false, то свойства Active всех подсоединенных компонентов сбрасывается в false. Этим и ограничивается связь свойств Connected и Active. Если после разрыва соединения компонент ADOConnection снова устанавливает соединение методом Open или заданием Connected = true, то свойства Active подсоединенных компонентов остаются равными false. Следовательно эти компоненты не подключаются к базе данных. В этом случае надо принять специальные меры для их подключения.

Для этого можно воспользоваться свойством DataSets компонента ADOConnection, которое является массивом всех компонентов наборов данных, подсоединенных к данному компоненту ADOConnection. Количество таких объектов определяется свойством DataSetCount. Эти свойства можно использовать, чтобы управлять всеми подсоединенными компонентами наборов данных. Например, код:

for(int i = 0; i < ADOConnection1->DataSetCount; i++)

ADOConnection1->DataSets[i]->Active = true;

обеспечивает активацию соединения с базой данных всех компонентов наборов данных.

Свойство CursorLocation определяет, какую библиотеку использует курсор при соединении с базой данных: клиентскую или сервера. Значение clUserClient (по умолчанию) обеспечивает большую гибкость. Все данные располагаются на компьютере-клиенте и тут же обрабатываются. При этом возможны операции сортировки и другие, которые могут не поддерживаться сервером. Впрочем, предложения SQL и в этом случае выполняются на сервере и на компьютер клиента передаются уже отобранные данные; соответствующие условию WHERE предложения SQL.

Значение clUseServer используется в командах, возвращающих большой объем данных.

Рассмотрим управление транзакциями. Транзакция начинается методом BeginTrans, который возвращает целое число, показывающее уровень вложенности данной транзакции. Успешное выполнение BeginTrans приводит к генерации события OnBeginTransComplete и установке свойства InTransaction в true. По значению этого свойства можно определить, находится ли компонент в состоянии формирования транзакции. А обработчик события OnBeginTransComplete можно использовать для выполнения каких-то действий перед началом транзакции.

Метод CommitTrans завершает транзакцию и сохраняет ее результаты в базе данных. При успешном выполнении CommitTrans генерируется событие OnCommitTransComplete и свойство InTransaction устанавливается в false.

Метод RollbackTrans осуществляет откат: отменяет все изменения, сделанные на протяжении транзакции. При успешном выполнении RollbackTrans генерируется событие OnRollbackTransComplete и свойство InTransaction устанавливается в true.

Свойство Attributes описывает, как при соединении обрабатываются транзакции, оставшиеся незавершенными. Свойство является множеством, которое может быть пустым, или содержать одно или два значения: xaCommitRetaining и xaAbortRetaining.

Значение xaCommitRetaining приводит к тому, что при соединении незавершенные транзакции завершаются (срабатывает метод CommitTrans). Завершение транзакции автоматически вызывает начало новой транзакции.

Значение xaAbortRetaining приводит к тому, что при соединении незавершенные транзакции отменяются с откатом назад (срабатывает метод RollbackTrans). Отмена транзакции автоматически вызывает начало новой транзакции.

Свойство IsolationLevel устанавливает уровень изоляции транзакции. Основные уровни:

 

ilReadUncommitted, ilBrowse

Видимы незафиксированные изменения, сделанные другими транзакциями.

ilReadCommitted, ilCursorStability

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

ilRepeatableRead

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

ilReadUncommitted, ilBrowse

Видимы незафиксированные изменения, сделанные другими транзакциями.

 

6. Обзор компонентов наборов данных

 

Рассмотрим особенности компонентов наборов данных ADO на примере ADOTable. Этот компонент может использоваться в приложениях вместо компонента Table, выполняющего аналогичные функции. Он вступает в контакт с указанной таблицей базы данных. База данных задается свойствами ConnectionString или Connection. Для управления таблицей в приложение вводится также компонент DataSource, в свойстве DataSet которого задается имя компонента ADOTable. Далее к DataSource подключаются компоненты отображения данных.

Имя таблицы задается свойством TableName. Какой именно вариант доступа: прямой или через оператор SELECT будет использоваться, определяется свойством TableDirect. По умолчанию TableDirect = false, что означает автоматическое создание компонентом ADOTable соответствующего оператора SELECT.

Соединение с базой данных осуществляется методом Open или установкой в true свойства Active. Но при этом, если связь с базой данных осуществляется через компонент ADOConnection, надо учитывать взаимосвязь свойства Active компонента ADOTable и свойства Connected компонента ADOConnection.

В компоненте ADOTable имеется два свойства, характеризующие курсор, используемый при навигации по таблице. Одно из них - CursorLocation, другое - CursorType описывает другие характеристики курсора. Это свойство может иметь значения:

 

ctUnspecified

Тип курсора не определен.

ctOpenForwardOnly

Курсор может перемещаться по таблице только вперед. Этот тип курсора повышает производительность приложения.

ctKeyset

При этом типе курсора записи, добавленные другими пользователями, невидимы, а записи, удаленные другими пользователями, недоступны. Этот тип используется по умолчанию.

ctDynamic

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

ctStatic

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

 

Свойство CacheSize указывает, сколько записей заносится в локальный буфер оперативной памяти. По умолчанию CacheSize = 1. Если задать, например, CacheSize=10, то при открытии базы данных в буфер загрузятся первые 10 записей. Пока будет идти работа с этими записями, все операции будут проводится в оперативной памяти без обращения к базе данных. Если указатель таблицы вышел за пределы 10, то в память загрузятся следующие 10 записей и т.д.

Основные способы работы с ADOTable не отличаются от способов, работы с компонентом Table. Точно так же, как в компонентах Table и Query, двойной щелчок на компоненте вызывает редактор полей, в котором можно задать свойства отдельных полей, ничем не отличающихся от полей компонентов BDE. Однако, в компонентах ADO невозможно работать со словарями. Так что в каждом компоненте свойства полей приходится задавать вручную. Кроме того, надо иметь в виду, что не все драйверы ADO могут работать с любыми типами полей. Например, драйвер Paradox ADO не работает с полями изображений. Так что в таблице Pers базы данных dbP не будет доступно поле Photo - фотографии сотрудников.

Из редактора полей также можно перетаскивать поля мышью на форму. При этом на форме автоматически будут создаваться соответствующие компоненты отображения данных.

Программный доступ к полям осуществляется по индексу через свойство Fields[i], по имени поля с помощью метода FieldByName("<имя>"), по имени объекта поля.

Ограничения на вводимые значения в компоненте ADOTable можно обеспечивать только на уровне полей.

Упорядочивание отображаемых записей производится установкой свойства IndexFieldNames. В этом свойстве можно задавать любое сочетание имен полей, по которым нужно упорядочить отображение, разделяя их точками с запятой. Например, строка «Dep» упорядочит записи в таблице Pers по значению поля Dep - отдел. Строка «Dep;Fam;Nam;Par» упорядочит записи по значению поля Dep - отдел, а внутри каждого отдела упорядочит по фамилии, имени и отчеству сотрудников. В компонентах ADO можно задавать любые сочетания полей, независимо от того, была ли индексирована таблица при ее создании по этим полям..

Фильтрация отображаемых данных может осуществляться помощью свойства Filter, в котором записываются условия отбора. В компонентах ADO в строке Filter имена полей обязательно должны отделяться пробелами от операций отношения. Так же пробелами должны окружаться логические операции and и or. Например,

(Year_b <= I960) and (Year_.b >= 1940)

Свойство Filter работает, если свойство Filtered = true.

Можно также использовать для фильтрации обработчик событий OnFilterRecord. Дополнительные возможности фильтрации обеспечивает свойство только времени выполнения FilterGroup. Этот параметр позволяет фильтровать записи, которые изменены, или должны были быть изменены, или удалены. Свойство может иметь следующие значения:

 

fgUnassigned

Не оказывает влияния на фильтрацию. Значение по умолчанию.

fgNone

Отменяет текущую фильтрацию и делает видимыми все записи.

fgPendingRecords

Фильтруются записи, которые были изменены, но еще не занесены в таблицу методом UpdateBatch или прерваны методом CancelBatch.

fgAffectedRecords

Фильтруются последние измененные записи.

fgFetchedReeords

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

fgPredicate

Фильтруются только что удаленные записи.

fgConflictingRecords

Фильтруются записи, которые должны были быть изменены, но это не получилось из-за ошибок.

 

Методы, используемые при программировании работы с базой данных, в ADOТаble в основном те же, что в Table и Query. Навигация по таблице осуществляется методами First, Next, Last и Prior. При редактировании данных используются методы Insert, Edit, Post и другие. Из методов поиска в ADO реализованы только методы Locate и Lookup.

Из методов, отсутствующих в компонентах BDE, интересными представляются методы сохранения набора данных в файле и чтения его из файла. Сохранение в файле осуществляется методом SaveToFile:

void _fastcall SaveToFile(const WideString FileName, TPersistFormat Format);

Параметр FileName указывает имя файла, в котором сохраняется набор данных. Параметр Format определяет формат файла. Этот параметр может принимать одно из двух значений: pfADTG - формат ADTG (Advanced Data Tablegram), или pfXML - формат XML. Например:

ADOTablel->SaveToFile("Test.adt",pfADTG);

Чтение данных из файла осуществляется процедурой LoadFromFile:

void _fastcall LoadFromFile(const WideString FileName);

где FileName - имя файла. Загружать файл в набор данных можно даже при закрытом соединении с базой данных. В момент загрузки соединение автоматически откроется.

Связь друг с другом компонентов ADOTable, работающих с разными таблицами, одна из которых главная, а другая - вспомогательная, осуществляется с помощью свойств MasterSource и MasterFields.

Компонент ADOQuery используется для выполнения произвольных запросов SQL. Его основное свойство SQL, содержащее запрос, и методы выполнения этого запроса ничем не отличаются от компонента Query. А соединение с базой данных, свойства и методы фильтрации и поиска аналогичны рассмотренным выше для компонента ADOTable.

Отличие от компонента Query заключается в методике работы с параметрами при динамических запросах.

В компоненте ADOQuery объекты, соответствующие параметрам, указанным в свойстве SQL, расположены в свойстве Parameters типа TParameters. Это массив параметров. Доступ к отдельным параметрам во время выполнения может осуществляться по индексу подсвойства Items, при этом значение определяется функцией Value. Доступ к отдельным параметрам может осуществляться по имени с помощью методов ParamByName, FindParam, GetParamList, ParamValues свойства Parameters. Например,

// значение параметра типа string

ADOQuery1->Parameters->Items[0]->Value = Edit1->Text;

ADOQuery1->Parameters->ParamByName("EDep")->Value=Edit1->Text;

ADOQuery1->Parameters->ParamValues["EDep"] = Edit1->Text;

// значение параметра типа integr

ADOQuery1->Parameters->ltems[1]->Value = Edit2->Text;

ADOQuery1->Parameters->ParamByName("PYear")->Value=Edit2->Text;

ADOQuery1->Parameters->FindParam("PYear")->Value = Edit2->Text;

ADOQuery1->Parameters->ParamValues["PYear"] = Edit2->Text;

В компоненте ADOQuery предусмотрена возможность вводить параметры во время проектирования. Для этого надо нажать кнопку с многоточием около свойства Parameters в окне Инспектора Объектов, затем в появившемся окне редактора параметров щелкнуть правой кнопкой мыши и выбрать в контекстном меню раздел Add.

Свойства DataType, Name, Value аналогичны свойствам Query. Свойство Direction в ADOQuery аналогично свойству ParamType в Query. Дополнительно для строковых параметров имеется свойство Size - число символов, а для числовых параметров -свойства Precision и NumericScale - общее максимальное число цифр и максимальное число цифр после десятичной запятой, свойство Attributes - множество, которое может содержать значения psSigned - число может быть со знаком, psNullable - значение может быть нулевым, psLong - длинное двоичное значение.

Компонент ADOStoredProc является аналогом компонента StoredProc, используется для выполнения хранимых на сервере процедур.

Имя выполняемой процедуры задается в свойстве РгосеdureName. После задания имени процедуры в свойстве Parameters появятся входные параметры процедуры. Выходные параметры представляются объектами полей компонента ADOStoredProc. Можно изменить их свойства, если выполнить двойной щелчок на компоненте ADOStoredProc, в появившемся окне Редактора Полей выполнить щелчок правой кнопкой мыши и выбрать раздел Add all fields. В окне появятся поля, соответствующие всем выходным параметрам процедуры.

Таким образом, при работе с хранимыми процедурами сначала нужно задать значения входных параметров, затем выполнить вызов процедуры оператором вида:

ADOStoredProcl->ExecProc() ;

а к возвращенным параметрам обращаться как к объектам полей компонента ADOStoredProc.