<< Предыдущая страница

void_fastcall TForm1::Button1Click(TObject *Sender)
{
    char sqls[250]; // массив для хранения команды SELECT
    char fmts[50]; // массив для зарплаты и надбавки
    int mins, adds; // десятичные эквиваленты

// Присвоить fmts зарплату, введенную пользователем
    strcpy(fmts, SalaryEdit->Text.c_str());
    mins = atoi(fmts);
// Деактивировать предыдущий запрос и очистить SQL
    Query1->Close();
    Query1->SQL->Clear () ;
    sprintf(sqls, "SELECT * FROM EMPLOYEE WHERE Salary<%s", fmts);
// Присвоить сформированную команду SELECT свойству SQL
    Query1->SQL->Add(sqls) ;
    try {
       Query1->Open(); // выполнить команду SELECT
    } catch(EDBEngineError* dbError) // обработка ошибок BDE
    {
       for (int i=0; i<dbErro-r->ErrorCount; i++)
          MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
    }

// Присвоить fmts надбавку [в %], введенную пользователем
    strcpy(fmts, AddEdit->Text.c_str());
    adds = atoi(fmts);
// Деактивировать предыдущий запрос и очистить SQL
    Query1->Close () ;
    Query1->SQL->Clear() ;
// Присвоить команду UPDATE свойству SQL
    Query1->SQL->Add("UPDATE EMPLOYEE set Salary = (Salary+((Salary*:adds))/100) WHERE (Salary < :mins)");
    Query1->ParamByName("mins")->AsInteger = mins;
    Query1->ParamByName("adds")->AsInteger = adds;
    try {
       Query1->ExecSQL(); // выполнить команду UPDATE
    } catch(EDBEngineError* dbError) // обработка ошибок BDE
    {
       for (int i=0; i<dbError->ErrorCount; i++)
          MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
   }
// Деактивировать предыдущий запрос и очистить SQL
    Query1->Close ();
    Query1->SQL->Clear();
// Восстановить команду SELECT
    Query1->SQL->Add(sqls) ;
    try {
       Query1->Open (); // выполнить команду SELECT
    } catch(EDBEngineError* dbError) // обработка ошибок BDE
    {
       for (int i=0; i<dbError->ErrorCount; i++)
          MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
    }
}

Листинг 5.8. Использование метода ExecSQL с командой UPDATE.

Рис. 5.16 показывает пример работы приложения, вызывающего повышение заданной параметром mins минимальной зарплаты на величину процентной надбавки, определяемой параметром adds. Приложение представляет пользователю список служащих, зарплата которых осталась меньше установленного минимума. Сравните последний столбец таблицы с оригинальными значениями (Рис. 5.15).

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

Аналогично таблице, компонента запроса также инкапсулирует следующие методы:

• First, Next, Prior, Last и MoveBy используются для навигации по результатам динамического запроса.

• Append, Insert, AppendRecord и InsertRecord добавляют новую запись к таблице. Delete вычеркивает текущую запись. Edit разрешает приложению модифицировать записи, a Post вызывает фактическое изменение содержимого базы данных.

5.2.1.4 Хранимые процедуры

Хранимая процедура представляет собой инкапсуляцию команд и данных (таблиц, индексов, областей значений) в некотором мета-объекте базы данных удаленного сервера. Компонента TStoredProc позволяет выполнить часто повторяющуюся процедуру, хранимую на сервере, и передать результаты приложению клиента.

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

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

Рис. 5.17 показывает свойства компоненты хранимой процедуры в окне Инспектора объектов:

wpe7.jpg (13434 bytes)

Рис. 5.17. Свойства хранимой процедуры.

Active разрешает или запрещает режим просмотра "живых данных", возвращаемых процедурой на этапе проектирования. Значение false устанавливается по умолчанию.

DatabaseName содержит псевдоним адресуемого сервера базы данных.

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

ParamBindMode задает метод, по которому фактические параметры ставятся в соответствие формальным параметрам в описании хранимой процедуры. Значение pbByName (по умолчанию) определяет соответствие по именам, а значение pbByNumber - по порядку перечисления в процедуре.

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

 

полная информация, необходимая для запуска хранимой процедуры, может оказаться недоступной. В таком случае вам придется самому определить тип каждого параметра (Input, Output, Result), тип его данных и, возможно, значения входных параметров. Редактор параметров отображает параметры в том поряд-

ке, в котором они перечислены в описании данной хранимой процедуры (Рис. 5.18). Нажатие кнопки ОК подготавливает сервер и вызывает запуск хранимой процедуры на стадии проектирования приложения. Только база данных Oracle позволяет добавлять, вычеркивать или удалять все параметры в определении хранимой на сервере процедуры, производя таким образом ее перегрузку. Отвечающие за такие действия кнопки редактора параметров Add, Delete и Clear обычно запрещены, поскольку ваше приложение клиента не может модифицировать хранимые процедуры других серверов.

Рис. 5.18. Редактор параметров хранимой процедуры.

Аналогично запросу, свойство Params содержит указатель на массив объектного типа TParams. Поэтому изменить значение параметра во время выполнения программы можно по индексу в массиве Items объектов типа TParams:

StoredProcl->Params[0]->Items[1]->AsString = Editl->Text;

или по имени параметра, посредством метода ParamByName:

StoredProcl->ParamByName("ЧИСЛО_СЛУЖАЩИХ")->AsInteger++;

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

StoredProcl->Prepare () ;

StoredProcl->ExecProc ();

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

Editl->Text = StoredProcl->Params[0]->Items[0]->AsString;

или по имени параметра, посредством метода ParamByName:

Edit2->Text =

StoredProcl->ParamByName("ЧИСЛО_СЛУЖАЩИХ")->AsInteger;

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

1. Установите псевдоним адресуемой базы данных сервера в свойстве DatabaseName.

2. Поместите на форму компоненту TDataSource и установите ее свойство DataSet = StoredProcl.'

3. Поместите на форму компоненту управления сеткой TDBGrid и установите ее свойство DataSource = DataSourcel.

4. Поместите компоненту TStoredProc на форму.

5. Укажите имя процедуры в свойстве StoredProcName.

6. Установите свойство Active = true для процедуры StoredProcl с тем, чтобы сразу же отобразить результаты в сетке.

7. Откройте редактор параметров, введите (если надо) их значения и нажмите кнопку ОК.

 

5.2.1.5 Соединения с базой данных и транзакции

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

Классическим примером транзакции является перевод денежных средств банковских счетов. Такая транзакция обычно состоит в добавлении определенной суммы перевода к новому счету и вычитании этой суммы из исходящего счета. Если выполнение любой из этих операций терпит неудачу, весь трансферт считается незавершенным. SQL серверы дают возможность "прокручивать назад" команды при возникновении ошибки, не производя никаких изменений в базе данных. Именно управление транзакциями является функцией компоненты TDatabase. Как правило, транзакция содержит несколько команд, поэтому начало транзакции надо отметить методом StartTransaction. Как только транзакция началась, все ее исполняемые команды находятся во временном состоянии, до тех пор, пока один из методов Commit или Rollback () не отметят конец транзакции. Вызов Commit фактически модифицирует данные, а вызов Rollback отменяет всякие изменения.

Рис. 5.19 показывает свойства компоненты соединения с базой данных в окне Инспектора объектов:

wpe8.jpg (11534 bytes)

Рис. 5.19. Свойства соединения с базой дачных.

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

DatabaseName позволяет создать локальный псевдоним базы данных в дополнение к значениям AliasName или DriverName.

DriverName содержит имя драйвера BDE при создании локального псевдонима по значению DatabaseName. Указание этого свойства является альтернативой значения AliasName.

Params содержит строчный массив параметров одиночного соединения.

Листинг 5.9 реализует транзакцию по изменению адреса фирмы на примере связанных таблиц CUSTOMER и ORDERS. Старый адрес, введенный пользователем в область редактирования EditOld, заменяется на новый, введенный в область редактирования EditNew. В этом примере компонентный объект Database1 использовался для одиночного соединения с базой данных, поддерживающего выполнение одиночной транзакции. Этот объект необходимо каким-то образом связать с псевдонимом базы данных - или установкой соответствующих свойств компоненты, или определив параметры соединения (такие как тип драйвера, имя сервера, имя пользователя, пароль) во время выполнения программы. Сначала опробуем первый способ соединения на стадии проектирования формы приложения, установив значения свойств компоненты, как показано на Рис. 5.19.

void_fastcall TForm1::Button1Click(TObject *Sender)
{
    char sqls[250]; // массив для хранения команды SQL
    try {
    Database1->StartTransaction ();
    Query1->SQL->Clear () ;
// Изменить EditOld на EditNew в таблице CUSTOMER
    sprintf(sqls, "UPDATE CUSTOMER set Addrl = \"%s\" WHERE (Addrl = \"%s\")", EditNew->Text.c_str(), EditOld->Text.c_str());
    Query1->SQL->Add(sqls) ;
    Query1->ExecSQL ();
    Query1->SQL->Clear () ;
// Изменить EditOld на EditNew в таблице ORDERS
    sprintf(sqls, "UPDATE ORDERS set ShipToAddrl = \"%s\" WHERE (ShipToAddrl = \"%s\")", EditNew->Text.c_str(), EditOld->Text.c_str()) ;
    Query1->SQL->Add(sqls) ;
    Query1->ExecSQL();
// Внести все изменения, сделанные до этого момента
    Database1->Commit();
    Table1->Refresh();
    Table2->Refresh() ;
    } catch(EDBEngineError* dbError) // обработка ошибок BDE
    {
       Database1->Rollback() ;
       for (int i=0; i<dbError->ErrorCount; i++)
             MessageBox (0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
       return;
    } catch (Exception* exception) // обработка исключений
    {
       MessageBox (0, exception->Message.c_str (), "Error", MB_OK) ;
       Database1->Rollback() ;
       return;
   }
}

Листинг 5.9. Транзакция, реализующая смену адреса фирмы.

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

Рис. 5.20. Результат прохождения транзакции.

Листинг 5.10 показывает, как соединиться с сервером базы данных во время выполнения программы, не создавая ее псевдонима. Ключевые моменты заключаются в том, чтобы указать DriverName и заполнить массив параметров информацией, необходимой для первого соединения (в нашем примере пользователь вводит свое имя и пароль в объекты компонент редактирования Editi и Edit2). Мы определили только те параметры соединения, которые не установлены для данного драйвера в утилите конфигурации BDE. Значение параметра SQLPASSTHRU MODE определяет один из трех возможных способов взаимодействия табличных методов Add, Append и Insert с компонентами запросов, которые соединены с той же базой данных. Использованное в примере значение NOT SHARED означает, что табличные методы и запросы используют два раздельных соединения с сервером. Сервер рассматривает их как соединения с двумя разными пользователями. До тех пор, пока транзакция не завершится, табличные методы не применяются, хотя результаты выполнения запросов могут менять содержимое базы данных, раздельно от действий активной транзакции. Два других значения SHARED NOAUTOCOMMIT и SHARED AUTOCOMMIT указывают, что табличные методы и запросы разделяют одно общее соединение с сервером. Если вам нужно включить табличные методы в транзакцию, используйте способы SHARED NOAUTOCOMMIT или NOT SHARED.

void_fasfccall TForm1::Button1Click(TObject *Sender)
{
    char name[20]; // буфер для имени пользователя
    char pass [20]; // буфер для пароля
    try {
// Закрыть базу данных и установить параметры
       Database1->Close() ;
       Database1->DriverName = "STANDARD";
       Database1->KeepConnection = true;
       Database1->LoginPrompt = false;
       Database1->Params->Add("SERVER NAME=...\\CBuilder\\EXAMPLES\\DATA\\EMPLOYEE.DB") ;
       Database1->Params->Add("SCHEMA CACHE=8") ;
       Database1->Params->Add("OPEN MODE=READ/WRITE") ;
       Database1->Params->Add("SQLPASSTHRU MODE=NOT SHARED");
      sprintf(name, "USER NAME=%s", Edit1->Text.c_str ());
       Database1->Params->Add(name);
       sprintf(pass, "PASSWORD=%s", Edit2->Text.c_str());
       Database1->Params->Add(pass);
// Снова открыть базу данных и указанную таблицу Database1->0pen() ;
       Tablel->0pen() ;
    } catch(EDBEngineError* dbError) // обработка ошибок BDE
    {
          for (int i=0; i<dbError->ErrorCount; i++)
             MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
    }
}

Листинг 5.10. Соединение с сервером без псевдонима.

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

Рис. 5.21. Первое соединение с "защищенной" базой дачных.

5.2.2 Компоненты управления данными

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

Устройство и работа большинства из этих интерфейсных элементов довольно очевидны, их эквиваленты знакомы нам по вкладке Standard стандартных Windows Палитры компонент, а глава 4 содержит подробное их описание.

Остановимся на особенностях использования исключительно важной и мощной компоненты навигатора базы данных TDBNavigator. Эта компонента придает приложениям СУБД новый стандартизованный облик с панелью управления как у видеомагнитофона (Рис. 5.22).

Рис. 5.22. Панель навигатора.

Нажимая на кнопки First, Prior, Next и Last, пользователь перемещается от записи к записи, а с помощью кнопок Insert, Delete, Edit, Post, Cancel и Refresh производит редактирование записей. Именно навигатор обеспечивает ту степень функциональности, в которой нуждается любое современное приложение баз данных. Все кнопки навигатора видимы по умолчанию.

Рис. 5.23 показывает свойства компоненты навигатора базы данных в окне Инспектора объектов:

Рис. 5.23. Свойства навигатора базы данных.

DataSource соединяет кнопки управления панели навигатора с компонентами доступа к наборам данных через компоненту источника. Изменяя значение этого свойства во время выполнения программы, можно использовать одну и ту же компоненту для навигации по разным таблицам. Разместите на форме две компоненты редактируемого ввода DBEditI и DBEdit2, связанные с таблицами CustomersTable и OrdersTable через источники данных CustomersSource и OrdersSource, соответственно. Когда пользователь выбирает название фирмы (поле Company в DBEditI), навигатор тоже должен соединяться с источником CustomersSource, а когда активизируется номер заказа (поле OrderNo в DBEdit2), навигатор должен переключаться на источник OrdersSource. Чтобы реализовать подобную схему работы навигатора, необходимо написать обработчик события OnEnter для одного из объектов компоненты редактирования, а затем разделить это событие с другим объектом (Листинг 5.11).

VisibleButtons позволяет убрать ненужные кнопки, например, все кнопки редактирования на форме, предназначенной исключительно для просмотра данных. В приложениях клиент/сервер рекомендуется запретитькнопки First и Last, так как их нажатие может проявляться в длительном прохождении запросов.

Во время выполнения программы можно динамически прятать или вновь показывать кнопки навигатора - в ответ на определенные действия пользователя или на изменения состояния приложения. Предположим, вы предусмотрели единый навигатор для редактирования таблицы CustomersTable и для просмотра таблицы OrdersTable. Когда навигатор подключается ко второй таблице, желательно спрятать кнопки редактирования Insert, Delete, Edit, Post, Cancel и Refresh, а при подключении к первой таблице - снова показать их. Листинг 5.11 показывает законченный текст обработчика события OnEnter с добавлениями кода для манипуляций кнопками панели навигатора.

void_fastcall TForm1:: DBEditlEnter(TObject *Sender)

    if (Sender == DBEdit1)
    {
       DBNavigator->DataSource = CustomersSource;
       Set<TNavigateBtn, 0, 9> btnShow;
       btnShow << nbFirst << nbPrior << nbNext << nbLast << nbInsert << nbDelete << nbEdit << nbPost << nbCancel << nbRefresh;
       DBNavigator->VisibleButtons = btnShow;
    } else
    {
       DBNavigator->DataSource = OrdersSource;
       Set<TNavigateBtn, 0, 9> btnShow;
       btnShow << nbFirst << nbPrior << nbNext << nbLast;
       DBNavigator->VisibleButtons = btnShow;
    }
}

Листинг 5.11. Переключение значения свойства DataSource навигатора и манипуляции со свойством VisibleButtons.

ShowHint разрешает или запрещает высвечивать подсказку с названием кнопки навигатора, когда на нее наведен курсор. Значение false (устанавливается по умолчанию) запрещает подсказки для всех кнопок.

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

Рис. 5.24 показывает работу приложения, использующего все описанные возможности навигатора.

Рис. 5.24. Разделение навигатора между двумя таблицами.

5.3 Итоги

C++Builder - это система программирования общего назначения, которая может использоваться для быстрой разработки любых приложений, в том числе одних из самых сложных - Систем Управления Базами Данных.

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

Итак, проектирование формы приложения СУБД в среде C++Builder в простейшем случае требует выполнения всего лишь трех или четырех действий:

1. Перенесите на форму компоненту набора данных (TTable или TQuery) из вкладки Палитры Data Access и установите ее свойства в соответствии со специфическими требованиями выбранной базы данных.

2. Перенесите на форму компоненту источника данных TDataSource и в свойстве DataSet укажите ссылку на объект набора данных (например. Table1 или Query1).

3. Перенесите на форму нужные компоненты отображения и редактирования данных из вкладки Data Controls и в их свойстве DataSource задайте источник данных (например, DataSource1). Определите отображаемое поле набора данных в свойстве DataField.

4. Если на предыдущем шаге вы выбрали компоненту сетки TDBGrid, то используйте ее совместно с компонентой навигатора TDBNavigator.

<< Предыдущая страница