Главная » C++, C++ Builder » Последний пример: работа с базами данных в CBuilder

0

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

На рис. 12.4 показана новая форма, которую нам надо добавить в DLL. При этом для работы с новой DLL нам не придется ничего менять в приложении Visual C++, созданном в предыдущем примере, поскольку интерфейс останется неизменным.

Рис. 12.4. Форма выбора записей

Если вы достаточно давно работаете в Visual C++, то скорее всего предположите, что для работы с базами данных через объекты VCL сначала надо произвести некую их инициализацию. К счастью, вы будете не правы. VCL использует статические библиотеки, которым никакая инициализация не нужна, а компоненты VCL сами знают, когда надо открыть, закрыть или инициализировать машину баз данных. Короче говоря, все это происходит, так сказать, за кулисами и не требует вашего вмешательства.

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

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

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

void __fastcall TForm1::Button2Click(TObject *Sender)

{

AnsiString strSearch = ""; AnsiString s = Edit1->Text;

// Проверяем каждое поле ввода

if ( Edit1->Text.Length() )

{

strSearch += "LASTNAME = ‘"; strSearch += Edit1->Text; strSearch += "’";

}

if ( Edit2->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND ";

strSearch += "FIRSTNAME = ‘"; strSearch += Edit2->Text; strSearch += "’";

}

if ( Edit3->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND ";

strSearch += "ADDRESS_1 = ‘";

strSearch += Edit3->Text; strSearch += "’";

}

if ( Edit4->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND ";

strSearch += "ADDRESS_2 = ‘";

strSearch += Edit4->Text; strSearch += "’";

}

if ( Edit5->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "CITY = ‘"; strSearch += Edit5->Text; strSearch += "’";

}

if ( Edit6->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "STATE = ‘"; strSearch += Edit6->Text; strSearch += "’";

}

if ( Edit7->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "ZIP_CODE = ‘"; strSearch += Edit7->Text; strSearch += "’";

}

TForm2 *pForm = new TForm2(NULL); pForm->SetSearchString(strSearch); pForm->ShowModal();

// Копируем значения из базы данных

Edit1->Text = pForm->Table1->FieldValues["LASTNAME"]; Edit2->Text = pForm->Table1->FieldValues["FIRSTNAME"]; Edit3->Text = pForm->Table1->FieldValues["ADDRESS_1"]; Edit4->Text = pForm->Table1->FieldValues["ADDRESS_2"]; Edit5->Text = pForm->Table1->FieldValues["CITY"];

Edit6->Text = pForm->Table1->FieldValues["STATE"]; Edit7->Text  = pForm->Table1->FieldValues["ZIP_CODE"]; delete pForm;

}

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

FIELD=’Value’ AND FIELD=’Value’

где FIELD — одно из полей нашей базы данных, а ‘Value’ — строка, введенная пользователем, представляющая собой значение для данного поля, по которому пользователь хочет осуществить поиск. Короче говоря, мы создаем форму QBE (Query By Example, запрос по примеру), которая добавит эти возможности в приложение Visual C++. В принципе, можно создавать запросы QBE и в Visual C++, но это малоприятное занятие. Оно представляет собой либо создание запросов SQL времени исполнения с последующим получением из них требуемых полей, либо написание совершенно ужасных параметрических запросов. А в CBuilder мы создали все,  что  нам  было нужно, с помощью нескольких простейших строк кода,  представляющих  собой  сопоставление имен полей и введенных значений.

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

Создание формы выбора записей

Форма выбора записей содержит два элемента — окно списка  (list  box)  и  кнопку OK.  Список будет содержать записи, из которых пользователь будет осуществлять выбор. По нажатии кнопки OK форма будет закрываться, сообщая вызывающей форме, что запись из базы данных была благополучно выбрана.

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

void TForm2::SetSearchString(AnsiString strSearch)

{

Table1->Filter = strSearch; Table1->Filtered = true;

// Грузим элементы в список

Table1->First();

while ( !Table1->Eof )

{

AnsiString s = Table1->FieldValues["LASTNAME"]; s += ",";

s += Table1->FieldValues["FIRSTNAME"];

ListBox1->Items->Add( s ); Table1->Next();

}

}

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

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

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

void __fastcall TForm2::Button1Click(TObject *Sender)

{

if ( ListBox1->ItemIndex == -1 ) return;

// Перемещаем указатель до нужной записи

Table1->First();

for ( int i=0; i<ListBox1->ItemIndex; ++i )

Table1->Next(); Close();

}

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

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

Возвратившись в вызывающую форму, мы читаем данные из таблицы и заносим их в соответствующие поля редактирования. Это позволит  пользователю  отредактировать  их  перед тем, как форма будет закрыта и они будут переданы в вызывающую программу Visual C++. Последнее, что должен обеспечить код формы Form1, — это удалить объект Form2, поскольку именно в этом коде выделялась под него память. Если этого не сделать, произойдет утечка оперативной памяти (утечка ресурсов),  поскольку этот объект уже никогда не будет удален из памяти после завершения работы программы. Утечка ресурсов может привести к сбою операционной системы, так что давайте сами удалять за собой все, что создавали.

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

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

И последнее, о чем хочется сказать по поводу нашего приложения, — если во время исполнения произойдет ошибка, система обработки исключительных ситуаций CBuilder автоматически перехватит ее, даже если вами не определен специальный обработчик  для  этой  ситуации.  Это может быть весьма важно для программиста, разрабатывающего приложения. Вы попрежнему можете использовать в своем коде блоки try … catch и даже смело рассчитывать на то, что исключительные ситуации, не обработанные вами, обработает лежащая  в  основе  вашего  кода VCL. Если исключительная ситуация произошла в DLL, то,  как  правило,  можно  считать,  что метод всетаки будет выполнен до конца. Это относится,  естественно, только к тем исключительным ситуациям, которые не являются фатальными; эти тут же прекратят выполнение функции. Постарайтесь обработать в своем коде как можно больше исключительных ситуаций, чтобы  возможно  меньшее  их  количество  проходило  мимо  вашего  внимания  и  сбивало  работу вашего приложения.

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

Источник: Теллес М. – Borland C++ Builder. Библиотека программиста – 1998

По теме:

  • Комментарии