Компонент TDatabase
Обычно
при разработке приложений,
использующих базы данных,
с помощью утилит
конфигурации BDE создаются
псевдонимы (алиасы),
указывающие на тип и
местоположение данных.
Компоненты типа TTable, TQuery,
TStoredProc обладают свойством
DatabaseName, при установке
которого на этапе
проектирования можно
выбрать необходимый
псевдоним из выпадающего
списка или явно указать
каталог, в котором
располагаются плоские
таблицы. Однако нередко
бывает необходимо создать
псевдоним динамически,
или переопределить
какие-либо параметры
настройки драйвера базы
данных (например, языковый
драйвер, размер буферов,
параметры кэширования
структур таблиц на рабочей
станции) для конкретного
приложения без
модификации файла
конфигурации BDE. В этом случае
обычно используется
компонент TDatabase,
помещаемый явно на форму
или в модуль данных. Если
определить свойство
DatabaseName этого компонента,
оно появится в списке
псевдонимов при установке
свойства DatabaseName
компонентов TTable, TQuery, TS toredProc.
Отметим,
что, если не поместить
компонент TDatabase на форму
(или в модуль данных), он в
любом случае будет создан
на этапе выполнения в
процессе создания формы
или модуля данных. Дело в
том, что именно этот
компонент отвечает за
взаимодействие с Borland Database
Engine, и поэтому его создание
инициируется компонентами
TTable, TQuery, TStoredProc, если
таковые присутствуют в
создаваемых на этапе
выполнения формах или
модулях данных.
Для
динамического создания
псевдонима следует
поместить компонент TDatabase
на форму или в модуль
данных и выбрать опцию
Database Editor из контекстного
меню этого компонента
(рис.4).
Рис.4.
Компонент TDatabase в модуле
данных
В
редакторе свойств Database Editor
(рис.5) можно выбрать либо
имя существующего (т.е.
описанного в файле
конфигурации BDE или
созданного с помощью
другого, созданного ранее,
компонента TDatabase)
псевдонима базы данных,
либо явно указать драйвер
БД и переопределить
параметры доступа к базе
данных. Нажатие на кнопку
Defaults приводит к внесению
всех параметров и их
значений, характерных для
данного псевдонима (или
данного драйвера, если
указан драйвер БД), в
список Parameter Overrides, и затем
можно внести в него
изменения. Если снять
отметку с флажка Login prompt,
можно подавить появление
диалога ввода пароля
пользователя (что иногда
бывает полезно при отладке
приложений, а также в
случае, когда требования к
безопасности данных
невысоки по сравнению с
требованиями к
производительности работы
пользователя). Опция Keep
inactive connection указывает,
сохранять ли соединение с
базой данных, если
пользователь закрыл все
таблицы. Если эта опция
выбрана, при закрытии и
последующем повторном
открытии таблиц
пользователь должен
заново регистрироваться
на сервере.
Отметим,
что переопределить
параметры псевдонима базы
данных можно также и с
помощью инспектора
объектов - они содержатся в
опубликованных свойствах
компонента TDatabase(рис.6).
Рис.
5. Редактор свойств
компонента TDatabase
Помимо
переопределения
параметров псевдонимов
или создания новых
псевдонимов компонент
TDatabase нередко используется
для минимизации числа
обращений к серверу. Как
было сказано выше,
компоненты TTable, TQuery и
TStoredProc инициируют
динамическое создание
компонента TDatabase, если он в
явном виде не присутствует
в форме или модуле данных.
Соответственно каждый раз,
когда в процессе
выполнения приложения
создается новая форма,
пользователь получает
диалог ввода имени и
пароля, и, что не менее
существенно, происходит
обращение клиентского
приложения к серверу баз
данных с целью выяснения
существования такого
пользователя,
правильности его пароля, а
также его прав на таблицы и
иные объекты базы данных
посредством вызовов
функций BDE, обращающихся, в
свою очередь, к функциям
прикладного программного
интерфейса клиентской
части соответствующего
сервера. Чтобы избежать
этой ситуации, на форму или
в модуль данных,
создаваемый при запуске
приложения, помещается
компонент TDatabase (или
несколько компонентов
TDatabase, если приложение
использует несколько
различных баз данных), и
свойство DatabaseName всех
компонентов TTable, TQuer y , TStoredProc
устанавливается равным не
имени соответствующего
псевдонима, а имени
соответствующего
компонента TDatabase.
Рис.7.
Связь компонента TTable с
компонентом TDatabase
Из
других параметров
серверных баз данных,
которые можно
переопределить с помощью
компонента TDatabase, весьма
важным является параметр
SQLPassThruMode, определяющий,
каким образом завершаются
транзакции (т.е.
согласованные изменения в
нескольких таблицах базы
данных), инициированные
компонентами TTable и TQuery, и
могут ли они использовать
общие соединения с базой
данных. Возможные значения
этого параметра - NOT SHARED,
SHARED AUTOCOMMIT и SHARED NOAUTOCOMMIT.
При
использовании значения NOT
SHARED для компонентов TQuery и
компонентов TTable создаются
отдельные соединения с
базой данных, что
позволяет избежать
возможных конфликтов и
непредсказуемых
результатов обновлений
данных в ряде случаев
(например, при попытке
обновления одной и той же
записи с помощью методов
TTable и с помощью SQL-запроса
на обновление данных,
инициированное
компонентом TQuery).
Наиболее
эффективным с точки зрения
минимизации соединений с
базой данных значением
этого параметра в
большинстве случаев
является значение SHARED
AUTOCOMMIT. При использовании
этого значения изменения
каждой записи в таблицах
немедленно фиксируются
сервером независимо от
того, к какому классу (TTable
или TQuery) принадлежит
компонент, их
инициировавший, при этом
компоненты обоих классов
могут использовать общие
соединения с базой данных.
Третье
возможное значение этого
параметра - SHARED NOAUTOCOMMIT. В
этом случае компоненты TTable
и TQuery могут также
использовать одно и то же
соединение с базой данных,
но без завершения
транзакций после
редактирования каждой
записи, но контроль за
завершением транзакций
следует осуществлять в
клиентском приложении.
Свойство
Transisolation компонента TDatabase
определяет уровень
изоляции транзакций
разных пользователей друг
от друга. Предположим, в
процессе транзакции
требуется использовать
значение, хранящееся в
поле какой-либо таблицы,
например, для проведения
расчетов. С момента начала
транзакции это значение
может быть изменено другим
пользователем, поэтому в
общем случае неочевидно,
какое именно значение
будет использовано -
реально существующее в
базе данных на момент,
когда оно было
затребовано, или то,
которое существовало на
момент начала транзакции.
Значение
свойства Transisolation, равное
tiDirtyRead, применяется, если в
этой ситуации
используются самые
последние данные,
независимо от того,
завершил ли изменившую их
транзакцию другой
пользователь. В этом
случае существует
потенциальная опасность
использовать данные,
реально не сохраненные в
базе данных, если другой
пользователь выполнил
откат транзакции. Отметим,
что не все серверные СУБД
поддерживают такой режим.
Значение
tiReadCommitted применяется, если
нужно использовать
последнее значение на тот
момент, когда оно
затребовано, но только
после того, как изменивший
его пользователь завершил
транзакцию. Этот режим
поддерживается
большинством серверных
СУБД.
Значение
tiRepeatableRead полностью
изолирует транзакцию от
вмешательства других
пользователей. При его
применении в течение всей
транзакции используются
одни и те же данные,
существовавшие на момент
начала транзакции,
независимо от того,
изменялись ли они другими
пользователями. Такой
режим может создать
проблемы при высокой
интенсивности транзакций.
Представим себе, например,
систему продажи
авиабилетов, когда оба
оператора одновременно
получают данные о том, что
данное место свободно , и продают два
билета на одно место. В
приложениях подобного
рода не рекомендуется
использовать значение
tiRepeatableRead.
Повлиять
на выполнение серверных
транзакций можно также
путем кэширования
внесенных пользователем
изменений вместо попытки
немедленного сохранения
их в базе данных, установив
равным true значение
свойства Cached Updates
компонента TTable или TQuery. В
этом случае накопленные в
локальном кэше изменения
пересылаются на сервер с
помощью метода ApplyUpdates()
компонента TTable или TQuery.
Кэширование
изменений полезно по
многим причинам. Во-первых,
такой метод ввода данных
снижает нагрузку на сеть,
так как взаимодействие
клиента с сервером
происходит не постоянно,
как в случае
непосредственного
редактирования таблиц на
сервере, а эпизодически.
Во-вторых, если сохранение
кэшированных данных на
сервере не удалось,
например, из-за блокировок
обновляемых записей
другими пользователями,
этот метод возвращает
значение false, но при этом
изменения сохраняются в
кэше, что позволяет
повторить попытку сохранения
данных позже либо внести в
изменяемые данные
необходимые коррективы.
Изменения также можно
отменить с помощью метода
CancelUpdates().
Отметим,
что выполнение метода
ApplyUpdates инициирует
транзакцию на сервере,
которая завершается после
внесения всех изменений из
кэша в базу данных, и лишь
после успешного ее
завершения внесенные
данные удаляются из кэша.
Поэтому до выполнения
метода ApplyUpdates серверные
данные остаются
неизмененными, что
позволяет, если этого
требует логика
клиентского приложения,
проанализировать
потенциальные изменения,
внести в них коррективы и
лишь затем сохранить их в
базе данных.
Рассмотрим
простейший пример
применения компонента
Database и кэширования данных.
Для этой цели скопируем на
сервер ORACLE 7 таблицу CLIENTS.DBF
из входящей в комплект
поставки C++Builder базы данных
DBDEMOS (например, с помощью
утилиты Data Migration Wizard) и
создадим приложение для
ввода данных в нее (рис. 8):
Рис.
8 Приложение для ввода
данных в таблицу ORACLE
Значения
свойств компонентов
созданного приложения
приведены в таблице 1.
Особо отметим, что
свойство CachedUpdates
компонента Table1должно
иметь значение true.
Таблица 1.
Компонент |
Свойство |
Значение |
Database1 |
DatabaseName |
my_database |
|
Params |
SERVER NAME=ORA
USER NAME=USER1
NET PROTOCOL=TNS
OPEN MODE=READ/WRITE
SCHEMA CACHE SIZE=8
LANGDRIVER=ancyrr
SQLPASSTHRU MODE=SHARED AUTOCOMMIT
SCHEMA CACHE TIME=-1
MAX ROWS=-1
BATCH COUNT=200
ENABLE SCHEMA CACHE=FALSE
SCHEMA CACHE DIR=
ENABLE BCD=FALSE
ENABLE INTEGERS=FALSE
LIST SYNONYMS=NONE
ROWSET SIZE=20
BLOBS TO CACHE=64
BLOB SIZE=32
PASSWORD=u |
Table1 |
Active |
true |
CachedUpdates |
true |
DatabaseName |
my_database |
TableName |
CLIENTS |
DataSource1 |
DataSet |
Table1 |
DBGrid1 |
DataSource |
DataSource1 |
DBNavigator1 |
DataSource |
DataSource1 |
DBImage1 |
DataSource |
DataSource1 |
DataField |
IMAGE |
|
LoginPrompt |
false |
Button1 |
Caption |
Сохранить |
Button2 |
Caption |
Отменить |
Button3 |
Caption |
Выход |
Нажатие
на кнопки Button1 и Button2
приводит к попытке
сохранения данных на
сервере и к отмене
внесенных изменений
соответственно.
//---------------------------------------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop
#include
"appl1.h"
//---------------------------------------------------------------------------
#pragma link
"Grids"
#pragma resource
"*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall
TForm1::TForm1(TComponent* Owner) :
TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
Table1->ApplyUpdates();
}
//---------------------------------------------------------------------------
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
Table1->CancelUpdates();
}
//---------------------------------------------------------------------------
void __fastcall
TForm1::Button3Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
При
выполнении метода Post
компонента Table1 новые
записи накапливаются в
кэше, а изменений в
таблице, хранящейся на
сервере, не происходит, что
можно проконтролировать,
например, с помощью
утилиты Database Explorer. При
нажатии на кнопку
"Сохранить" внесенные
записи переносятся на
сервер, что также можно
проконтролировать,
перечитав редактируемую
таблицу (рис.9):
Рис.9.
Перенос содержимого кэша
на сервер
Назад | Содержание | Вперед