Подборка статей по С++ Builder : Создание серверов автоматизации с помощью C++Builder

Библиотека программиста на C/C++ максимум полезного - ничего лишнего
РАЗДЕЛЫ::
:: главная
:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/autosrv.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Создание серверов автоматизации с помощью C++Builder

Наталия Елманова

Что такое автоматизация

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

Отметим, однако, что программирование с помощью макроязыков имеет свои недостатки, так как не существует спецификаций, которым должны подчиняться макроязыки. Соответственно в общем случае у каждого программируемого приложения имеется свой макроязык, отличный от макроязыков других программируемых приложений (Отличием здесь являются продукты фирмы Microsoft, где в качестве макроязыка выбрано подмножество Visual Basic - Visual Basic for Applications. Прим. ред).

Более удобной реализацией программируемости настольных приложений было бы наличие в них возможности предоставлять свои сервисы другим приложениям с помощью универсального механизма, не зависящего от встроенных макроязыков и позволяющего, в частности, использовать обычные языки программирования. Именно для этой цели и предназначен механизм, называемый автоматизацией (Automation) (Ранее этот механизм назывался OLE Automation. Прим. ред). В этом случае приложение, предоставляющее тот или иной сервис, использует для этой цели интерфейсы содержащихся внутри его адресного пространства COM-объектов, и называется сервером автоматизации. Приложение, использующее сервис, называется контроллером автоматизации и может быть написано с помощью подавляющего большинства современных средств разработки. Отметим, что серверами автоматизации являются, в частности, такие популярные приложения, как Microsoft Office (Word, Excel, PowerPoint), Seagate Crystal Reports, Microsoft Internet Explorer и даже сама оболочка Windows 95/98/NT.

В общем случае клиент и сервер находятся в разных адресных пространствах, и, соответственно, для управления сервером клиент должен обращаться к методам объектов, находящихся в другом адресном пространстве. Для этой цели используется технология LRPC (Local Remote Procedure Calls - локальные вызовы удаленных процедур).

Каждый COM-сервер (каковым является сервер автоматизации) и каждый класс COM-объектов обладает уникальным 128-битовым идентификатором GIUD (Global Unique Identifier). При обращении к классам COM-объектов он иногда называется CLSID (идентификатор класса). При создании COM-серверов (в том числе и серверов автоматизации) с помощью C++Builder GUID и CLSID генерируются автоматически, хотя при необходимости можно сгенерировать их с помощью вызова стандартной функции Windows API CoCreateGUID. Информация обо всех COM-серверах и классах COM-объектов хранится в системном реестре, что позволяет клиенту "не знать", в каком каталоге (или на каком компьютере локальной сети) находится COM-сервер.

В общем случае COM-сервер представляет собой приложение, которое создает COM-объект и делает его доступным для других программ. .Сервер автоматизации предоставляет для доступа объект специального типа - dispatch object. При этом в адресном пространстве приложения-контроллера, управляющего сервером, присутствует вариантная переменная, содержащая интерфейс IDispatch, открывающий предоставляющий доступ к этому объекту на COM-сервере..

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

Подготовительный этап: создание приложения, подлежащего автоматизации

Для создания сервера автоматизации следует создать обычное приложение и затем добавить к нему подобный объектописание класса COM-объектов, создаваемых этим приложением, для осуществления доступа к функциональности, которую нужно автоматизировать.

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

Главная форма будущего сервера автоматизации

Рис. 1. Главная форма будущего сервера автоматизации

Создадим обработчики событий, связанные с нажатием на кнопки, и характерные для текстовых редакторов:

 //--------------------------------------------------------------
 #include <vcl.h>
 #pragma hdrstop

 #include "main1.h"
 //--------------------------------------------------------------
 #pragma package(smart_init)
 #pragma resource "*.dfm"
 TForm1 *Form1;
 //--------------------------------------------------------------
 __fastcall TForm1::TForm1(TComponent* Owner)
     : TForm(Owner)
 {
 }
 //--------------------------------------------------------------

 void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
 {
 Memo1->Lines->Clear();
 }
 //--------------------------------------------------------------
 void __fastcall TForm1::SpeedButton2Click(TObject *Sender)
 {
 if (OpenDialog1->Execute())
  Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
 }
 //--------------------------------------------------------------
 void __fastcall TForm1::SpeedButton3Click(TObject *Sender)
 {
 if (SaveDialog1->Execute())
  Memo1->Lines->SaveToFile(SaveDialog1->FileName);
 }
 //--------------------------------------------------------------
 void __fastcall TForm1::SpeedButton4Click(TObject *Sender)
 {
 Close();
 }
 //--------------------------------------------------------------

Сохраним проект. Отметим, что пока созданный нами текстовый редактор представляет собой обычное Windows-приложение и не является сервером автоматизации.

Превращение приложения в сервер автоматизации

Для превращения созданного нами выше приложения в сервер автоматизации выберем элемент Automation Object со страницы ActiveX репозитария объектов:

Рис. 2. Выбор Automation Object из репозитария объектов

Введем имя класса, под которым данный класс COM-объектов будет зарегистрирован в реестре.

Диалоговая панель ввода имени класса

Рис. 3. Диалоговая панель ввода имени класса

После этого мы окажемся в редакторе библиотеки типов (Type Library Editor), в котором нам предстоит определить свойства и методы созданного класса COM-объектов.

Библиотека типов вновь созданного сервера

Рис. 4. Библиотека типов вновь созданного сервера

Библиотека типов

Что представляет собой библиотека типов и зачем она нужна? По существу, библиотека типов представляет собой двоичный файл с описанием интерфейсов COM-объекта и их методов.

Обычно такие описания создаются с помощью специального языка IDL (Interface Definition Language) и используются для того, чтобы разработчики знали, как создать код, реализующий методы COM-объекта (или вообще методы объекта, расположенного за пределами адресного пространства разрабатываемого приложения, так как IDL используется не только в COM-технологии, но и в иных технологиях, реализующих вызовы удаленных процедур или функций, например, CORBA). Помимо этого, описания методов на языке IDL могут быть использованы для автоматической генерации серверного и клиентского кода (так называемого proxy-кода и stub-кода) с помощью соответствующих утилит.

С другой стороны, proxy-код и stub-код могут быть сгенерированы динамически. В этом случае клиент должен динамически получать информацию о свойствах и методах интерфейсов COM-объекта, и в этом случае наличие библиотеки типов, содержащей такую информацию, может быть весьма удобно. Отметим, что библиотеку типов можно в принципе сгенерировать на основе описания на языке IDL с помощью специального компилятора MIDL, но в данном случае в этом нет необходимости.

Итак, приступим к редактированию библиотеки типов. Предположим, что мы хотим автоматизировать загрузку файла в окно редактора, сохранение набранного текста, очистку окна редактирования, определение и изменение ширины и видимости окна. Создадим также метод, добавляющий строку к редактируемому тексту. С этой целью реализуемjопишем для нашего сервера методы FileNew, FileOpen, FileSave, AddLine и их параметры, а также свойства Width и Visible, и опишем их параметры.

Отметим, что типы данных, используемые для описания параметров методов, не совпадают с типами данных С++, так как в этом случае используются типы данных, принятые в IDL. Наиболее часто используемые типы данных языка IDL приведены в таблице 1. Таблица 1. Наиболее часто используемые типы данных языка IDL

Тип данных | Описание
short | Двухбайтовое целое число со знаком
long | Четырехбайтовое целое число со знаком
single | Четырехбайтовое действительное число
double | Восьмибайтовое действительное число
BSTR | Двоичная строка
DATE | Дата
VARIANT_BOOL | true= -1, false = 0
VARIANT | Указатель на вариантную переменную
int | Целое (размер в байтах в общем случае зависит от разрядности операционной системы)

Подробный список типов данных IDL можно найти в документации С++Builder.

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

Метод FileNew параметров не имеет. Методы FileOpen и FileSave имеют один строковый параметр типа BSTR - имя файла. Метод Addline также имеет один строковый параметр, задающий добавляемую строку. Свойство Visible имеет логический тип VARIANT_BOOL, при этом оно может быть как прочитано, так и изменено. Свойство Width имеет целый тип int (число пикселов) и также доступно как для чтения, так и для записи.

В результате библиотека типов приобретет примерно следующий вид:

Библиотека типов сервера автоматизации после описания свойств и методов объекта IDispatch

Рис. 5. Библиотека типов сервера автоматизации после описания свойств и методов объекта IDispatch

Создание кода реализации методов объекта автоматизации

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

 //--------------------------------------------------------------
 #include <vcl.h>
 #pragma hdrstop
 #include <atl\atlvcl.h>

 #include "main3.h"
 #include "main1.h"
 //--------------------------------------------------------------
 #pragma package(smart_init)

 STDMETHODIMP TMyAuto3Impl::get_Width(int* Value)
 {
   try
   {
      *Value=Form1->Width;
   }
   catch(Exception &e)
   {
     return Error(e.Message.c_str(), IID_IMyAuto3);
   }
   return S_OK;
 };
 //--------------------------------------------------------------
 STDMETHODIMP TMyAuto3Impl::set_Width(int Value)
 {
   try
   {
   Form1->Width=Value;
   }
   catch(Exception &e)
   {
     return Error(e.Message.c_str(), IID_IMyAuto3);
   }
   return S_OK;
 };
 //--------------------------------------------------------------

 STDMETHODIMP TMyAuto3Impl::get_Visible(VARIANT_BOOL* Value)
 {
   try
   {
       *Value=Form1->Visible;
   }
   catch(Exception &e)
   {
     return Error(e.Message.c_str(), IID_IMyAuto3);
   }
   return S_OK;
 };
 //--------------------------------------------------------------

 STDMETHODIMP TMyAuto3Impl::set_Visible(VARIANT_BOOL Value)
 {
   try
   {
    Form1->Visible=Value;
   }
   catch(Exception &e)
   {
     return Error(e.Message.c_str(), IID_IMyAuto3);
   }
   return S_OK;
 };
 //--------------------------------------------------------------

 STDMETHODIMP TMyAuto3Impl::NewFile()
 {
   try
   {
    Form1->Memo1->Lines->Clear();
   }
   catch(Exception &e)
   {
     return Error(e.Message.c_str(), IID_IMyAuto3);
   }
   return S_OK;
 };
 //--------------------------------------------------------------

 STDMETHODIMP TMyAuto3Impl::SaveFile(BSTR Filename)
 {
   try
   {
      Form1->Memo1->Lines->SaveToFile(Filename);
   }
   catch(Exception &e)
   {
     return Error(e.Message.c_str(), IID_IMyAuto3);
   }
   return S_OK;
 };
 //--------------------------------------------------------------

 STDMETHODIMP TMyAuto3Impl::AddLine(BSTR Line)
 {
   try
   {
     Form1->Memo1->Lines->Add(Line);
   }
   catch(Exception &e)
   {
     return Error(e.Message.c_str(), IID_IMyAuto3);
   }
   return S_OK;
 };
 //--------------------------------------------------------------

 STDMETHODIMP TMyAuto3Impl::OpenFile(BSTR Filename)
 {
   try
   {
 Form1->Memo1->Lines->LoadFromFile(Filename);
   }
   catch(Exception &e)
   {
     return Error(e.Message.c_str(), IID_IMyAuto3);
   }
   return S_OK;
 };
 //--------------------------------------------------------------

Скомпилируем и запустим сервер на выполнение. При этом он зарегистрируется в реестре.

Запись о сервере автоматизации в реестре Windows

Рис. 6. Запись о сервере автоматизации в реестре Windows

В действительности в разделах реестра HKEY_LOCAL_MASHINE\SOFTWARE и HKEY_CLASSES_ROOT содержится несколько записей, связанных с данным сервером и его интерфейсами, в том числе и информация о местоположении сервера.

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

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

к списку

Разработка: Улитин Дмитрий


Подборка статей по С++ Builder : Borland C++Builder 3: новые возможности создания корпоративных информационных систем

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/brl3.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Borland C++Builder 3: новые возможности создания корпоративных информационных систем.

Наталия Елманова

Средства создания многозвенных систем
Дополительные инструменты
BDE Administrator
SQL Explorer
SQL BuilderM
Компоненты для создания систем принятия решений
Возможности публикации данных в Internet

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

Появления новой версии разработчики ждали давно - с момента выхода Delphi 3, обладавшей набором интересных и полезных возможностей, отсутствовавших в первой версии C++Builder. Теперь можно с уверенностью сказать, что новый С++Builder оправдал ожидания разработчиков: он не только предоставляет те же возможности, что и Delphi 3, но и довольно широкий спектр дополнительных возможностей, пользователям Delphi пока неизвестных. В данной статье будут рассмотрены наиболее интересные особенности C++Builder 3.0, связанные с созданием информационных систем.

Средства создания многозвенных систем.

Говоря о С++Builder как об инструменте создания корпоративных информационных систем, в том числе ИС масштаба предприятия, следует остановиться на такой замечательной возможности, как средства создания серверов приложений и "тонких" клиентов. В комплект поставки версии C++Builder Client/Server Suite входит MIDAS Development Kit, представляющий собой набор компонентов и брокеров middleware, позволяющих создавать и отлаживать серверы приложений и использующие их клиентские приложения. Иными словами, С++Builder, наравне с Delphi, теперь также поддерживает MIDAS.

Простейший MIDAS-ориентированный сервер приложений можно создать буквально за несколько минут. Для этого достаточно выбрать из репозитория объектов C++Builder пиктограмму Remote DataModule и в удаленный модуль данных поместить какой-либо компонент доступа к данным (например, TTable или TQuery), установить значения его свойств DatabaseName и TableName (или SQL), установить значение его свойства Active равным true. Затем нужно экспортировать его из модуля данных (рис….), выбрав соответствующую опцию контекстного меню, а затем скомпилировать и выполнить созданное приложение, чтобы зарегистрировать его в реестре . Далее следует запустить приложение scktsrvr.exe (Borland MIDAS Socket Server), чтобы сервер приложений мог быть запущен удаленно по протоколу TCP/IP.

Библиотека типов, связанная с удаленным модулем данных

Рис.1. Библиотека типов, связанная с удаленным модулем данных

Теперь можно создать клиентское приложение (причем не обязательно на том же самом компьютере - главное, чтобы компьютер с сервером приложений был доступен по протоколу TCP/IP), поместив на форму компоненты TMidasConnection, TClientDataSet, связанный с ним компонент TDataSource и связанные с ним интерфейсные элементы для отображения данных (например, TDBGrid). У компонента TMidasConnection следует установить значение свойства ComputerName равным IP-адресу компьютера, на котором находится сервер приложений, свойства ServerName равным имени сервера приложений (в формате <имя исполняемого файла>.<имя OLE-сервера>), а свойства ConnectType равным TCP/IP. Затем свойство Connected можно установить равным true, и сервер приложений после этого должен запуститься удаленно. Теперь можно установить значение свойства RemoteServer компонента TClientDataSet равным имени компонента TMidasConnection, а также значение свойства ProviderName равным имени экспортированного объекта из удаленного модуля данных сервера приложений. Осталось установить свойство Active этого компонента равным true, вызвать в каком-нибудь обработчике событий метод ApplyUpdates компонента TClientDataSet - и простейший "тонкий" клиент готов!

Рис.2. "Тонкий" клиент

Следует отметить, что в C++Builder, так же как и в Delphi, имеется возможность создания активных форм - для этой цели используется библиотека Active Template Library. Этим можно воспользоваться для создания "тонкого" клиента, отображаемого в броузере, и не требующего предварительной установки, наcтройки и конфигурации, но имеющего, в отличие от Web-приложений типа CGI или ISAPI, полноценный Windows-интерфейс.

"Тонкий" клиент в виде ActiveX

Рис.3. "Тонкий" клиент в виде ActiveX

Что дает использование трехзвенных информационных систем? Об их преимуществах сказано уже немало. Во-первых, "тонкий" клиент, подобный рассмотренному выше, не требует наличия на рабочей станции ни клиентской части серверной СУБД, ни библиотеки Borland Database Engine, и, соответственно, не требует конфигурации клиентской части и настройки BDE. Единственное, что требуется на рабочей станции - наличие библиотеки dbclient.dll размером 140 Кб, которую можно поставлять вместе с клиентским приложением. Во-вторых, для такого клиентского приложения требуется меньше ресурсов, чем для классического "толстого" клиента. В-третьих, вместо создания дистрибутива достаточно скопировать на рабочую станцию исполняемый файл и dbclient.dll (или загрузить в Internet Explorer web-страницу с компонентом ActiveX). Таким образом, налицо прямое снижение затрат, связанных с конфигурацией рабочих станций и ресурсоемкостью клиентских приложений.

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

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

Отметим, однако, что MIDAS Development Kit позволяет лишь создавать и отлаживать серверы приложений. Входящий в его состав ConstraintBroker обладает ограниченным временем действия, а Business ObjectBroker обладает ограничением на число экспортируемых OLE-объектов. Только полнофункциональная версия MIDAS позволяет использовать эти брокеры без подобных ограничений (и не нарушать при этом лицензионные соглашения).

Отметим, что возможности создания многозвенных систем ограничиваются не только MIDAS-ориентированными серверами. Наряду с версией C++Builder Client/Server Suite имеется версия C++Builder Enterprise, позволяющая создавать "тонкие" клиентские приложения для сервера приложений Borland Entera c помощью компонентов TEnteraConnection и TEnteraProvider. Отметим, что Entera существует для большого количества платформ, что позволяет создавать многоуровневые системы в гетерогенной среде, где не только серверы баз данных, но и серверы приложений функционируют на платформах, отличных от Windows (например, на UNIX-платформах), что позволяет выбрать наиболее оптимальное для предприятия сочетание удобства, масштабируемости и надежности.

Дополительные инструменты

BDE Administrator

В комплект поставки C++Builder теперь входит новая версия утилиты конфигурации BDE - BDE Administrator, устанавливающаяся теперь в панель управления Windows. Эта утилита обладает более удобным интерфейсом и некоторыми полезными возможностями по сравнению с утилитой конфигурации BDE из прежней версии C++Builder. Во-первых, в процессе конфигурации доступа к серверным СУБД она позволяет вызвать утилиты из состава клиентской части серверной СУБД, например, консольные приложения для интерактивной работы с SQL, такие, как SQL Plus из клиентской части Oracle 7 или Interactive SQL из клиентской части IB Database, утилиты для установки и конфигурации клиентской части, ODBC-администратор и т.д.. Во-вторых, эта утилита считывает конфигурационые файлы клиентских частей серверных СУБД (такие, как TNSNAMES.ORA для Oracle 7) и при выборе серверной СУБД предлагает список доступных баз данных из этих файлов. В-третьих, эта утилита позволяет проверить корректность настроек псевдонимов путем установки соединения с базой данных и не требует запуска для этой цели других приложений, как это было в прежней версии С++Builder.

Рис.4. BDE Administrator

SQL Explorer

Значительные изменения по сравнению с прежней версией претерпела и утилита SQL Explorer. Теперь с ее помощью можно не только просматривать, но и создавать и редактировать объекты серверных СУБД, такие как таблицы, индексы, триггеры, хранимые процедуры, серверные ограничения (Constraints), представления (Views), табличные области и другие объекты серверных СУБД. Фактически новая версия SQL Explorer представляет собой удобную и простую утилиту администрирования одновременно всех имеющихся в доступности баз данных, что по достоинству могут оценить менеджеры информационных систем, содержащих несколько различных СУБД, например, одновременно Oracle, IB Database и таблицы Paradox.

Рис. 5. SQL Explorer

SQL Builder

В C++Builder 3.0 включено новое средство построения SQL-запросов - SQL Builder, заменивший Visual Query Builder, входивший в комплект поставки прежней версии, а также всех версий Delphi.

Рис. 6. SQL Builder

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

Редактор текста запросов SQL Builder

Рис. 7. Редактор текста запросов SQL Builder

Отметим также, что возможно выполнение созданного запроса непосредственно из SQL Builder, что позволяет проверить правильность его построения.

Компоненты для создания систем принятия решений.

Среди компонентов, связанных с использованием баз данных, появилась группа Decision Cube для создания систем поддержки принятия решений. Эти компоненты применяются при создании приложений, используемых менеджерами высшего звена и предназначенных для многомерного анализа данных. Decision Cube представляет собой набор многомерных кросс-таблиц, содержащих агрегатные данные, вычисленные на основе какого-либо запроса. Компонент TDecisionGrid позволяет просматривать различные двумерные и трехмерные сечения такого куба как на этапе проектирования приложения, так и на этапе выполнения, а компонент TDecisionGraph - создать диаграмму, соответствующую текущему сечению.

Рис.8. Decision Cube

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

Редактор свойств компонента TDecisionQuery

Рис.9. Редактор свойств компонента TDecisionQuery

Для обмена многомерными данными между TDecisionQuery и компонентами отображения многомерных данных TDecisionGraph и TDecisionGrid используется компонент TDecisionSource - аналог компонента TDataSource.

Управлять отображением сечений можно с помощью компонента TDecisionPivot либо путем "сворачивания" и "разворачивания" размерностей непосредственно в компоненте TDecisionGrid.

Управление отображением сечений

Рис.10. Управление отображением сечений

Возможности публикации данных в Internet.

Для создания web-приложений можно, как известно, использовать абсолютно любое средство разработки, лишь бы полученное приложение было способно выполняться в той операционной системе, под управлением которой функционирует web-сервер. Поэтому, говоря о создании таких приложений, было бы преувеличением сказать, что до появления C++Builder 3 такой возможности не было - создать, например, динамически загружаемую библиотеку, являющуюся расширением Internet Information Server или Netscape Fast Track (т.е. web-приложение в стандарте ISAPI/NSAPI) с успехом можно было и с помощью C++Builder 1.0, а CGI-приложения, как показывает опыт российских разработчиков, без особых проблем создаются с помощью Clipper, FoxPro и даже с помощью обычных командных файлов. Однако такие приложения так или иначе требуют написания некоторого количества кода, и вопрос удобства того или иного средства разработки так или иначе связан с желанием минимизировать рутинную работу, связанную с процессом "ручного" кодирования. И в этом отношении C++Builder 3.0 является весьма удобным средством разработки, так как обладает неплохими возможностями визуального проектирования подобного рода приложений.

Среди доступных разработчику средств визуальной разработки web-приложений хотелось бы особо отметить средства, позволяющие осуществлять публикацию данных в Internet. Для этой цели используются компоненты TDataSetTableProducer и TDataSetQueryTableProducer, получающие данные из компонента TTable или TQuery и представляющие их в виде HTML-документа в табличном виде (рис. 11).

Редактор свойств компонента TDataSetTableProducer

Рис.11. Редактор свойств компонента TDataSetTableProducer

Пример подобной публикации данных в Internet, созданный сотрудниками Борланд АО (прайс-лист, передаваемый в броузер с помощью динамически загружаемой библиотеки в стандарте NSAPI), можно найти по адресу http://www.borland.ru.

Отметим также, что сама возможность размещения в объекте TWebModule компонентов доступа к данным существенно облегчает создание CGI-приложений, связанных как с публикацией данных, так и с обработкой пользовательского ввода (например, в HTML-формах), заключающегося в изменении или добавлении данных. Типичными примерами таких приложений являются анкеты, которые в изобилии встречаются на многих web-серверах.

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

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

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


Подборка статей по С++ Builder : Поставка приложений, созданных с помощью С++ Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/builder.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Поставка приложений, созданных с помощью С++ Builder

Наталия Елманова

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

  • Поставка исполняемых файлов
  • Установка и настройка Borland Database Engine
  • Установка дополнительных компонентов приложения
  • Способы создания дистрибутивов
  • Создание дистрибутивов с помощью InstallShield Express

Поставка исполняемых файлов

При окончательной сборке исполняемого файла (то есть непосредственно перед созданием дистрибутива) следует убрать из исполняемого файла отладочную информацию. Для этого следует из менеджера проектов вызвать диалоговую панель Project Options и нажать кнопку Release.

Рис. 1. Установка параметров компилятора C++ при окончательной сборке приложения.

Если в проекте используются модули и формы Delphi, следует отключить все опции в секции Debugging и включить опцию Optimization в секции Code Generation на странице Pascal.

В той же диалоговой панели следует выбрать пиктограмму приложения (страница Application). Eе можно создать с помощью входящего в комплект поставки С++ Builder графического редактора, который можно запустить, выбрав пункт меню Tools/image Editor. Помимо этого, следует указать название приложения (то есть строку, которая будет отображаться в панели задач Windows 95) и имя файла справки, в котором содержатся экраны контекстно-зависимой помощи. Более подробно о создании контекстно-зависимой помощи для приложений С++ Builder можно узнать в книге "Введение в Borland C++ Builder" (Н.Елманова, С.Кошель. - М.: Диалог-МИФИ, 1997).

Рис. 2. Установка названия, пиктограммы и имени файла справки.

Если ссылка на справочный файл присутствует в опциях проекта, можно связать экраны помощи с интерфейсными элементами приложения. Для этой цели следует установить значения свойства HelpContext интерфейсных элементов приложения равным номерам разделов соответствующих экранов помощи из секции [MAP] файла проекта справки. В этом случае после компиляции приложения нажатие на клавишу F1 будет выводить на экран окно справочной системы с разделом справки, описывающим интерфейсный элемент, имеющий фокус ввода.

Установка и настройка Borland Database Engine

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

Если приложение использует базы данных, следует, помимо приложения, установить на компьютер пользователя библиотеку Borland Database Engine. Установка этой библиотеки заключается в копировании файлов этой библиотеки на компьютер пользователя, внесении сведений о ней в реестр (раздел HKEY_LOCAL_MACHINE//SOFTWARE//Borland//Database Engine), установке пиктограммы для утилиты конфигурации BDE, а также настройке псевдонимов, необходимых для корректной работы данного приложения.

В процессе установки BDE могут возникнуть следующие проблемы. Во-первых, у пользователя на компьютере могут быть установлены другие приложения, использующие эту библиотеку. Если скопировать на такой компьютер файл конфигурации BDE поверх имеющегося, работоспособность этих приложений может быть нарушена из-за того, что исчезнут описания псевдонимов, используемых ими. Поэтому при добавлении своих псевдонимов лучше пользоваться опцией File/Merge утилиты конфигурации BDE или создать псевдонимы вручную.

Можно посмотреть на эту проблему с другой стороны. Как избежать потери работоспособности вашего приложения, если другой программист установит свой файл конфигурации BDE поверх установленного вами? Возможное решение этой проблемы - создавать псевдонимы динамически во время работы приложения или анализировать их существование при запуске и создавать их, если по каким-то причинам они исчезли. Многие разработчики хранят информацию о них в локальных ini-файлах вне IDAPI32.CFG.

Вторая проблема может быть связана с несовпадением версий BDE. Если с момента выхода используемой вами версии BDE прошло какое-то время (а на данный момент после C++ Builder появились такие продукты, как Delphi 3.0 и IntraBuilder 1.5, содержащие более новую версию BDE, чем С++ Builder), есть риск заменить старой версией BDE более новую, если на компьютере пользователя установлены эти средства разработки или созданные с их помощью приложения, что может нарушить их работоспособность.

Чтобы избежать подобных неприятностей, можно воспользоваться одной недокументированной особенностью BDE. Дело в том, что время создания файлов этой библиотеки совпадает с номером версии BDE, чем и можно воспользоваться. Путь к файлам BDE следует искать в реестре Windows (ключ HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Database Engine, параметры CONFIGFILE01 и DLLPATH).

Рис. 3. Сведения о BDE в реестре Windows 95

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

Отметим, что следует также установить на компьютер пользователя необходимые драйверы SQL Links для доступа к серверным СУБД, если они используются, а также клиентское программное обеспечение для работы с соответствующей серверной СУБД (например, SQL*Net для Oracle). Кроме того, пользователь должен обладать необходимыми привилегиями для доступа к используемым вашим приложением объектам базы данных - таблицам, хранимым процедурам и др. Если вы готовите и серверную часть приложения, следует предоставить администратору базы данных так называемый DDL-сценарий серверной части (DDL - Data Definition Language), представляющий собой сценарий создания объектов базы данных, написанный на SQL либо на его процедурном расширении, характерном для данного сервера баз данных. Подобный сценарий может быть создан вручную либо с помощью какого-либо CASE-средства типа ERwin компании Logic Works (CASE, что расшифровывается как Computer-Aided Software/System Engineering, представляет собой технологию проектирования БД, основанную на графическом описании сущностей, атрибутов и связей между ними).

Если ваше приложение использует ODBC, следует установить соответствующий ODBC-драйвер и, возможно, 32-разрядный ODBC-администратор - он в общем случае не обязан присутствовать на компьютере пользователя.

Установка дополнительных компонентов приложения

Если ваше приложение использует ActiveX-компоненты, следует установить в каталог Windows\System соответствующий файл *.OCX и внести в реестр соответствующие ветви (HKEY_CLASS_ROOT\CLSID\....) для регистрации соответствующих OLE-серверов.

Если ваше приложение использует какие-либо продукты третьих фирм (например, Run-time-версии генераторов отчетов сторонних производителей, например, Crystal Reports), следует установить их на компьютер пользователя в соответствии с инструкциями по поставке, предоставленными компанией-производителем.

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

Самый простой способ - не использовать экзотических шрифтов. Шрифты типа Arial, Courier и Times New Roman, как правило, есть на всех компьютерах. Если же такой уверенности нет, можно либо предложить пользователю выбирать шрифты для приложения при его запуске и сохранять настройки в файлах (это неудобно, так как при этом надписи могут не уместиться на кнопках, метки могут "наехать" друг на друга и т.д.), либо инсталлировать нужные шрифты вместе с приложением. В случае Windows 95 достаточно скопировать их в каталог Fonts. Можно также присвоить "своим" шрифтам уникальные нестандартные имена, чтобы случайно не заменить ими шрифты, используемые другими приложениями (заменив в англоязычной версии Windows шрифт Arial его русскоязычной версией, вы в некоторых случаях можете лишить вашего пользователя возможности писать в текстовых процессорах по-французски или по-немецки этим шрифтом).

Способы создания дистрибутивов

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

Во-первых, оно, как следует из вышеизложенного, копирует файлы (*.exe, *.ocx, *.dll, *.hlp, шрифты, данные и др.) в соответствующие каталоги.

Во-вторых, оно может модифицировать реестр.

В-третьих, оно может модифицировать переменную окружения PATH.

В-четвертых, оно создает программную группу и пиктограммы, а также модифицирует меню Windows-95 или Windows NT.

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

В-шестых, оно может при необходимости перезапустить Windows.

Как создать инсталляционное приложение? Существует несколько вариантов решения этой проблемы. Во-первых, его можно написать на C++ Builder или Delphi, используя функции Windows API для модификации реестра согласно алгоритму, изложенному выше. Подробности создания таких инсталляционных программ с помощью Delphi 2.0 описаны, например, в статье Алексея Федорова "Delphi: сделай инсталлятор сам" (Компьютер-Пресс, 1996, N 9, стр.46-50), и приведенные в этой работе примеры легко переносятся с Object Pascal на C++. Во-вторых, такое приложение можно создать с использованием генераторов дистрибутивов (InstallShield, Wise и др.). Отметим, что использование этих средств обычно подразумевает написание специализированного скрипта на языке, напоминающем С (в случае InstalShield) или Basic (в случае Wise).

Однако самый простой способ создать инсталляционное приложение - использовать входящий в комплект поставки версий Borland C++ Builder версий Professional и Client/Server Suite упрощенный генератор дистрибутивов Install Shield Express, позволяющий предоставить пользователю возможность выбора варианта инсталляции, добавить деинсталлятор (точнее, файл с расширением *.isu, используемый утилитой установки и удаления программ Windows 95), установить BDE, добавить псевдонимы баз данных, редактировать диалоги инсталляционного приложения, создавать программные группы, создавать дистрибутивные дискеты.

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

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

Создание дистрибутивов с помощью InstallShield Express

При запуске Install Shield Express появляется окно с радиогруппой, предлагающее открыть существующий проект или создать новый.

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

Рис. 4. Создание нового дистрибутива

Флажок "Include custom setup type" следует выбрать, если вы планируете предоставить пользователю возможность выбора варианта инсталляции Custom Setup для самостоятельного выбора варианта инсталляции. После этого появится главный экран InstallShield, в котором перечислена последовательность шагов, которые следует выполнить для создания дистрибутива (рис. 5).

Рис. 5. Главный экран InstallShield Express

В разделе Set the Visual Design следует заполнить поля со сведениями о приложении: название приложения, как оно будет выглядеть в программной группе, имя исполняемого файла, каталог, в который следует установить приложение (рис. 6)

Рис. 6. Установка параметров приложения

Отметим, что Install Shield содержит несколько переменных, идентифицирующих диски и каталоги компьютера пользователя, что позволяет не знать реальную систему каталогов и дисков этого компьютера:

  • <INSTALLDIR> - каталог для установки, указанный пользователем,
  • <WINDIR> - каталог, в котором содержится Windows
  • <WINSYSDIR> - каталог Windows\System
  • <WINDISK> - диск, на котором содержится Windows
  • <WINSYSDISK> - диск, на котором содержится каталог Windows\System
  • <ProgramFilesDir> - каталог Program Files.

Щелкнув на закладке Main Window, можно установить заголовок инсталляционного приложения, цвет фона и логотип (рис.7).

Рис. 7. Установка внешнего вида экрана инсталлятора

Щелкнув на закладке Features, можно обнаружить одну-единственную опцию - Automatic Uninstaller. Ее рекомендуется оставить выбранной.

Следующий раздел - Specify InstallShield Options for Borland C++ - предназначен для выбора компонентов, часто поставляемых с приложениями: BDE, SQL Links и др. Выбрав нужный компонент (в нашем случае BDE) и нажав кнопку Settings, получим последовательность из четырех диалоговых окон для выбора частей BDE, поставляемых с данным приложением (рис. 8 ), и создания псевдонимов.

Рис. 8. Определение устанавливаемых частей BDE

При установке параметров псевдонимов можно выбрать тип псевдонима и местоположение данных. Остальные параметры псевдонима (в том числе и языковые драйверы) можно указать в текстовом редакторе в нижней части окна (рис. 9):

Рис. 9. Установка параметров псевдонимов BDE

Щелкнув по закладке Advanced Options, можно ознакомиться со списком файлов дополнительных компонентов (в нашем случае BDE) и сведениями о них.

Следующий раздел - Specify Components and Files - предназначен для определения групп файлов, компонентов приложения и типов установки. Щелкнув на закладке Groups, можно создать группы файлов для создания из них компонентов дистрибутива. Можно также использовать Explorer и переносить из него нужные файлы методом drag-and-drop (для этого нужно нажать кнопку Launch Explorer). Не рекомендуется оставлять группы пустыми.

Рис. 10. Создание групп файлов

Щелкнув на закладке Components, можно определить компоненты дистрибутива (их сможет выбирать пользователь в случае выбора варианта инсталляции Custom, поэтому можно дать им русскоязычные названия) и указать, из каких групп файлов они состоят. Не следует оставлять компоненты, не содержащие групп.

Рис. 11. Создание компонентов дистрибутива

Следующая закладка - Setup Types - предназначена для определения вариантов установки. Если щелкнуть на ней, может оказаться, что вариант инсталляции его один - Typical (например, вы забыли отметить флажок "Include custom setup type"). В этом случае следует выбрать раздел Select User Interface Components и, щелкнув на закладке Components, в списке Dialog Boxes отметить опции Setup Type и Custom Setup. После этого можно вернуться к закладке Setup Types и определить варианты инсталляции. Как правило, варианты Custom и Typical содержат все возможные компоненты, а вариант Compact - минимальный набор компонентов, пригодный для нормальной работы приложения.

Рис. 12. Определение состава вариантов инсталляции

В разделе Select User Interface Components можно выбрать диалоги, в которых пользователь вводит необходимую информацию во время инсталляции (например, сведения о себе и компании, серийный номер продукта), знакомится с лицензионным соглашением и файлам readme, указывает каталог для инсталляции, выбирает тип установки и т.д. Возможен также предварительный просмотр диалогов при нажатии кнопки Preview.

Следующий раздел - Make Registry Changes - позволяет создавать на компьютере пользователя новые ключи реестра (закладка Keys) и значения ключей (Values). Это может оказаться полезным, если вы используете в вашем приложении компоненты ActiveX или создаете OLE-сервер. Ключи и их значения можно копировать из редактора реестра, если он поддерживает такую опреацию.

Рис.13. Определение ключей и значений реестра компьютера пользователя.

Следующий раздел - Specify Folders and Icons - позволяет определить состав будущей программной группы, а также определить параметры командной строки (закладка Advanced).

Рис. 14. Определение состава программной группы.

Наконец, последний раздел - Run Disk Builder. После сохранения инсталляционного скрипта (с помощью нажатия на кнопку с изображением дискеты на панели инструментов главного окна InstallShield) и выбора типа носителей происходит создание на жестком диске образов дистрибутивных дискет. Выбрав затем раздел Create Distribution Media, можно записать на дискеты созданный дистрибутив.

Рис. 15. Создание образов дискет.

Опцию Test Run можно использовать для проверки работы инсталляционного приложения. Однако не рекомендуется делать это на компьютере, где производится разработка приложений. Лучше провести тестовые испытания на компьютере, похожем на компьютеры ваших пользователей. Кроме того, рекомендуется создать на этом компьютере копию Windows, чтобы в случае некорректной работы инсталлятора можно было вернуть программное обеспечение в исходное состояние.

Запуск инсталляционной программы приводит к последовательному появлению выбранных в разделе Select User Interface Components диалогов, подобных изображенному на рис. 16.

Рис. 16. Так выглядит один из диалогов программы установки

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

Рис. 17. Результат работы инсталляционного приложения.

Если при создании дистрибутива вы выбрали опцию Automatic Uninstaller, то в случае возникновения необходимости деинсталляции установленного приложения следует использовать утилиту "Установка и удаление программ" в панели управления Windows.

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

В заключение отметим, что поставка приложений, созданных с помощью Delphi 2.0 и Delphi 3.0, осуществляется практически точно так же, как и поставка приложений, созданных с помощью C++ Builder.


Подборка статей по С++ Builder : О проектировании данных для клиент-серверных приложений С++Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/builder_ap.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

О проектировании данных для клиент-серверных приложений С++Builder

Наталия Елманова
"Компьютер-Пресс", 1998, N 2

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

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

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

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

Не вдаваясь в подробности теории реляционных СУБД (этой теме посвящено немало книг и статей), перечислим некоторые термины, применяющиеся при проектировании, и основные принципы проектирования данных.

  • Терминология, используемая при проектировании баз данных
  • Нормализация данных
  • Пример проектирования простейшей информационной системы
  • Создание автоматизированных рабочих мест
  • О генерации уникальных первичных ключей
  • Некоторые выводы

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

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

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

Атрибут - это характеристика сущности. Атрибут выражает одно законченное и определенное свойство сущности (например, дату рождения пациента). При проектировании данных рекомендуется создавать атомарные атрибуты (например, выделение страны и города в отдельные атрибуты при описании адреса в дальнейшем позволит анализировать экземпляры сущности "Пациент" по признаку принадлежности к той или иной стране или тому или иному городу).

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

Связи обладают свойством, называемым кардинальностью. Например: "Заказчик может иметь 0,1 или много заказов" (связь типа "0,1 или много"), "заказ содержит 1 или много товаров" (связь типа "1 или много"), "у автомобиля ровно 4 колеса" (связь типа "ровно n"), "билет резервируется для 0 или 1 пассажира (связь типа "0 или 1"). Наиболее типичным является первый случай, называемый иногда связью "один ко многим".

При логическом проектировании нередко также создаются связи "многие ко многим". Типичный пример такой связи - связь врач-пациент, когда любой пациент может лечиться у любого врача. В случае такой связи в общем случае невозможно определить, какой экземпляр одной сущности соответствует выбранному экземпляру другой сущности, что делает неосуществимой физическую (на уровне индексов и триггеров) реализацию такой связи между соответствующими таблицами. Поэтому перед переходом к физической модели все связи "многие ко многим" должны быть переопределены (некоторые CASE-средства, если таковые используются при проектировании данных, делают это автоматически). Обычно для этой цели вводится промежуточная сущность, обладающая ассоциативными атрибутами. В нашем примере объектом, представляющим собой экземпляр подобной сущности, может являться посещение пациентом врача (то есть реальное событие), либо регистрирующий его документ - статистический талон), а ассоциативными атрибутами, отличающими по существу один ее экземпляр от другого, могут быть дата посещения, цель посещения и иные содержащиеся в статистическом талоне сведения.

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

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

При создании связей между сущностями (например, "один ко многим") в дочернюю сущность передаются атрибуты, составляющие первичный ключ в родительской сущности. Эти атрибуты образуют в дочерней сущности внешний ключ.

Нормализация данных

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

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

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

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

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

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

Пример проектирования простейшей информационной системы

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

Целью данной информационной системы является хранение данных о посещениях пациентами поликлиники с целью дальнейшей статистической обработки, или, иными словами, регистрация статистических талонов, содержащих набор сведений, сходный с представленным на рис.1

Рис.1. Примерное содержание статистического талона

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

Рис.2. Первый вариант модели данных - выделяем сущности "Врач" и "Пациент"

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

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

Рис.3. Модель данных после нормализации.

Возникает вопрос: а как быть с остальными атрибутами этой сущности? Ведь очевидно, что атрибуты "Ставка" и "Категория" тоже повторяются. Однако атрибут "Категория" - это обычно целое число, и поэтому нет смысла выносить его в отдельный справочник. Что касается атрибута "Ставка", характеризующего продолжительность рабочего времени, то это обычно дробное число (например, 1.25). В принципе можно было бы вынести его в отдельный справочник, но из соображений разумной достаточности в данном примере мы этого делать не будем (нормализуя модель, можно дойти до такого состояния, когда выполнение запросов к таблицам окажется весьма длительным из-за их большого числа).

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

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

Как уже отмечалось в данном цикле статей, использование серверной СУБД значительно облегчает разработку информационных систем по сравнению с использованием плоских таблиц типа dBase или Paradox, так как в последнем случае реализация правил ссылочной целостности, а также механизмов авторизации пользователя и методов ограничения доступа к данным может быть реализована только в приложении (фактически разработчик вынужден "изобретать велосипед при наличии самолетов", создавая в своих приложениях код, уже реализованный в серверах баз данных). Поэтому для реализации данной информационной системы воспользуемся какой-либо серверной СУБД (например, Oracle Workgroup Server 7.2 for Windows NT).Для создания физической модели выберем соответствующую платформу в используемом CASE-средстве и опишем характеристики таблиц, соответствующих описанным в логической модели сущностям, и содержащихся в них полей, соответствующих их атрибутам (рис.4)

Рис.4. Физическая модель данных

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

Рис.5. Описание реакции сервера на попытки нарушения ссылочной целостности данных

После этого можно создать на сервере схему базы данных, либо сгенерировать DDL-сценарий создания базы данных, представляющий собой набор предложений на процедурном расширении языка SQL данной серверной СУБД) с целью его последующего выполнения. В результате выполнения DDL-сценария на сервере должны быть созданы пустые таблицы, а также иные объекты базы данных, которые можно просмотреть с помощью утилиты Database Explorer (рис.6)

Рис.6. Созданные на сервере объекты базы данных

Создание автоматизированных рабочих мест

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

Сколько и каких автоматизированных рабочих мест должно быть создано, обычно решается на этапе проектирования данных. В данном простейшем случае очевидно, что работа по вводу данных в информационную систему подразделяется на три основных дочерних работы: ввод сведений о врачах поликлиники (это обычно делается в отделе кадров, и соответственно следует создать АРМ инспектора отдела кадров), ввод сведений о пациентах (рабочее место медрегистратора), ввод сведений о статистических талонах (рабочее место медстатистика). Помимо этого, кто-то должен осуществлять заполнение справочников "Цель посещения" и "Статус" (можно добавить эти функции в АРМ медстатистика, так как объем этих таблиц невелик). Что касается списка диагнозов, он может быть либо введен в базу данных кем-либо из персонала поликлиники, либо импортирован администратором базы данных из какого-либо другого формата, так как этот список основан на реально существующем и неоднократно опубликованном документе.

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

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

Первые два АРМ-а создать относительно несложно (одно из них предназначено для ввода данных в одну таблицу, другое - в две таблицы, связанные соотношением "один ко многим"). Для этой цели можно воспользоваться утилитой Database Form Wizard и затем внести небольшие изменения в сгенерированные формы и код, в частности, для обеспечения возможности не использовать мышь при массовом вводе данных, а также для установки фокуса ввода в первое поле формы при создании новой записи (в обработчике события OnNewRecord соответствующего компонента TTable). Кроме того, следует исключить возможность ввода пользователем значения поля PROF_ID в список врачей - в этом случае значение этого поля генерируется автоматически в соответствии со значением в выбранной строке списка специальностей, что исключает попытки создания некорректной записи уже на уровне клиентского приложения (рис. 7,8)

Рис. 7. Автоматизированное рабочее место медрегистратора

Рис. 8. Автоматизированное рабочее место инспектора отдела кадров.

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

Рис.9. Автоматизированное рабочее место медстатистика - ввод справочных таблиц

Используя серверную СУБД и сгенерировав базу данных с учетом правил ссылочной целостности в соответствии со схемой данных, представленной на рис.4, мы, конечно, можем быть уверены, что ввести в базу данных некорректные значения полей DOCT_ID, DIAG_ID, STAT_ID, PURP_ID, PAT_ID сервер пользователю не даст. Однако это вовсе не означает, что можно поместить на форму набор компонентов TDBEdit для ввода данных в эти поля. Такая реализация интерфейса приведет к совершенно неприемлемым условиям труда пользователя, вводящего данные - он должен помнить наизусть все табельные номера, номера карт и коды во вспомогательных таблицах и в случае ошибок сталкиваться с сообщениями, сгенерированными триггерами сервера. Более разумно использовать выбор нужного значения из списка, поэтому для ввода данных в эти поля поместим на форму компоненты TDBLookupCombBox и выберем в качестве значения ListSource источники данных, связанные с соответствующими словарными таблицами, в качестве значения КеуField - имена их первичных ключей, а в качестве ListField - имена полей, в которых хранятся соответствующие названия статусов, диагнозов, имена врачей и др. Теперь пользователь может выбирать значения полей DOCT_ID, DIAG_ID, STAT_ID, PURP_ID, PAT_ID из выпадающих списков, в которых содержатся не коды, а символьные строки (рис 10). Таким образом, мы избавились от необходимости непосредственного ввода пользователем значений внешних ключей, заменив его выбором из соответствующих списков.

Рис.10. Автоматизированное рабочее место медстатистика - ввод статистических талонов.

Такая реализация автоматизированного рабочего места, разумеется, не является идеальной. Если список статусов и целей посещения обычно действительно невелик, и выпадающий список здесь вполне уместен, то список пациентов, скорее всего, будет составлять не меньше сотни строк. Поэтому вполне уместно либо иметь этот список в отсортированном по алфавиту виде (например, создав соответствующий индекс в базе данных, либо используя в качестве источника данных компонент TQuery с соответствующим предложением Order by). В этом случае при наборе начала фамилии пациента указатель в списке автоматически установится на ближайшую подходящую позицию. Примерно так же можно поступить и со списком диагнозов. Возможен также ввод каких-либо признаков (например, специальности врача или типа заболевания) и дальнейший выбор диагноза или фамилии врача из отфильтрованного списка, причем такая фильтрация записей может быть реализована как с помощью параметризованного запроса, выполняемого на сервере, так и с помощью локального фильтра в клиентском приложении. Какую из реализаций выбрать, зависит от размера справочной таблицы - локальные фильтры могут быть более производительны, чем параметризованный запрос, если таблица целиком кэшируется на рабочей станции, то есть ее объем сравним с емкостью буферов для кэширования данных.

Отметим также, что в списке пациентов могут быть полные тезки. В этом случае следует создать в списке пациентов вычисляемое поле (например, включающее фамилию, имя , отчество и дату рождения пациента) и использовать его для поиска.

О генерации уникальных первичных ключей

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

Проблема подобного рода может быть решена с помощью генерации уникальных первичных ключей как в клиентском приложении, так и на сервере. В приложении генерация первичного ключа осуществляется, например, путем создания длинной случайной символьной строки с помощью какого-либо генератора, использующего в качестве параметров время с точностью до долей секунды и какие-либо уникальные характеристики рабочей станции так, чтобы вероятность повторов была чрезвычайно мала. Этот способ обычно используется с теми СУБД, которые сами не поддерживают возможность генерации таких ключей (например, dBase). Он неудобен, так как при наличии нескольких различных приложений, использующих одну и ту же базу данных, требуется в каждом из них повторить код, отвечающий за генерацию первичных ключей.

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

В качестве примера рассмотрим, как можно использовать возможности генерации первичных ключей, предоставляемые Oracle, для автоматического заполнения поля TAG таблицы VISIT. С этой целью воспользуемся генератором последовательностей Oracle, создающим последовательности уникальных целых чисел, используемых клиентскими приложениями в качестве значений первичных ключей. Создадим такую последовательность с помощью следующего набора операторов (исполнить их можно с помощью SQL Plus или с помощью утилиты Database Explorer):

     create sequence seqtag
     start with 200
     increment by 1
     nominvalue
     nomaxvalue
     nocycle
     nocache

На главной форме приложения заменим компонент TDBEdit, в который ранее вводилось значение поля TAG, компонентом TDBText, чтобы исключить возможность ввода этого значения пользователем. В модуль данных нашего клиентского приложения поместим компонент TUpdateSQL, выберем его имя в качестве значения свойства UpdateObject компонента TTable, с помощью которого вводятся данные в таблицу VISIT, сгенерируем операторы SQL для вставки, удаления и модификации записи и изменим оператор SQL для вставки следующим образом:

     Insert into VISIT
     (TAG, STAT_ID, DIAG_ID, PURP_ID, PAT_ID, DOCT_ID, DATE_VIZ)
     values
     (seqtag.NEXTVAL, :STAT_ID, :DIAG_ID, :PURP_ID, :PAT_ID, :DOCT_ID, :DATE_VIZ)

Далее изменим свойство CachedUpdates этого компонента TTable на true и обеспечим возможность сохранять в базе данных изменения, накопленные в кэше, переопределив реакцию на нажатие кнопок компонента TDBNavigator либо введя для этой цели либо дополнительные интерфейсные элементы (например, кнопку):

     void __fastcall TMAINFORM::Button1Click(TObject *Sender)
     {
     DataModule2->Table1->ApplyUpdates();
     DataModule2->Table1->CommitUpdates();
     }

Метод ApplyUpdates() нужен для сохранения изменений в базе данных, а метод CommitUpdates() - для очистки кэша.

Скомпилировав и выполнив приложение, можно убедиться в том, что первичные ключи в базе данных генерируются автоматически (например, с помощью Database Explorer). Окончательный вид приложения представлен на рис.11.

Рис. 11. Окончательный вид главной формы АРМ медстатистика.

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

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

     CREATE TRIGGER MEDIC1.VI_INS BEFORE INSERT
     ON MEDIC1.VISIT
     FOR EACH ROW
     BEGIN
     UPDATE VISIT SET TAG=seqtag.nextval where TAG=10;
     END;

Триггер можно создать, например, с помощью утилиты SQL Plus, и с помощью SQL Explorer можно просматривать его текст (рис.12):

Рис.12. Просмотр текста созданного триггера с помощью SQL Explorer.

В этом случае компонент TUpdateSQL уже не требуется, но при этом нужно добавить в приложение код, присваивающий значение полю TAG, так как в противном случае клиентское приложение не допустит отправки запроса на сервер:

     void __fastcall TDataModule2::Table1BeforeInsert(TDataSet *DataSet)
     {
     Table1TAG->Value=10;
     }

Некоторые выводы

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

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

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


Подборка статей по С++ Builder : Перенос приложений C++ Builder в архитектуру клиент/сервер

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/builder_cs.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Перенос приложений C++ Builder в архитектуру клиент/сервер

Наталия Елманова

  • Введение
  • Немного истории
  • Особенности архитектуры клиент/сервер
  • Серверные СУБД и унаследованные данные
  • Перенос унаследованных данных с помощью Data Migration Wizard
  • Перенос унаследованных данных с использованием CASE-средств
  • Некоторые выводы

Введение

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

Немного истории

Давайте отвлечемся от современных технических средств и программных продуктов, используемых при построении информационных систем, и вспомним, что происходило два десятилетия назад. В те времена при построении информационных систем самой популярной была модель "хост-компьютер + терминалы", реализованная на базе мэйнфреймов (например, IBM-360/370, или их отечественных аналогов - компьютеров серии ЕС ЭВМ), либо на базе так называемых мини-ЭВМ (например, PDP-11, также имевших отечественный аналог - СМ-4). Характерной особенностью такой системы была полная "неинтеллектуальность" терминалов, используемых в качестве рабочих мест - их работой управлял все тот же хост-компьютер (рис.1)

Рис.1. Этап 1: модель "хост-компьютер + терминалы"

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

В чем были недостатки подобной архитектуры вычислений? Главным образом в полной зависимости пользователя от администратора хост-компьютера. Фактически пользователь (а нередко и программист) не имел возможности настроить рабочую среду под свои потребности - используемое программное обеспечение, в том числе и текстовые редакторы, компиляторы, СУБД, целиком и полностью было коллективным.

Не будет, наверное, большим преувеличением сказать, что в значительной степени именно этот недостаток подобных систем привел к бурному (и не прекратившемуся до сих пор) развитию индустрии персональных компьютеров. Наряду с дешевизной и простотой эксплуатации достаточно привлекательной особенностью настольных информационных систем стала так называемая персонализация рабочей среды, когда пользователь мог выбрать для себя инструменты для работы (текстовый редактор, СУБД, электронную таблицу и др.), наиболее соответствующие его потребностям. Естественно, и инструментов этих на рынке программных продуктов появилось довольно много. Именно в этот период появились электронные таблицы и настольные СУБД (dBase, FoxBase, Сlipper, Paradox и др.), нередко совмещающие в себе собственно СУБД и средства разработки приложений, использующих базы данных.

Рис.2. Этап 2: автономная персональная обработка данных

Следующим этапом развития архитектуры информационных систем было появление сетевых версий вышеупомянутых СУБД, позволяющих осуществлять многопользовательскую работу с общими данными в локальной сети. Этот подход сочетал в себе как удобства персонализации пользовательской среды и простоты эксплуатации, так и преимущества, связанные со вновь открывшимися возможностями совместного использования периферии (главным образом сетевых принтеров и сетевых дисков, в том числе хранящих коллективные данные). Отметим, что большинство информационных систем, реально эксплуатируемых сегодня в нашей стране, имеют именно такую архитектуру (в том числе некоторые информационные системы, реализованные на Delphi и C++Builder), и в случае не очень большого количества пользователей системы, не слишком большого объема данных и количества таблиц, невысоких требований к защите данных этот подход себя, безусловно, оправдывает.

Рис.3. Этап 3: коллективная обработка данных с использованием сетевых версий настольных СУБД и файлового сервера

Недостатки использования сетевых версий настольных СУБД обычно начинают проявляться в процессе длительной эксплуатации информационной системы, когда со временем увеличивается объем введенных пользователями данных, а иногда и число пользователей. Обычно в первую очередь замечается снижение производительности работы приложений, хотя нередко возникают и иные сбои в работе (например, разрушение индексов, нарушение ссылочной целостности данных). Причины этих явлений кроются в самой концепции хранения коллективных данных в файлах на сетевом диске и сосредоточении всей обработки данных внутри пользовательского приложения.

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

Возросший сетевой трафик - не единственная неприятность, подстерегающая администратора и разработчика подобной информационной системы. Весьма частым явлением в этом случае является нарушение ссылочной целостности данных. Возьмем уже ставший классическим пример, приводимый во всех учебниках по базам данных. Имеются две таблицы формата dBase: список заказчиков какой-либо компании, и список их заказов, связанные по какому-либо полю, например, CUST_ID (рис.4). Вполне разумными требованиями, предъявляемыми к такой БД (иногда называемыми бизнес-правилами), являются уникальность значений этого поля в списке заказчиков (иначе как узнать, чьи заказы с этим CUST_ID находятся в таблице заказов?), и отсутствие в списке заказов записей со значениями CUST_ID, отсутствующими в таблице заказчиков (то есть "ничьих" заказов). В случае dBase выполнение таких требований реализуется на уровне логики приложения. При этом практически всегда имеется возможность произвольного редактирования таблиц иными средствами (от dBase III до обычных текстовых редакторов), что может привести к нарушению этих требований. Обычно для предотвращения подобных прецедентов используются различные организационно-технические меры, например, запрещение средствами сетевой ОС доступа к файлам с таблицами иначе как из конкретного приложения. При этом полной гарантии сохранения ссылочной целостности все равно нет, так как остается вероятность так называемых незавершенных транзакций (то есть попытки согласованного изменения данных в нескольких таблицах - например, удаления заказчика вместе со всеми его заказами, во время которого произошел сбой питания, в результате чего удалилась только часть данных). Отметим также, что если информационная система содержит несколько приложений, использующих общие данные, каждое из них должно содержать код, предотвращающий некорректное с точки зрения ссылочной целостности изменение данных.

Рис.4.Пример связи "один-ко-многим"

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

Особенности архитектуры клиент/сервер

Что же представляет собой архитектура клиент/сервер? В определенной степени ее можно назвать возвратом к модели "хост-компьютер+терминалы", так как ядром такой системы является сервер баз данных, представляющий собой приложение, осуществляющее комплекс действий по управлению данными - выполнение запросов, хранение и резервное копирование данных, отслеживание ссылочной целостности, проверку прав и привилегий пользователей, ведение журнала транзакций. При этом в качестве рабочего места может быть использован обычный персональный компьютер, что позволяет не отказываться от привычной рабочей среды.

Рис.5. Этап 4: обработка данных в архитектуре клиент/сервер

В чем преимущества клиент-серверных информационных систем по сравнению с их аналогами, созданными на основе сетевых версий настольных СУБД?

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

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

Кроме того, для описания серверных бизнес-правил в наиболее типичных ситуациях (как в примере с заказчиками и заказами) существуют весьма удобные инструменты - так называемые CASE-средства (CASE означает Computer-Aided System Engineering), позволяющие описать подобные правила, и создавать реализующие их объекты базы данных (индексы, триггеры), буквально рисуя мышью связи между таблицами без какого бы то ни было программирования. В этом случае клиентское приложение будет избавлено от значительной части кода, связанного с реализацией бизнес-правил непосредственно в приложении. Отметим также, что часть кода, связанного с обработкой данных, также может быть реализована в виде хранимых процедур сервера, что позволяет еще более "облегчить" клиентское приложение, а это означает, что требования к рабочим станциям могут быть не столь высоки. Это в конечном итоге удешевляет стоимость информационной системы даже при использовании дорогостоящей серверной СУБД и мощного сервера баз данных.

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

Итак, клиент-серверная информационная система состоит в простейшем случае из трех основных компонентов:

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

Есть и более сложные реализации архитектуры клиент/сервер, например, трехуровневые информационные системы с использованием серверов приложений, но это уже тема отдельного разговора.

Серверные СУБД и унаследованные данные

Одной из наиболее распространенных проблем, связанных с модернизацией эксплуатируемых информационных систем, является использование в них данных, унаследованных от прежних версий и содержащихся, как правило, в форматах настольных СУБД. В частности, вопросы модернизации устаревших систем на основе xBase и переноса их на платформу Oracle, а также этапы модернизации с организационной точки зрения были подробно проанализированы Алексеем Ярцевым в статье "Миграция из xBase в Oracle'' ("Компьютер -Пресс", 1997, N 8, стр.137-140).

В данной работе хотелось бы рассмотреть некоторые технические аспекты проблемы использования унаследованных данных применительно к С++Builder.

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

Перенос унаследованных данных с помощью Data Migration Wizard

Способов переноса данных может быть несколько. Первый, самый примитивный, и, к сожалению, весьма распространенный, представляет собой перенос структур данных и самих данных "как есть" с одной платформы на другую с заменой типов данных настольной СУБД на эквивалентные им типы данных сервера. Этой цели служит утилита Data Migration Wizard, входящая в комплект поставки C++Builder. Рассмотрим ее работу на простом примере из двух связанных таблиц. Для этой цели вполне подойдут таблицы CLIENTS.DBF (список клиентов) и HOLDINGS.DBF (список зданий), связанные отношением "один ко многим". Такой пример выбран потому, что таблица CLIENTS содержит графические и мемо-поля, при переносе которых с платформы на платформу нередко возникают проблемы (в отличие от символьных и числовых полей, для которых обычно можно подобрать эквивалентный тип данных). В качестве серверной СУБД используем 90-дневную trial-версию Personal Oracle 7.2 для Windows 95 или Oracle Workgroup Server 7.2.для Windows NT (в принципе подобный пример можно выполнить и с помощью входящего в комплект поставки С++Builder локального сервера InterBase). Перед тем как выполнить данный пример, запустим сервер и с помощью входящей в комплект поставки утилиты администрирования сервера (в нашем случае Personal Oracle Navigator) опишем пользователя USER1, от имени которого будут создаваться таблицы, проверив заодно наличие соединения с базой данных.

Рис 6. Создание пользователя с помощью Personal Oracle Navigator

Далее опишем параметры псевдонима используемой базы данных:

Рис.7. Создание псевдонима для доступа к данным Oracle

Отметим, что 2: - имя локального сервера Oracle (при работе с Oracle Workgroup Server for Windows NT, расположенном на том же компьютере, что и С++Builder, можно использовать такое же имя сервера). В случае удаленного сервера следует описать местоположение и доступ к базе данных (имя БД, сетевой протокол, IP-адрес или сетевое имя компьютера) с помощью утилиты SQL*Net Easy Configuration, входящей в комплект поставки серверов Oracle последних версий.

После описания доступа к БД рекомендуется проверить правильность настроек BDE. Это можно сделать, запустив SQL Explorer и попытавшись открыть какую-либо из таблиц Oracle (любая современная серверная СУБД, как правило, содержит какие-либо таблицы в качестве примера). Если список таблиц получить не удается, следует проверить, есть ли в переменной окружения PATH каталог типа С:\ORAWIN95\BIN, и есть ли в каталоге WINDOWS\SYSTEM файлы типа ORA72.DLL или ORANT71.DLL.

После проверки настроек BDE можно приступить непосредственно к переносу таблиц. Запустим Data Migration Wizard:

Рис.8. Выбор исходной БД в Data Migration Wizard

Далее выберем псевдоним БД, в которую мы осуществляем экспорт данных, и затем выберем имена экспортируемых таблиц:

Рис.9. Выбор таблиц для переноса данных с помощью Data Migration Wizard

После этого получим экран, содержащий список таблиц и индексов, подлежащих переносу.

Рис.10. Список таблиц и индексов, подлежащих переносу.

Если нажать кнопку Modify Mapping Information For Selected Item, получим сведения о том, в какие типы данных будут преобразованы поля исходных таблиц. При необходимости в эти сведения можно внести правки.

Рис.11. Внесение правок в правила преобразования полей

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

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

Для создания такого приложения воспользуемся инструментом под названием Database Form Wizard. Запустим C++Builder, создадим новый проект, удалим из него пустую форму и выберем со страницы Forms репозитария объект Database Form:

Рис.12. Выбор Database Form из репозитария С++Builder

Далее выберем создание формы master/detail c использованием объектов TTable:

Рис.13. Выбор типа будущей главной формы приложения

После выбора псевдонима базы данных Oracle из списка псевдонимов определим master-таблицу. В нашем случае это только что созданная таблица CLIENTS:

Рис.14. Выбор master-таблицы

Затем определим, какие поля этой таблицы нам нужны (можно выбрать все), и как они будут расположены на форме (выберем горизонтальное расположение полей). Затем определим detail-таблицу (в данном случае HOLDINGS), выберем все поля и в качестве способа их отображения выберем размещение их в TDBGrid. Наконец, укажем, что таблицы связаны по полю ACCT_NBR:

Рис.15. Установка связи между таблицами

Далее выбираем опцию Generate a Main Form и в результате получим форму, похожую на представленную на рис. 16:

Рис.16. Вот что обычно получается при использовании Database Form Wizard

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

Рис.17. Главная форма приложения после "приведения в порядок"

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

Если мы попытаемся удалить первую запись в таблице CLIENTS, нам это удастся, несмотря на то, что в таблице HOLDINGS есть связанные с ней записи. Точно так же можно добавить запись с произвольным значением поля ACC_NBR в таблицу HOLDINGS, и это нам также удастся. Таким образом, в созданной нами базе данных из двух таблиц связь "один-ко-многим" фактически отсутствует.

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

Перенос унаследованных данных с использованием CASE-средств

Рассмотрим альтернативный способ переноса данных на сервер, более дорогой и сложный, но приводящий в целом к более качественному результату. Этот способ базируется на использовании CASE-средств (CASE расшифровывается как Computer-Aided System Engineering) для восстановления схемы базы данных по имеющимся таблицам (так называемого обратного проектирования), замены платформы и описания связей между таблицами с точки зрения реакции сервера на попытки того или иного изменения данных со стороны клиентского приложения. В качестве такого средства рассмотрим, например, ERwin 3.0 - CASE-средство компании Logic Works, предназначенное для проектирования баз данных и на сегодняшний день являющееся одним из наиболее простых и доступных по цене средств такого класса.

Прежде чем заняться обратным проектированием, следует описать ODBC-источник, соответствующий каталогу, в котором хранятся исходные dBase-таблицы (это делается с помощью панели управления Windows).

Рис.18. Описание ODBC-источника с помощью панели управления Windows

Попробуем осуществить обратное проектирование с помощью ERwin , используя созданный ODBC-источник. Для этого из меню главного окна ERwin выберем опцию Tasks/Reverse Engineering .

Первая проблема, с которой при этом можно столкнуться, заключается в том, что Erwin не поддерживает формат данных dBase 5.0 (с прежними версиями dBase такой проблемы нет), и при обратном проектировании структура таблиц, содержащих графические поля, не всегда восстанавливается. Обычно эта проблема решается путем выбора сходной по структуре платформы (dBase III, Clipper, FoxPro) и последующей коррекции результатов обратного проектирования. Особое внимание при этом следует обращать на специфические типы данных (например, BLOB-поля), так как различия между платформами заключаются, в частности, в способах хранения подобных типов данных (но, разумеется, не только в этом).

Выберем в качестве исходной платформы Clipper, ответим на вопросы, предлагаемые в последующем диалоге (можно оставить то, что предложено по умолчанию) и в результате получим модель данных, похожую на изображенную на рис.19 (она содержит описание всех dBase-таблиц из каталога, содержащего данные для примеров, поставляемых с C++Builder):

Рис.19. Результат обратного проектирования каталога CBUILDER\EXAMPLES\DATA

Отредактируем полученную модель данных, убрав все таблицы, кроме CLIENTS и HOLDINGS, определив ACC_NBR первичный ключ таблицы CLIENTS и изменив тип данных поля IMAGE на МЕМО (опция Column Editor контекстного меню таблицы). Создадим также неидентифицирующую связь "один-ко-многим" между таблицами CLIENTS и HOLDINGS, выбрав для этой цели соответствующую пиктограмму на "плавающей" инструментальной панели.

Далее следует выбрать другую целевую платформу (в нашем случае Oracle). В результате получим примерно следующий вид модели данных:

Рис.20. Примерный вид модели данных для генерации БД в Oracle

Теперь можно описать свойства имеющейся связи между таблицами. Так как это связь "один-ко-многим", это следует явно указать в диалоге, вызываемом с помощью опции Relationship Editor контекстного меню связи. В том же диалоге на другой странице трехстраничного блокнота следует выбрать из предлагаемых выпадающих списков возможную реакцию сервера на попытки нарушения ссылочной целостности со стороны клиента. Например, при попытке удалить запись из таблицы CLIENTS можно либо совершить каскадное удаление (то есть удалить все соответствующие записи из таблицы HOLDINGS), либо запретить удаление, если имеются соответствующие записи в дочерней таблице, с выдачей диагностического сообщения.

Рис.21. Определение реакции сервера на попытки нарушения ссылочной целостности

После этого можно выбрать из меню опцию Tasks/Forward Engineer/Schema Generation и после установки соединения с Oracle сгенерировать базу данных, выбрав в появившейся диалоговой панели опции для генерации структуры. Можно также просмотреть и сохранить скрипт на языке PL/SQL (это процедурное расширение SQL, используемое для написания триггеров и хранимых процедур Oracle), называемый также DDL-сценарием (DDL расшифровывается как Data Definition Language).

Рис.22. DDL-сценарий генерации схемы базы данных

Далее можно попытаться снова воспользоваться Data Migration Wizard для переноса данных, отказываясь при этом от удаления уже сгенерированных таблиц. Однако в ряде случаев удобнее создать приложение для переноса данных из старой БД в новую. Как было отмечено выше, унаследованные данные могут не удовлетворять правилам ссылочной целостности, установленным на сервере. В этом случае от приложения, используемого для переноса данных, требуется некоторая гибкость (например, предоставление возможности редактирования исходных данных или создание дополнительных таблиц, содержащих записи, не удовлетворяющие бизнес-правилам новой базы данных, для последующего анализа).

Создадим простейшее приложение для переноса данных. Для этого создадим форму следующего вида (рис.23):

Рис.23. Форма приложения для переноса данных на сервер

Для переноса данных с одной платформы на другую обычно используется компонент TBatchMove. Этот компонент обеспечивает копирование данных из одной таблицы в другую. Основные свойства этого компонента следующие: Source - таблица (или запрос), откуда копируются данные, Destination - таблица, куда копируются данные, Mapping - определяет соответствие между колонками исходной и результирующей таблиц (для идентичных таблиц это свойство определять не обязательно), Mode - тип перемещения (batAppend - добавляет новые строки в результирующую таблицу, batUpdate - заменяет строки в результирующей таблице на соответствующие строки оригинала, batCopy - копирует строки в результирующую таблицу, переписывая ее, batDelete - удаляет записи в результирующей таблице, соответствующие записям оригинала), KeyViolTableName и ProblemTableName - имена дополнительных таблиц для помещения записей, чье копирование запрещено правилами ссылочной целостности или по каким-либо причинам невозможно (например, из-за несоответствия типов данных), ChangedTableName - имя таблицы для помещения измененных записей. Копирование данных происходит при выполнении метода Execute(). Отметим, что этот метод может быть вызван непосредственно из среды разработки с помощью контекстного меню компонента TBatchMove.

Установим следующие значения свойств используемых компонентов:

Компонент | Свойство | Значение
Table1 | DatabaseName | Oracle7
 | Exclusive | true
 | TableName | CLIENTS
 | Active | true
Table2 | DatabaseName | BCDEMOS
 | TableName | ClLIENTS.DBF
 | Active | true
DataSource1 | DataSet | Table1
DataSource2 | DataSet | Table2
DBGrid1 | DataSource | DataSource1
DBGrid2 | DataSource | DataSource2
DBNavigator1 | DataSource | DataSource1
DBNavigator2 | DataSource | DataSource2
BatchMove1 | Source | Table2
 | Destination | Table1
 | Mode | batAppend
Button1 | Caption | CLIENTS
Button2 | Caption | HOLDINGS
Button3 | Caption | Добавить
Button4 | Caption | Очистить
Button5 | Caption | Выход

Создадим также обработчики событий для кнопок:

     //--------------------------------------------------------
     #include <vcl\vcl.h>
     #pragma hdrstop
     #include "upsize1.h"
     //--------------------------------------------------------
     #pragma link "Grids"
     #pragma resource "*.dfm"
     TForm1 *Form1;
     //--------------------------------------------------------
     __fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner)
     {
     }
     //--------------------------------------------------------
     void __fastcall TForm1::Button1Click(TObject *Sender)
     {
     Table1->Close();
     Table2->Close();
     Table1->TableName="CLIENTS";
     Table2->TableName="CLIENTS.DBF";
     Table1->Open();
     Table2->Open();
     }
     //--------------------------------------------------------
     void __fastcall TForm1::Button2Click(TObject *Sender)
     {
     Table1->Close();
     Table2->Close();
     Table1->TableName="HOLDINGS";
     Table2->TableName="HOLDINGS.DBF";
     Table1->Open();
     Table2->Open();
     }
     //--------------------------------------------------------
     void __fastcall TForm1::Button5Click(TObject *Sender)
     {
     Table1->EmptyTable();
     }
     //--------------------------------------------------------
     void __fastcall TForm1::Button3Click(TObject *Sender)
     {
     BatchMove1->Execute();
     }
     //--------------------------------------------------------
     void __fastcall TForm1::Button4Click(TObject *Sender)
     {
     Close();
     }
     //--------------------------------------------------------

Скомпилируем и запустим приложение. Кнопки CLIENTS и HOLDINGS осуществляют выбор между той или иной парой таблиц. При нажатии кнопки Добавить происходит перенос данных из активной в данный момент таблицы dBase (CLIENTS или HOLDINGS) в соответствующую таблицу Oracle. Отметим, что наличие поддержки ссылочной целостности можно проверить, попытавшись перенести данные из таблицы HOLDINGS до того, как перенесены данные из таблицы CLIENTS. При этом перенос осуществлен не будет, и в процессе выполнения приложения появится диагностическое сообщение примерно следующего вида (рис.24):

Рис.24. Диагностическое сообщение при попытке добавления записей в detail-таблицу при пустой master-таблице.

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

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

Теперь, если мы попытаемся удалить запись из таблицы CLIENTS при наличии связанных с ней записей в таблице HOLDINGS, нам это не удастся. При этом клиентским приложением будет выдано диагностическое сообщение о наличии записей в дочерней таблице (рис. 25):

Рис.25. Диагностическое сообщение при попытке удаления записей из master-таблицы при наличии связанных с ней записей в detail-таблице.

Точно так же окажется невозможным добавить запись с произвольным значением поля ACC_NBR в таблицу HOLDINGS. Причина такого поведения созданной информационной системы очевидна: при проектировании базы данных с помощью ERwin помимо самих таблиц и индексов были созданы также специальные объекты базы данных, называемые триггерами. Триггер - это специальная процедура, выполняющаяся при наступлении определенного события, например, при попытке удаления записи в таблице CLIENTS. При описании свойств связи между таблицами мы выбирали, как сервер будет реагировать на подобные события, и в соответствии с нашим выбором были сгенерированы триггеры для выполнения соответствующих действий (в данном случае - для передачи клиентскому приложению диагностического сообщения).

Разумеется, пользователь приложения должен видеть нечто более вразумительное, нежели англоязычное сообщение с именем триггера и словами про "integrity constraint" и "key violation". Можно сделать это путем перехвата исключения в клиентском приложении, но более предпочтительно делать это на сервере, так как тогда будет исключена необходимость в повторении кода в случае, когда с одной и той же базой данных работают несколько приложений. Современные CASE-средства позволяют это сделать, и интересующиеся этой проблемой могут найти ее решение в документации по используемому CASE-средству и в документации, прилагаемой к соответствующему серверу баз данных.

Некоторые выводы

Таким образом, архитектура клиент/сервер обладает рядом существенных преимуществ по сравнению с традиционной архитектурой информационных систем, основанных на сетевых версиях настольных СУБД: более высокой производительностью, более низким сетевым трафиком, улучшенными средствами обеспечения безопасности и целостности данных, возможностью задания бизнес-правил. Отметим также, что при использовании современных средств проектирования баз данных, и, в частности, CASE-технологии, разработчик имеет возможность возложить на сервер значительную часть проблем, которые ранее были проблемами самих клиентских приложений, что может существенно облегчить их создание.


Подборка статей по С++ Builder : Начинаем работать в Borland C++ Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/cb2.shtml]
Авторы: Виктор Алексанкин, Наталия Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Начинаем работать в Borland C++ Builder

Виктор Алексанкин, Наталия Елманова

Компьютер-Пресс, 1997, N 4.

  • Введение
  • Среда разработки C++ Builder
  • Компоненты C++ Builder
    • Свойства компонентов
    • События
    • Методы
  • Менеджер проектов
  • Создание приложений в С++ Builder
  • Пример: создание простейшего приложения

Введение

Borland C++ Builder - выпущенное недавно компанией Borland средство быстрой разработки приложений, позволяющее создавать приложения на языке C++, используя при этом среду разработки и библиотеку компонентов Delphi. В настоящей статье рассматривается среда разработки C++ Builder и основные приемы, применяемые при проектировании пользовательского интерфейса.

Среда разработки C++ Builder

C++ Builder представляет собой SDI-приложение, главное окно которого содержит настраиваемую инструментальную панель (слева) и палитру компонентов (справа). Помимо этого, по умолчанию при запуске C++ Builder появляются окно инспектора объектов (слева) и форма нового приложения (справа). Под окном формы приложения находится окно редактора кода.

Рис.1. Среда разработки C++ Builder

Формы являются основой приложений C++ Builder. Создание пользовательского интерфейса приложения заключается в добавлении в окно формы элементов объектов C++ Builder, называемых компонентами. Компоненты C++ Builder располагаются на палитре компонентов, выполненной в виде многостраничного блокнота. Важная особенность C++ Builder состоит в том, что он позволяет создавать собственные компоненты и настраивать палитру компонентов, а также создавать различные версии палитры компонентов для разных проектов.

Компоненты C++ Builder

Компоненты разделяются на видимые (визуальные) и невидимые (невизуальные). Визуальные компоненты появляются во время выполнения точно так же, как и во время проектирования. Примерами являются кнопки и редактируемые поля. Невизуальные компоненты появляются во время проектирования как пиктограммы на форме. Они никогда не видны во время выполнения, но обладают определенной функциональностью (например, обеспечивают доступ к данным, вызывают стандартные диалоги Windows 95 и др.)

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

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

Каждый компонент C++ Builder имеет три разновидности характеристик: свойства, события и методы.

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

Рис.3. Инспектор объектов

Свойства компонентов

Свойства являются атрибутами компонента, определяющими его внешний вид и поведение. Многие свойства компонента в колонке свойств имеют значение, устанав иваемое по умолчанию (например, высота кнопок). Свойства компонента отображаются а странице свойств (Properties). Инспектор объектов отображает опубликованные (published) свойства компонентов. Помимо published-свойств, компоненты могут и чаще всего имеют общие (public), опубликованные свойства, которые доступны только во время выполнения приложения. Инспектор объектов используется для установки свойств во время проектирования. Список свойств располагается на странице свойств инспектора объектов. Можно определить свойства во время проектирования или написать код для видоизменения свойств компонента во время выполнения приложения.

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

События

Страница событий (Events) инспектора объектов показывает список событий, распознаваемых компонентом (программирование для операционных систем с графическим пользовательским интерфейсом, в частности, для Windows 95 или Windows NT предполагает описание реакции приложения на те или иные события, а сама операционная система занимается постоянным опросом компьютера с целью выявления наступления какого-либо события). Каждый компонент имеет свой собственный набор обработчиков событий. В C++ Builder следует писать функции, называемые обработчиками событий, и связывать события с этими функциями. Создавая обработчик того или и ого события, вы поручаете программе выполнить написанную функцию, если это событие произойдет.

Для того, чтобы добавить обработчик событий, нужно выбрать на форме с помощью мыши компонент, которому необходим обработчик событий, затем открыть страницу событий инспектора объектов и дважды щелкнуть левой клавишей мыши на колонке з ачений рядом с событием, чтобы заставить C++ Builder сгенерировать прототип обработчика событий и показать его в редакторе кода. При этом автоматически генерируется текст пустой функции, и редактор открывается в том месте, где следует вводить код. Курсор позиционируется внутри операторных скобок { ... }. Далее нужно ввести код, который должен выполняться при наступлении события. Обработчик событий может иметь параметры, которые указываются после имени функции в круглых скобках.

Рис.4. Прототип обработчика событий.

Методы

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

     Edit1->Show();

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

Менеджер проектов

Файлы, образующие приложение - формы и модули - собраны в проект. Менеджер проектов показывает списки файлов и модулей приложения и позволяет осуществ ять навигацию между ними. Можно вызвать менеджер проектов , выбрав пункт меню View/Project Manager. По умолчанию вновь созданный проект получает имя Project1.cpp.

Рис.5. Менеджер проектов

По умолчанию проект первоначально содержит файлы для одной формы и исходного кода одного модуля. Однако большинство проектов содержат несколько форм и модулей. Чтобы добавить модуль или форму к проекту, нужно щелкнуть правой кнопкой мыши и выбрать пункт New Form из контекстного меню. Можно также добавлять существующие формы и модули к проекту, используя кнопку Add контекстного меню менеджера проектов и выбирая модуль или форму, которую нужно добавить. Формы и модули можно удалить в любой момент в течение разработки проекта. Однако, из-за того, что форма связана всегда с модулем, нельзя удалить одно без удаления другого, за исключением случая, когда модуль не имеет связи с формой. Удалить модуль из проекта можно, используя кнопку Remove менеджера проектов.

Если выбрать кнопку Options в менеджере проектов, откроется диалоговая панель опций проекта, в которой можно выбрать главную форму приложения, определить, какие формы будут создаваться динамически, каковы параметры компиляции модулей (в том числе созданных в Delphi 2.0, так как C++ Builder может включать их в проекты) и компоновки.

Рис. 6. Установка опций проекта

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

Разумеется, C++ Builder обладает встроенной системой контекстно-зависимой помощи, доступной для любого элемента интерфейса и являющейся обширным источником справочной информации о C++ Builder.

Создание приложений в С++ Builder

Первым шагом в разработке приложения C++ Builder является создание проекта. Файлы проекта содержат сгенерированный автоматически исходный текст, который становится частью приложения, когда оно скомпилировано и подготовлено к выполнению. Чтобы создать новый проект, нужно выбрать пункт меню File/New Application.

C++ Builder создает файл проекта с именем по умолчанию Project1.cpp, а также make-файл с именем по умолчанию Project1.mak. При внесении изменений в проект, таких, как добавление новой формы, C++ Builder обновляет файл проекта.

Рис.7 Файл проекта

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

  • Файл формы с расширением.DFM, содержащий информацию о ресурсах окон для конструирования формы
  • Файл модуля с расширением.CPP, содержащий код на C++.
  • Заголовочный файл с расширением .H, содержащий описание класса формы.

Когда вы добавляете новую форму, файл проекта автоматически обновляется.

Для того чтобы добавить одну или более форм к проекту , выберите пункт меню File/New Form. Появится пустая форма, которая будет добавлена к проекту. Можно воспользоваться пунктом меню File/New, выбрать страницу Forms и выбрать подходящий шаблон из репозитория объектов.

Рис.8 Шаблоны форм

Для того, чтобы просто откомпилировать текущий проект, из меню Compile нужно выбрать пункт меню Compile. Для того, чтобы откомпилировать проект и создать исполняемый файл для текущего проекта, из меню Run нужно выбрать пункт меню Run. Компоновка проекта является инкрементной (перекомпилируются только изменившиеся модули).

Если при выполнении приложения возникает ошибка времени выполнения, C++ Builder делает паузу в выполнении программы и показывает редактор кода с курсором, установленным на операторе, являющемся источником ошибки. Прежде чем делать необходимую коррекцию, следует перезапустить приложение, выбирая пункт меню Run из контекстного меню или из меню Run, закрыть приложение и лишь затем вносить изменения в проект. В этом случае уменьшится вероятность потери ресурсов Windows.

Пример: создание простейшего приложения

Теперь попробуем создать простейшее приложение, позволяющее вводить текст в редактируемое поле и добавлять этот текст к списку при нажатии мышью на кнопку. Выберем пункт меню File/New Application для создания проекта и сохраним его главную форму под именем samp1.cpp, а сам проект под именем samp.mak. Поместим на форму компоненты Button, Edit и ListBox со страницы Standard палитры компонент.

Рис. 9. Размещение компонентов на форме

После этого выберем на форме компонент Edit и удалим текущее значение свойства Text. Затем установим свойство Caption для Button1 равным "Добавить".

Чтобы добавить обработчик события OnClick для кнопки Добавить, нужно выбрать эту кнопку на форме, открыть страницу событий в инспекторе объектов и дважды щелкнуть мышью на колонке справа от события OnClick. В соответствующей строке ввода появится имя функции. C++ Builder сгенерирует прототип обработчика событий и покажет его в редакторе кода. После этого следует ввести следующий код в операторные скобки { ... } тела функции:

     void __fastcall TForm1::Button1Click(TObject *Sender)
     {
     if (!(Edit1->Text == ""))
         {
         ListBox1->Items->Add(Edit1->Text);
         Edit1->Text = "" ;
         }
     }

Для компиляции приложения в меню Run выберем пункт Run. Теперь можно что-нибудь ввести в редактируемое поле, нажать мышью на кнопку Добавить и убедиться, что вводимые строки добавляются к списку.

Рис.10. Так выглядит готовое приложение.

Теперь модифицируем приложение, добавив кнопки Удалить и Выход. Для этого добавим еще две кнопки, изменим их свойство Caption и создадим обработчики событий, связанных с нажатием на эти кнопки:

Рис. 11. Модифицированное приложение

Для кнопки Удалить:

     void __fastcall TForm1::Button2Click(TObject *Sender)
     {
         if (!(ListBox1->ItemIndex == -1))
         ListBox1->Items->Delete(ListBox1->ItemIndex);
     }
Для кнопки Выход:
     Close();

Сохраним и скомпилируем приложение, а затем протестируем его.

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


Подборка статей по С++ Builder : Изучаем компоненты Borland C++ Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/cb3.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Изучаем компоненты Borland C++ Builder

Наталия Елманова

Компьютер-Пресс, 1997, N 5, стр. 107-113.

В настоящей статье, второй в этом цикле, рассматриваются приемы манипуляции компонентами на форме и рассматривается пример применения некоторых наиболее часто используемых компонентов.

  • Выбор компонентов для групповых операций
  • Установка разделяемых свойств компонентов
  • Изменение размера компонентов
  • Выравнивание компонентов
  • Пример: Создание текстового редактора
    • Проектирование формы приложения
    • Создание обработчиков событий
    • Создание меню

Выбор компонентов для групповых операций

Для эффективной разработки пользовательских интерфейсов приложений C++ Builder нередко возникает необходимость в манипулировании компонентами на формах.. Большинство операций для манипулирования компонентами находятся в меню Edit: К различным опциям этого меню следует обращаться после того, как на форме вы ран один или несколько компонентов, свойства которых требуется изменить.

Выбрать один компонент можно следующими способами:

  • Выбрав с помощью мыши компонент на форме
  • Выбрав имя компонента в селекторе объектов.
  • Переходом к компоненту на форме, нажимая клавишу Tab.

Выбрать несколько компонентов можно следующими способами:

  • Удерживая нажатой клавишу Shift, щелкнуть мышью на каждом компоненте
  • Нажать левую клавишу мыши и окружить нужные компоненты прямоугольным контуром

Установка разделяемых свойств компонентов

Большинство визуальных компонентов имеют общие свойства, (например, Visible, Width, Left). Для установки одинаковых значений общих свойств для нескольких компонентов необходимо выполнить следующие действия:

1. Выбрать несколько настраиваемых компонентов. При этом страница свойств и спектора объектов будет отображать только те свойства, которые имеются у всех выбра ных компонентов.

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

2. Установить значения свойств, общих для выделенных компонентов.

Рис.2 показывает результаты изменения свойства Font и Left . Все выбранные компоненты приобрели одинаковые значения этих свойств.

Рис. 2 Установка разделяемых свойств компонентов

Изменение размера компонентов

Изменение размера компонента можно проводить как при добавлении его на форму, так и после этого.

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

Рис. 3. Изменение размера компонента при его добавлении на форму.

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

Для изменения размеров нескольких компонентов следует выбрать их одним из описанных выше способов. Далее нужно выбрать пункт меню Edit/Size. Появится диалоговое окно Size. Выберите опции размера. Для точной установки размера в пикселах можно ввести числа в поля Width и Height. Далее нужно нажать кнопку OK.

Рис. 4. Установка свойств компонентов c использованием меню EDIT/SIZE

Можно добавить несколько копий компонента одного типа, выбирая компонент из палитры при нажатой клавише Shift. В этом случае вокруг компонента появляется п ямоугольник, окружающий этот компонент. После этого каждый щелчок мышью на фо ме приводит к появлению на ней копии компонента. Закончив режим многократного копирования, следует щелкнуть мышью на инструменте выбора курсора (первая кнопка на палитре компонентов с изображением стрелки).

Выравнивание компонентов

Для выравнивания компонентов на форме можно использовать следующие комбинации клавиш:
Shift + стрелки | Изменяет размер компонента на один пиксел в направлении выбранной стрелки
Shift + Ctrl + стрелки | Перемещает компонент на одну единицу сетки в направлении выбранной стрелки
Ctrl + стрелки | Перемещает компонент на один пиксел в направлении выбранной стрелки

Можно также выровнять компоненты, используя пункт меню View/Alignment Palette. Для этого нужно:

  1. Выбрать компоненты для выравнивания.
  2. Выбрать пункт меню View/Alignment Palette.
  3. Выбрать нужную кнопку (см. рис. 5).

Рис.5 Выравнивание компонентов с помощью View/Alignment Palette

Можно выровнять компоненты, используя пункт меню Edit/Align. Для этого нужно:

  1. Выбрать компоненты для выравнивания.
  2. Выбрать пункт меню Edit/Align. Появится диалоговое окно Alignment.
  3. Выбрать нужную опцию и нажать на кнопку OK (рис 6).

Рис.6. Выравнивание компонентов с помощью меню Edit/Align

Можно изменить условия выравнивания компонент, используя пункт меню Options/Environment. Для этого нужно:

  1. Выбрать пункт меню Options/Environment. Диалоговое окно Environment появится открытым на странице Preferences.
  2. В группе Form designer можно выбрать следующие опции:
    • Display grid - сделать сетку из точек на форме видимой для выравниваемых компонентов
    • Snap to grid - заставить левые и верхние стороны компонентов расположиться а линиях сетки.
  3. Для того, чтобы изменить расстояние между узлами сетки, нужно ввести новые значения вместо имеющихся. Значение по умолчанию -- 8 пикселей по оси X (по горизонтали) и по оси Y (по вертикали).
  4. Нажать OK.

    Рис. 7. Выравнивание компонентов с помощью страницы Preferences диалоговой панели Environment

Пример: Создание текстового редактора

Проектирование формы приложения

Попробуем использовать полученные знания для создания текстового редактора, с помощью которого можно было бы создавать новые файлы, открывать имеющиеся, ре актировать и сохранять их, а также использовать буфер обмена для работы с фрагментами текста. Для этого создадим новый проект, основанный на пустой форме, и сохраним ее под именем Edit1.cpp. Сам проект сохраним под именем Edit.mak.

На пустой форме разместим компонент TPanel - будущую инструментальную панель нашего редактора. Свойству Align полученного компонента Panel1 присвоим значение alTop, а свойству Caption - пустую строку.

Далее разместим на форме компонент TMemo и присвоим его свойству Align значение alClient, свойству ScrollBars - значение ssVertical, а свойству Lines - пустой массив строк (редактор свойств, являющихся строковыми массивами, как п авило, представляет собой обычный текстовый редактор).

Вспомним о том, что наш будущий текстовый редактор должен открывать и сохра ять файлы. Для этой цели воспользуемся стандартными диалогами Windows 95, содержащимися в библиотеке comdlg32.dll. Для этого поместим на форму два диалога со страницы Dialogs: TOpenDialog и TSaveDialog. Изменим свойство Filter созданного только что компонента OpenDialog1, внеся две строки в диалоговую панель Filter Editor и нажав кнопку OK (рис. 8).

Рис. 8. Установка свойства Filter компонента OpenDialog1.

Теперь можно взять в буфер обмена строку, образовавшуюся в колонке значений апротив свойства Filter, выбрать компонент SaveDialog1 и вставить содержимое буфера обмена в строку напротив свойства Filter. Этим самым мы установим такое же значение свойства Filter для второго диалога. При желании можно изменить заголовки диалоговых панелей (свойство Caption) и другие параметры (свойство Options).

Обратите внимание на то, что языковая версия библиотеки может быть в общем случае как русской, так и английской, так как это ресурс Windows, а не вашего приложения. Поэтому, если вашим пользователям нужно, чтобы стандартные диалоги Windows были русскоязычными, рекомендуйте им установить русскую версию Windows 95 или Windows NT Workstation, либо попробуйте заменить на компьютерах пользователей имеющуюся версию comdlg32.dll на русскоязычную. Впрочем, на странице System имеется достаточное количество компонент для создания "самодельных" диалогов для работы с файлами...

И, наконец, разместим на форме компонент StatusBar со страницы Win95. Отредактируем его свойство Panels (это свойство представляет собой набор компонентов-панелей, на которых выводится необходимая пользователю информация). Редактор этого свойства представляет собой диалог (рис.9). Создадим панель, на которой будет появляться имя редактируемого файла. Для этого нажмем кнопку New и изменим параметр Width созданной панели, сделав его равным 100. В поле Text введем значение "Без имени". Затем нажмем кнопку ОК.

Рис. 9. Установка свойства Panels компонента StatusBar1.

Далее выберем с помощью мыши компонент Panel1 и разместим на нем девять компонентов типа TSpeedButton. Сделать это проще всего, нажав клавишу Shift и выбрав SpeedButton со страницы Additional палитры компонентов.

Оснастим наши кнопки рисунками. Для этого присвоим значения свойствам Glyph этих кнопок. С этой целью можно воспользоваться обширным набором картинок, вхо ящих в состав С++ Builder (каталог CBuilder\images\Buttons). Для нашего примера из этого каталога были выбраны файлы Doorshut.bmp,Filenew.bmp, Fileopen.bmp, Fileclose.bmp, Filesave.bmp, Cut.bmp, Copy.bmp, Paste.bmp, Help.bmp (рис.10).

Рис. 10. Установка свойства Glyph компонентов SpeedButton1,...,SpeedButton9.

Далее, используя описанные выше приемы манипуляции компонентами, разместим кнопки группами, как показано на рис.11. Присвоим свойству ShowHint этих кнопок значение True, а свойству Hint - значения "Выход", "Создать", "Открыть", "Сохранить", "Сохранить как...", "Вырезать", "Копировать", "Вставить", "О п ограмме". Это приведет к появлению желтых ярлычков с комментариями под кнопками, когда на кнопке находится курсор мыши.

Рис. 11. Вид главной формы приложения.

Создание обработчиков событий

Теперь напишем обработчики событий OnClick для наших кнопок.

Кнопка SpeedButton3 отвечает за открытие файла для редактирования и отображение имени файла на панели состояния:

     void __fastcall TForm1::SpeedButton3Click(TObject *Sender)
     {
     if (OpenDialog1->Execute()) Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
     StatusBar1->Panels->Items[0]->Text=OpenDialog1->FileName;
     }

Кнопка SpeedButton5 отвечает за сохранение редактируемого файла под выбранным именем и отображение имени файла на панели состояния.

     void __fastcall TForm1::SpeedButton5Click(TObject *Sender)
     {
     if (SaveDialog1->Execute()) Memo1->Lines->SaveToFile(SaveDialog1->FileName);
     StatusBar1->Panels->Items[0]->Text=SaveDialog1->FileName;
     }

Кнопка SpeedButton2 отвечает за очистку окна редактирования. Однако в случае, когда в редакти уемом буфере содержится набранный текст, следует спросить пользователя, желает ли он сохранить текст. Для этой цели не имеет смысла создавать отдельную форму, содержащую всего-навсего текст вопроса и две кнопки. Более удобно воспользоваться функцией Windows API MessageBox,имеющей четыре параметра:
Параметр | Объяснение
hWnd | Идентификатор окна-владельца (число, может быть равным 0)
lpText | Текст сообщения (символьная строка)
lpCaption | Заголовок панели сообщения (символьная строка)
uType | Стиль панели сообщения (целая именованная константа, например, MB_OK, MB_ABORTRETRYIGNORE и др.) - полный список стилей можно найти в справочной системе Borland C++ Builder

Возвращаемое значение функции MessageBox - целая именованная константа, указ вающая на тип нажатой пользователем кнопки: IDABORT, IDCANCEL, IDIGNORE, IDNO, IDOK, IDRETRY или IDYES. В нашем случае удобно предложить пользователю выбрать о ну из кнопок "Да" или "Нет" и сохранять набранный текст в виде файла, если пользователь нажмет кнопку "Да" (что именно окажется написанным на кнопке - "Да" и и "Yes" - зависит от языковой версии операционной системы).

Для сохранения набранного текста можно использовать готовую функцию SpeedButton5Click. В соответствии с этим обработчик события при нажатии на кнопку SpeedButton2 будет выглядеть следующим образом:

     void __fastcall TForm1::SpeedButton2Click(TObject *Sender)
     {
     if (Memo1->Lines->Count>0)
     {
     if (MessageBox(0,"Сохранить содержимое окна редактирования? ",
     "Подтвердите сохранение",MB_YESNO)==IDYES)
     {
     SpeedButton5Click(Sender)
     }
     };
     Memo1->Clear();
     StatusBar1->Panels->Items[0]->Text="Без имени";
     }

Кнопка SpeedButton1 закрывает окно приложения. В этом случае нужно также предложить пользователю сохранить набранный текст, воспользовавшись только что созданной функцией SpeedButton2Click:

     void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
     { SpeedButton2Click(Sender);
     Close();
     }
Кнопка SpeedButton4 отвечает за сохранение редактируемого файла:
     void __fastcall TForm1::SpeedButton4Click(TObject *Sender)
     {
     if (StatusBar1->Panels->Items[0]->Text=="Без имени")
     SpeedButton5Click(Sender);
     else Memo1->Lines->SaveToFile(StatusBar1->Panels->Items[0]->Text)
     }

Здесь требуются некоторые пояснения. Если пользователь открыл существующий фай или уже сохранил редактируемый файл под каким-либо именем, оно указано на панели состояния (StatusBar1), и открытие диалога для выбора имени файла уже не требуется. Если же имя файла не определено (пользователь только что создал новый файл), следует вызвать диалог сохранения файла, воспользовавшись функцией SpeedButton5Click.

Кнопки SpeedButton6 и SpeedButton7 отвечают за перенос и копирование выделенного в окне редактирования фрагмента текста в буфер обмена.

     void __fastcall TForm1::SpeedButton6Click(TObject *Sender)
     {
     Memo1->CutToClipboard();
     }
     //--------------------------------------------------------
     void __fastcall TForm1::SpeedButton7Click(TObject *Sender)
     {
     Memo1->CopyToClipboard();
     }
Кнопка SpeedButton8 отвечает за сохранение редактируемого файла:
     void __fastcall TForm1::SpeedButton8Click(TObject *Sender)
     {
     Memo1->PasteFromClipboard();
     }

Кнопка SpeedButton9 отвечает за вывод на экран диалоговой панели "О программе". Наличие подобной иалоговой панели является стандартом для современных приложений. Для разнообразия воспользуемся готовым шаблоном панели About из репозитория объектов C++ Builder. Выберем пункт меню File/New и со страницы Forms блокнота, содержащегося в диалоговой панели New Items, выберем шаблон AboutBox с опцией Copy. Отредактируем полученную форму:

Рис. 12. Вид диалоговой панели About.

Теперь наше приложение состоит из двух форм. Главной формой приложения является созданная первой форма Form1. По умолчанию при запуске приложения обе формы создаются автоматически, и главная форма будет показана на экране. Однако отметим, что создание формы, в том числе и не отображенной на экране, отбирает у операционной системы некоторые ресурсы. Может быть, это несущественно для небольшого приложения, но в общем случае рекомендуется формы, обращение к которым происходит редко, создавать динамически и уничтожать после использования. Для этого следует вызвать диалоговую панель опций проекта (пункт меню Options/Project) и перенести AboutBox в список Available Forms (рис. 13 )

Рис. 13. Изменение опций проекта.

Обработчик события при нажатии на кнопку SpeedButton9 будет выглядеть следующим образом:

     void __fastcall TForm1::SpeedButton9Click(TObject *Sender)
     {
     Application->CreateForm(__classid(TAboutBox), &AboutBox);
     AboutBox->ShowModal();
     AboutBox->Free();
     }

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

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

Можно скомпилировать приложение и проверить его работу, проверив, что происхо ит при нажатии на кнопки. Однако готовым его назвать нельзя хотя бы по той причине, что оно практически не управляется с клавиатуры (а полноценное Windows-приложение обязано быть работоспособным без использования мыши - это не то ько правило хорошего тона при программировании, но и требование стандарта Microsoft). Дело в том, что компонент TSpeedButton не может получить фокус ввода (это его особенность). Поэтому кнопки инструме тальных панелей всегда дублируют пункты главного меню приложения.

Создание меню

Итак, создадим меню для нашего редактора. Для этой цели поместим на главную фо му приложения компонент TMainMenu со страницы Standard. Нажав правую клавишу мыши, из контекстного меню выберем пункт Menu Designer. Перемещаясь с помощью стрелок клавиатуры, создадим новые компоненты - пункты меню верхнего и последующего уровней, вводя текстовые строки в колонку значений напротив свойства Caption.

Создадим следующие меню: "&Файл" (с пунктами "Созд&ать", "&Открыть...", "&Сохранить", "Сохранить &как...", '"-","В&ыход"), "&Вид" (с пунктом "&Инструментальная панель"), "&Редактирование" (с пунктами "&Вырезать" "&Копировать", "Вс&тавить") и "&?" с пунктом "&О программе".

Если в свойстве Caption какого-либо пункта меню стоит знак "-", в этом месте появится горизонтальная разделительная линия.

Значок "&" нужен для связывания с пунктом меню так называемых "горячих" клавиш. Если перед какой-либо буквой в названии пункта меню стоит такой значок, то при отображении меню эта буква оказывается подчеркнутой, и нажатие на соответствующую уквенную клавишу при нажатой клавише Alt приведет к активизации соответствующего пункта меню. Разумеется, в одном меню все "горячие" клавиши должны быть разными, хотя C++ Builder этого не проверяет.

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

Рис. 14. Создание меню с помощью Menu Designer.

Теперь в инспекторе объектов выберем страницу событий и свяжем уже созданные функции SpeedButton1Click, ... SpeedButton9Click с соответствующими пунктами меню, выбрав названия функций из выпадающего списка.

У нас остались неиспользованными пункт меню "Панель инструментов". Присвоим свойству Checked этого пункта меню значение true. Создадим для пункта меню "Панель инструментов" следующий обработчик события OnClick:

     void __fastcall TForm1::N9Click(TObject *Sender)
     {
     N9->Checked=!N9->Checked;
     Panel1->Visible=N9->Checked;
     }

Наконец, создадим контекстные меню для различных элементов главной формы при ожения. Для этого положим на форму два компонента TPopupMenu - один с пунктами "Вырезать", "Копировать", "Вставить", а второй - с пунктом "Скрыть". Выберем подходящие обработчики события OnClick из имеющихся функций для этих пунктов меню. И, наконец, для компонентов Memo1 и Panel1 выберем из выпадающего списка соответствующие имена контекстных меню.

Итак, мы создали текстовый редактор с панелью инструментов, главным и контекстным меню и диалоговой панелью "О программе". Окончательный вид работающего приложения представлен на рис. 15.

Рис. 15. Так выглядит готовое приложение.

В заключение отметим, что можно несколько облегчить свою работу, воспользовавшись шаблоном Аpplication Wizard со страницы Projects репозитория объектов. Однако в любом случае необходим перевод меню на русский язык и создание интерфейсных элементов для редактирования данных (в нашем случае это один компонент TMemo), а также создание обработчиков событий, связанных с этими интерфейсными элементами.


Подборка статей по С++ Builder : Оптимизация приложений С++Builder в архитектуре клиент/сервер

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/cb_498.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Оптимизация приложений С++Builder в архитектуре клиент/сервер

Наталия Елманова

Контроль запросов с помощью SQL Monitor
Минимизация обращений к серверу и сети
Оптимизация использования сервера
Оптимизация клиентского приложения

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

Однако сам по себе факт переноса имеющейся базы данных из настольной СУБД на какой-либо сервер баз данных с соответствующей корректировкой настроек BDE (или других средств доступа к данным) отнюдь не гарантирует повышения производительности информационной системы в целом. Представьте себе, например, базу данных, содержащую одну-единственную таблицу из сотни записей и пяти целочисленных полей, содержащуюся в Oracle Workgroup Server, функционирующем под управлением Windows NT на персональном компьютере с 16 Мб оперативной памяти, и однопользовательское приложение, использующее навигационные методы для ее редактирования. В этом случае, безусловно, проще хранить данные в таблице формата dBase или Paradox - производительность системы будет в этом случае, скорее всего, намного выше, так как такой сервер, как Oracle, требует сам по себе немало ресурсов, а объем обрабатываемых данных и технология их обработки не оправдывают затрат, связанных с приобретением, установкой и эксплуатацией серверной СУБД такого класса. Данный пример, конечно, несколько утрирует реальную ситуацию, но иногда на практике происходят и более экзотические случаи…

Итак, какие шаги нужно предпринять для того, чтобы действительно повысить эффективность работы пользователей и производительность системы в целом? Первым шагом в данном направлении является, конечно, выбор сервера. В этом случае, к сожалению, нельзя давать однозначных рекомендаций типа "возьмите Oracle, он надежен" или "возьмите IB, он недорого стоит". Выбор сервера, управляющей им операционной системы и соответствующего аппаратного обеспечения должен осуществляться с учетом реальных и потенциально ожидаемых условий эксплуатации системы, таких, как скорость роста объема данных (например, в мегабайтах в месяц), интенсивность транзакций, вероятность многопользовательского доступа к одной или соседним записям в таблицах (при высокой вероятности желательно выбрать сервер, при использовании которого можно избежать страничных блокировок), потенциальный рост интенсивности работы пользователей, наличие повышенных требований к безопасности и защите данных (некоторые серверные СУБД выпускаются в разных исполнениях, отличающихся друг от друга степенью защищенности данных), необходимость использования продуктов сторонних производителей (таких, как ODBC-драйверы, дополнительные библиотеки и утилиты и др.), наличие связанных с этим проблем (типичным примером из недавней реальной практики была, например, проблема поиска ODBC-драйвера к серверу Centura SQLBase 6.0, поддерживающего использование хранимых процедур). Не менее, чем технические, важны и финансовые аспекты этой проблемы. Планируется ли использовать для установки серверной СУБД уже имеющеся вычислительные мощности и операционную систему или следует приобрести новые? В какую сумму обойдется приобретение серверной СУБД, клиентских лицензий, аппаратного обеспечения? Сколько будет стоить администрирование этой СУБД и управляющей ей операционной системы, а также обучение будущих администраторов и программистов? Сколько подключений к серверу допускается при приобретении одной лицензии - одно, два, четыре? Каковы условия, налагаемые лицензионными соглашениями при использовании мультиплексирования соединений за счет эксплуатации серверов приложений, если в дальнейшем возможен переход к трехзвенной архитектуре? Принятие решения о выборе серверной СУБД существенно зависит от ответа на все эти вопросы, и не всегда технические аспекты или мнение разработчиков определяют в конечном итоге выбор сервера. Нередки также случаи, когда предполагается использование уже имеющейся в наличии серверной СУБД (или даже готовой базы данных).

Предположим, что сервер выбран (исходя из вышеизложенных или каких-либо иных соображений). Каким образом следует использовать предоставляемые им возможности? Эффективность эксплуатации информационной системы с точки зрения производительности зависит от согласованной работы трех ее составных частей - сервера баз данных, клиентского приложения и клиентской части серверной СУБД, функционирующих на рабочей станции, и сети, и неоптимальная работа одной из этих частей может свести к нулю результат всех усилий, направленных на оптимизацию работы остальных частей. Таким образом, проблема оптимизации работы информационной системы достигается путем решения нескольких задач: оптимизации клиентской части, оптимизации серверной части, снижения сетевого трафика. Ниже мы рассмотрим некоторые приемы, способствующие в той или иной степени решению этих задач. Однако перед этим изучим один из простейших способов контроля содержимого запросов, пересылаемых на сервер баз данных библиотекой BDE, и результатов их выполнения, с помощью утилиты SQL Monitor, входящей в комплект поставки С++Builder.

Контроль запросов с помощью SQL Monitor.

SQL Monitor используется для контроля запросов, пересылаемых клиентским приложением серверу баз данных посредством BDE, и их результатов, а также измерения времени между ними. Для его запуска следует выбрать пункт SQL Monitor из меню Database C++Builder. Главное окно SQL Monitor состоит из двух частей. В верхней части отображаются последовательно генерируемые SQL-предложения и сведения об откликах сервера, а также порядковый номер и время их наступления, а в нижней части - полный текст SQL-запроса. Список, отображаемый в верхнем окне, можно сохранить в файле для дальнейшего анализа. На рис.1 представлен типичный вывод сведений при работе приложения, рассмотренного в предыдущей статье данного цикла.

Вывод сведений о выполнении сервером запросов в SQL Monitor

Рис.1. Вывод сведений о выполнении сервером запросов в SQL Monitor

При использовании SQL Monitor возможен выбор типов отображаемых сведений. Их можно выбрать в диалоге Trace Options, вызываемом из меню Options.

Диалог Trace Options для выбора действий, отображаемых в SQL Monitor

Рис.2. Диалог Trace Options для выбора действий, отображаемых в SQL Monitor

SQL Monitor позволяет отображать сведения о следующих действиях:

  • Prepared Query Statements - SQL-предложения, передаваемые на сервер
  • Executed Query Statements - SQL-предложения, готовые к выполнению сервером
  • Statement Operations - действия, выполняемые сервером (FETCH, EXECUTE и др.)
  • Connect/Disconnect - действия, связанные с установкой или разрывом соединения с сервером.
  • Transactions - действия, связанные с выполнением транзакций (BEGIN, COMMIT, ROLLBACK)
  • Blob I/O - действия, связанные с передачей Blob-полей
  • Miscellaneous - другие действия
  • Vendor Errors - сообщения об ошибках, возвращаемые сервером
  • Vendor Calls - вызовы функций API клиентской части, связанных с обращением к серверу

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

Минимизация обращений к серверу и сети

Минимизация связей с сервером влияет на производительность всех составных частей информационной системы - клиента, сервера и сети. Лишние связи с сервером приводят к созданию дополнительных объектов (таких, как TDatabase) в клиентском приложении, генерации дополнительных запросов к серверу для выяснения прав пользователя на доступ к тем или иным объектам базы данных, а также к непроизводительному использованию ресурсов сервера. Для минимизации связей с сервером можно использовать такие приемы, как использование в явном виде компонента TDatabase вместо неявного их создания, использование кэширования данных и структуры, хранение сведений о метаданных в клиентском приложении, использование локальных фильтров и др.

Использование компонента TDatabase

При использовании нескольких компонентов TDataSet следует иметь в виду, что каждый из них стремится во время выполнения создать неявно свой объект TDatabase для связи с сервером. Если же поместить компонент TDatabase на форму или в модуль данных на этапе проектирования приложения, и связать с ним все компоненты TDataSet, указав его имя в качестве значения свойства DatabaseName этих компонентов, все они будут использовать одну общую связь, обеспеченную этим компонентом.

Использование параметра SQLPASSTHRU MODE

Еще один способ минимизации связей с сервером заключается в изменении значения параметра SQLPASSTHRU MODE компонента TDatabase (либо псевдонима, созданного утилитой конфигурации BDE). Этот параметр определяет, могут ли использоваться общие соединения с базой данных запросами, сгенерированными приложением (например, с помощью компонента TQuery), и запросами, сгенерированными самой библиотекой BDE (например, при реализации навигационных методов компонента TTable). Значением этого параметра по умолчанию является NOT SHARED, позволяющее избежать возможных конфликтов при многопользовательском обновлении данных, но создающее отдельные соединения с базой данных для обоих типов запросов.

Наиболее эффективным с точки зрения минимизации соединений с базой данных значением этого параметра в большинстве случаев является значение SHARED AUTOCOMMIT. При использовании этого значения изменения каждой записи в таблицах немедленно фиксируются сервером независимо от типа вызвавшего их запроса, но при этом оба типа запросов могут использовать одно и то же соединение с базой данных. Этот режим наиболее близок к режиму, в котором используются сетевые версии настольных СУБД. Однако так как сервер в этом случае должен немедленно фиксировать результаты изменения записей, он инициирует и завершает отдельную транзакцию при изменении каждой записи, что может привести к перегрузке сервера и сети и к снижению производительности вместо ожидаемого ее повышения. Поэтому эффективность использования такого режима должна быть обязательно проверена путем тестирования.

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

Кэширование метаданных на рабочей станции

Еще один способ минимизации связей с сервером заключается в использовании кэширования структуры таблиц на рабочей станции. В этом случае снижается число обращений к серверу с целью определения метаданных, т.е. количества столбцов в используемых в приложении таблицах, их имен и типов данных. Для этой цели используются следующие параметры псевдонима базы данных (или компонента TDatabase):
ENABLE SCHEMA CACHE - разрешено ли кэширование метаданных;
SCHEMA CACHE SIZE - количество таблиц, структура которых кэшируется;
SCHEMA CACHE TIME - время хранения информации в кэше в секундах; значение -1 соответствует времени хранения данных в кэше до закрытия приложения;
SCHEMA CACHE DIR - каталог для кэширования метаданных.

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

Использование потомков TField в клиентском приложении

Другим способом хранения на рабочей станции приложении сведений о метаданных является использование компонентов - потомков TField. Так как соответствующие объекты хранят сведения о структуре таблиц непосредственно в приложении, на этапе выполнения не производится обращений на сервер с целью получения метаданных. Использование потомков TField предпочтительнее, чем использование методов FieldByName() или свойства Fields, так как последние используют обращение к серверу для получения сведений о типах полей. Ограничения на применение компонентов - потомков TField такие же, как и в предыдущем случае - их использование рекомендуется при стабильной структуре таблиц. Помимо этого, изменение структуры данных на сервере может потребовать модификации приложения и, как следствие, установку его новой версии на рабочие станции.

Кэширование данных на рабочей станции

Помимо кэширования метаданных нередко применяется и кэширование на рабочей станции самих данных. Для этой цели следует установить равным true значение свойства CachedUpdates соответствующего компонента TDataSet. В этом случае все внесенные пользователем изменения сохраняются в локальном кэше. Сохранение данных на сервере производится с помощью метода ApplyUpdates() компонента TDataSet, а метод CommitUpdates() очищает кэш. В целом такой метод снижает сетевой трафик и суммарное число соединений с сервером, так как, во-первых, при редактировании данных в кэше не требуется наличия соединения с сервером, а во-вторых, сохранение нескольких записей из кэша на сервере может быть осуществлено путем выполнения одной-единственной транзакции. Помимо этого, снижается суммарное число блокировок записей на сервере, так как в процессе редактирования данных в кэше необходимости в блокировках нет.

Использование локальных фильтров при небольших объемах данных

Если компонент TDataSet доставляет на рабочую станцию небольшой по объему набор данных, сравнимый с размером кэша рабочей станции (определяемого параметрами MINBUFSIZE и MAXBUFSIZE системных настроек BDE), он будет полностью кэшироваться на рабочей станции. В этом случае применение локальных фильтров более предпочтительно, чем использование запросов с предложением WHERE, направляемых на сервер, так как в первом случае не требуется обращение к серверу.

Оптимизация использования сервера

Использование хранимых процедур

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

Однако следует иметь в виду, что хранимые процедуры пишутся на процедурном расширении SQL используемого сервера. Cуществуют официальные стандарты непроцедурного языка SQL ANSI/ISO SQL-86, SQL-89 и SQL-92, но на сегодняшний день не существует стандартов на процедурные расширения этого языка. Каждая серверная СУБД имеет свой набор процедурных расширений, отличающийся от соответствующих расширений других СУБД. Некоторые сервера, например Borland IB Database, поддерживают создание и использование в процедурах функций, определенных пользователем (UDF - User Defined Functions), а некоторые не поддерживают. Поэтому при смене платформы хранимые процедуры, скорее всего, потребуется переписывать. Отметим также, что чаще всего серверные хранимые процедуры создаются путем ручного кодирования, и для их создания, как правило, не существует удобных визуальных средств разработки и отладки наподобие имеющихся в C++Builder. Поэтому при принятии решения о создании тех или иных хранимых процедур не мешает оценить возможные трудозатраты - иногда может оказаться, что они не стоят ожидаемого эффекта.

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

Использование предварительной подготовки запросов

При использовании компонентов TQuery нередко бывает полезно использовать метод Prepare(), особенно если компонент TQuery содержит параметризованный запрос. Метод Prepare() осуществляет пересылку запроса на сервер, где он оптимизируется и компилируется, а при открытии запроса на сервер в этом случае посылаются только его параметры. Особенно заметным повышение производительности может оказаться тогда, когда параметризованные запросы с различными значениями параметров повторяются часто - в этом случае повторная подготовка запроса не потребуется. Если же метод Prepare() не вызывается явно, он будет автоматически вызываться неявно каждый раз при пересылке параметров, инициируя пересылку всего текста запроса на сервер.

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

Использование представлений (View) и параметризованных запросов.

Нередко начинающие программисты используют динамическое создание запросов на этапе выполнения, изменяя содержимое строкового массива, содержащегося в свойстве SQL компонента TQuery (например, периодически модифицируя предложение WHERE). При часто повторяющихся запросах такого типа это не самый оптимальный способ пересылки запросов на сервер, так как в этом случае обязательно осуществляется предварительная подготовка запросов, заключающаяся в пересылке всего текста на сервер, а также оптимизации и компиляции его сервером. Более предпочтительным в этом случае является использование параметризованных запросов и метода Prepare(), либо использование представлений (View) сервера, представляющих собой не что иное как хранимый на сервере заранее скомпилированный запрос. В последнем случае можно избежать не только лишних повторных компиляций запроса сервером, но и излишней перегрузки клиента генерацией запросов.

Использование свойства UpdateMode

Свойство UpdateMode компонентов TDBDataSet определяет состав оператора WHERE, генерируемого BDE при обновлении данных. Рассмотрим, каким получится оператор WHERE при редактировании поля SYMBOL содержащейся на сервере Oracle Workgroup Server копии таблицы HOLDINGS из входящей в комплект поставки C++Builder базы данных BCDEMOS при разных значениях этого свойства. Сгенерированные SQL-предложения можно пронаблюдать с помощью SQL Monitor.

По умолчанию значением свойства UpdateMode является UpWhereAll, и в этом случае BDE генерирует предложение WHERE, содержащее все поля таблицы. При этом сгенерированный оператор SQL, если только он не переопределен с помощью компонента TUpdateSQL, будет выглядеть следующим образом:

UPDATE "HOLDINGS" SET "SYMBOL"=:1 WHERE "ACCT_NBR"=:2 AND "SYMBOL"=:3 AND "SHARES"=:4 AND "PUR_PRICE"=:5 AND "PUR_DATE"=:6 AND "ROWID"=:7.

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

Одним из других возможных значений этого свойства является UpWhereChanged, при котором в предложении WHERE содержатся только поля, измененные в данном запросе, и ключевые поля. В этом случае запрос имеет следующий вид:

UPDATE "HOLDINGS" SET "SYMBOL"=:1 WHERE "ROWID"=:2 AND "SYMBOL"=:3

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

Третьим возможным значением свойства UpdateMode является UpWhereKeyOnly. В этом случае предложение WHERE содержит только ключевое поле:

UPDATE "HOLDINGS" SET "SYMBOL"=:1 WHERE "ROWID"=:2

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

Повышение эффективности SQL-запросов

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

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

SELECT * FROM <имя таблицы> WHERE (SELECT COUNT (*) FROM <имя таблицы> WHERE <условие>) >0
заставит сервер при выполнении внутреннего подзапроса перебрать все строки таблицы, проверяя соответствие каждой записи указанному условию, тогда как запрос вида

SELECT * FROM <имя таблицы> WHERE EXISTS (SELECT * FROM <имя таблицы> WHERE <условие>)
заставит сервер перебирать записи до нахождения первой записи, удовлетворяющей указанному условию. Лишний перебор записей на сервере, естественно, занимает некоторое время - чудес не бывает.

Многие приемы оптимизации связаны с использованием индексов. Если какое-либо поле таблицы часто используется в предложении WHERE, сравнивающем его значение с какой-либо константой или параметром, наличие индекса для этого поля ускоряет подобные операции. По этой же причине рекомендуется индексировать внешние ключи у таблиц с большим числом записей. Однако следует иметь в виду, что поддержка индексов замедляет операции вставки записей, поэтому при проектировании данных следует взвесить все "за" и "против" создания индексов, а еще лучше - провести соответствующее тестирование, заполнив таблицы случайными данными (для этой цели можно написать соответствующее приложение, а еще лучше - воспользоваться готовыми средствами тестирования типа SQA Suite).

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

Особо следует отметить проблемы, связанные с использованием вложенных запросов. Дело в том, что скорость выполнения запроса существенно зависит от числа уровней вложенности подзапросов (время выполнения примерно пропорционально произведению числа записей в таблицах, используемых в подзапросах). Фактически проверка соответствия условию WHERE каждой записи из внешнего подзапроса инициирует выполнение внутреннего подзапроса, что особенно заметно сказывается при большом числе записей. В практике автора чуть более года назад был случай, когда при приведении в порядок одной из используемых корпоративных информационных систем после выполнения нескольких обычных запросов на обновление данных в таблице с несколькими десятками тысяч записей, выполнявшихся в течение нескольких секунд, был инициирован вложенный запрос на обновление данных к этой же таблице. Этот запрос выполнялся более двух часов (чего, вообще говоря, и следовало ожидать). Поэтому использовать вложенные запросы следует только в тех случаях, когда без них нельзя обойтись. Альтернативой использования вложенных запросов может служить фильтрация результатов обычного запроса в клиентском приложении либо последовательное выполнение нескольких запросов с созданием временных таблиц на сервере.

Оптимизация клиентского приложения

Методы оптимизации клиентского приложения мало чем отличаются от методов оптимизации обычных приложений C++Builder. Обычно оптимизация заключается в повышении быстродействия приложения и в снижении объема используемых ресурсов операционной системы.

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

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

Еще одним способом экономии ресурсов клиентского приложения является использование более экономичных интерфейсных элементов в случаях, где это возможно (например, TDBText или TLabel вместо TDBEdit, TLabel вместо TDBMemo при отображении полей, редактирование которых не предполагается, TDBGrid вместо TDBControlGrid и т.д.).

Еще один прием, повышающий быстродействие клиентского приложения, заключается в сокращении числа операций, связанных с выводом данных из таблиц на экран, например, при "пролистывании" большого количества строк в компонентах типа TDBGrid или TDBCtrlGrid в процессе навигации по набору данных или какой-либо их обработки. В этом случае рекомендуется на время отключать связь интерфейсных элементов с компонентом TDataSource, установив значение его свойства Enabled равным false (пример использования этого приема будет приведен ниже).

О навигационных методах и "клипперном" стиле програмирования

Говоря об оптимизации клиент-серверных информационных систем, хотелось бы отдельно остановиться на одной очень распространенной ошибке, совершаемой программистами, имеющими большой опыт работы с настольными СУБД и средствами разработки, базирующимися на xBase-языках, такими, как Clipper, dBase, FoxPro и др. При использовании средств разработки такого рода какое-либо изменение данных в таблице согласно каким-либо правилам осуществляется обычно путем создания цикла типа:

USE HOLDINGS
GO TOP
DO WHILE !EOF()
PUR_PRICE=PUR_PRICE+10
SKIP
ENDDO
CLOSE

В приведенном фрагменте xBase-кода PUR_PRICE - имя поля таблицы HOLDINGS, подверженного изменению.

При переходе к архитектуре клиент/сервер и средствам разработки, поддерживающим SQL, поначалу возникает естественное желание продолжать писать подобный код, используя циклы и навигацию по таблице. Это не так страшно в случае использования C++Builder с настольными СУБД - локальный SQL, способный быть альтернативой в этом случае, в конечном итоге также инициирует перебор записей таблицы. Вообще говоря, то же самое происходит и при выполнении запроса типа UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE+10 на сервере баз данных, но подобный цикл является внутренним процессом сервера, в котором не задействованы ни клиент, ни сеть. Однако при использовании "клипперного" стиля программирования библиотека BDE вовсе не обязана догадываться, что имел в виду программист, написавший подобный цикл, и генерирует вовсе не такие запросы!

Рассмотрим простой пример. Создадим копию таблицы HOLDINGS.DBF из входящей в комплект поставки C++Builder базы данных DBDEMOS на каком-либо сервере баз данных, например, Personal Oracle (воспользовавшись, например, утилитой Data Migration Wizard из комплекта поставки Borland C++Builder). Затем создадим новое приложение, состоящее из одной формы, включающей компоненты TDBGrid, TTable, TDataSource, TQuery, TDBNavigator и три кнопки (рис.3).

Главная форма приложения для тестирования SQL-запросов, генерируемых BDE

Рис.3. Главная форма приложения для тестирования SQL-запросов, генерируемых BDE

Установим следующие значения свойств используемых компонентов (табл.1):

Таблица 1.

Компонент | Свойство | Значение
DBNavigator1 | DataSource | DataSource1
DBGrid | DataSource | DataSource1
Button1 | Caption | 'Use SQL'
Button2: | Caption | 'Update records'
Button3: | Caption | 'Exit'
DataSource1 | DataSet | Table1
Table1 | DatabaseName | ORACLE7
 | TableName | HOLDINGS
 | UpdateMode | UpWhereKeyOnly
Table1PUR_PRICE | FieldName | 'PUR_PRICE'
Query1 | DatabaseName | ORACLE7
 | SQL | 'UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE+10'

Теперь создадим обработчики событий, связанные с нажатием на кнопки. Кнопка Update records реализует аналог фрагмента xBase-кода, приведенного выше:

 void __fastcall TForm1::Button2Click(TObject *Sender)
 {
 Table1->First();
 DataSource1->Enabled=false;  //Не будем издеваться над видеоадаптером!
 while (!Table1->Eof)
 {
  Table1->Edit();
  Table1PUR_PRICE->Value=Table1PUR_PRICE->Value+10;
  Table1->Next();
 }
 DataSource1->Enabled=true; //Посмотрим, что получилось...
 }

Временное отключение связи между DataSource1 и Table1 в данном обработчике событий сделано для того, чтобы исключить перерисовку компонента DBGrid1 при изменении каждой записи.

Кнопка Use SQL реализует выполнение одиночного SQL-запроса UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE+10:

 void __fastcall TForm1::Button1Click(TObject *Sender)
 {
 Query1->Prepare();
 Query1->ExecSQL();
 Table1->Refresh(); //Посмотрим на результат...
 }

Скомпилировав приложение, запустим SQL Monitor и посмотрим, какие запросы генерируются BDE при нажатии на эти кнопки.

При использовании кнопки Update records log-файл имеет следующий вид:

  1. 14:37:08 SQL Prepare: ORACLE - UPDATE "HOLDINGS" SET "PUR_PRICE"=:1 WHERE "ROWID"=:2
  2. 14:37:08 SQL Execute: ORACLE - UPDATE "HOLDINGS" SET "PUR_PRICE"=:1 WHERE "ROWID"=:2
  3. 14:37:08 SQL Stmt: ORACLE - Close
  4. 14:37:08 SQL Prepare: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
  5. 14:37:08 SQL Execute: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
  6. 14:37:08 SQL Misc: ORACLE - Set rowset size
  7. 14:37:08 SQL Stmt: ORACLE - Fetch
  8. 14:37:08 SQL Stmt: ORACLE - EOF
  9. 14:37:08 SQL Stmt: ORACLE - Close
  10. 14:37:08 SQL Prepare: ORACLE - UPDATE "HOLDINGS" SET "PUR_PRICE"=:1 WHERE "ROWID"=:2

И так далее, пока не кончатся все записи…

  319. 14:37:10 SQL Prepare: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
  320. 14:37:10 SQL Execute: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
  321. 14:37:10 SQL Misc: ORACLE - Set rowset size
  322. 14:37:10 SQL Stmt: ORACLE - Fetch
  323. 14:37:10 SQL Stmt: ORACLE - EOF
  324. 14:37:10 SQL Stmt: ORACLE - Close

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

При использовании кнопки Use SQL log-файл имеет совершенно другой вид:

  1. 14:35:51 SQL Prepare: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  2. 14:35:51 SQL Transact: ORACLE - Set autocommit on/off
  3. 14:35:51 SQL Execute: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10 14:35:51 SQL Stmt: ORACLE - Close

Остальные SQL-запросы, содержащиеся в log-файле, генерируются BDE при выполнении метода Refresh() компонента Table1:

  5. 14:35:51 SQL Prepare: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
  6. 14:35:51 SQL Execute: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
  7. 14:35:51 SQL Misc: ORACLE - Set rowset size
  8. 14:35:51 SQL Stmt: ORACLE - Fetch
  9. 14:35:51 SQL Stmt: ORACLE - EOF
  10. 14:35:51 SQL Stmt: ORACLE - Close
  11. 14:35:51 SQL Prepare: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE (("ACCT_NBR" IS NULL OR "ACCT_NBR"> :1)) ORDER BY "ACCT_NBR" ASC
  12. 14:35:51 SQL Execute: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE (("ACCT_NBR" IS NULL OR "ACCT_NBR"> :1)) ORDER BY "ACCT_NBR" ASC
  13. 14:35:51 SQL Misc: ORACLE - Set rowset size
  14. 14:35:51 SQL Stmt: ORACLE - Fetch

Если из текста обработчика события Button1Click удалить строку

Table1->Refresh();,

то действия с 5-го по 14-е выполняться не будут. Кроме того, при нажатии на эту же кнопку несколько раз подряд log-файл будет иметь следующий вид:

  1. 14:11:36 SQL Prepare: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  2. 14:11:36 SQL Execute: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  3. 14:11:40 SQL Stmt: ORACLE - Reset
  4. 14:11:40 SQL Execute: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  5. 14:14:17 SQL Stmt: ORACLE - Reset
  6. 14:14:17 SQL Execute: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  7. 14:14:19 SQL Stmt: ORACLE - Reset

Как видим, компиляция запроса сервером осуществляется в этом случае только один раз.

Итак, мы видим, что "клипперный" стиль программирования при работе с SQL-серверами абсолютно неприемлем - он приводит к перегрузкам сервера, сети и рабочей станции одновременно, а разница в скорости выполнения заметна даже при небольшом объеме таблицы и использовании локального сервера, поэтому, анализируя причины низкой производительности приложений, стоит посмотреть - а нет ли в клиентском приложении подобных фрагментов кода?

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


Подборка статей по С++ Builder : Создание контроллеров автоматизации с помощью C++Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/com_3.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Создание контроллеров автоматизации с помощью C++Builder

Наталия Елманова

    Тестирование сервера автоматизации
      Создание контроллера
      Некоторые комментарии
    Создание контроллеров для произвольных серверов автоматизации
      Коллекции объектов внутри серверов автоматизации
      Использование информации из библиотек типов

Тестирование сервера автоматизации

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

Создание контроллера

На главной форме будущего приложения-контроллера разместим компоненты TEdit, TCheckBox, TOpenDialog, TSaveDialog, а также десять кнопок.

Главная форма контроллера автоматизации

Рис. 1. Главная форма контроллера автоматизации

Создадим обработчики событий, связанные с нажатием на кнопки (при этом следует сослаться на h-файл модуля ComObj):

 //---------------------------------------------------------------------------
 #include <vcl.h>
 #pragma hdrstop
 #include <ComObj.hpp>
 #include "autocon.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #pragma resource "*.dfm"
 TForm2 *Form2;
 Variant Serv;
 //---------------------------------------------------------------------------
 __fastcall TForm2::TForm2(TComponent* Owner)
     : TForm(Owner)
 {
 }
 //---------------------------------------------------------------------------

 void __fastcall TForm2::Button3Click(TObject *Sender)
 {
   Serv=CreateOleObject("Project1.MyAuto3");
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm2::Button1Click(TObject *Sender)
 {
 if (VarType(Serv)==varDispatch)
 Edit1->Text=IntToStr(Serv.OlePropertyGet("Width"));
 // имеет смысл проверить, что именно лежит в вариантной переменной…
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm2::Button2Click(TObject *Sender)
 {
 if (VarType(Serv)==varDispatch) Serv.OlePropertySet("Width",StrToInt(Edit1->Text));
 }
 //---------------------------------------------------------------------------

 void __fastcall TForm2::Button12Click(TObject *Sender)
 {
 if (VarType(Serv)==varDispatch)
 CheckBox1->Checked=Serv.OlePropertyGet("Visible");
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm2::Button13Click(TObject *Sender)
 {
 if (VarType(Serv)==varDispatch) Serv.OlePropertySet("Visible",CheckBox1->Checked);
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm2::Button11Click(TObject *Sender)
 {
 if (VarType(Serv)==varDispatch) Serv=Unassigned;
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm2::Button5Click(TObject *Sender)
 {
 if (VarType(Serv)==varDispatch)
 {if (OpenDialog1->Execute())
 Serv.OleProcedure("OpenFile",OpenDialog1->FileName);}
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm2::Button6Click(TObject *Sender)
 {
  if (VarType(Serv)==varDispatch)
  {  if (SaveDialog1->Execute())
  Serv.OleProcedure("SaveFile",SaveDialog1->FileName);}
 }

 //---------------------------------------------------------------------------
 void __fastcall TForm2::Button7Click(TObject *Sender)
 {
 if (VarType(Serv)==varDispatch) Serv.OleProcedure("NewFile");
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm2::Button4Click(TObject *Sender)
 {
 if (VarType(Serv)==varDispatch)
  Serv.OleProcedure("AddLine",Edit1->Text);
 }
 //---------------------------------------------------------------------------

Некоторые комментарии

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

Для управления сервером автоматизации мы создали переменную типа Variant (в C++Builder для этой цели имеется соответствующий класс) и вызвали функцию CreateOleObject, содержащуюся в модуле ComObj библиотеки VCL.

При выполнении функции CreateOleObject произойдет следующее. Эта функция, вызвав несколько функций Windows API, создаст экземпляр COM-объекта IDispatch и вернет его внутри вариантной переменной. Этот объект, в свою очередь, содержит интерфейс объекта (в данном случае нашего сервера автоматизации), методы которого мы хотим вызывать из приложения. Если исследовать реализацию функции CreateOleObject в исходном тексте модуля ComObj, можно обнаружить, что она, в свою очередь, вызывает функцию Windows API CoCreateInstance, являющуюся частью спецификации COM, назначение которой - создать объект из исполняемого файла или DLL.Переменная типа Variant может содержать разнообразные данные (строку, число и др., в том числе и интерфейс COM-объекта).

Отметим, что, в отличие от Visual Basic или Delphi, C++Builder не позволяет обращаться к методам и свойствам вариантных переменных, существование которых заранее неизвестно. Поэтому допустимый в Delphi код вида

 if VarType(Serv)=varDispatch then Serv.Width:=StrToInt(Edit1.Text);

не имеет аналога в C++Builder. Дело в том, что при создании контроллеров с помощью Delphi в случае объектов типа Variant, в отличие от объектов другого типа, например, TForm, компилятор не проверяет, имеется ли в действительности такое свойство (в данном случае Width) у данного объекта. На этапе выполнения такого кода происходит вызов функций Windows API, в результате работы которых меняется свойство Width объекта, содержащегося не в адресном проcтранстве созданного контроллера, а в адресном пространстве Excel.

В С++Builder достичь такого же результата можно с помощью следующего кода:

 if (VarType(Serv)==varDispatch) Serv.OlePropertySet("Width",StrToInt(Edit1->Text));

В этом случае на этапе выполнения производится вызов тех же самых функций Windows API, что и в предыдущем случае. OlePropertySet представляет собой оболочку для метода вариантной переменной Exec() (наряду с OlePropertyGet, OleProcedure и OleFunction, позволяющими получать значения свойств объектов автоматизации и выполнять их методы). Отметим, что в Delphi также можно использовать вызовы OlePropertySet, OlePropertyGet, OleProcedure, OleFunction.

После запуска контроллера при нажатии кнопки Connect запускается сервер. При нажатии кнопки Disconnect он выгружается. При нажатии кнопок New File, Open File и Save File происходит очистка окна редактирования, загрузка текста в окно редактирования сервера из файла, сохранение текста в файле. Кнопка Get Visible показывает и скрывает окно сервера в зависимости от наличия отметки возле надписи Visible, при этом в невидимом состоянии сервер продолжает выполнять свои функции. Нажатие кнопки Set Visible приводит отметку возле надписи Visible в соответствие значению свойства Visible сервера. Нажатие кнопки Get Width приводит к тому, что в строке редактирования в верхней части окна контроллера отображается ширина окна сервера в пикселах. Если ввести в строку редактирования другое число и нажать кнопку Set Width, ширина окна сервера станет равной введенному числу пикселов. Нажатие кнопки Add String приводит к тому, что в редактируемый текст добавляется строка, находящаяся в этот момент в поле редактирования.

Контроллер и сервер автоматизации, запущенные одновременно

Рис. 2. Контроллер и сервер автоматизации, запущенные одновременно

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

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

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

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

Обычно такие сведения содержатся в документации или файлах справочной системы, поставляемых с данным сервером, как, например, это сделано в MS Office или Seagate Crystal Reports Professional. Но в принципе такую информацию можно получить и из библиотеки типов.

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

Как и в предыдущем случае, для управления сервером автоматизации следует создать переменную типа Variant (в C++Builder для этой цели имеется соответствующий класс) и вызвать функцию CreateOleObject:

 Variant XL;
 ……

 XL=CreateOleObject("Excel.Application.8");

В качестве параметра функции CreateOleObject передается имя объекта, который мы хотим создать. Найти его можно в реестре Windows:

Запись в реестре, соответствующая выбранному серверу

Рис. 3. Запись в реестре, соответствующая выбранному серверу.

Коллекции объектов внутри серверов автоматизации.

Внутри некоторых OLE-серверов (в частности, приложений MS Office) существует иерархия вложенных объектов примерно следующего вида:

Примерная иерархия вложенных объектов OLE-сервера

Рис. 4. Примерная иерархия вложенных объектов OLE-сервера.

Свойствами объектов Excel могут являться так называемые коллекции объектов. Например, коллекция Workbooks является свойством объекта Excel.Application, при этом она содержит набор вложенных объектов - рабочих книг Excel, а те, в свою очередь, обладают свойством Worksheets, представляющим собой коллекцию рабочих листов, каждый из которых обладает свойством Cells, являющимся коллекцией ячеек. Аналогично, коллекция Charts также является свойством рабочей книги. Аналогично, внутри свойствами объектов Word могут быть коллекции Paragraphs, Words, Tables.

В С++Builder обращение к члену коллекции производится следующим образом:

Variant MyWorkbook=XL.OlePropertyGet("WorkBooks").OlePropertyGet("Item",1);

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

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

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

 //---------------------------------------------------------------------------
 #include <vcl.h>
 #pragma hdrstop
 #include <ComObj.hpp>
 #include "xlauto2.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #pragma resource "*.dfm"
 TForm1 *Form1;
 Variant XL,v0,v1,v2;
 //Function Item("Item");
 //---------------------------------------------------------------------------
 __fastcall TForm1::TForm1(TComponent* Owner)
     : TForm(Owner)
 {
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm1::Button1Click(TObject *Sender)
 {
 XL=CreateOleObject("Excel.Application.8");
 XL.OlePropertySet("Visible",true);
 v0=XL.OlePropertyGet("Workbooks");
 v0.OleProcedure("Add");
 v1=v0.OlePropertyGet("Item",1);
 v0=v1.OlePropertyGet("Worksheets") ;
 v0.OlePropertyGet("Item",1).OlePropertySet("Name","Бухгалтерия желтая");
 v0.OlePropertyGet("Item",2).OlePropertySet("Name","Бухгалтерия красная");
 for (int j=1;j<3;j++)
 {
 v1=v0.OlePropertyGet("Item",j);
 for (int i=1;i<11;i++)
 {
 v1.OlePropertyGet("Cells").OlePropertyGet("Item",i,1).OlePropertySet("Value",i);
 v1.OlePropertyGet("Cells").OlePropertyGet("Item",i,2).OlePropertySet("Value",i*5);
 v2=v1.OlePropertyGet("Cells").OlePropertyGet("Item",i,2);
 v2.OlePropertyGet("Font").OlePropertySet("Color",clBlue);
 v2.OlePropertyGet("Font").OlePropertySet("Bold",true);
 v2.OlePropertyGet("Interior").OlePropertySet("ColorIndex",9-3*j);
 }
 v1.OlePropertyGet("Cells").OlePropertyGet("Item",11,1).OlePropertySet("Value","=SUM(A1:A10)");
 v1.OlePropertyGet("Cells").OlePropertyGet("Item",11,2).OlePropertySet("Value","=SUM(B1:B10)");
 }
 XL.OlePropertySet("DisplayAlerts",false); //отключить диагностику при закрытии сервера
 XL.OlePropertyGet("Workbooks").OlePropertyGet("Item",1).OleProcedure("SaveAs","test.xls");
 XL.OleProcedure("Quit");
 }
 //---------------------------------------------------------------------------

Отметим, что для запуска Excel в фоновом режиме без отображения его окна на экране достаточно убрать из кода С++Builder строку:

 XL.OlePropertySet("Visible",true);

Результат работы приложения

Рис. 5. Результат работы приложения

Следует обратить внимание на то, что вариантная переменная XL объявлена за пределами процедуры манипуляции OLE-сервером. Это сделано для того, чтобы существование вариантной переменной не ограничивалось данной процедурой.

Использование информации из библиотек типов

Отметим, что в приведенном выше примере рабочая книга и рабочие листы были созданы с применением параметров, предлагаемых по умолчанию. Каким образом можно изменить эти параметры?

Рассмотрим следующий пример кода:

 XL=CreateOleObject("Excel.Application.8");
 XL.OlePropertySet("Visible",true);
 v0=XL.OlePropertyGet("Workbooks");
 v0.OleProcedure("Add",-4109);

Данный пример заставляет Excel создать пустой лист с диаграммой на основе шаблона, отличного от принятого по умолчанию.

Откуда в данном случае взялась константа -4109? Каким образом можно узнать, какими еще константами можно пользоваться при автоматизации с использованием данного сервера?

Ответы на эти вопросы можно получить, открыв библиотеку типов данного сервера. Сделать это можно, выбрав пункт меню File/Open, указав в списке возможных файлов Type Library и выбрав соответствующую библиотеку типов (для Excel это Excel8.olb, для Word - MSWord8.olb). При этом по истечение некоторого времени (эти библиотеки очень велики) будут созданы файлы с описанием всех использованных констант, а также свойств и методов содержащихся в сервере объектов (в данном случае Excel_TLB.cpp и Excel_TLB.h для С++Builder, либо Excel_TLB.pas для Delphi). При создании этих файлов появятся сообщения об ошибках, связанные с тем, что в коде Excel и Word могут использоваться зарезервированные слова Object Pascal или С++.

Редактор библиотеки типов при этом представляет все вложенные объекты в виде иерархической структуры, внутри которой можно найти значения необходимых констант. В частности, в разделе шаблонов можно найти список констант, характеризующих различные типы шаблонов листов Excel:

Значения констант, описывающих шаблоны MS Excel 97

Рис. 6. Значения констант, описывающих шаблоны MS Excel 97

Именно там и содержится ссылка на константу xlWBATChart = -4109, соответствующую шаблону диаграммы.

При необходимости использовать в коде приложения-клиента именованные константы можно сослаться на файл Excel_TLB.h иди Excel_TLB.pas в тексте модуля приложения.

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

Свойства и методы коллекции Workbooks

Рис. 7. Свойства и методы коллекции Workbooks

Используя сведения о константах, объектах, свойствах и методах OLE-сервера, можно модифицировать приведенный выше пример. Теперь мы создадим в Excel график на основе данных из рабочих листов, скопируем его в буфер обмена и перенесем в документ Word (об объектах и константах которого узнаем из библиотеки типов Word), снабдив график подписью. Соответствующий код выглядит следующим образом:

 //---------------------------------------------------------------------------
 #include <vcl.h>
 #pragma hdrstop

 #include "ole2.h"
 #include <ComObj.hpp>
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #pragma resource "*.dfm"
 TForm1 *Form1;
 Variant XL,v0,v1,v2, v22, vrange, WD,a,b,c;

 //---------------------------------------------------------------------------
 __fastcall TForm1::TForm1(TComponent* Owner)
     : TForm(Owner)
 {
 }
 //---------------------------------------------------------------------------

 void __fastcall TForm1::Button1Click(TObject *Sender)
 {
 XL=CreateOleObject("Excel.Application.8");
 XL.OlePropertySet("Visible",true);
 v0=XL.OlePropertyGet("Workbooks");
 v0.OleProcedure("Add");
 v1=v0.OlePropertyGet("Item",1);
 v0=v1.OlePropertyGet("Worksheets") ;
 v22=v1.OlePropertyGet("Charts") ;
 v22.OleProcedure("Add");
 v0.OlePropertyGet("Item",1).OlePropertySet("Name","Бухгалтерия желтая");
 v0.OlePropertyGet("Item",2).OlePropertySet("Name","Бухгалтерия красная");
 for (int j=1;j<3;j++)
 {
 v1=v0.OlePropertyGet("Item",j);
 for (int i=1;i<11;i++)
 {
 v1.OlePropertyGet("Cells").OlePropertyGet("Item",i,1).OlePropertySet("Value",i);
 v1.OlePropertyGet("Cells").OlePropertyGet("Item",i,2).OlePropertySet("Value",i*5);
 v2=v1.OlePropertyGet("Cells").OlePropertyGet("Item",i,2);
 v2.OlePropertyGet("Font").OlePropertySet("Color",clBlue);
 v2.OlePropertyGet("Font").OlePropertySet("Bold",true);
 v2.OlePropertyGet("Interior").OlePropertySet("ColorIndex",9-3*j);
 }
 v1.OlePropertyGet("Cells").OlePropertyGet("Item",11,1).OlePropertySet("Value","=SUM(A1:A10)");
 v1.OlePropertyGet("Cells").OlePropertyGet("Item",11,2).OlePropertySet("Value","=SUM(B1:B10)");
 }
 vrange=v0.OlePropertyGet("Item",1).OlePropertyGet("Range","A1:A10");
 v1=v22.OlePropertyGet("Item",1);
 v2=v1.OlePropertyGet("SeriesCollection");
 v2.OleProcedure("Add",vrange);
 vrange=v0.OlePropertyGet("Item",1).OlePropertyGet("Range","B1:B10");
 v2.OleProcedure("Add",vrange);
 v1.OleProcedure("Select");
 XL.OlePropertyGet("Selection").OleProcedure("Copy");
 WD=CreateOleObject("Word.Application.8");
 WD.OlePropertySet("Visible",true);
 WD.OlePropertyGet("Documents").OleProcedure("Add");
 a=WD.OlePropertyGet("Documents");
 b=a.OleFunction("Item",1);
 for (int i=1;i<5;i++)
 {b.OlePropertyGet("Paragraphs").OleProcedure("Add");};
 c=b.OleFunction("Range",1,2);
 c.OleProcedure("Paste");
  c=b.OleFunction("Range",3,3);
 c.OlePropertySet("Text","График, скопированный из рабочей книги  Excel ");
 XL.OlePropertySet("DisplayAlerts",false); //ioee??eou aeaaiinoeeo
 XL.OlePropertyGet("Workbooks").OlePropertyGet("Item",1).OleProcedure("SaveAs","test.xls");
 XL.OleProcedure("Quit");
 WD.OlePropertySet("DisplayAlerts",false); //ioee??eou aeaaiinoeeo
 b.OleProcedure("SaveAs","test2.DOC");
 WD.OleProcedure("Quit");

 }
 //---------------------------------------------------------------------------

Результаты работы приложения представлены на рис. 8.

Результат автоматизации создания диаграммы Excel и переноса ее в Word

Рис.8. Результат автоматизации создания диаграммы Excel и переноса ее в Word

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

Следующая статья данного цикла будет посвящена использованию OLE-документов в приложениях C++Builder.


Подборка статей по С++ Builder : Использование COM-технологии в C++Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/com_4.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Использование COM-технологии в C++Builder:
- удаленный запуск серверов автоматизации
- создание и использование OLE-документов в приложениях

Наталия Елманова
    1. Удаленный запуск серверов автоматизации
      1.1. Использование интерфейсов
      1.2. Маршалинг и удаленный доступ
      1.3. Как осуществить удаленный доступ
    2. Использование OLE-документов в приложениях C++Builder
      2.1. Создание OLE-объектов
      2.2. Хранение OLE-объектов в базах данных
      2.3. Использование свойств OleContainer
      2.4. Вместо заключения

Последняя статья данного цикла посвящена двум немаловажным вопросам использования COM-технологии в C++Builder: удаленному запуску серверов автоматизации и использованию в приложениях C++Builder OLE-документов.

1. Удаленный запуск серверов автоматизации

В предыдущих статьях данного цикла было рассказано о создании серверов и контроллеров автоматизации. Отметим, что серверы автоматизации могут выполняться как в адресном пространстве контроллера (такие серверы называются in-process-серверами и реализуются обычно в виде динамически загружаемых библиотек), так и в собственном адресном пространстве (такие серверы называются out-of-process-серверами и реализуются, как правило, в виде исполняемых файлов). Приложения MS Office, а также сервер автоматизации, создание которого рассматривалось в одной и предыдущих статей, относятся ко второму типу COM-серверов (такие COM-серверы называются локальными).

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

Один из часто встречающихся примеров практического использования удаленного доступа - использование удаленного запуска MS Excel, установленного на одном-единственном компьютере локальной сети, для печати отчетов и диаграмм. В этом случае можно снизить требования к ресурсам рабочих станций, содержащих приложения-контроллеры Excel (и сэкономить некоторые средства на покупке нескольких копий Excel).

Второй пример использования удаленного запуска серверов автоматизации, вызывающий в последнее время немалый интерес разработчиков информационных систем - это пример трехзвенной информационной системы с "тонким" клиентом - контроллером автоматизации, и сервером автоматизации (в данном случае он иногда называется сервером приложений), предоставляющим "тонкому" клиенту сервисы, связанные с доступом к данным, содержащимся, в свою очередь, в какой-либо серверной СУБД. В этом случае, помимо экономии ресурсов компьютеров, содержащих контроллеры, имеется еще одно преимущество, связанное с тем, что для доступа к данным используется программное обеспечение, требующее отдельной установки, сложной настройки и поддержки, такое как клиентская часть серверной СУБД, а также, как правило, библиотека Borland Database Engine, драйверы SQL Links, а иногда и ODBC-драйверы. Если это программное обеспечение используется только сервером, но не используется контроллерами, сопровождение такой системы значительно облегчается.

При рассмотрении работы таких серверов возникает естественный вопрос: каким образом контроллер обращается к свойствам и методам объектов, расположенных за пределами его адресного пространства? Для этого следует рассмотреть, каким образом вообще взаимодействуют COM-клиент и COM-сервер.

1.1 Использование интерфейсов

Единственным способом взаимодействия клиента c COM-объектом является использование интерфейсов. Интерфейс связан с группой функций, связанных, в свою очередь, с COM-объектом, и не содержит их реализации. На этапе выполнения интерфейс всегда представляется указателем, идентифицируемым с помощью идентификатора IID(Interface Identifier - по существу, это тот же GUID) и указывающим на другой указатель, который, в свою очередь, указывает на таблицу, содержащую адреса реализации каждой функции из этой группы (рис.1).

Рис.1. Структура интерфейса

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

Реализация интерфейса COM-объекта представляет собой создание в памяти подобной структуры и предоставление указателя на нее. Фактически такая структура похожа на структуру, используемую для построения таблиц виртуальных функций в C++, поэтому таблица указателей на функции в этой структуре иногда называется "vtable" (или таблицей виртуальных методов).

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

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

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

1.2 Маршалинг и удаленный доступ

Если сервер является внутренним ("in-process"), т.е. выполненным в виде DLL, она загружается в адресное пространство клиента с помощью функции Win32 API LoadLibrary. В этом случае значение указателя на интерфейс непосредственно доступно клиенту.

Если сервер локальный, COM использует функцию CreateProcess, загружая исполняемый файл и инициализируя COM в адресном пространстве последнего. Затем COM в процессе сервера связывается с COM в процессе клиента каким-либо доступным способом. В этом случае обычно нет возможности передать клиенту точное значение указателя на интерфейс, так как это указатель на объект в другом адресном пространстве. В этом случае используется маршалинг, создающий так называемый "marshalling packet", содержащий необходимую информацию для соединения с процессом, в котором создан объект. Этот пакет создается с помощью функции COM API CoMarshalInterface, затем он передается процессу клиента любым доступным способом, где другая функция CoUnMarshalInterface превращает этот пакет в указатель на интерфейс. В действительности маршалинг создает в обоих адресных пространствах два объекта: "stub"("заглушка") - представитель клиента в адресном пространстве сервера, имеющий дело с реальным указателем на интерфейс, и "proxy" - представитель сервера в адресном пространстве клиента. Эти два объекта соединяются между собой каким-либо доступным способом, и представитель сервера передает клиентскому процессу указатель на интерфейс. Сходная технология используется при осуществлении вызовов удаленных процедур (RPC - Remote Procedure Calls).

Естественно, proxy-объект не содержит реализации интерфейса. Все аргументы вызываемых функций помещаются в пакет, передаваемый stub-объекту с использованием RPC. Stub-объект распаковывает переданные аргументы, помещает их в стек и обращается к реальному объекту, используя существующий указатель на интерфейс. Результат выполнения функции упаковывается в пакет и посылается proxy-объекту, который распаковывает его и передает клиенту.

В случае сервера, расположенного на удаленном компьютере, при обращение к серверу COM соединяется со специальным резидентным процессом удаленного компьютера, контролирующим удаленный запуск сервисов на нем. Этот процесс, называемый иногда Service Control Manager - SCM, осуществляет запуск сервера на удаленном компьютере и возвращает указатель на интерфейс клиентскому компьютеру и клиентскому процессу. В качестве SCM могут быть использованы средства различных производителей, как основанные непосредственно на технологии Microsoft DCOM (Distributed COM), так и являющиеся расширением этой технологии (например, ObjectFactory из комплекта поставки OLEnterprise корпорации Inprise). Обязательное наличие такого процесса (и возможностей его конфигурации) диктуется элементарными соображениями безопасности - было бы неестественно, если бы любой пользователь сети мог запустить любой процесс на любом из компьютеров, к этой сети подключенных. В остальном маршалинг осуществляется точно так же, как и в случае локального сервера, за исключением того, что proxy и stub, общаясь с помощью того же самого механизма RPC, физически находятся на разных компьютерах (рис. 2).

Осуществление удаленного доступа к серверу автоматизации

Рис. 2. Осуществление удаленного доступа к серверу автоматизации

1.3 Как осуществить удаленный доступ

Итак, для осуществления удаленного запуска сервера автоматизации следует запустить на стороне компьютера-сервера Service Control Manager, предварительно сконфигурировав его так, чтобы удаленный пользователь мог осуществить запуск данного сервера.

Использование Microsoft DCOM в качестве Service Control Manager

Рассмотрим использование Microsoft DCOM в качестве Service Control Manager. Для конфигурации DCOM существует утилита DCOMCNFG, входящая в комплект поставки Windows NT.

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

Настройка прав доступа к серверу автоматизации

Рис.3. Настройка прав доступа к серверу автоматизации

Для этого требуется, в свою очередь, экспорт сведений о пользователях сети с первичного контроллера домена, что возможно только в случае выбора на таком компьютере установки User Level Access Control раздела Network панели управления Windows.

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

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

Настройка удаленного запуска приложения-сервера

Рис. 4. Настройка удаленного запуска приложения-сервера

После этого можно попытаться запустить контроллер. Сервер при этом запустится удаленно.

Хотелось бы особо обратить внимание на достоинства и недостатки использования DCOM в качестве SCM. Несомненным достоинством этого способа является то, что DCOM интегрирован в операционную систему Windows NT и не требует установки и запуска каких-либо утилит для своего функционирования. Недостатки же этого способа удаленного запуска серверов автоматизации таковы:

  • В сети обязательно должен быть первичный контроллер домена
  • Могут потребоваться существенные изменения настроек сети (для установки опции User Level Access Control)
  • Сервер автоматизации нужно хотя бы один раз запустить на компьютере с контроллером для его регистрации

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

Использование Inprise OLEnterprise в качестве Service Control Manager

Рассмотрим альтернативный способ запуска удаленных серверов автоматизации, использующий в качестве Service Control Manager продукт OLEnterprise (Inprise Corporation), входящий в комплект поставки C++Builder Client/Server Suite.

Использование OLEnterprise в качестве SCM базируется на предоставлении прав на удаленный запуск конкретного приложения всем пользователям, имеющим установленную клиентскую часть этого продукта. В первую очередь следует запустить на компьютере, содержащем сервер автоматизации, утилиту Object Factory, которая, по существу, и выполняет роль SCM. Предоставление прав на запуск сервера владельцем компьютера, содержащего этот сервер, осуществляется путем публикации соответствующих записей реестра (или, в терминах OLEnterprise, экспорта соответствующего сервера автоматизации в глобальный реестр), с помощью утилиты Object Explorer:

Публикация сведений о приложении-сервере в глобальном реестре

Рис. 5. Публикация сведений о приложении-сервере в глобальном реестре

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

Импорт записи из глобального реестра в реестр компьютера, содержащего контроллер автоматизации

Рис. 6. Импорт записи из глобального реестра в реестр компьютера, содержащего контроллер автоматизации

После этого при запуске контроллера сервер запустится удаленно.

В данном примере компьютер, на котором запускается контроллер автоматизации, "знает" об одном единственном компьютере, содержащем сервер (точно так же, как и в рассмотренном выше примере использования DCOM). Однако возможности OLEnterprise этим не исчерпываются. В комплект его поставки входит утилита Business ObjectBroker, назначение которой - взаимодействовать с опубликованными частями реестров сети в поисках сервера автоматизации для обратившихся к нему клиентов. Если сконфигурировать OLEnterprise так, чтобы использовать Business ObjectBroker, клиенты автоматизации должны обращаться к нему, а не непосредственно к серверам автоматизации, как в предыдущем случае. В ответ они будут получать указание, с каким из компьютеров сети соединиться для использования сервера автоматизации.

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

Рассмотрим достоинства и недостатки использования OLEnterprise в качестве SCM. Достоинства этого способа очевидны: наличие первичного контроллера домена в сети в этом случае не является обязательным, существенных изменений настроек сети не требуется, сервер автоматизации не нужно запускать на компьютере, содержащем контроллер. Имеется возможность запускать удаленные серверы автоматизации под управлением Windows 95. Помимо этого, можно иметь несколько дублирующих друг друга серверов автоматизации на разных компьютерах сети, и можно написать код контроллера таким образом, чтобы в случае сбоя используемого сервера данный контроллер с помощью Business ObjectBroker данный компьютер был подключен к другому компьютеру, содержащему сервер, что повышает надежность такой системы. Отметим, что в случае использования DCOM это практически невозможно - в настройках DCOM местоположение сервера всегда указывается явно, а использование брокеров не предусмотрено.

Недостатки у этого способа тоже есть - в отличие от DCOM, автоматически устанавливающегося вместе c Windows NT, OLEnterprise требует отдельного приобретения и установки, в том числе установки клиентской части OLEnterprise на компьютеры, содержащие контроллеры автоматизации.

Отметим, что список возможных SCM отнюдь не исчерпывается DCOM и OLEnterprise. Помимо них, возможно использование других подобных резидентных утилит, позволяющих при тех или иных условиях запускать удаленным пользователям серверы автоматизации. Например, Borland Socket Server, входящий в комплект поставки C++Builder Client/Server Suite, является еще одним типом SCM и позволяет запускать определенные типы серверов автоматизации, не требуя при этом на компьютере, содержащем контроллер, никаких дополнительных клиентских частей и настроек.

2. Использование OLE-документов в приложениях C++Builder

Последняя из спецификаций COM, рассматриваемая в данном цикле статей, - OLE-документы. Это составные документы, содержащие внедренные или связанные объекты, созданные другими приложениями, выступающими в этом случае в качестве OLE-серверов. Данная спецификация описывает правила создания контейнеров для таких документов с "активацией по месту". Для создания и использования в приложениях таких внедренных или связанных объектов предназначен компонент TOleContainer.

2.1 Создание OLE-объектов

Компонент TOleContainer позволяет поместить OLE-объект на поверхности формы. Основные свойства этого компонента: AutoActivate - определяет, каким образом активизируется OLE-объект, OleClassName - имя класса OLE-объекта, содержащегося в контейнере. Основные методы: InsertObjectDialog - выводит стандартную диалоговую панель Object для выбора типа объекта или загрузки его из файла, CreateObject - создает OLE-объект, DestroyObject - уничтожает объект.

Создадим простейший пример использования OleContainer. С этой целью поместим на форму компонент TPanel со свойством Align, равным alClient, на него - компонент TOleContainer, кнопку и главное меню (можно создать в нем один пункт). Панель и меню нужны для отображения инструментальных панелей и меню OLE-серверов, обслуживающих отображаемые в TOleContainer объекты.

орма с TOleContainer

Рис.7. Форма с TOleContainer

Создадим обработчик события, связанный с нажатием на кнопку:

 void __fastcall TForm1::Button1Click(TObject *Sender)
 {
 OleContainer1->InsertObjectDialog();
 }

Запустив приложение и нажав на кнопку, получим диалог для вставки объекта:

Диалог вставки объекта

Рис.8. Диалог вставки объекта

В списке перечислены все OLE-серверы, зарегистрированные на данном компьютере.

Можно выбрать один из них (например, документ Word). Теперь после двойного щелчка на объекте TOleContainer получим на компоненте TPanel инструментальную панель MS Word, главное меню и сам документ.

Активный объект в OleContainer<

Рис.9. Активный объект в OleContainer

Если тип объекта, включаемого в TOleContainer, известен заранее, можно использовать метод CreateObject:

 void __fastcall TForm1::Button1Click(TObject *Sender)
 {
 OleContainer1->CreateObject("Word.Document",True);
 }

Второй параметр этого метода указывает, отображать ли объект внутри OleContainer в виде пиктограммы.

Модифицируем приложение, добавив еще две кнопки с надписями "Свойства" и "Вставка", и создадим соответствующие обработчики событий:

 void __fastcall TForm1::Button2Click(TObject *Sender)
 {
 OleContainer1->ObjectPropertiesDialog();
 }
 //--------------------
 void __fastcall TForm1::Button3Click(TObject *Sender)
 {
 OleContainer1->PasteSpecialDialog();
 }

Скомпилируем приложение. Добавим какой-нибудь объект в OLEContainer.

Нажав на кнопку "Свойства", получим стандартный диалог с описанием свойств объекта.

Диалог с описанием свойств объекта

Рис.10. Диалог с описанием свойств объекта

Нажав кнопку "Вставить", получим диалог вставки объекта:

Диалог специальной вставки

Рис.11. Диалог специальной вставки

Выбрав тип вставки объекта, можем вставить его в OLEContainer:

2.2 Хранение OLE-объектов в базах данных

Для хранения OLE-объектов могут быть использованы как поля специальных типов (поддерживаемые, например, Paradox или dBase)., так и обычные blob-поля.

Основная идея такого приложения заключается в том, что OleContainer позволяет сохранить содержащийся в нем объект в файле или в памяти, а затем поместить его в blob-поле.

Модифицируем созданное приложение, добавив компоненты TTable, TdataSource и TDBNavigator и заменив надписи на кнопках на "Сохранить" и "Показать". Создадим копию таблицы biolife.db из набора демонстрационных примеров, свяжем с ней компонент TTable и создадим набор объектов TFields. Свяжем TDataSource c TTable и TDBNavigator с TDataSource.

Изменим обработчики событий:

 void __fastcall TForm1::Button2Click(TObject *Sender)
 {
 if (OleContainer1->OleObjectInterface!=NULL)// определим, не пуст ли контейнер
 {OleContainer1->SaveToFile("aaa.dat");
 Table1->Edit();
 Table1Graphic->LoadFromFile("aaa.dat");}
 }
 //-----------------------------------------------
 void __fastcall TForm1::Button3Click(TObject *Sender)
 {
 Table1Graphic->SaveToFile("aaa.dat");
 OleContainer1->LoadFromFile("aaa.dat");
 }

Теперь, если TOleContainer не пуст, при нажатии на кнопку "Сохранить" его содержимое сохранится в blob-поле таблицы, а при нажатии на кнопку "Показать" OLE-объект из blob-поля отобразится в TOleContainer.

Рис.12. Неактивный OLE-объект, изъятый из blob-поля при нажатии на кнопку "Показать"

2.3 Использование свойств OleContainer

Как определить характеристики OLE-объекта, помещенного в OleContainer, и управлять им?

Имя класса OLE-объекта можно получить с помощью свойства OleClassName. Список доступных команд доступен с помощью свойства ObjectVerbs. Метод DoVerb позволяет вызвать одну из этих команд, ссылаясь на ее порядковый номер в спискe. Целочисленное свойство PrimaryVerb содержит номер команды из этого списка, выполняющейся при активации OLE-объекта. Состояние контейнера можно определить с помощью свойства State.

Для иллюстрации их работы на панель с OleContainer поместим компоненты TComboBox и TEdit и изменим обработчики событий:

 void __fastcall TForm1::Button1Click(TObject *Sender)
 {
 OleContainer1->CreateObject("Word.Document",True);
 Edit1->Text=OleContainer1->OleClassName;
 ComboBox1->Items=OleContainer1->ObjectVerbs;
 }
 //---------------------------------------------------------------------------
 void __fastcall TForm1::ComboBox1Click(TObject *Sender)
 {
 if (OleContainer1->State!=osEmpty)
 OleContainer1->DoVerb(ComboBox1->ItemIndex);
 }
 //---------------------------------------------------------------------------

Пример использования свойств TOleContainer

Рис.13 Пример использования свойств TOleContainer

2.4 Вместо заключения

Этой статьей заканчивается цикл, посвященный использованию COM-технологии в C++Builder. Мы рассмотрели создание различных типов COM-серверов и COM-клиентов (элементы управления ActiveX и содержащие их контейнеры, серверы и контроллеры автоматизации, OLE-документы и OLE-контейнеры) и наиболее типичные примеры их использования.

Хотелось бы обратить внимание на то, что COM реализует концепции объектно-ориентированного подхода и повторного использования кода не на уровне наследования реализации классов внутри одного приложения, как при традиционном использовании C++, а на уровне разных приложений и операционной системы. Это позволяет создавать "классы" объектов, предоставляющих различные сервисы другим приложениям и самой операционной системе, с помощью довольно широкого спектра средств разработки.

Отметим, однако, что, помимо COM, существуют и другие технологии, позволяющие создавать и использовать объекты, предоставляющие сервисы другим приложениям. В общем случае, например, при использовании технологии CORBA(Common Object Request Broker Architecture) или DCE (Distributed Computing Environment) такие объекты могут функционировать не только под управлением празличных версий Windows, как в случае COM, но и под управлением других операционных систем, что позволяет повторно использовать код не только на уровне операционной системы, но и на уровне всей сети, реализуя тем самым распределенную обработку данных. Но это тема уже другого цикла статей...


Подборка статей по С++ Builder :

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/data_acc.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Компоненты Data Access, используемые в приложениях клиент/сервер

Наталия Елманова
"Компьютер-Пресс", 1998, N 1

Компоненты TStoredProc, TDatabase, TSession, TBatchMove и TUpdateSQL используются, как правило, намного реже, чем TTable, TQuery и TDataSource, однако в ряде случаев их применение позволяет эффективно использовать возможности, предоставляемые серверными СУБД.

  • Компонент TStoredProc
  • Компонент TDatabase
  • Компонент TSession
  • Компонент TBatchMove
  • Компонент TUpdateSQL

Компонент TStoredProc

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

Основные свойства компонента TStoredProc: DatabaseName - имя (alias) базы данных, в которой содержится данная процедура, StoredProcName - имя хранимой процедуры, Params (массив компонентов TParams) - параметры хранимой процедуры, а также ряд свойств, унаследованных от TDataS e t: Active, Fields, Eof, Bof, State и др. Основные методы: ExecProc - выполняет хранимую процедуру, ParamByName - возвращает параметр, используя его имя. TStoredProc обладает также рядом методов, унаследованных от TDataSet: Append, AppendRecord, Close, Ope n , Delete, Edit, Post и т.д.

Рассмотрим простейший пример использования хранимой процедуры. Для этой цели воспользуемся локальным сервером InterBase, входящим в комплект поставки C++ Builder. Создадим новый проект, на главную форму которого поместим компонент TStoredProc, четыре компонента TEdit и компонент TButton. Установим значение свойства DatabaseName компонента StoredProc1 равным IBLOCAL (этот псевдоним демонстрационной базы данных Employee.gdb создается автоматически при установке сервера InterBase). Далее щелкнем на колонке значений напротив свойства StoredProcName, после чего на экране должен появиться стандартный диалог для ввода имени пользователя и пароля (рис.1):

Рис.1. Диалог регистрации пользователя

По умолчанию при установке InterBase системный администратор базы данных имеет имя SYSDBA и пароль MASTERKEY (в дальнейшем его можно изменить).

После соединения с сервером (он должен перед этим быть запущен) напротив имени свойства StoredProcName появится выпадающий список имеющихся в базе данных хранимых процедур. Выберем для примера процедуру ORG_CHART, присваивающую своим выходным параметрам значения одной из записей хранящейся на сервере таблицы. Выбрав свойство Params, можно просмотреть параметры хранимой процедуры с помощью соответствующего редактора свойств (рис. 2).

Рис.2. Редактор свойства Params компонента TStoredProc.

Создадим обработчик события OnClick для кнопки Button1:

     void __fastcall TForm1::Button1Click(TObject *Sender)
     {
     StoredProc1->ExecProc();
     Edit1->Text=StoredProc1->Params->Items[0]->AsString;
     Edit2->Text=StoredProc1->Params->Items[1]->AsString;
     Edit3->Text=StoredProc1->Params->Items[2]->AsString;
     Edit4->Text=StoredProc1->Params->Items[3]->AsString;
     Edit5->Text=StoredProc1->Params->Items[4]->AsString;
     }

После компиляции и запуска приложения нажатие на кнопку инициирует запуск хранимой процедуры и вывод ее параметров в соответствующие компоненты Edit1...Edit5 (рис.3).

Рис.3. Результаты выполнения хранимой процедуры ORG_CHART.

Компонент 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. Перенос содержимого кэша на сервер

Компонент TSession

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

Основные свойства этого компонента: Databases - массив активных компонентов TDataBase, SessionName - имя сеанса. Основные методы: GetDatabaseNames, GetAliasNames - возвращают списки драйверов и псевдонимов, GetAliasParams - возвращает параметры для данного псевдонима, GetDriverNames - возвращает имена доступных драйверов BDE, GetTableNames и GetStoredProcNames - возвращает список таблиц базы данных.

Компонент TBatchMove

Этот компонент обеспечивает копирование данных из одной таблицы в другую. Основные свойства: Source - таблица (или запрос), откуда копируются данные, Destination - таблица, куда копируются данные, Mapping - определяет соответствие между колонками исходной и результирующей таблиц (для идентичных таблиц это свойство определять не обязательно), Mode - тип перемещения (batAppend - добавляет новые строки в результирующую таблицу, batUpdate - заменяет строки в результирующей таблице на соответствующие строки оригинала, batCopy - копирует строки в результирующую таблицу, переписывая ее, batDelete - удаляет записи в результирующей таблице, соответствующие записям оригинала), KeyViolTableName и ProblemTableName - имена дополнительных таблиц для помещения записей, чье копирование запрещено правилами ссылочной целостности или по каким-либо причинам невозможно (например, из-за несоответствия типов данных), ChangedTableName - имя таблицы для помещения измененных записей.

Копирование происходит при выполнении метода Execute. Отметим, что этот метод может быть вызван непосредственно из среды разработки с помощью контекстного меню компонента TBatchMove.

Чаще всего подобное копирование используется при смене сервера баз данных или при переносе desktop-приложения в архитектуру клиент/сервер. В этом случае возможно выявить нарушения ссылочной целостности в исходных таблицах и при необходимости отредактировать данные, связав с компонентами TTable управляющие элементы через соответствующие компоненты TDataSource. Пример подобного применеия этого компонента был приведен в предыдущей статье данного цикла.

Компонент TUpdateSQL

TUpdateSQL предназначен для модификации данных на сервере с помощью заранее подготовленных SQL-предложений. Он позволяет определить различные операторы SQL для удаления, вставки и модификации записи, в том числе отличные от простых операторов DELETE, INSERT, APPEND. Эти операторы SQL содержатся в свойствах DeleteSQL, InsertSQL, ModifySQL соответственно.

Имя компонента TUpdateSQL может быть значением свойства UpdateObject какого-либо компонента TDataSet (например, TTable или TQuery). Если в этом случае используется кэширование данных, то в процессе выполнения транзакции, инициированной применением метода ApplyUpdates компонента TDataSet при попытке вставки, удаления или изменения записи генерируется SQL-запрос, содержащийся в свойстве InsertSQL, DeleteSQL или ModifySQL соответственно. Эти свойства можно редактировать, выбрав пункт UpdateSQL editor из контекстного меню компонента TUpdateSQL. Если же кэширование не используется, соответствующие SQL-запросы генерируются при выполнении метода Post компонента TDataSet.

Отметим, что эти три свойства поддерживают специальное расширение SQL, обеспечивающее возможность использования в запросах значений полей, существовавших до начала выполнения транзакции, переносящей на сервер данные из кэша (обычно это требуется при создании предложения WHERE в SQL-запросах). Такие значения полей обозначаются следующим образом: префикс "OLD_" + <имя поля>.

На этапе выполнения в процессе сохранения какой-либо записи на сервере компонент TUpdateSQL выбирает один из трех описанных в его свойствах запросов в соответствии со значением свойства UpdateKind компонента TDataSet, пересылает параметры запроса на сервер и выполняет запрос с целью фиксации на сервере данного обновления.

Для управления каждым отдельным обновлением внутри транзакции, переносящей данные из кэша на сервер, можно использовать событие OnApplyUpdate соответствующего компонента TDataSet, а также параметры UpdateKind (тип обновления) и UpdateAction, которому должно быть присвоено значение uaApplied, если обновление было успешным. Если установить значение этого параметра равным uaSkip, данная запись в кэше будет проигнорирована и не перенесена на сервер.

Для изучения поведения TUpdateSQL внесем изменения в уже созданное приложение, заменив компонент Table1 на компонент TQuery и добавив на форму один компонент TUpdateSQL и компонент TCheckBox. Свойства этих компонентов изменим следующим образом (табл.2):

Таблица 2

Компонент | Свойство | Значение
Query1 | Active | true
CachedUpdates | true
UpdateObject | UpdateSQL1
SQL | Select * from clients
DataSource1 | DataSet | Query1
CheckBox1 | Caption | Использовать кэширование
UpdateSQL1 ModifySQL
 update CLIENTS
 set LAST_NAME = :LAST_NAME,
 FIRST_NAME = :FIRST_NAME,
 ACCT_NBR = :ACCT_NBR
 where
 LAST_NAME = :OLD_LAST_NAME and
 FIRST_NAME = :OLD_FIRST_NAME and
 ACCT_NBR = :OLD_ACCT_NBR
DeleteSQL
 delete from CLIENTS
 where
 LAST_NAME = :OLD_LAST_NAME and
 FIRST_NAME = :OLD_FIRST_NAME and
 ACCT_NBR = :OLD_ACCT_NBR
InsertSQL
insert into CLIENTS
 (LAST_NAME, FIRST_NAME, ACCT_NBR)
 values
 (:LAST_NAME, :FIRST_NAME, :ACCT_NBR)

Cтандартные SQL-запросы для добавления, удаления и изменения записи наиболее удобно создавать с помощью UpdateSQL editor, выбранного из контекстного меню компонента UpdateSQL1 (рис. 10)

Рис.10. UpdateSQL editor

Добавим в запрос вычисляемое поле STATUS для отображения состояния записи в кэше, уберем часть полей из списка отображаемых колонок DBGrid1, внесем изменения в существующие обработчики событий и добавим несколько новых (для события OnCalcField компонента Query1 и для события OnClick компонента CheckBox1):

     //---------------------------------------------------------------------------
     #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)
     {
     Query1->ApplyUpdates();
     }
     //---------------------------------------------------------------------------
     void __fastcall
     TForm1::Button3Click(TObject *Sender)
     {
     Close();
     }
     //---------------------------------------------------------------------------
     void __fastcall
     TForm1::Button2Click(TObject *Sender)
     {
     Query1->CancelUpdates();

     }
     //---------------------------------------------------------------------------
     void __fastcall
     TForm1::Query1CalcFields(TDataSet *DataSet)
     {
     AnsiString
     UpdStr[] = {"Не
     изменялась",
     "Изменена",
     "Добавлена",
     "Удалена"};
     if
     (Query1->CachedUpdates)
     Query1STATUS->Value=UpdStr[Query1->UpdateStatus()];
     }
     //---------------------------------------------------------------------------
     void __fastcall
     TForm1::CheckBox1Click(TObject *Sender)
     {
     Query1->CachedUpdates=CheckBox1->Checked;
     }
     //---------------------------------------------------------------------------

Если перед запуском приложения запустить утилиту SQL Monitor, то можно пронаблюдать, как генерируются запросы, содержащиеся в свойствах компонента UpdateSQL1, при использовании кэширования и без него, а также как отображается состояние конкретной записи при использовании кэширования (рис.11):

Рис.11. Приложение с использованием UpdateSQL

Отметим, что свойства DeleteSQL, InsertSQL, ModifySQL компонента TUpdateSQL могут содержать более сложные запросы, нежели сгенерированные автоматически в его редакторе свойств, в соответствии с логикой содержащего его приложения.


Подборка статей по С++ Builder : Настройка параметров доступа к данным в C++ Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/database.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Настройка параметров доступа к данным в C++ Builder

Наталия Елманова

  • Библиотека Borland Database Engine (BDE)
  • Утилита настройки BDE
  • Настройка драйверов баз данных
  • Создание и настройка псевдонимов баз данных
  • Системные настройки BDE
  • Настройка параметров отображения даты, времени и чисел
  • Немного о средствах языковой настройки BDE
  • Особенности настройки BDE для работы с некоторыми источниками данных
    • dBase
    • Paradox
    • ODBC-источники
    • Oracle
    • Interbase

Библиотека Borland Database Engine (BDE)

Если созданное с помощью С++ Builder приложение в процессе работы обращается к базам данных, оно, как правило, использует для этой цели библиотеку BDE (Borland Database Engine), основанную на технологии IDAPI (Integrated Database Application Program Interface). Эта библиотека устанавливается автоматически при установке С++ Builder. По умолчанию она устанавливается в каталог C:\Program Files\Borland\Common Files\BDE.

Следует отметить, что файлы, входящие в состав библиотеки BDE, предназначены для использования не только приложениями, созданными с помощью С++ Builder, но и многими другими продуктами Borland (Visual dBase, Paradox, Delphi, Borland C++, IntraBuilder), созданными на их основе приложениями, а также офисными приложениями Corel (например, электронной таблицей Quattro Pro), генератором отчетов Crystal Reports (Seagate Software) . Поэтому при наличии нескольких использующих BDE приложений все 32-разрядные приложения (в том числе C++ Builder) используют установленную последней 32-разрядную версию BDE. В этом случае по умолчанию программа установки C++ Builder предложит поставить BDE в каталог, где установлена уже используемая версия BDE.

Рис.1. Связь приложений с источниками данных с помощью BDE

BDE обеспечивает для созданных приложений:

  • непосредственный доступ к локальным базам данных (dBase, Paradox, текстовые файлы)
  • доступ к SQL-серверам (Oracle, Sybase, MS SQL Server, InterBase, Informix, DB2) с помощью драйверов Borland SQL Links
  • доступ к любым источникам данных, имеющим драйвер ODBC (Open DataBase Connectivity), например, к файлам электронных таблиц (Excel, Lotus 1-2-3), серверам баз данных, не имеющим драйверов SQL Links (например, Gupta/Centura)
  • создание приложений клиент-сервер, использующих разнородные данные
  • высокую производительность при работе с плоскими таблицами
  • использование SQL (Structured Query Language - язык запросов к серверным СУБД), в том числе при работе с локальными данными
  • изоляцию приложения от средств языковой поддержки
  • изоляцию приложения от конфигурации системы и сети

Утилита настройки BDE

При возникновении необходимости доступа к данным в большинстве случаев для их источника (как правило, это какая-либо конкретная база данных) создается псевдоним (alias), имя которого используется приложением. Параметры этого и других псевдонимов, а также параметры настройки драйверов баз данных содержатся в файле idapi32.cfg, расположенном в том же каталоге, что и файлы BDE. Для изменения содержания этого файла, а также значений соответствующих этой библиотеке ключей реестра Windows 95 и Windows NT используется утилита конфигурации BDE - BDECFG32.EXE.

Утилита конфигурации состоит из одной формы, содержащей блокнот из шести страниц.

Страница Drivers содержит параметры доступа к различным типам данных.

Страница Aliases содержит сведения о псевдонимах источников данных, к которым посредством BDE обращаются приложения, установленные на данном компьютере.

Страница System содержит параметры настройки работы самой библиотеки BDE, связанные с использованием памяти, сетевого окружения и других ресурсов, доступных использующим BDE приложениям. Эти параметры содержатся в реестре Windows 95 (или Windows NT).

Страницы Date, Time, Number содержат правила отображения в приложениях календарных дат, времени и числовых данных в соответствии с правилами, принятыми в той или иной стране.

Настройка драйверов баз данных

Для настройки драйверов баз данных используется страница Drivers утилиты настройки BDE. В левой части этой страницы имеется список доступных для BDE драйверов, куда входят драйверы для dBase и Paradox, установленные на данном компьютере драйверы SQL Links для доступа к серверным СУБД, а также имена ODBC-источников данных, созданные с помощью 32-разрядного администратора ODBC панели управления Windows (рис. 5.2).

Рис. 2. Настройка драйверов баз данных

При нажатии на кнопку New ODBC Driver можно добавить в список новый ODBC-источник данных (перед этим, естественно, следует установить соответствующий ODBC-драйвер и описать источник данных с помощью ODBC-администратора в панели управления Windows). Можно также удалить ODBC-драйвер из файла конфигурации BDE (Delete ODBC Driver).

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

Создание и настройка псевдонимов баз данных

Для доступа приложений к данным средства разработки Borland используют механизм псевдонимов, описывающих доступ к конкретным источникам данных. Создать описание нового источника данных можно, нажав кнопку New Alias в левой части окна (рис. 6.3, 6.4).

Рис. 3. Выбор псевдонима БД и установка параметров

После этого в появившейся диалоговой панели следует ввести имя-псевдоним для этого источника и выбрать нужный драйвер из предложенного списка (того, что определен на странице Drivers). Имя STANDARD в этом случае соответствует таблицам dBase, Paradox и текстовым файлам (в формате CSV - Comma Separated Value).

Рис. 4.Создание нового псевдонима БД

В правой части страницы Aliases содержатся параметры конкретных источников данных, которые можно модифицировать, заменяя значения по умолчанию (в том числе унаследованные с предыдущей страницы), например, имена каталогов, серверов, имя пользователя по умолчанию, языковый драйвер и др.

Системные настройки BDE

Страница системных настроек позволяет указать параметры системы и сети, используемые BDE и хранящиеся в реестре Windows (рис. 5, табл. 1).

Рис. 5. Страница System утилиты конфигурации BDE.

Таблица 1. Параметры системы и сети, используемые BDE.

Параметр | Описание | Значение по умолчанию
VERSION | версия BDE |
LOCAL SHARE | Если этот параметр равен true, можно использовать одни и те же данные приложениями, использующими BDE, и приложениями, не использующими BDE | FALSE
MINBUFSIZE | Минимальный размер буфера оперативной памяти для кэширования данных из БД. Возможные значения - от 32 до 65535 Кб, но не более, чем объем доступной Windows оперативной памяти |
MAXBUFSIZE | Максимальный размер буфера оперативной памяти для кэширования данных из БД. Должен быть выше, чем MINBUFSIZE, но не более, чем объем доступной Windows оперативной памяти. Должен быть кратен 128. | 2048
LANGDRIVER | Языковый драйвер, соответствующий национальной версии операционной системы |
MAXFILEHANDLES | Максимальное число файлов, открываемых BDE. Может принимать целые значения от 5 до 256 | 48
SYSFLAGS | Внутренний параметр BDE |
LOW MEMORY USAGE LIMIT | Максимально допустимый объем основной памяти, используемый BDE | 32
AUTO ODBC | если этот параметр принимает значение true, используются все ODBC-источники из файла ODBC.INI | FALSE
DEFAULT DRIVER | драйвер, используемый первым, если тип БД - FILE, и имя таблицы не имеет расширения. |
SQLQRYMODE | Метод исполнения запросов к серверам. Может принимать значения NULL, SERVER, LOCAL | NULL
SHAREDMEMSIZE | Максимальный размер для разделяемой оперативной памяти | 2048
SHAREDMEMLOCATION | Предпочтительный адрес для размещения разделяемой области памяти. | E000 (Windows 95) 7000(Windows NT)

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

Страница Date позволяет указать параметры отображения дат (см. табл.2).

Таблица 2. Параметры настройки отображения дат

Параметр | Описание | Значение по умолчанию
SEPARATOR | Символ-разделитель числа, месяца и года | Значение, содержащееся в настройках панели управления Windows 95/NT
MODE | Параметр, определяющий порядок следования числа. месяца и года. Может принимать значения: 0(MDY), 1( DMY), 2(YMD). | Значение, содержащееся в настройках панели управления Windows 95/NT
FOURDIGITYEAR | Параметр, определяющий отображаемое число цифр года. Может принимать значения: TRUE (4 цифры), FALSE (2 цифры) | TRUE
YEARBIASED | Параметр, определяющий, прибавлять или нет 1900 к значению года, если предыдущий параметр равен FALSE | TRUE
LEADINGZEROM | Параметр, определяющий, указывать ли лидирующие нули перед значением месяца, если оно является однозначным числом. | FALSE
LEADINGZEROD | Параметр, определяющий, указывать ли лидирующие нули перед значением числа, если оно является однозначным числом. | FALSE

Страница Time позволяет указать параметры отображения времени (см. табл. 3).

Таблица 3. Параметры настройки отображения времени

Параметр | Описание | Значение по умолчанию
TWELVEHOUR | Параметр, определяющий интервал отображения часов: 0-12 (TRUE) или 0-24 (FALSE) | TRUE
AMSTRING | Символьная строка для указания первой половины дня, если TWELVEHOUR=TRUE | AM
PMSTRING | Символьная строка для указания второй половины дня, если TWELVEHOUR=TRUE | PM
SECONDS | Параметр, определяющий, указывать ли секунды в значении времени | TRUE
MILSECONDS | Параметр, определяющий, указывать ли миллисекунды в значении времени . | FALSE

Страница Number позволяет указать параметры отображения числовых данных (табл. 4).

Таблица 4. Параметры настройки отображения чисел

Параметр | Описание | Значение по умолчанию
DECIMALSEPARATOR | Символ, отделяющий дробную часть числа | Значение, содержащееся в настройках панели управления Windows 95/NT
THOUSANDSEPARATOR | Символ, отделяющий друг от друга "тройки" разрядов в многозначных числах | Значение, содержащееся в настройках панели управления Windows 95/NT
DECIMALDIGITS | Максимальное число десятичных разрядов числа, полученного при преобразовании символьной строки | 2
LEADINGZERON | Параметр, определяющий, указывать ли нуль перед дробной частью числа, чье абсолютное значение меньше единицы | TRUE

Немного о средствах языковой настройки BDE

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

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

Отметим, что в общем случае языковая настройка баз данных и использующих их приложений включает в себя не только настройку параметра LANGDRIVER на страницах Drivers, Aliases и System. Некоторые СУБД имеют свои средства языковой настройки, причем эти средства могут быть многоуровневыми. Например, в случае использования какой-либо серверной СУБД может потребоваться, помимо настройки BDE, настройка языковых параметров сервера, настройка аналогичных параметров клиентской части или даже выбор соответствующей языковой версии сервера. Подробнее эти проблемы будут рассмотрены чуть позже.

Помимо этого, существует проблема, связанная с различиями DOS- и Windows-кодировок для русского языка. Языковые драйверы BDE существуют для обеих кодировок. Какой из них выбрать, зависит от многих факторов: наличия и объема унаследованных данных, наличия других приложений, использующих эти же данные и др. Например, разумно, используя таблицы формата dBase III совместно с приложениями для DOS, созданными на Clipper, применить DOS-кодировку для этих таблиц.

Особенности настройки BDE для работы с некоторыми источниками данных

dBase

При работе с таблицами dBase настройка соответствующего драйвера сводится к настройке следующих параметров:

Параметр | Описание | Значение по умолчанию
VERSION | Внутренний параметр BDE | 1.0
TYPE | Тип сервера (SQL-сервер или файловый сервер) | FILE
LANGDRIVER | Языковый драйвер, определяющий набор символов и порядок алфавитной сортировки | dBASE ENU cp437
LEVEL | Версия формата dBase при создании таблиц | 5
MDX BLOCK SIZE | Размер блока, отводимого под индексные файлы *.mdx. Может быть целым числом, кратным 512 K | 1024
MEMO FILE BLOCK SIZE | Размер блока, отводимого под файлы memo-полей *.dbt. Может быть целым числом, кратным 512 K | 1024

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

С языковой настройкой таблиц dBase обычно не возникает проблем. Для DOS-кодировки обычно используется языковый драйвер dBASE RUS cp866, а для Windows - 'ascii' ANSI.

Следует отметить, что к таблицам dBase можно обращаться, используя ODBC.

Особое внимание следует обратить на то, что расширение *.dbf имеют не только таблицы формата dBase, но и таблицы Clipper, FoxBase, FoxPro. Однако эти СУБД могут иметь другой формат индексных файлов и мемо-полей, которые в явном виде могут не поддерживаться библиотекой BDE (например, индексы *.cdx). В этом случае рекомендуется либо доступ через ODBC (что не всегда эффективно с точки зрения производительности), либо использование библиотек третьих фирм, обеспечивающих интерфейс с такими СУБД (например, Apollo компании SuccessWare).

Paradox

При работе с таблицами Paradox настройка соответствующего драйвера сводится к настройке следующих параметров:

Параметр | Описание | Значение по умолчанию
VERSION | Внутренний параметр BDE | 1.0
TYPE | Тип сервера (SQL-сервер или файловый сервер) | FILE
NET DIR | Местоположение файла PDOXUSRS.NET, управляющего совместным использованием таблиц Paradox в сети. | FILE
LANGDRIVER | Языковый драйвер, определяющий набор символов и порядок алфавитной сортировки | 'ascii'ANSI
LEVEL | Версия формата Paradox при создании таблиц (3,4,5 или 7) | 5
BLOCK SIZE | Размер блока, отводимого для хранения записей таблиц Paradox. Может быть целым числом, кратным 1024 K. Возможные значения зависят от значения параметра LEVEL. | 2048
FILL FACTOR | Максимальный процент заполнения блока для индексных файлов. | 95
STRICTINTEGRTY | Параметр, определяющий, могут ли таблицы Paradox модифицироваться приложениями, не поддерживающими ссылочную целостность. | TRUE

Как и в случае dBase, русские языковые драйверы для Paradox существуют для обеих кодировок - и DOS, и Windows. Однако указание на используемый язык содержится еще и внутри самих таблиц. Поэтому при создании таблицы Paradox (например, с помощью Database Desktop) следует обязательно указать используемый языковый драйвер при описании свойств таблицы. Как показывает опыт, при неверном определении языка таблицы могут возникнуть проблемы при последующем вводе в нее данных, содержащих русские буквы - некоторые из них после выхода из редактируемого поля могут превратиться в латинские.

ODBC-источники

При работе с ODBC-источниками требуется настройка следующих параметров:

Параметр | Описание | Значение по умолчанию
VERSION | Внутренний параметр BDE | 1.0
TYPE | Идентификатор ODBC-источника | FILE
DLL | Имя 16-разрядной динамической библиотеки, содержащей драйвер | IDODBC16.DLL
DLL32 | Имя 32-разрядной динамической библиотеки, содержащей драйвер | IDODBC32.DLL
ODBC DRIVER | ODBC-драйвер для соединения с сервером |
DRIVER FLAGS | Внутренний параметр BDE |
USER NAME | Имя пользователя в диалоге ввода пароля |
ODBS DSN | Имя источника данных, описанного в администраторе ODBC |
OPEN MODE | Параметр, определяющий, в каком режиме открываются таблицы - READ/WRITE eee READ ONLY | READ/WRITE
LANGDRIVER | Языковый драйвер, определяющий набор символов и порядок алфавитной сортировки | 'ascii'ANSI
SCHEMA CASHE SIZE | Число таблиц, чья структура кэшируется. Возможные значения - от 0 до 32 | 8
SQLQRYMODE | Метод выполнения запросов. Возможные значения: LOCAL - запрос обрабатывается только клиентским приложением, SERVER - запрос выполняется только сервером, NULL (пустая строка) - запрос передается клиенту, если сервер не может его обработать. | NULL
SQLPASSTHRU MODE | Определяет режим совместного использования одного и того же псевдонима направляемыми на сервер и локальными запросами: NOT SHARED - совместное использование запрещено, SHARED AUTOCOMMIT - совместное использованием разрешено с автоматическим завершением транзакций, SHARED NOAUTOCOMMIT - совместное использованием разрешено с завершением транзакций по правилам сервера. | SHARED AUTOCOMMIT
TRACE MODE | Численное значение, определяющее уровень вывода отладочной информации. |
SCHEMA CACHE TIME | Время нахождения информации о структуре таблиц в кэше в секундах от 1 до 2147483647. Другие значения: -1 - до закрытия БД, 0 - информация не кэшируется | -1
BATCH COUNT | Число записей, помещаемых в пакет до завершения транзакции | Число записей, умещающихся в 32 К.
MAX ROWS | Максимальное число записей, которые драйвер может доставить на рабочую станцию при выполнении одиночного SQL-запроса | -1 (нет ограничений)
ROWSET SIZE | Число записей, доставляемых в одном блоке данных (поддерживается не всеми ODBC- драйверами). | 20

При создании псевдонимов баз данных на страницу Aliases по умолчанию заносятся параметры со страницы Drivers для соответствующего ODBC-драйвера. При необходимости многие из них можно переопределить.

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

Следует обратить внимание на то, что перед описанием ODBC-источника в файле конфигурации BDE обязательно нужно установить соответствующий ODBC-драйвер и описать соответствующий источник данных в панели управления Windows 95/NT, используя соответствующий ODBC-администратор. При этом следует обратить внимание на некоторую терминологическую неувязку. Дело в том, что ODBC-драйвер с точки зрения BDE, создаваемый при нажатии кнопки New ODBC Driver на странице Drivers утилиты конфигурации BDE, на самом деле представляет собой указание не на реальный ODBC-драйвер, установленный в панели управления Windows, а на конкретный источник данных, доступ к которому осуществляется с помощью реального ODBC-драйвера (с точки зрения панели управления). При этом следует еще создать и соответствующий псевдоним базы данных, что окончательно сбивает с толку некоторых начинающих пользователей. Таким образом, последовательность действий при осуществлении доступа к ODBC-источникам следующая:

  • Установить нужный ODBC-драйвер (и, возможно, соответствующий ODBC-администратор для панели управления Windows).
  • Описать с помощью ODBC-администратора необходимый источник данных в панели управления.
  • Запустить утилиту конфигурации BDE и нажать кнопку New ODBC Driver на странице Drivers.
  • Придумать и ввести имя так называемого ODBC-драйвера с точки зрения BDE
  • Выбрать "настоящий" ODBC-драйвер из установленных в операционной системе
  • Выбрать имя источника данных
  • Нажать OK. В списке драйверов появится новый так называемый ODBC-драйвер (с точки зрения BDE).
  • Перейти на страницу Aliases и создать псевдоним, связанный со вновь созданным драйвером с точки зрения BDE.

Рис. 6. Описание нового ODBC-драйвера "с точки зрения BDE".

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

Для корректного отображения русских букв и установки правильного порядка алфавитной сортировки можно попытаться использовать какой-либо из русскоязычных драйверов dBase или Paradox. Однако следует помнить, что некоторые ODBC-драйверы имеют свои процедуры настройки, которые могут, в частности, включать опцию перекодировки OEM->ANSI (т.е. DOS->Windows). Кроме того, если доступ через ODBC осуществляется к какой-либо серверной СУБД, следует обратить внимание на возможности языковой настройки сервера и клиентской части.

Oracle

Для настройки доступа к серверам Oracle следует настроить примерно тот же набор параметров, что и в случае ODBC-источников. Помимо этого, для драйвера ORACLE существует дополнительный набор параметров:

Параметр | Описание | Значение по умолчанию
VENDOR INIT | Имя библиотеки для соединения клиента с сервером (ORANT.DLL, ORA72.DLL и др.) | ORANT.DLL
SERVER NAME | Имя псевдонима (alias) БД, указанного в файле TNSNAMES.ORA. Если сервер локальный, то SERVER NAME=@2: |
NET PROTOCOL | TNS - если используется SQL*Net версии 2.0 или выше, или имя сетевого протокола для доступа к серверу, если используется более ранняя версия SQL*Net. |
ENABLE SCHEMA CASHE | Разрешается ли кэширование на рабочей станции структуры таблиц, содержащихся на сервере. | FALSE
SCHEMA CASHE DIR | Каталог для кэширования структуры таблиц. | 1.0
ENABLE BCD | Разрешен ли перевод числовых и денежных величин в формат BCD (binary coded decimals) во избежание ошибок округления. | FALSE
ENABLE INTEGERS | Разрешено ли преобразование числовых величин с фиксированной запятой в целый формат. | FALSE
LIST SYNONYMS | Разрешены ли синонимы (альтернативные имена таблиц и представлений) в структурах таблиц: NONE - нет, PRIVATE - разрешены личные синонимы, ALL - разрешены личные и общие синонимы. | NONE

Проблемы установки соединения с Oracle из приложений Borland связаны главным образом с правильной настройкой сетевого программного обеспечения Oracle SQL*Net. Последние версии серверов Oracle (7.2, 7.3) содержат в своем составе SQL*Net версии 2.0 или выше. Для конфигурации SQL*Net 2.0 и выше следует настроить псевдонимы баз данных Oracle с помощью утилиты SQL*Net Easy Configuration, описав сетевое имя или адрес сервера, имя базы данных и тип сетевого протокола. После этого рекомендуется проверить наличие соединения с сервером с помощью утилиты Oracle SQL Plus. Только после проверки соединения можно конфигурировать псевдоним BDE.

При конфигурации псевдонима BDE в качестве сетевого протокола можно указать TNS (Transparent Network Substrate - высокоуровневая надстройка Oracle над сетевыми протоколами). В качестве имени сервера следует указать имя псевдонима базы данных Oracle, указав перед этим именем символ '@' (об этом в документации не сказано). Для локального сервера (или в случае, когда клиентское приложение функционирует на одном компьютере с сервером баз данных, например, Oracle Workgroup Server for Windows NT) в качестве имени сервера можно использовать строку '@2:' .

Для корректного отображения русских букв и установки правильного порядка алфавитной сортировки можно использовать драйверы dBase RUS CP866 или Paradox ANSI Cyrillic в зависимости от того, какая кодировка - DOS или Windows - будет использоваться. Однако корректная работа с русским языком будет обеспечена только в том случае, если удачно подобрано сочетание трех параметров языковой настройки: языковой настройки сервера (она указывается при его установке), языковой настройки клиентской части (значение HKEY_LOCAL_MACHINE//SOFTWARE)//ORACLE//NLS_LANG реестра Windows 95) и собственно языкового драйвера BDE. Подробности языковой настройки клиентской части и сервера можно найти в документации к серверу Oracle.

InterBase

Для настройки доступа к серверам InterBase следует настроить примерно тот же набор параметров, что и в случае ODBC-источников. Помимо этого, для InterBase существует дополнительный набор параметров:

Параметр | Описание | Значение по умолчанию
BLOBS TO CACHE | Определяет, сколько BLOB-полей кэшируется на рабочей станции. Возможные значения: 64 - 65536 | 64
BLOB SIZE | Определяет размер буфера для BLOB-полей, передаваемых в результате запроса. Возможные значения - 32-100 К. Параметр применим только в случае нередактируемых данных | 32 К

Отметим, что при соединении с локальным сервером InterBase следует указывать путь к файлу базы данных.

При возникновении проблем связи C++ Builder с InterBase следует проверить наличие связи с помощью утилиты Interactive SQL. При наличии связи в Interactive SQL следует проверить параметры конфигурации драйвера и псевдонима BDE, а при отсутствии - попробовать найти ошибки в сетевом протоколе, проверив соединение с помощью утилит PING и TELNET.

Более подробно о работе с C++ Builder можно узнать из книги Н.З.Елмановой и С.П.Кошеля "Borland C++ Builder", выходящей в августе в издательстве "Диалог-МИФИ".


Подборка статей по С++ Builder : Структуры файлов в C++ Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/dig.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Структуры файлов в C++ Builder

Наталия Елманова, Центр Информационных Технологий

Структура файла проекта

Для каждого приложения C++ Builder создается один файл проекта, один make-файл и один файл ресурсов. Файл проекта генерируется при выборе пункта меню File/New Application. Первоначально файлу проекта присваивается по умолчанию имя Project1.cpp. Если в процессе разработки приложения добавляются формы и модули, C++ Builder обновляет файл проекта.

Для просмотра файла проекта следует выбрать пункт меню View/Project Source. Эта операция выполнит загрузку исходного текста файла проекта в редактор кода (рис. 1).

Рис. 1. Просмотр файла проекта в редакторе кода

Файл проекта имеет такую же структуру, как и файл модуля. Подобно файлу модуля, это файл исходного кода на языке C++, который компилируется с другими файлами при создании исполняемого файла.

В файле проекта имеется определенный набор ключевых элементов:

  • Директива препроцессора #include <vcl\vcl.h> предназначена для включения в текст проекта заголовочного файла, ссылающегося на описания классов библиотеки компонентов.
  • Директива препроцессора #pragma hrdstop предназначена для ограничения списка заголовочных файлов, доступных для предварительной компиляции.
  • Директива USEFORM сообщает, какие модули и формы используются в проекте.
  • директива USERES компилятора присоединяет файлы ресурсов к выполняемому файлу. При создании проекта автоматически создается файл ресурсов с расширением *.res для хранения курсоров, пиктограммы приложения и др.
  • Application->Initialize() Это утверждение критично только в случае, если приложение является OLE automation-сервером. В остальных случаях оно фактически ничего не делает.
  • Application->CreateForm() Это утверждение создает форму приложения. По умолчанию, каждая форма в приложении имеет свое утверждение CreateForm.
  • Application->Run() Это утверждение запускает приложение (точнее, переводит его в состояние ожидания наступления одного из событий, на которое оно должно реагировать).
  • Конструкция try...catch используется для корректного завершения приложения в случае возникновения ошибки при инициализации, создании форм, запуске приложения.

Структура make-файла

Make-файл - это текстовый файл, содержащий макросы, директивы и инструкции по компиляции и сборке проекта для утилиты make.exe. Отметим, что make-файл по существу ничем не отличается от обычного знакомого пользователям С и С++ make-файла. Он генерируется автоматически при создании проекта, и его содержимое зависит от установок, указанных программистом в опциях проекта. Чтобы увидеть содержание make-файла, следует выбрать пункт меню View/Project Makefile (рис. 2).

Рис. 2. Пример структуры make-файла

Структура модуля

Модули являются основой создания библиотек и приложений в C++ Builder. Модуль содержит исходный текст на языке C++ и первоначально представляет собой файл с расширением *.CPP. В дальнейшем каждый такой файл компилируется в объектный файл с расширением *.OBJ. Объектные файлы, в свою очередь, собираются компоновщиком в выполняемый файл с расширением *.EXE.

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

 #include <vcl\vcl.h>;

 #pragma hdrstop;

 #include "Unit2.h"

Директива #include "Unit2.h" указывает на то, что в текст модуля должен быть включен соответствующий заголовочный файл.

При создании модуля используются следующие правила:

  • Имя должно быть уникальным. Два модуля с одним и тем же именем не могут использоваться одновременно в одном и том же проекте.
  • Если модуль связан с формой, то имя исходного файла модуля и файла формы (с расширением *.dfm) должны быть одинаковыми. При создании обработчика событий в инспекторе объектов в тексте файла модуля генерируется шаблон функции, в который разработчик должен вводить код, выполняемый при наступлении обрабатываемого события.

Структура h-файла

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

Рис. 3. Пример структуры h-файла

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

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

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

Внутри модуля функции могут быть определены и ссылаться друг на друга в произвольном порядке. Если данный модуль ссылается на другие формы и модули, следует с помощью директивы препроцессора #include включить в него соответствующий h-файл с помощью пункта меню File/Include Unit Hdr... . После этого интерфейсные элементы другого модуля будут доступны в данном модуле.

Файл формы

Форма является одним из важнейших элементов приложения C++ Builder. Процесс редактирования формы происходит при добавлении к форме компонентов, изменении их свойств, создании обработчиков событий.

Рис. 4. Структура файла формы

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

Файл модуля (.cpp) - cодержит исходный код, связанный с формой.

h-файл(.h) - cодержит описание класса формы, то есть описания содержащихся на форме компонентов и обработчиков событий.

Файл формы (.dfm) - двоичный файл, содержащий сведения об опубликованных (то есть доступных в инспекторе объектов) свойствах компонентов, содержащихся в форме. Двоичный файл формы содержит информацию, используемую для конструирования формы из компонентов, расположенных на ней. При добавлении компонента к форме и заголовочный файл, и двоичный файл формы модифицируются. При редактировании свойств компонента в инспекторе объектов эти изменения сохраняются в двоичном файле формы.

Хотя в C++ Builder файл .dfm сохраняется в двоичном формате, его содержание можно просмотреть с помощью редактора кода. Для этого нужно нажать правую клавишу мыши над формой и из контекстного меню формы выбрать пункт View as Text.

Отметим, что при изъятии какого-либо компонента с формы в буфер обмена в последнем реально оказывается часть тестового представления файла формы, содержащая описание данного компонента. В этом можно убедиться, выполнив затем операцию вставки из буфера обмена в любом текстовом редакторе. Модули Delphi 2.0

C++ Builder создан на основе визуальной библиотеки компонентов Borland Delphi, ставшей за последние два года весьма популярной среди разработчиков. По этой причине этот продукт имеет общую с Delphi библиотеку классов, часть из которых осталась написанной на Object Pascal. Из этого следует, что в приложениях можно использовать компоненты, созданные для Delphi 2.0.

Однако совместимость с Delphi этим не исчерпывается. В проектах C++ Builder можно использовать не только библиотеку компонентов Delphi, но и код, написанный на Object Pascal, а также формы и модули Delphi. Эти возможности появились благодаря включению в С++ Builder обоих компиляторов -- С++ и Object Pascal.

Рис. 5. Типы файлов, используемые в проектах С++ Builder

В соответствии с этим в качестве частей проекта могут быть использованы модули, написанные на Object Pascal. В этом можно убедиться, взяв формы какого-нибудь из примеров, созданных в Delphi 2.0 и включив их в проект С++ Builder. Возьмем в качестве такого примера приложение Graphex из набора примеров Delphi 2.0. Создадим новый проект, удалим из него созданную по умолчанию форму и добавим два модуля из приложения Graphex - Graphwin.pas и Bmpdlg.pas.

Рис. 6. Добавление модулей Delphi к проекту.

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

В случае использования форм Delphi к проекту добавляются два файла - файл формы с расширением *.dfm и файл модуля с расширением *.pas. Описание класса формы содержится в самом модуле, следовательно, для этого не требуется отдельный файл заголовка.

Рассмотрим более подробно структуру модуля Delphi. В качестве простейшего примера приведем структуру модуля, связанного с формой, содержащей единственный интерфейсный элемент - кнопку закрытия окна:

 unit Unit1;
 interface
 uses  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

 type TForm1 = class(TForm) Button1: TButton;

 procedure Button1Click(Sender: TObject);

     private
     { Private declarations }

     public
         { Public declarations }

    end;

 var  Form1: TForm1;

 implementation

 {$R *.DFM}

 procedure TForm1.Button1Click(Sender: TObject);
 begin
 close;
 end;
 end.

Основными элементами модуля являются:

  • Заголовок модуля - предназначен для идентификации модуля и должен быть уникальным для данного проекта. Совпадает с именем файла с расширением *.pas.
  • Секция интерфейса (начинается зарезервированным словом interface). Секция интерфейса является местом, где объявляются константы, типы, переменные, процедуры* и функции, которые доступны для использования другими программными модулями. При объявлении процедур и функций в секции интерфейса необходимыми являются только их заголовки. Тела, или реализации, этих процедур и функций располагаются в секции реализации.
  • Секция реализации (начинается зарезервированным словом implementation) содержит процедуры и функции, включая обработчики событий. Объявления обработчиков событий должны присутствовать в секции интерфейса. Помимо этого, секция реализации может иметь дополнительные процедуры, функции и объявления констант, типов и переменных, упоминания о которых нет в секции интерфейса, но эти объявления видимы только в пределах данного модуля.
  • Секции инициализации и завершения (начинаются с зарезервированных слов initialization и finalization) - необязательные секции для размещения операторов, выполняемых соответственно при запуске и завершении приложения. Предложение uses (необязательное) содержит имена других модулей, используемых данным модулем и может располагаться непосредственно за ключевым словом interface и за ключевым словом implementation. Предложение uses делает секцию интерфейса других модулей доступной в данном модуле.

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

*В терминологии Pascal процедурами обычно называют функции, не возвращающие значений (в частности, обработчики событий). Елманова Н.З., Кошель С.П. Введение в Borland С++ Builder


Подборка статей по С++ Builder : Работа с базами данных в Borland C++ Builder. Часть 1.

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/glava1.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Работа с базами данных в Borland C++ Builder. Часть 1.

Наталия Елманова, Центр Информационных Технологий

Компьютер-Пресс, 1997, N 6, 7.

  • Введение
  • Компонент TDataSource
  • Компонент TTable
  • Компонент TField
  • Компонент TDBGrid
  • Пример 1. Разработка простейшего приложения
  • Пример 2. Разработка приложения, использующего вычисляемое поле
  • Компоненты TDBLookup
  • Пример 3. Разработка приложения, использующего компонент просмотра баз данных

Введение

Используя Borland C++ Builder, можно создать приложения, работающие как с однопользовательскими базами данных (БД), так и с серверными СУБД, такими как Oracle, Sybase, Informix, Interbase, MS SQL Server, DB2, а также с ODBC-источниками. Возможности C++ Builder, связанные с созданием приложений, использующих базы данных, весьма обширны для того, чтобы описать их в одной статье. Поэтому сегодня мы рассмотрим лишь простейшие возможности работы с таблицами баз данных.

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

Набор данных в C++ Builder является потомком абстрактного класса TDataSet (абстрактный класс - это класс, от которого можно порождать другие классы, но нельзя создать экземпляр объекта данного класса). Например, классы TQuery, TTable и TStoredProc, содержащиеся на странице палитры компонентов Data Access, - наследники TDBDataSet, который, в свою очередь, является наследником TDataSet. TDataSet содержит абстракции, необходимые для непосредственного управления таблицами или запросами, обеспечивая средства для того, чтобы открыть таблицу или выполнить запрос и перемещаться по строкам.

Компонент TDataSource

Компонент DataSource действует как посредник между компонентами TDataSet (TTable, TQuery, TStoredProc) и компонентами Data Controls - элементами управления, обеспечивающими представление данных на форме. Компоненты TDataSet управляют связями с библиотекой Borland Database Engine (BDE), а компонент DataSource управляет связями с данными в компонентах Data Controls.

В типичных приложениях БД компонент DataSource, как правило, связан с одним компоненом TDataSet (TTable или TQuery) и с одним или более компонентами Data Controls (такими, как DBGrid, DBEdit и др.). Связь этого компонента с компонентами TDataSet и DataControls осуществляется с использованием следующих свойств и событий:

  • Cвойство DataSet компонента DataSource идентифицирует имя компонента TDataSet. Можно присвоить значение свойству DataSet на этапе выполнения или с помощью инспектора объектов на этапе проектирования.
  • Cвойство Enabled компонента DataSource активизирует или останавливает взаимосвязь между компонентами TDataSource и Data Controls. Если значение свойства Enabled равно true, то компоненты Data Controls, связанные с TDataSource, воспринимают изменения набора данных. Использование свойства Enabled позволяет временно разъединять визуальные компоненты Data Controls и TDataSource, например, для того, чтобы в случае поиска в таблице с большим количеством записей не отображать на экране пролистывание всей таблицы.
  • Свойство AutoEdit компонента DataSource контролирует, как инициируется редактирование в компонентах Data Controls. Если значение свойства AutoEdit равно true, то режим редактирования начинается непосредственно при получении фокуса компонентом Data Controls, связанным с данным компонентом TDataSet. В противном случае режим редактирования начинается, когда вызывается метод Edit компонента TDataSet, например, после нажатия пользователем кнопки Edit на компоненте DBNavigator. · Событие OnDataChange компонента DataSource наступает, когда происходит изменение значения поля, записи, таблицы, запроса.
  • Cобытие OnUpdateData компонента DataSource наступает, когда пользователь пытается изменить текущую запись в TDataSet. Обработчик этого события следует создавать, когда требуется соблюсти условия ссылочной целостности или ограничения, накладываемые на значения полей изменяемой базы данных.

Компонент TTable

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

  • Active - указывает, открыта (true) или нет (false) данная таблица.
  • DatabaseName - имя каталога, содержащего искомую таблицу, либо псевдоним (alias) удаленной БД (псевдонимы устанавливаются с помощью утилиты конфигурации BDE, описание которой присутствует во многих источниках, посвященных продуктам Borland, либо с помощью SQL Explorer, вызываемого с помощью пункта меню Database/Explore). Это свойство может быть изменено только в случае, если таблица закрыта (ее свойство Active равно false), например:
         Table1->Active = false;
     Table1->DatabaseName = "BCDEMOS"
     Table1->Active = true;
  • TableName - имя таблицы.
  • Exclusive - если это свойство принимает значение true, то никакой другой пользователь не может открыть таблицу, если она открыта данным приложением. Если это свойство равно false (значение по умолчанию), то другие пользователи могут открывать эту таблицу.
  • IndexName - идентифицирует вторичный индекс для таблицы. Это свойство нельзя изменить, пока таблица открыта.
  • MasterFields - определяет имя поля для создания связи с другой таблицей.
  • MasterSource - имя компонента TDataSource, с помощью которого TTable будет получать данные из связанной таблицы.
  • ReadOnly - если это свойство равно true, таблица открыта в режиме "только для чтения". Нельзя изменить свойство ReadOnly, пока таблица открыта.
  • Eof, Bof - эти свойства принимают значение true, когда указатель текущей записи расположен на последней или соответственно первой записи таблицы.
  • Fields - массив объектов TField. Используя это свойство, можно обращаться к полям по номеру, что удобно, когда заранее неизвестна структура таблицы:
         Edit1->Text=Table1->Fields[2]->AsString;

Наиболее часто при работе с компонентом TTable используются следующие методы:

  • Open и Close устанавливают значения свойства Active равными True и False соответственно.
  • Refresh позволяет заново считать набор данных из БД.
  • First, Last, Next, Prior перемещают указатель текущей записи на первую, последнюю, следующую и предыдущую записи соответственно, например:
         Table1->First();
     while (!Table1->Eof)
     {
     //что-то делаем...
     Table1->Next();
     };
  • MoveBy перемещает указатель на указанное число строк (оно может быть и отрицательным) в пределах таблицы
  • Insert, Edit, Delete, Append - переводят таблицу в режимы вставки записи, редактирования, удаления, добавления записи соответственно.
  • Post - осуществляет физическое сохранение измененных данных. Например:
         Table2->Insert();
     Table2->Fields[0]->AsInteger = 100;
     Table2->Fields[1]->AsString =Edit1->Text;
     Table2->Post();
  • Cancel - отменяет внесенные изменения, не сохраненные физически.
  • FieldByName - предоставляет возможность обращения к данным в полях по имени поля:
         S=Table1->FieldByName("area")->AsString;
  • SetKey переключает таблицу в режим поиска.
  • GotoKey начинает поиск строки, значение Fields[n] которой равно выбранному, где n - номер колонки таблицы, начиная с 0:
         Table1->SetKey();
     Table1->Fields[0]->AsString=Edit1->Text;
     Table1->GotoKey();
  • SetRangeStart, SetRangeEnd, ApplyRange позволяют выбрать нужные строки на основе диапазона значений какого-либо поля.
         Table1->SetRangeStart();
     Table1->Fields[0]->AsString = Edit1->Text;
     Table1->SetRangeEnd();
     Table1->Fields[0]->AsString = Edit2->Text;
     Table1->ApplyRange();
  • FreeBookmark, GetBookmark, GotoBookmark- позволяют создать помеченную строку в таблице и затем вернуться к ней позже. Методы Bookmark используют класс TBookmark. Метод GetBookmark устанавливает закладку на текущей cтроке таблицы. GotoBookmark осуществляет перемещение в таблице к строке, ранее отмеченной закладкой. Метод FreeBookmark используется для уничтожения объекта типа TBookmark:
         TBookmark Marker =Table1->GetBookmark();
     Table1->GotoBookmark(Marker);
     Table1->FreeBookmark(Marker);

Cобытия компонента TTable позволяют строить и контролировать поведение приложе ий БД. Например, событие BeforePost наступает перед вставкой или изменением записи, событие AfterPost - после сохранения вставленной или измененной записи, событие AfterDelete - после удаления записи и т.д.

Чтобы внести компонент TTable в форму, нужно выполнить следующее:

  1. Используя страницу Data Access палитры компонентов, разместить компонент TTable на форме или в модуле данных.
  2. Свойству DatabaseName присвоить имя каталога, где находится БД, либо псевдо има БД.
  3. Свойству TableName присвоить имя таблицы или выбрать таблицу из выпадающего списка.
  4. Внести в форму компонент DataSource и установить значение свойства DataSet равным имени компонента TTable.
  5. Внести компоненты Data Controls и связать их с компонентом DataSource для того, чтобы отобразить на экране данные из таблицы БД.

Компонент TField

Объекты класса TField являются свойством объекта TDataSet (напомним, что некоторые свойства объектов сами являются объектами с их собственными наборами свойств, и TField - один из них).

Свойство Fields объекта типа TDataSet позволяет обращаться к отдельным полям набора данных. Свойство Fields является массивом или набором объектов TField, динамически создающимся во время выполнения приложения. Элементы массива соответствуют колонкам таблицы.

Объект TField не делает никаких предположений относительно типов данных, с которыми он связан. Он имеет несколько свойств, позволяющих установить или вернуть обратно значения поля, например, AsString, AsBoolean, AsFloat, AsInteger. Наиболее часто используются свойства Text (cтрока текста, выводимого в связанный с данным полем интерфейсный элемент) и FieldName (имя поля базы данных).

Fields Editor позволяет создать так называемый статический список полей таблицы, добавляемых к описанию класса формы. Когда впервые используются такие компоненты TDataSet, как компонент TTable или TQuery, список полей для них динамически генерируется в процессе выполнения приложения на основе имеющихся столбцов таблиц или результатов SQL-запроса. Fields Editor позволяет определить и затем модифицировать статический список компонентов Field на этапе проектирования приложения. При внесении колонок с использованием Fields Editor для каждого из полей, добавленных к TDataSet, возникают объекты TField, после чего можно увидеть эти поля в инспекторе объектов и использовать в приложениях их свойства, события и методы.

Использовать Fields Editor нужно следующим образом:

  1. Разместить компонент TTable или TQuery на форме.
  2. Установить свойство DatabaseName для TTable или TQuery.
  3. Установить свойство TableName компонента TTable или свойство SQL компонента TQuery.
  4. Выбрать компонент TDataSet на форме и нажать правую клавишу мыши, после чего появится контекстное меню.
  5. Из контекстного меню выбрать Fields Еditor. Появится пустое окно с заголовком, совпадающим с именем компонента TTable.
  6. Снова нажать правую клавишу мыши над пустым окном и из контекстного меню выб ать опцию Add Fields.

    Имена всех колонок таблицы или запроса появятся в диалоговой панели Add Fields (рис. 1.)

    Рис.1. Формирование списка полей

  7. Выбрать поля, которые нужно внести в список объектов, и нажать OK.

    Рис. 2. Сформированный список полей, доступных на этапе проектирования

  8. Если требуется создать вычисляемое поле на основе имеющихся полей, нажать правую клавишу мыши и из контекстного меню выбрать New Field для создания нового поля на основе существующего или для создания вычисляемого поля (в дальнейшем следует создать код обработчика события OnCalcFields компонента TTable, где и производятся необходимые вычисления).
  9. Если необходимо удалить статическое поле из списка полей в наборе данных, нужно нажать правую клавишу мыши и из контекстного меню выбрать Delete.

После того, как в Fields Editor добавлены поля, они появятся в инспекторе объектов, а ссылки на них - в h-файле формы.

Если теперь применить операцию drag-and-drop к выделенным в Fields Editor полям, перенеся их на форму, то можно получить готовую форму с необходимым набором интерфейсных элементов (в нашем случае - DBEdit, позволяющий отображать и редактировать строковые, числовые, денежные и другие поля, чьи значения представимы в виде строки символов, и DBImage, позволяющий отображать графические поля и использовать Clipboard для их редактирования). Если к такой форме добавить компонент TDBNavigator (этот компонент реализует основные методы TTable и TQuery, связанные с редактированием данных) и связать его с имеющимся компонентом TDataSource, а затем скомпилировать проект, получим приложение для просмотра и редактирования данных в таблице.

Рис. 3. Результат переноса на форму полей из Fields Editor

При работе Fields Editor создаются объекты, соответствующие видимым в инспекторе объектов полям. Эти объекты являются потомками объектного типа TField. Таблица 1 описывает существующие классы таких объектов:

Таблица 1. Потомки TField

Потомок | Описание
TStringField | Текстовые данные фиксированной длины до 8192 символов.
TAutoIncField | Целые числа от -2,147,483,648 до 2,147,483,647. Предназначен для нумерации ст ок в наборе данных. Потомок TIntegerField.
TIntegerField | Целые числа от -2,147,483,648 до 2,147,483,647.
TSmallIntField | Целые числа от -32768 до 32767.
TWordField | Целые числа от 0 до 65535.
TFloatField | Действительные числа с абсолютной величиной от 1.2x10e-324 до 1.7x10e308 с точностью до 15-16 цифры.
TCurrencyField | Действительные числа с абсолютной величиной от 1.2x10e-324 до 1.7x10e308 с точностью до 15-16 цифры.
TBooleanField | Значения true или false.
TDateTimeField | Значения даты и времени.
TDateField | Значения даты.
TTimeField | Значения времени.
TBlobField | Произвольное поле данных без ограничений размера.
TBytesField | Произвольное поле данных без ограничений размера.
TVarBytesField | Произвольное поле данных до 65535 символов с фактической длиной, представленной в первых двух байтах.
TMemoField | Текст произвольной длины.
TGraphicField | Графическое поле произвольной длины, например, битовый массив.

Компонент TDBGrid

Компонент TDBGrid обеспечивает табличный способ отображения на экране строк данных из компонентов TTable или TQuery. Приложение может использовать TDBGrid для отображения, вставки, уничтожения, редактирования данных БД. Обычно DBGrid используется в сочетании с DBNavigator, хотя можно использовать и другие интерфейсные элементы, включив в их обработчики событий методы First, Last, Next, Ptior, Insert, Delete, Edit, Append, Post, Cancel компонента TTable.

Внешний вид таблицы (например, надписи в заголовках столбцов) может быть изменен с помощью редактора свойств Columns Editor. Для вызова Columns Editor нужно либо выбрать соответствующую опцию в контекстном меню компонента DBGrid или щелкнуть мышью в колонке значений напротив свойства Columns в инспекторе объектов.

Рис. 4. Установка свойств столбцов DBGrid с помощью Columns Editor

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

Поля Float, Integer и Date обладают свойством DisplayMask. Это свойство можно использовать, чтобы форматировать данные в компоненте DBGrid или другом компоненте Data Controls. Например, экранный формат mm-dd-yy может использоваться для размещения полей типа дата.

Некоторые компоненты TField (например, TStringField) обладают свойством EditMask, которое можно установить, вводя данные в DBGrid и другие компоненты Data Controls. Для установки свойства EditMask нужно установить компонент Field в Object Inspector и выбрать свойство EditMask, после чего появится диалоговая панель Input Mask Editor, представленная на рис. 5. Чтобы проверить маску редактирования, нужно ввести значение в поле Test Input.

Рис. 5. Диалоговая панель Input Mask Editor

Пример 1. Разработка простейшего приложения

Попробуем создать простейшее приложение для редактирования таблицы БД. Для этого воспользуемся таблицей COUNTRY базы данных BCDEMOS, содержащейся в комплекте поставки C++ Builder.

Создадим новый проект и сохраним его главную форму как COUNT1.CPP, а сам проект как COUNT.MAK. Используя страницу Data Access палитры компонентов, разместим компоненты TTable и DataSource на форме. Установим свойство Database Name компонента Table1 равным BCDEMOS, свойство TableName равным COUNTRY.DB, а свойство Active равным true. Далее установим значение свойства DataSet компонента DataSource1 равным Table1.

Используя страницу Data Controls палитры компонентов, внесем компоненты DBGrid и DBNavigator в форму. Присвоим свойству DataSource обоих компонентов значение DataSource1.

Рис. 6. Главная форма приложения COUNT

Полученный проект можно скомпилировать и протестировать.

Отметим, что есть и более простой способ построить подобное приложение. Для этой цели можно воспользоваться утилитой SQL Explorer, вызываемой с помощью пункта меню Database/Explore. Выбрав страницу Databases в левой части окна SQL Explorer и щелкнув на значке "+" возле имени соответствующей базы данных, нужно выбрать опцию Tables. Затем в правой части окна нужно выбрать закладку Summary и перетащить с помощью мыши значок с именем нужной таблицы (в нашем случае COUNTRY.DB) на пустую форму. На форме появятся компоненты TDBGrid, TDataSource и TTable. После этого следует добавить DBNavigator, связать его с TDataSource и сделать таблицу активной.

Рис. 7. SQL Explorer

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

Таблица, используемая в предыдущем примере, содержит поля Area (площадь в кв.км ) и Population (население). Попробуем создать вычисляемое поле, содержащее п отность населения стран, представленных в этой таблице. Для этого запустим Fields Editor для компонента Table1, внесем все поля этой таблицы в набор данных и создадим новое поле с названием pop2. Выберем Float в списке Field type. Убедимся, что выбрана радиокнопка Calculated, и щелкнем на кнопке OK

Рис. 8. Создание вычисляемого поля

Создадим обработчик события OnCalcFields компонента Table:

     Table1pop2->Value=Table1Population->Value/Table1Area->Value;

Установим значение свойства DisplayFormat компонента Table1Pop2 равным .## . Запустим и протестируем приложение и убедимся в том, что вычисляемое поле действительно присутствует.

Компоненты TDBLookup

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

C++ Builder предоставляет четыре компонента для просмотра и ввода значений в таблицы:

  • Компонент TDBLookupListBox (страница Data Controls)
  • Компонент TDBLookupComboBox(страница Data Controls)
  • Компонент TDBLookupList (страница Win 3.1)
  • Компонент TDBLookupCombo(страница Win 3.1)

Последние два компонента присутствуют в палитре компонентов с целью совместимости с формами, созданными в версии Delphi 1.0 (как известно, в C++ Builder можно использовать формы Delphi 1.0 и 2.0).

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

Компоненты DBLookupList и DBLookupListBox отличаются от компонента DBListBox тем, что позволяют согласовать выбранное значение из списка с текущей строкой другой таблицы БД, тогда как для DBListBox список значений для выбора определен заранее и не имеет отношения к таблицам БД.

Компоненты DBLookupCombo и DBLookupComboBox являются похожими на ComboBox компонентами, то есть они похожи на DBLookupList и DBLookupListBox, за исключением того, что пользователь может либо выбирать значение в списке, либо вводить новое значение. ComboBox, на который похожи DBLookupCombo и DBLookupComboBox, сочетает в себе возможности ListBox с возможностями компонента Edit. DBLookupCombo и DBLookupComboBox отличаются от компонента ComboBox тем, что позволяют согласовать выбранное значение с текущей строкой другой таблицы БД. При размещении компонентов DBLookupList, DBLookupListBox, DBLookupCombo или DBLookupComboBox на форме эта форма в приложении должна содержать DataSource и компонент - потомок TDataSet (например, TTable).

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

В этом примере рассматривается связь таблиц CUSTOMER (компании-заказчики) и ORDERS (их заказы) из базы данных BCDEMOS и используется компонент DBLookupListBox для того, чтобы отобразить на экране имя компании, соответствующее значению CustID, находящемуся в таблице ORDERS.

Чтобы создать форму для выведения на экран выбранных из таблицы ORDERS полей, следует открыть новый проект и сохранить главную форму как DBLOOK1.CPP, а проект как DBLOOK.MAK. Далее нужно разместить на форме компоненты TTable, DataSource, DBGrid и DBNavigator. Далее следует присвоить свойству Database Name компонента Table1 значение BСDEMOS, свойству TableName - значение ORDERS.DB, а свойству Active - значение true. Свойству DataSet компонента DataSource1 присвоим значение Table1, а свойству AutoEdit - значение false. Свойству DataSource компонентов DBGrid1 и DBNavigator1 присвоим значение DataSource1. Далее используем Fields Editor, чтобы внести в набор данных Table1 поля OrderNo, CustNo, SaleDate, ItemsTotal, AmountPaid. Расположим поля в Fields Editor так, чтобы OrderNo был бы первым, а CustNo - вторым. Можно скомпилировать и сохранить это приложение, затем запустить и протестировать его.

Теперь внесем в приложение компонент DBLookupListBox и установим его свойство DataSource равным DataSource1, а свойство DataField равным CustNo. Поместим на форму новые компоненты TTable и DataSource. Установим свойство DatabaseName для компонента Table2 равным BCDEMOS, свойство TableName равным CUSTOMER.DB, а свойство Active равным true. Свяжем DataSource2 с Table2. Свяжем компонент DBLookupListBox1 с DataSource2, установив значение свойства ListSource компонента DBLookupListBox1 равным DataSource2, свойства KeytField равным CustNo, а свойства ListField равным Company. Можно скомпилировать и сохранить приложение, затем запустить и протестировать его.

В компоненте DBLookupListBox будет выделено название компании, соответствующее значению CustNo в текущей строке DBGrid (рис. 9). Можно использовать DBLookupListBox для выбора заказчика по имени, при этом в таблице Orders будет устанавливаться соответствующее значение CustNo.

Рис. 9. Приложение с компонентом DBLookupListBox.

В следующих статьях будет рассмотрено использование компонента TQuery и возможности создания и использования SQL-запросов в приложениях С++ Builder.


Подборка статей по С++ Builder : Работа с базами данных в Borland C++ Builder. Часть 2.

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/glava2.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Работа с базами данных в Borland C++ Builder. Часть 2.

Наталия Елманова, Центр Информационных Технологий

Компьютер-Пресс, 1997, N 6, 7.

  • Введение
  • Компонент TQuery
  • Visual Query Builder
  • Пример использования компонента TQuery
  • Модули данных
  • SQL Explorer и словарь данных

Введение

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

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

Компонент TQuery

Компонент TQuery, как и компонент TTable, обладает всеми свойствами компонента TDataSet.

Как и в случае с компонентом TTable, компонент TDataSource управляет взаимодействием между компонентами Data Controls и компонентом TQuery. Обычно приложение имеет один компонент DataSource для каждого компонента TQuery.

Наиболее часто используются следующие свойства компонента TQuery:

  • Active - указывает, открыт (true) или закрыт (false) данный запрос
  • Eof, Bof - эти свойства принимают значение true, когда указатель текущей записи расположен на последней или соответственно первой строке набора данных, являющегося результатом выполнения запроса.
  • DatabaseName - имя каталога либо псевдоним (alias) удаленной БД, к которой осуществляется запрос.
  • DataSource - указывает источник данных для параметризованных запросов (т.е. запросов с параметрами, значение которых заранее неизвестно).
  • Fields - это свойство доступно только во время выполнения (run-time only) и используется для чтения или модификации поля, определяемого по порядковому номеру.
  • Params - содержит параметры для параметризованного запроса, как SomeNo в следующем примере:
         Select * from Orders where CustNo=:SomeNo
  • SQL - строковый массив, содержащий текст оператора запроса SQL.

Отметим, что язык запросов SQL (Structured Query Language), традиционно применяемый при работе с серверными СУБД, может быть использован и при работе с таблицами формата dBase и Paradox. Не вдаваясь в подробное описание синтаксиса этого языка (с ним можно познакомиться в других источниках, например, в книге М.Грабера "Введение в SQL"), отметим одну его особенность. SQL - язык непроцедурный. На нем можно написать, что нужно получить в результате запроса, но нельзя написать, как это сделать, то есть нельзя описать саму процедуру выполнения запроса. Дело в том, что реализация выполнения тех или иных операторов SQL серверами баз данных может быть различна, и в большинстве случаев неинтересна клиентскому приложению, создаваемому с помощью С++ Builder. В случае таблиц dBase или Paradox реализацию SQL берет на себя библиотека Borland Database Engine.

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

  • RequestLive - если это свойство имеет значение true и синтаксис запроса таков, что его результат может быть модифицируем, пользователь может редактировать данные с сохранением их в базе данных. Если RequestLive имеет значение false, результат запроса возвращается в состоянии read-only.

Наиболее часто используются следующие методы компонента TQuery:

  • ExecSQL - выполняет SQL-запрос, содержащийся в свойстве SQL, если запрос не возвращает данные. Следует употреблять этот метод при вставке, редактировании или удалении данных. При выполнении же оператора SELECT (выбор данных) следует использовать метод Open. Следующий пример показывает применение метода ExecSQL:
         Query1->Close();
     Query1->SQL->Clear();
     Query1->SQL->Add("Delete emp where empno=1010");
     Query1->ExecSQL();
  • Open - открывает компонент TQuery. Он эквивалентен присвоению свойству Active значения true. Используется, если результатом запроса является набор данных (такие запросы обычно начинаются с оператора SELECT). Пример использования метода Open:
         Query1->Open();
  • Close - закрывает компонент TQuery. Вызов Close эквивалентен присвоению свойству Active значения false. Пример использования метода Close:
         Query1->Close();
  • Prepare - обеспечивает передачу серверу баз данных запроса, содержащегося в свойстве SQL, для оптимизации и компиляции. Полный запрос с параметрами не передается, пока не вызваны методы Open или ExecSQL. Даже если метод Prepare не вызывается явно, он будет вызван неявно, если используются методы Open или ExecSQL (в этом можно убедиться, запустив утилиту SQL Monitor). Пример использования метода Prepare:
         Query1->Close();
     Query1->SQL->Add("Delete emp where empno=:empno");
     Query1->Prepare();

    Компоненты TQuery обладают большим разнообразием методов, унаследованных от TDataSet. Наиболее часто используются следующие методы:

  • First, Last, Next, Prior перемещают указатель текущей записи на первую, последнюю, следующую и предыдущую записи соответственно, например:
  • MoveBy перемещает указатель на определенное количество строк.
  • Insert, Edit, Delete, Append, Post, Cancel - позволяют модифицировать результат запроса. Метод Insert позволяет вносить в результат запроса строки, как в следующем примере:
         Query2->Insert();
     Query2->Fields[0]->AsInteger = 100;
     Query2->Fields[1]->AsString =Edit1->Text;
     Query2->Post();

    Метод Post подтвержает операции Insert, Update или Delete, совершая реальное физическое изменение в базе данных. Метод Cancel отменяет незавершенные операции Insert, Delete, Edit или Append.

  • FreeBookmark, GetBookmark, GotoBookmark- - позволяют создавать закладки (маркированные строки) в запросе и затем вернуться к такой строке позже.

Прежде чем перейти к непосредственному использованию запросов, следует остановиться на весьма полезном инструменте - генераторе запросов Visual Query Builder, с помощью которого можно определить свойство SQL компонента TQuery, если по каким-либо причинам это неудобно делать непосредственно в редакторе свойств.

Visual Query Builder

Visual Query Builder (VQB) является визуальным средством для построения запросов, базирующихся на SQL. С помощью этого средства можно строить комплексные запросы, обладая некоторыми знаниями SQL или не имея таких знаний вовсе. Запросы строятся шаг за шагом путем последовательного добавления выражений, таблиц, полей и отношений, пока не получится требуемый результат.

Visual Query Builder можно вызвать, выбирая компонент TQuery и нажимая правую кнопку мыши, после чего появляется контекстное меню, из которого следует выбрать опцию Query Builder.

Примечание. Visual Query Builder входит в комплект поставки не всех версий Borland C++ Builder либо может быть не установлен. В этом случае в контекстном меню может не быть соответствующей опции.

После выбора Query Builder на экране появится диалоговая панель Databases, позволяющая выбрать БД и осуществить соединение с ней (рис. 1).

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

После соединения с базой данных на экран выводится окно Visual Query Builder и диалоговая панель для выбора таблиц, используемых в запросе (рис. 2).

Рис.2. После соединения с БД появляется окно Visual Query Builder и диалоговая панель Add Table.

Панель инструментов Visual Query Builder позволяет выбирать операции, которые можно выполнить при создании, тестировании и просмотре текста запроса. Таблица 8.1 описывает назначения кнопок на панели инструментов Visual Query Builder:

Таблица 1. Панель инструментов Visual Query Builder

Кнопка |  | Назначение
 | New | Создает новый запрос.
 | Open | Открывает файл запроса.
 | Save As | Сохраняет запрос в файле.
 | Options | Помещает на экране диалоговую панель Options, позволяющую установить различные опции запроса.
 | Table | Помещает на экран диалоговую панель Add Table, позволяющую вносить таблицы в оператор SQL.
 | Expression | Помещает на экран диалоговую панель Expression, позволяющую создавать вычисляемые поля, допустимые с точки зрения языка SQL.
 | SQL | Выводит окно с текстом сгенерированного SQL-запроса.
 | Run | Выполняет сгенерированный запрос и выводит на экран результаты.
 | OK | Устанавливает свойство SQL компонента TQuery равным сгенерированному запросу и закрывает Visual Query Builder.
 | Cancel | Отменяет присвоение свойства SQL компонента TQuery и закрывает Visual Query Builder.
 | Help | Выводит на экран оперативную помощь для Visual Query Builder.

Диалоговая панель Add Table (рис. 2) появляется при открытии Visual Query Builder или при выборе соответствующей кнопки диалоговой панели. Она позволяет вносить в запрос таблицы, выводя список всех таблиц в текущей БД. Если отметить опцию Include System Tables, для построения запроса будут доступны системные таблицы данной БД.

Рис. 3. В рабочем пространстве окна Visual Query Builder появится таблица, выбранная из списка

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

  1. Если диалоговая панель Add Table не присутствует на экране, щелкнуть кнопкой Table на панели инструментов, чтобы вывести ее на экран.
  2. Выбрать имя таблицы из списка таблиц в диалоговой панели и щелкнуть на кнопке Add. Описание структуры выбранной таблицы появится в верхней части окна окна Visual Query Builder.
  3. Повторить п.2, пока все требуемые таблицы не будут внесены в запрос, и затем нажать кнопку Close.

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

Рис. 4. Создание комбинированного запроса

Чтобы скомбинировать информацию из нескольких таблиц, нужно определить, как будут соединяться разные таблицы. С помощью Visual Query Builder можно определить колонки, имена которых служат для связи таблиц. С этой целью нужно выбрать имя колонки, по которой осуществляется межтабличная связь, в одной из связываемых таблиц, нажать левую клавишу мыши и переместить курсор (он изменит форму) на имя соответствующей колонки другой таблицы. В результате в рабочем пространстве запроса образуется линия, связывающая колонки этих двух таблиц (рис. 8.7).

Можно просмотреть или отредактировать критерий соединения таблиц, два раза щелкнув на линии, показывающей это соединение в верхней части окна Visual Query Builder. При этом появляется диалоговый блок Join (рис.5):

Рис. 5. Выбор критерия соединения таблиц

Чтобы определить критерий отбора для запроса, нужно использовать строку Criteria таблицы в нижней части окна Visual Query Builder.

Строка Criteria допускает любые выражения запроса, являющиеся допустимыми с точки зрения предложения WHERE оператора SQL (табл. 2).

Таблица 2. Допустимые выражения в предложении WHERE SQL-запроса

Выражение | Описание
= | Равно
> | Больше, чем
< | Меньше, чем
!= | Не равно
like | Строка символов в сравнении с образцом.
Between | Не меньше, чем начальное значение и не больше, чем конечное.
In | Содержится в списке

Выражения, введенные в строку Criteria, являются условиями AND. Например, если имеется таблица с колонкой Name и требуется найти каждый элемент в этой колонке, начинающийся с буквы С, следует ввести в строку Criteria Name LIKE'C%'. Результат эквивалентен внесению AND Name LIKE 'C%'в предложение WHERE оператора SQL.

Условия OR вводятся как выражения в строку OR (ниже строки Criteria).

Можно отсортировать результаты в восходящем (ascending) или нисходящем (descending) порядке для выбранной колонки. Чтобы определить порядок сортировки для колонки, следует поместить указатель мыши на колонке около строки Sort , щелкнуть правой кнопкой мыши и выбрать Ascending или Descending из контекстного меню.

Можно сгруппировать результаты запроса, используя строку Option.

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

Рис. 6. Выбор опции для колонки

Можно определить условия для результатов группировки, используя строку Group Condition, что эквивалентно внесению выражения с предложением HAVING в SQL-операторе SELECT, использующем предложение GROUP BY.

Visual Query Builder позволяет определить выражения как часть запроса. Выражения представляют собой операции вычислений на основе числовых значений, строковые операции и т.д.

Чтобы создать вычисляемое поле, следует щелкнуть кнопкой Expression на панели инструментов. Появится диалоговая панель Expression ( см. рис. 7).

Рис. 7. Вызов диалоговой панели Expression .

Диалоговая панель Expression позволяет использовать в запросах арифметические операции умножения, деления, сложения, вычитания, имена колонок и составные выражения, такие как avg, count, min, max и sum. Можно редактировать вручную или строить выражения, используя блок редактора Expression.

Рис. 8. Диалоговая панель Options

Диалоговая панель Options (рис. 8) используется для того, чтобы определить опции для оператора SQL. Можно, например, удалить повторяющиеся записи (Remove Duplicate Records), использовав предложение DISTINCT оператора SQL. Чтобы вызвать диалоговую панель Options, нужно щелкнуть кнопкой Options на панели инструментов.

Можно выполнить запрос, сгенерированный Visual Query Builder. Результаты запроса отобразятся на экране в диалоговой панели Result Window (окно результатов). Это окно позволяет убедиться, что колонки запроса, критерий выбора, критерии группировки и сортировки были определены корректно. Чтобы выполнить запрос, нужно щелкнуть кнопкой Run на панели инструментов. Появится окно Result Window, подобное представленному на рис. 8.

Рис. 9. Результаты запроса отобразятся на экране в Result Window

Можно проверить текст запроса, просматривая окно SQL Statement, для чего следует щелкнуть на кнопке SQL на панели инструментов. Это окно показывает сгенерированный оператор SQL. При добавлении или изменении колонок запроса, критерия отбора, критерия группировки или сортировки содержимое окна SQL Statement редактируется автоматически (рис. 10).

Рис. 10. Сгенерированный оператор SQL в окне SQL Statement.

Для завершения работы Visual Query Builder следует нажать кнопку с зеленой галочкой на инструментальной панели. После этого свойству SQL компонента TQuery, для которого был активизирован Visual Query Builder, будет присвоен сгенерированный текст SQL-запроса.

Пример использования компонента TQuery

Попробуем применить полученные сведения для создания приложения, использующего компоненты TTable, TQuery, TDataSource, TDBGrid. Воспользуемся для этой цели таблицами Customer.db и Orders.db, имеющимися в базе данных BCDEMOS, входящей в комплект поставки Borland C++ Builder. Данное приложение должно будет выводить списки клиентов из таблицы Customer, их заказов из таблицы Orders, а также позволять выбирать диапазон номеров клиентов.

Создадим новый проект и сохраним его главную форму как CUST1.CPP, а проект как CUST.MAK.

Изменим заголовок заголовок формы на "Контроль заказов". Разместим на форме компонент TDBGrid, два компонента TGroupBox, один компонент TTable, два компонента TQuery, три компонента TDataSource. На компонент GroupBox1 поместим три компонента TRadioButton и два компонента TButton. На компонент GroupBox2 поместим два компонента TEdit, два компонента TEdit и один компонент TButton.

Рис. 11. Расположение компонентов на форме приложения CUST

Установим следующие свойства для этих компонентов:

Имя компонента | Свойство | Значение
Table1 | DatabaseName | BCDEMOS
 | TableName | CUSTOMER.DB
 | Active | false
DataSource1 | DataSet | Table1
DBGrid1 | DataSource | DataSource1
Query1 | Database Name | BCDEMOS
 | SQL | select * from orders
 | Active | false
DataSource2 | DataSet | Query1
Query2 | DatabaseName | BCDEMOS
DataSource3 | DataSet | Query2
Button1 | Caption | Открыть список &клиентов
Button2 | Caption | Открыть список &заказов
RadioButton1 | Caption | Клиенты
 | Checked | true
RadioButton2 | Caption | Заказы
GroupBox1 | Caption |
GroupBox2 | Caption |
Button3 | Caption | Установить &диапазон
Edit1 | Text |
Edit2 | Text |
Label1 | Caption | Начало:
Label2 | Caption | Конец:
RadioButton3 | Caption | Заказы клиентов

Создадим обработчик события OnClick для кнопки Button1:

     void __fastcall TForm1::Button1Click(TObject *Sender)
     {
     if (Table1->Active)
     {
     Table1->Close();
     Button1->Caption = "Ioe?uou nienie &eeeaioia";
     }
     else
     {
     Table1->Open();
     Button1->Caption= "Cae?uou nienie &eeeaioia";
     }
     }

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

Создадим обработчик события OnClick для кнопки Button2:

     void __fastcall TForm1::Button2Click(TObject *Sender)
     {
     if (Query1->Active)
     {
     Query1->Active = false;
     Button2->Caption = "Ioe?uou список &caeacia";
     }
     else
     {
     Query1->Active = true;
     Button2->Caption = "Заe?uou список &caeacia";
     }
     }

При нажатии на кнопку Button2 будет то открываться, то закрываться запрос Query1, содержащий список заказов:

Создадим обработчики событий OnClick для радиокнопок RadioButton1 и RadioButton2:

     void __fastcall TForm1::RadioButton1Click(TObject *Sender)
     {
     DBGrid1->DataSource = DataSource1;
     }
     //------------------------------------------------------------------------
     void __fastcall TForm1::RadioButton2Click(TObject *Sender)
     {
     DBGrid1->DataSource =DataSource2;
     }

Теперь с помощью этих радиокнопок можно переключаться между списком клиентов и списком заказов.

Создадим обработчик события OnClick для кнопки Button3:

     void __fastcall TForm1::Button3Click(TObject *Sender)
     {
     if (Table1->Active)
     {
     Table1->SetRangeStart();
     Table1->Fields[0]->AsString = Edit1->Text;
     Table1->SetRangeEnd();
     Table1->Fields[0]->AsString = Edit2->Text;
     Table1->ApplyRange();
     }
     }

Теперь с помощью полей редактирования Edit1 и Edit2 и кнопки Button3 можно выбрать диапазон номеров клиентов для отображения сведений о них в DBGrid1.

Затем с помощью Visual Query Builder установим свойство SQL компонента Query2. Выберем в качестве имени базы данных BCDEMOS и внесем в запрос таблицы CUSTOMER и ORDERS. Затем установим связь между таблицами, проведя линию от поля CustNo в таблице CUSTOMER к полю CustNo в таблице ORDERS.

Внесем в запрос следующие поля:

  • Customer.CustNo
  • Customer.Company
  • Orders.OrderNo
  • Orders.AmountPaid

Рис. 12. Использование Visual Query Builder для создания комбинированного запроса

Теперь отсортируем результаты запроса по номеру покупателя и выйдем из Visual Query Builder.

Используя инспектор объектов, выберем компонент Query2 и установим его свойство Active равным true.

Создадим обработчик события OnClick для RadioButton3:.

     void __fastcall TForm1::RadioButton3Click(TObject *Sender)
     {
     DBGrid1->DataSource= DataSource3;
     }

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

Рис. 13. Так выглядит готовое приложение

Нажмем кнопку "Открыть список клиентов". Введем значения полей "Начало" и "Конец" (например, 1200 и 1700 соответственно) и затем нажмем кнопку "Установить диапазон". Убедимся, что значения номеров заказов действительно находятся в пределах этого диапазона.

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

Модули данных

В предыдущем примере мы размещали компоненты со страницы Data Access непосредственно на главную форму приложения. Однако наличие на форме большого количества невидимых компонентов в ряде случаев затрудняет проектирование пользовательского интерфейса. Кроме того, нередко бывает удобно отделить компоненты, отвечающие за доступ к данным и бизнес-логику информационной системы, от интерфейсных элементов, например, для обегчения ее дальнейшей модернизации. Для этой цели в C++ Builder имеется специальный тип, называемый модулем данных - TDataModule. Компонент этого типа можно условно считать специальным видом формы, хотя он и порожден непосредственно от TСomponent. Такой компонент-контейнер может содержать компоненты со страницы Data Access, а сам он не виден пользователю во время выполнения.

Создадим другую версию рассмотренного выше примера с использованием модуля данных. В этом случае компоненты Data Access помещаются в модуль данных, а ссылка на соответствующий h-файл помещается в модуль, связанный с главной формой приложения.

Рис.14. Размещение компонентов доступа к данным в DataModule

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

     //----------------------------------------------------
     #include <vcl\vcl.h>
     #pragma hdrstop

     #include "cust1.h"
     #include "custdm1.h" // Ссылка на модуль данных
     //-----------------------------------------------------
     #pragma link "Grids"
     #pragma resource "*.dfm"
     TForm1 *Form1;
     //-----------------------------------------------------
     __fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner)
     {
     }
     //-----------------------------------------------------
     void __fastcall TForm1::Button1Click(TObject *Sender)
     {
     if (DataModule1->Table1->Active)
     {
     DataModule1->Table1->Close();
     Button1->Caption = "Открыть таблицу &клиентов";
     }
     else
     {
     DataModule1->Table1->Open();
     Button1->Caption= "Закрыть таблицу &клиентов";
     };
     }
     //-----------------------------------------------------
     void __fastcall TForm1::Button2Click(TObject *Sender)
     {
     if (DataModule1->Query1->Active)
     {
     DataModule1->Query1->Active = false;
     Button2->Caption = "Открыть таблицу &заказов";}
     else

     {
     DataModule1->Query1->Active = true;
     Button2->Caption = "Закрыть таблицу &заказов";
     }
     }
     //-----------------------------------------------------
     void __fastcall TForm1::RadioButton1Click(TObject *Sender)
     {
     DBGrid1->DataSource = DataModule1->DataSource1;
     }
     //-----------------------------------------------------
     void __fastcall TForm1::RadioButton2Click(TObject *Sender)
     {
     DBGrid1->DataSource = DataModule1->DataSource2;
     }
     //-----------------------------------------------------
     void __fastcall TForm1::RadioButton3Click(TObject *Sender)
     {
     DBGrid1->DataSource = DataModule1->DataSource3;
     }
     //----------------------------------------------------
     void __fastcall TForm1::Button3Click(TObject *Sender)
     {
     if (DataModule1->Table1->Active)
     {
     DataModule1->Table1->SetRangeStart();
     DataModule1->Table1->Fields[0]->AsString = Edit1->Text;
     DataModule1->Table1->SetRangeEnd();
     DataModule1->Table1->Fields[0]->AsString = Edit2->Text;
     DataModule1->Table1->ApplyRange();
     }
     }
     //----------------------------------------------------

SQL Explorer и словарь данных

SQL Explorer является удобным инструментом при работе с базами данных. Для его запуска следует выбрать пункт меню Database/Explore или запустить его в качестве отдельного приложения. В левой части окна Explorer на странице Databases представлены в виде раскрывающегося списка свойства всех описанных в файле конфигурации BDE псевдонимов. В правой части окна можно просмотреть содержимое таблиц, ввести и выполнить SQL-запрос к таблице, а также получить сведения о псевдонимах баз данных (драйвер БД, местоположение, используемый языковый драйвер и иные параметры, содержащиеся в файле конфигурации BDE), сведения о таблицах (тип таблицы, версия, дата последнего обновления и др.) и, при необходимости, внести в них изменения.

Рис. 15. SQL Explorer

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

Рис.16. Просмотр и модификация словаря данных в SQL Explorer

Отметим, что пользователь может создавать свои словари данных (пункт меню Dictionary/New). По умолчанию словари данных создаются в виде таблиц формата Paradox, но не возбраняется хранить их в других форматах или на серверах баз данных (это имеет смысл при работе над крупными проектами).


Created using
htm2chm 3.0

Download it NOW!


Подборка статей по С++ Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/index.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244
    Авторы: Виктор Алексанкин, Наталия Елманова:
  • Начинаем работать в Borland C++ Builder (Компьютер-Пресс, 1997, N 4)

    Автор: Наталия Елманова:
  • Изучаем компоненты Borland C++ Builder, (Компьютер-Пресс, 1997, N 5, стр. 107-113)
  • Borland C++Builder 3: новые возможности создания корпоративных информационных систем
  • Работа с базами данных в Borland C++ Builder. Часть 1. (Компьютер-Пресс, 1997, N 6)
  • Работа с базами данных в Borland C++ Builder. Часть 2. (Компьютер-Пресс, 1997, N 7)
  • Поставка приложений, созданных с помощью С++ Builder (Компьютер-Пресс, 1997)
  • Настройка параметров доступа к данным в C++ Builder (Компьютер-Пресс, 1997)
  • Создание отчетов (Компьютер-Пресс, 1997)
  • Перенос приложений C++ Builder в архитектуру клиент/сервер (Компьютер-Пресс, 1997)
  • Компоненты Data Access, используемые в приложениях клиент/сервер (Компьютер-Пресс, 1998, N 1)
  • О проектировании данных для клиент-серверных приложений С++Builder (Компьютер-Пресс, 1998, N 2)
  • Использование COM-технологии в C++Builder:
    - удаленный запуск серверов автоматизации
    - создание и использование OLE-документов в приложения
  • Создание контроллеров автоматизации с помощью C++Builder
  • Оптимизация приложений С++Builder в архитектуре клиент/сервер
  • Создание серверов автоматизации с помощью C++ Builder
  • Создание элементов управления ActiveX с помощью C++ Builder
  • Структуры файлов в C++ Builder

Оценить эту публикацию


Подборка статей по С++ Builder : Создание элементов управления ActiveX с помощью C++ Builder

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/ole_ax3.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Создание элементов управления ActiveX с помощью C++ Builder

Наталия Елманова

1 Что такое ActiveX

Технология ActiveX, рассматриваемая в данной статье, базируется на технологии Microsoft COM (Component Object Model - модель компонентных объектов), позволяющей создавать и использовать программные компоненты, предоставляющие различные сервисы другим приложениям, компонентам и операционной системе. COM представляет собой одну из реализаций концепции распределенных вычислений, базирующейся в общем случае на предоставлении возможности приложениям использовать для расширения своей функциональности готовые компоненты и объекты (иногда они называются сервисами). Технология COM позволяет использовать объектно-ориентированный подход не в рамках одного приложения, а в рамках операционной системы, но, в отличие от стандартных классов, определенных в исходном тексте и реализуемых как объекты в адресном пространстве одного процесса, эти компоненты могут в общем случае располагаться в адресных пространствах разных процессов и даже на разных компьютерах.

В настоящее время существуют три типа спецификаций COM, определенных Microsoft и включающих большое количество интерфейсов и функций:

  • OLE-документы - составные документы, содержащие внедренные или связанные объекты. Эта спецификация описывает правила создания контейнеров для таких документов с "активацией по месту". Отметим, что компонент OLEContainer Delphi и C++Builder создан с учетом этой спецификации (этой теме будет посвящена одна из следующих статей данного цикла).
  • OLE Automation. Эта спецификация описывает, как создать сервер и контроллер, управляющий его поведением с помощью скриптов или макросов. Эта спецификация также поддерживается Delphi и C++Builder (об этом также пойдет речь в ближайших статьях данного цикла).
  • Управляющие элементы ActiveX, использующие специальный вариант протокола Automation (о них-то и пойдет речь в данной статье).

Использование COM, и, в частности, технологии ActiveX, позволяет обеспечить создание приложений, собираемых из готовых компонентов - элементов управления ActiveX, отличающееся от привычной пользователям C++Builder или Delphi разработки приложений с помощью VCL-компонентов тем, что такая "сборка" не зависит от того, на каком языке написаны как готовые компоненты, так и использующее их приложение - лишь бы средство разработки поддерживало возможность использования таких компонентов в разрабатываемом приложении (такое приложение обычно называется контейнером).

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

Как любой COM-сервер, элемент управления ActiveX обладает уникальным идентификатором GUID и должен быть зарегистрирован в реестре. На основании этой записи может быть осуществлен поиск местоположения файла с расширением *.ocx, содержащего его реализацию.

Таким образом, создав элемент управления ActiveX, обладающий интересующей Вас функциональностью, Вы можете в дальнейшем позволить его пользователям встраивать этот элемент в свои приложения (например, написанные на Visual Basic, PowerBuilder, Delphi, C++Builder и др.), отображать его в web-броузере в составе выгруженной с Вашего web-сервера HTML-страницы, включать его в состав документов MS Office, при этом Вы не обязаны предоставлять исходный текст этого компонента.

Когда следует создавать управляющие элементы ActiveX? Естественно, в тех случаях, когда функциональность, содержащаяся в таком элементе, уникальна. Нет смысла создавать ActiveX, реализующий функциональность кнопки или текстового редактора - таких элементов управления, в том числе выполненных в виде ActiveX, на рынке готовых компонентов более чем достаточно. Нет смысла также создавать ActiveX, если он будет использоваться только в C++Builder - в этом случае проще создать VCL-компонент, который будет работать в использующем его приложении значительно быстрее, чем аналогичный ActiveX. Но создание элемента управления, реализующего, к примеру, часть автоматизированного рабочего места какой-либо категории сотрудников Вашего предприятия может в ряде случаев оказаться весьма полезным, особенно в следующих двух случаях. Первый случай - использование на предприятии различных средств разработки, например, C++Builder и Visual Basic; в этом случае разработчик, использующий Visual Basic, может встраивать в свои приложения ActiveX, созданный другим разработчиком и реализующий какую-либо функциональность, используемую несколькими различными автоматизированными рабочими местами. Второй случай - широкое использование Internet или intranet при автоматизации предприятия. В этом случае ActiveX, реализующий подобную функциональность, может быть встроен в HTML-страницу и отображен в web-броузере. Такой подход существенно облегчает решение проблемы обновления версий автоматизированных рабочих мест, так как вместо установки новых версий на рабочих станциях достаточно заменить один экземпляр ActiveX, хранящийся на web-сервере. Наиболее ярким примером такого подхода может быть выполненный в виде ActiveX "тонкий" клиент, получающий данные от удаленного сервера приложений, являющегося, в свою очередь, клиентом серверной СУБД.

Такой набор преимуществ сделал эту технологию за последние два года весьма популярной, и именно поэтому многие современные средства разработки, такие, как Delphi или С++Builder, позволяют создавать элементы управления ActiveX. Эти средства обычно имеют встроенные механизмы поддержки спецификации ActiveX с помощью автоматической генерации соответствующего кода (хотя, конечно, не возбраняется писать подобный код вручную).

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

2 Создание элементов управления ActiveX на основе VCL-компонентов

Как было сказано выше, C++Builder 3 позволяет создавать элементы управления ActiveX на основе VCL-компонентов. Для этой цели используется библиотека Microsoft ATL (Active Template Library), являющаяся на сегодняшний день индустриальным стандартом и позволяющая создавать элементы ActiveX, представляющие собой скомпилированный код и не требующие дополнительных run-time-библиотек для их выполнения. Процесс создания такого элемента управления весьма прост.

Для создания элемента управления ActiveX следует выбрать из репозитария объектов страницу ActiveX и далее - элемент ActiveX Control.

Далее следует заполнить появившуюся диалоговую панель:

Выбор имени ActiveX, имен модулей и базового VCL-класса

Рис.1. Выбор имени ActiveX, имен модулей и базового VCL-класса

Следует выбрать VCL-компонент, на основе которого будет создан элемент ActiveX. В качестве примера выберем TCalendar.

Отметим, что при выборе опции Include Design-Time Licence автоматически будет сгенерирован файл с расширением *.lic, без которого данный ActiveX нельзя будет использовать ни в каких средствах разработки, но можно поставлять с готовыми приложениями. Использование такого файла бывает удобно в случае, когда ActiveX поставляется бесплатно его автором в составе готового продукта, но требует отдельного лицензирования при встраивании его другими пользователями в свои разработки.

В результате работы ActiveX Control Wizard будут созданы несколько модулей, сгенерирован уникальный идентификатор (GUID) будущего ActiveX, а также соответствующая библиотека типов.

Проект библиотеки ActiveX в С++Builder

Рис.2. Проект библиотеки ActiveX в С++Builder

Библиотека типов содержит сведения о свойствах, событиях и методах компонента ActiveX, унаследованных от исходного VCL-компонента.

Библиотека типов созданного элемента ActiveX

Рис.3. Библиотека типов созданного элемента ActiveX

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

Далее следует сохранить и скомпилировать проект и зарегистрировать элемент ActiveX в реестре. Это делается с помощью выбора пункта меню Run/Register ActiveX Server.

После этого можно протестировать созданный ActiveX-компонент, открыв его, например, в Visual Basic. Отметим, что последние версии именно этого средства разработки широко используют элементы управления ActiveX в качестве составных частей создаваемых с их помощью приложений; фактически приложения Visual Basic собраны целиком из ActiveX-компонентов. Более того, спецификация ActiveX создана с учетом того, что в первую очередь Visual Basic и Visual C++ (и лишь затем остальные средства разработки) могут быть контейнерами для этих элементов управления. Поэтому тестирование поведения ActiveX в VisualBasic может более или менее гарантировать, что в других средствах разработки этот управляющий элемент будет вести себя точно так же.

В случае отсутствия Visual Basic можно воспользоваться и более широко распространенным Visual Basic for Applications. С этой целью можно запустить Microsoft Word 97, создать новый документ и выбрать на панели инструментов кнопку "Редактор Visual Basic". Далее следует выбрать в окне "Project" имя вновь созданного документа, щелкнуть по нему правой кнопкой мыши и из полученного контекстного меню выбрать опцию "Вставить/UserForm". На экране появится редактор форм Visual Basic и панель элементов. Далее нужно щелкнуть правой клавишей мыши по панели элементов и выбрать из контекстного меню опцию "Дополнительные элементы". Теперь следует из появившегося списка всех зарегистрированных элементов управления ActiveX выбрать нужный, и он автоматически окажется на панели элементов Visual Basic (можно поместить его на единственную имеющуюся страницу управляющих элементов или создать свою, выбрав опцию "Создать страницу" из контекстного меню ярлычков блокнота, расположенного на панели элементов).

После этого можно поместить наш ActiveX на форму и попытаться изменить какие-либо его свойства, используя для этой цели окно "Свойства".

Тестирование ActiveX в Visual Basic

Рис.4. Тестирование ActiveX в Visual Basic

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

Private Sub CommandButton1_Click()
UserForm1.Show
End Sub

Теперь можно нажать на панели инструментов "Visual Basic" кнопку "Выход из режима конструктора". После этого нажатие на созданную в теле документа кнопку приведет к появлению диалоговой панели с созданным нами элементом управления.

Можно было бы, конечно, протестировать поведение созданного ActiveX, установив его в палитру компонентов Delphi или C++Builder,, но это не самый лучший способ тестирования - ведь в основе создания нашего ActiveX лежит та же самая библиотека VCL, что и в основе создаваемого приложения для тестирования ActiveX. Использование для этой цели любого не имеющего отношения к VCL средства разработки, способного использовать элементы управления ActiveX в создаваемых приложениях, более оправдано. При этом следует заметить, что Visual Basic for Applications представляет собой наиболее часто встречающееся средство разработки такого класса, так как входит в состав самого популярного в нашей стране офисного пакета.

3 Создание страниц свойств

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

Для создания страницы свойств выберем из репозитария объектов страницу ActiveX и элемент Property Page. В результате получим форму, на которой можно размещать интерфейсные элементы.

Создадим страницу для редактирования свойств CalendarDate и GridLineWidth. Для этого разместим на вновь созданной форме два компонента TStaticText и два компонента TEdit.

Страница свойств на этапе проектирования

Рис.5. Страница свойств на этапе проектирования

В созданной форме имеются сгенерированные прототипы обработчиков событий UpdatePropertyPage и UpdateObject. Добавим в них соответствующий код:

 void __fastcall Tpp2f::UpdatePropertyPage(void)
 {
  Edit1->Text=OleObject.OlePropertyGet("CalendarDate");
  Edit2->Text=OleObject.OlePropertyGet("GridLineWidth");
 }
 //-----------------------------------------------
 void __fastcall Tpp2f::UpdateObject(void)

 {
  OleObject.OlePropertySet("CalendarDate",Variant(Edit1->Text));
  OleObject.OlePropertySet("GridLineWidth",Variant(Edit2->Text));
 }

Далее следует создать ссылку на странице свойств в модуле, описывающем реализацию элемента ActiveX. С этой целью следует модифицировать h-файл .Модификация кода заключается во вставке строки, указывающей на необходимость регистрации страницы свойств:

 BEGIN_PROPERTY_MAP(TCCX2Impl)
   PROP_PAGE(CLSID_pp2f)
 END_PROPERTY_MAP()

Следует также включить ссылку на h-файл страницы свойств в h-файл реализации ActiveX.

Далее следует заново скомпилировать библиотеку ActiveX и зарегистрировать ее.

Если теперь в среде разработки Visual Basic поместить на пользовательскую форму наш ActiveX и выбрать страницу свойств "Специальные", получим созданную нами страницу:

Страница свойств на этапе тестирования ActiveX

Рис.6. Страница свойств на этапе тестирования ActiveX

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

Результат использования страницы свойств

Рис.7. Результат использования страницы свойств

4 Создание активных форм

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

Попробуем создать простейший пример такого элемента управления. Для его создания следует выбрать со страницы ActiveX репозитария объект ActiveForm, ответить на вопросы об имени компонента, после чего в дизайнере форм получим пустую форму - заготовку будущего ActiveX. Добавим на эту форму компоненты TCheckBox, TButton, TImage и TOpenPictureDialog.

Активная форма на этапе проектирования

Рис.8. Активная форма на этапе проектирования

Создадим обработчики событий, связанных с TCheckBox и TButton:

 void __fastcall TActiveFormX::Button1Click(TObject *Sender)
 {
 if  (OpenPictureDialog1->Execute())
  Image1->Picture->LoadFromFile(OpenPictureDialog1->FileName);
 }
 //---------------------------------------------------------------------------

 void __fastcall TActiveFormX::CheckBox1Click(TObject *Sender)
 {
 Image1->Stretch=CheckBox1->Checked;
 }

Теперь можно скомпилировать приложение, зарегистрировать созданный ActiveX и протестировать его указанным выше способом.

Тестирование активной формы в Visual Basic

Рис.9. Тестирование активной формы в Visual Basic

Можно также протестировать созданный ActiveX c помощью отображения его в Internet Explorer. Для этой цели можно выбрать пункт меню Project/Web Deployment Options и на странице Project в полях Target dir, Target URL, HTML dir этого диалога указать имя какого-нибудь локального каталога.

Затем можно выбрать опцию Project/Web Deploy и по окончании работы Web Deployment Wizard открыть в Internet Explorer автоматически сгенерированную С++Builder html-страницу c именем, совпадающим с именем созданного проекта:

Тестирование активной формы в Internet Explorer

Рис.10. Тестирование активной формы в Internet Explorer

Отметим, что для успешного отображения ActiveX в броузере требуется Microsoft Internet Explorer версии 3.0 и выше, при этом настройки уровня безопасности должны позволять запуск активного содержимого, расположенного в Intranet-зоне. Если в качестве броузера используется Netscape Navigator, он должен быть оснащен модулем расширения (plug-in), позволяющим интерпретировать тег <OBJECT> языка HTML как элемент управления ActiveX (естественно, такая возможность существует только для версий Navigator, выполняющихся под управлением 32-разрядных версий Windows). Отметим также, что сгенерированную автоматически страницу можно в дальнейшем отредактировать с помощью любого html-редактора (или даже любого текстового редактора).

При поставке ActiveX через Web процедура аналогична описанной, но вместо локальных каталогов в строке URL следует указать Internet-адрес Web-сервера:

Настройка опций поставки ActiveX через Internet

Рис.11. Настройка опций поставки ActiveX через Internet

Помимо этого, следует обратить внимание на дополнительные "пакеты" или другие файлы, которые следует включить в поставку, если опции проекта таковы, что требуют использования каких-либо дополнительных библиотек.. Разделение ActiveX на несколько файлов и выделение отдельных пакетов может быть использовано для того, чтобы уменьшить в целом время загрузки ActiveX через Internet, например, в случае предстоящей необходимости обновления версии ActiveX или при поставке нескольких разных ActiveX - в этом случае часть "пакетов" может быть установлена один раз, а далее производится поставка лишь небольшой содержательной части элемента ActiveX. Впрочем, не возбраняется создавать ActiveX и в виде одного файла. Отметим также, что при выборе опции Include CAB File compression можно собрать используемые файлы в один файл с расширением *.cab, фактически представляющий собой архив, что также уменьшает примерно в два раза время загрузки файлов через Internet.

Следует отметить, что в активных формах можно использовать практически все компоненты C++Builder, кроме TMainMenu. Возможна также динамическая генерация дополнительных форм на этапе выполнения, при этом дополнительные формы уже не будут содержаться в контейнере, а будут представлять собой обычные формы Windows (и, естественно, могут содержать в том числе и TMainMenu).

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


Подборка статей по С++ Builder : Создание отчетов

:: документация
:: функции
:: форум
:: ссылки
ПОПУЛЯРНЫЕ СТАТЬИ::
1 . Visual C++ для начинающих
2 . Основы программирования при помощи MFC
3 . Язык программирования "C"
4 . C++. Бархатный путь
5 . Подборка статей по С++ Builder
все статьи...
ПОИСК ПО САЙТУ::

в документации
в функциях

ДОКУМЕНТАЦИЯ::
документация / Подборка статей по С++ Builder/ ..
[http://cpp.samara.ws/articles/builder/report.shtml]
Автор: Наталья Елманова Оригинал: Центр информационных технологий
 Оценка читателей:
6.26 / 244

Создание отчетов

Наталия Елманова

  • Введение
  • Создание простого отчета
  • Создание отчетов "master-detail"
  • Отображение графических и мемо-полей в отчетах
  • Предварительный просмотр отчетов

Введение

Для генерации отчетов в C++ Builder используется страница QReport напалитре компонентов. Этот набор компонентов позволяет проектировать отчетына основе таблиц, запросов, списков, текстовых файлов, массивов, используядизайнер форм C++ Builder.

В комплект поставки C++ Builder входят три шаблона отчетов, содержащиесяна странице Forms репозитария объектов:

Шаблон | Описание
QuickReport Mailing Labels | Шаблон для создания почтовых этикеток
QuickReport List | Шаблон для создания простого табличного отчета
QuickReport Master/detail | Шаблон для создания отчета Master/Detail.

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

Создание простого отчета

Отчеты QuickReport основаны на наборе горизонтальных полос (bands).При построении отчета на форму помещаются несколько компонентов QRBand(наследник TPanel) различных типов.

Для создания простейшего отчета разместим на форме следующие компоненты(рис. 1):

  • TQRBand - компонент, представляющий собой часть отчета - контейнердля размещения данных (например, заголовок отчета, верхний или нижний колонтитулстраницы, верхний или нижний колонтитул группы и др.). Компоненты TQRBandпечатаются в зависимости от их типа в необходимых местах отчета, независимоот их взаимного расположения на форме. Наиболее часто используемое свойствоэтого компонента – BandType, тип "полосы" (колонтитул страницыили группы, "полоса" данных и др.). Возможные значения: rbTitle– заголовок отчета, rbPageHeader – верхний колонтитул страницы, rbColumnHeader– верхний колонтитул колонки в многоколоночном отчете, rbDetail – полосас табличными данными (повторяется столько раз, сколько строк имеется внаборе данных, авляющемся основой отчета), rbPageFooter – нижний колонтитулстраницы, rbOverlay – фон страницы, печатается в левом верхнем углу каждойстраницы, rbGroupHeader – заголовок группы, rbSubDetail – "полоса"табличных данных для Detail-таблицы, rbGroupFooter – нижний колонтитулгруппы, rbSummary – печатается в конце отчета). Свойству BandType созданногонами компонента присвоим значение rbTitle
  • TQuickReport (этот компонент отвечает за превращение формы в отчет).
  • TQRLabel, помещенный на QRBand1 (этот компонент предназначен для выводастатического текста, и его свойству Caption можно присвоить значение, равноетексту заголовка будущего отчета).

Рис. 1. Создание заголовка отчета

Если нажать правую клавишу мыши над компонентом QuickReport1 и выбратьиз контекстного меню опцию Preview Report, появится окно просмотра, в которомбудет отображена страница отчета с созданным заголовком.

Для модификации отчета следует изменить свойство BandType компонентаQRBand1 на rbDetail и добавить на форму компонент TTable. Далее нужно установитьего свойство DataBase равным имени псевдонима, например, BCDEMOS, свойствоTableName равным имени таблицы, например, CUSTOMER.DB, а затем свойствоActive равным true. После этого нужно добавить на форму компонент TDataSourceи установить его свойство DataSet равным имени добавленного ранее компонентаTable1, а затем установить свойство DataSource компонента QuickReport равнымимени созданного компонента DataSource1. После этого можно добавить компонентTQRDBText на QRBand1(этот компонент предназначен для вывода содержимогополей таблицы или запроса, служащего источником данных проектируемого отчета),установить свойство DataSource равным имени созданного ранее компонентаDataSource1 и выбрать нужное поле в качестве значения свойства DataField.Если есть необходимость, можно добавить другие компоненты TQRDBText и выбратьдругие поля таблицы для отображения в отчете (рис. 2).

Рис. 2. Модификация табличного отчета

Если теперь из контекстного меню компонента QuickReport выбрать опциюPreview Report, можно увидеть модифицированный табличный отчет.

Теперь попробуем создать отчет с заголовком отчета и колонтитулами.Для этого нужно использовать при создании отчета несколько различных компонентовTQRBand.

Для создания отчета с заголовком и колонтитулами следует cоздать новуюформу, разместить четыре компонента TQRBand на форме (они получат по умолчаниюимена QRBand2,...., QRBand5) и установить их свойства BandType равнымисоответственно rbTitle, rbColumnHeading, rbDetail и rbPageFooter. Далееследует добавить на форму компонент TTable и установить его свойство DatabaseNameравным BCDEMOS, свойство TableName равным Customer, а затем свойство Activeравным true. Затем нужно добавить на форму компонент TDataSource и установитьв его свойстве DataSet имя добавленного ранее компонента Table1 и установитьсвойство DataSource компонента QuickReport равным имени созданного компонентаDataSource1. Затем следует добавить несколько компонентов TQRLabel в качествезаголовка отчета и столбцов поверх соответствующих компонентов TQRBand,присвоив необходимые значения свойству Caption каждого из них.

Затем следует разместить три компонента QRDBText на компонент QRBandсо свойством BandType, равным DetailBand и установить их свойства DataSourceравными DataSource1, а свойства DataField равными Company, Phone и Fax.Наконец, для отображения номера страницы нужно поместить компонент TQRSysData(этот компонент предназначен для вывода сведений, не зависящих от содержимогоданных, таких как номер страницы, дата, время и др.) на компонент TQRBandсо свойством BandType, равным rbPageFooter и установить его свойство Dataравным qrcPageNumber, а свойство Text равным “Стр. “

После этого форма будет выглядеть, как на рис. 3.

Можно снова выбрать опцию Preview Report и просмотреть содержание новогоотчета.

Рис.3. Отчет с заголовком и колонтитулами

Создание отчетов "master-detail"

Преобразуем созданный отчет в отчет "master-detail". Для этогоследует добавить компонент TTable, установить его свойство DatabaseNameравным BCDEMOS, свойство TableName равным ORDERS.DB, а затем установитьсвойство Active равным true. После этого установим свойство MasterSourceравным DataSource1. Затем выберем свойство MasterFields, вызвав диалоговуюпанель для установки связи master/detail (рис. 4 ) и из списка доступныхиндексов выберем CustNo. Затем выделим имя поля CustNo в обоих спискахполей и нажмем кнопку Add, а кнопку OK.

Рис. 4. Установка связи master/detail

Добавим на форму компонент TDataSource, установив его свойство DataSetравным Table2 . Затем добавим к форме новый компонент TQRBand (c именемQRBand6). После этого добавим компонент TQRDetailLink, предназначенныйдля установки связей между источниками данных в отчетах, и установим егосвойство DataSource равным DataSource2. Затем установим его свойство Masterравным QuickReport, а свойство DetailBand равным QRBand6. Свойство BandTypeкомпонента QRBand6 автоматически примет значение rbSubDetail.

Наконец, поместим два компонента TQRDBText на QRBand6, установим ихсвойства DataSource равными DataSource2, а свойства DataField равными OrderNoи AmountPaid. Слева от них поместим два компонента TQRLabel с названиямиэтих полей (рис. 5).

Рис. 5. Форма отчета "master-detail".

Выберем опцию Preview Report из контекстного меню компонента QuickReportдля предварительного просмотра отчета (рис.6).

Рис. 6. Отчет "master-detail".

Отметим, что если компонент QuickReport не связан с компонентом DataSource,то при печати отчета выводится только одна запись из набора данных, чтолегко позволяет печатать текущую запись.

Отображение графических и мемо-полейв отчетах

QuickReport позволяет создавать отчеты с использованием любых типовданных. Если вместо определения DataSource создать обработчик события OnNeedData,можно с помощью QuickReport напечатать любые данные, меняя свойства компонентовTQRLabel, что во многих случаях используется для печати произвольной информации(иногда не имеющей отношения к базам данных).

QuickReport не имеет собственного компонента для отображения графическихполей. Вместо этого можно использовать стандартные компоненты Timage илиTDBImage (рис. 7).

Рис. 7. Использование TDBImage для отображения графических полей

Следует отметить, что графические поля баз данных может печатать далеконе всякий профессиональный генератор отчетов. Например, ReportSmith, входившийв комплект поставки ряда продуктов Borland, может печатать графическиеизображения, не имеющие непосредственного отношения к данным (например,взятые из файлов формата *.bmp), но отнюдь не графические поля таблиц.

Для отображения мемо-полей можно использовать компонент TQRDBText. Еслисодержимое мемо-поля, отображаемого с помощью этого компонента, не умещаетсяв одну строку, высота этого компонента (и высота содержащего его компонентаTQRBand) в режиме предварительного просмотра и при печати отчета увеличиваетсятаким образом, чтобы внутри компонента TQRDBText уместилось все содержимоеmemo-поля. Чтобы избежать наложения получившегося текста на другие элементыотчета при его печати, можно просто размещать компоненты TQRDBText, отображающиеmemo-поля, в нижней части TQRBand (рис 7).

Рис. 7. В левой нижней части данного отчета компонент TQRDBTextотображает memo-поле

Рис. 8. А вот так выглядят memo-поля в отчете

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

Предварительный просмотр отчетов

В некоторых случаях требуется предварительный просмотр отчетов на этапевыполнения. Для этой цели используется метод Preview() компонента TQuickReport.При его выполнении на экране появится стандартная форма просмотра, изображеннаяна рис. 8.

Если внешний вид стандартной формы просмотра по какой-либо причине васне устраивает, можно создать свою форму предварительного просмотра с помощьюкомпонента QRPreview. Этот компонент обладает свойствами PageNumber и Zoom,которые можно использовать для просмотра произвольной страницы отчета впроизвольном масштабе.

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

 void __fastcall TForm1::ShowPreview()
     {
      Form2->ShowModal();
     }
     void __fastcall TForm1::Button1Click(TObject *Sender)
     {
     QRPrinter->OnPreview=ShowPreview;
     Form4->QuickReport1->Preview();
     Form2->ShowModal();
     }

Кроме того, нужно внести прототип функции ShowPreview() в соответствующийh-файл:

     __published:
  // IDE-managed Components
     TButton *Button1;
     void __fastcall Button1Click(TObject *Sender);
     void __fastcall ShowPreview(void);

Приведенный пример кода показывает, как связать созданную форму с компонентомQuickReport. Эта связь достигается написанием обработчика события QRPrinter->OnPreview.Это событие не имеет прямого отношения к компоненту QuickReport, иначенужно было бы связывать все созданные отчеты с окном просмотра. Использованиесобытия объекта QRPrinter обычно означает написание общего для всех отчетовобработчика события, после чего окно просмотра можно использовать для всехимеющихся в приложении отчетов.

Более подробно о компонентах, используемых для создания отчетов, можнопрочесть в книге "Введение в Borland C++ Builder" Н.Елмановойи С.Кошеля, вышедшей в июле этого года в издательстве " Диалог-МИФИ".