Создание приложения ODBC

Сдавался/использовался2005г., Минск, Белорусский государственный университет информатики и радиоэлектроники
ПримечаниеОткрытый интерфейс доступа к базам данных (Open Database Connectivity, ODBC) - это программный интерфейс, который позволяет приложению обращаться к различным СУБД, используя структурированный язык запросов SQL
Загрузить архив:
Файл: ref-25787.zip (492kb [zip], Скачиваний: 103) скачать

Лабораторная работа №6.

                  Тема: Создание приложения ODBC.

1. Проект MFC AppWizard.

Прежде всего надо создать (или выбрать) саму базу данных. Первым шагом на этом пути является выбор в качестве проекта MFC AppWizard рис. 1.

Рис.1

Рис.2Первое окно мастера MFC AppWizard

Начнем с создания самого простого приложения для работы с базой данных. Поэтому выберите переключатель Single document и нажмите кнопку Next. В результате мы перейдем к самому интересному для нас окну мастера MFC AppWizard — Step 2 of 6, показанному на рис.3 — именно здесь мы будем определять параметры базы данных.

Первое, что сразу бросается в глаза — это группа переключателей What database support would you like to include? (Какую поддержку базы данных вы хотите включить в проект) и кнопка Data Source... (Источник данных). Сначала рассмотрим поддержку. Здесь возможны четыре варианта:

· None (Нет) — не обеспечивается поддержка баз данных (по умолчанию). Не используйте этот переключатель, если есть хоть малейший шанс, что приложению потребуется работа с базой данных — добавление такой поддержки позднее потребует значительных усилий.

· Header files only (Только заголовочные файлы) — AppWizard создает и включает файл заголовка , обеспечивая основную поддержку базы данных. Вы можете создавать наборы записей (recordset) и затем проверять и модифицировать их. При выборе этого переключателя, если в дальнейшем потребуется обеспечить работу с базами данных, основную работу можно будет "поручить" мастеру ClassWizard.

· Database view without file support (Просмотр базы данных без файловой поддержки) — предписывает AppWizard создать основанное на формах приложение, в котором просмотр и модификация записей производится через набор записей. Приложение не поддерживает сериализацию документов, так как большинство приложений баз данных работает на основе записей быстрее, чем на

              основе файлов.

· Database view with file support (Просмотр базы данных с файловой поддержкой) — предписывает создать, основанное на формах приложение, в котором просмотр и модификация записей также производится через набор записей. В данном случае приложение поддерживает сериализацию, что может использоваться, например, для обновления файла настроек пользователя.

Рис. 3

Выберитепереключатель Database view with file support.

Теперь необходимо указать источник данных, с которым мы будем работать. Для этого нажмите кнопку Data Source..., после чего откроется диалоговое окно Database Options (Параметры базы данных), показанное на рис.4

Рис.4 Диалоговое окно Database Options

Теперь требуется выбрать один из переключателей Recordset type (Тип набора записей). Набор записей (Recordset) — это, говоря несколько упрощенно, выборка из таблицы (или запроса) базы данных.

Существуют три вида набора записей:

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

· Dynaset (Динамический набор) — динамическая копия данных, которые содержатся в одной или нескольких таблицах базы данных. В отличие от моментального снимка, любые внесенные в набор изменения сразу же становятся доступны всем остальным пользователям базы данных. При выборе этого переключателя Visual C++ создает отдельный указатель для каждой запрашиваемой записи. Кроме того, с сервера загружаются только те записи, которые действительно нужны для заполнения формы. Преимущества такого подхода очевидны — записи на экране появляются практически немедленно. Помимо этого, вы также будете в курсе тех изменений, которые другиепользователи осуществляют в базе данных. И, наконец, другие пользователи будут знать об изменениях, которые делаете вы, так как если записи изменились, то они сразу же загружаются на сервер. Совершенно очевидно, что этот подход требует практически постоянного доступа к серверу, что будет снижать общую пропускную способность сети, а также производительность приложения. Этот переключатель следует выбирать для создания приложений, где пользователь больше всего времени будет тратить на корректировку данных. Это также наилучший выбор для больших баз данных, потому что будет загружаться только та информация, которая действительно нужна пользователю.

· Table (Таблица). Предыдущие две опции работают на уровне записей, при использовании же табличного подхода (доступен только для модели DАО и нами использоваться не будет) результат выполнения запроса помещается во временную таблицу. Это не только сокращает объем информации, загружаемой с сервера, но и означает, что вы, как программист, сразу можете непосредственно манипулировать полями и записями временной таблицы. Каковы затраты при таком режиме работы? Если невнимательно подойти к формированию запроса, то процесс загрузки с сервера может оказаться более длительным, чем при использовании набора записей. Кроме того, можно не увидеть никаких изменений, сделанных другими пользователями, а они не смогут увидеть изменения, сделанные вами.

Для нашего примера установите переключатель Snapshot и нажмите кнопку ОК. После регистрации соединения с базой данных вы увидите блок диалога Select Database Tables. Выберите таблицы базы данных, где необходимо выделить те таблицы или запросы (queries), которые требуется включить в набор записей. В нашем простейшем случае это пока одна таблица, например — jobs.

После нажатия на кнопку ОК вы вернетесь к диалоговому окну MFC AppWizard — Step 2 of 6, где, правда, изменилась надпись под кнопкой Data Source..., которая теперь сообщает о выбранных таблицах. На данном этапе конфигурирование приложения для использования базы данных pubs завершается. Три следующие окна мастера для нас неинтересны, поэтому просто четыре раза нажмите кнопку Next. В результате на экране появится диалоговое окно MFC AppWizard — Step 6 of 6, аналогичное показанному на рис. 5. В отображенном списке выделите элемент CDBView, чтобы обратить внимание на его базовый класс — CRecordView, с которым мы подробно познакомимся чуть ниже. Нажмите кнопку Finish, и Visual C++ отобразит диалоговое окно New Project Information.

Рис. 5 Диалоговоеокно MFC AppWizard — Step 6 of 6

Рис. 6 Приложение, созданное мастером AppWizard, не предоставляет      пользователюникакой информации о содержимом базы данных

2. Извлечение информации из базы данных.

Подготовка формы для отображения данных.

В нашемпримере мы должны научиться манипулировать данными одной единственной таблицы — jobs БД pubs. Используемые  нами столбцы этой таблицы перечислены в таблице 1.

Таблица 1. Используемые столбцы таблицы jobs.

Нужно создать форму для отображения информации о должностях (см. Рис. 7).

Рис. 7

Примечание:

Если вы хотите,использовать русские названия для полей (столбцов) таблицы, то перед тем, как набирать эти названия, необходимо изменить основной язык для окна диалога.  Щелкните правой кнопкой мыши на идентификаторе окна диалога IDD_PRIM_FORM (на рис. 1 он обведен рамкой) и в появившемся контекстном меню выберите команду Properties (Свойства), в результате чего на экране появится диалоговое окно Dialog Properties (Свойства диалога) (рис. 8). В комбинированном списке Language (Язык) выберите Russian. – в двух местах Dialog

Рис. 8

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

Придадим окну формы более привлекательный вид. Необходимо немного модифицировать функцию: CMainFrame::PreCreateWindow

 

ClassView→CMainFraim→PreCreateWindow(CREATESTRUCT& cs).

Вот как она должна выглядеть:

BOOL CMainFrame::PreCreateWindowfCREATESTRUCTS cs) 

{

// Изменяем размеры главного окна

cs.cx = 376;

cs.cy = 272;

return CFrameWnd::PreCreateWindow(cs);

}

Примечание:

Ваши значения могут отличаться от приведенных здесь значений (поскольку они зависят от размеров окна диалога). (В данном случае они равны 242x106, и в качестве шрифта установлен MS Sans Serif, 8.). Подберите значения, улучшающие вид вашей формы. В результате этих минимальных добавлений внешний вид окна формыулучшился.

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

 

3. Отображение и обновление содержимого базы данных.

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

1.

2.

3.

4.

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

Убедитесь в этом, просмотрев соответствующий код в вашем проекте.

********ClassCPrimSet *******

CString CPrimSet::GetDefaultConnect()

{

// Параметрыисточникаданных

// CONNECT1- название источника данных

return _T("ODBC;DSN=CONNECT1");

}

CString CPrimSet::GetDefaultSQL()

{// Параметрытаблицы

return _T("[dbo].[jobs]");

}

class CPrimSet : public CRecordset

{

public:

CPrimSet(CDatabase* pDatabase = NULL);

DECLARE_DYNAMIC(CPrimSet)

// // Переменные для отображения данных строки таблицы

//{{AFX_FIELD(CPrimSet, CRecordset)

int        m_job_id;

CString           m_job_desc;

BYTEm_min_lvl;

BYTEm_max_lvl;

//}}AFX_FIELD

...

           };

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

Раскройте спроектированное ранее диалоговое окно IDD_DB_FORM и при нажатой клавише дважды щелкните на элементе управления. В результате на экране появится диалоговое окно Add Member Variable (Добавить компонентную переменную), показанное на рис. 9. В комбинированном списке Member variable name (Имя компонентной переменной) этого окна необходимо выбрать соответствующую переменную — в данном случае m_pSet->m_job_id.

Рис. 9. Диалоговоеокно Add Member Variable

Примечание:

Здесь m_pSet — указатель на объект класса, производного от CRecordset, содержащий текущую запись. Переменная m_job_id в этом объекте содержит текущее значение поля job_id из таблицы jobs. Если вы посмотрите содержимое комбинированного списка Member variable name, то увидите указатель для всех полей запроса, который для нас сконструировала библиотека MFC, и поэтому можно повторить рассмотренную процедуру сопоставления элементов управления формы с переменными для каждой из них.

После того как все элементы управления сопоставлены переменным, обязательно убедитесь, что все сделано корректно. Используйте для этого команду View/Class Wizard... (или одноименную кнопку на панели инструментов), чтобы отобразить диалоговое окно MFC ClassWizard, в котором выберите вкладку Member Variables (Компонентные переменные). В раскрывающемся списке Class name необходимо выбрать класс CPrimView. Список компонентных переменных должен выглядеть так, как показано на рис. 10.

Рис. 10. Компонентные переменные класса CPrimView

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

Рис. 11

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

3. Добавление и удаление записей в таблице.

Добавление записей в таблицу.

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

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

1. Раскройте вкладку Resource View (ресурсы) окна проектов. Щелкните по значку +, расположенному рядом с папкой Toolbar, а затем по IDR_MAINFRAME. Появится панель инструментов.

2. Выделите пустую кнопку, расположенную справа, и нарисуйте какой-либо символ New Record (Новая запись), например, такой, как показан на рис. 12. Переместите кнопку на место, расположенное сразу после четырех кнопок перемещения по базе данных.

Рис. 12. Новая кнопка на панели инструментов для добавления новой записи

3. Дважды щелкните по вновь созданной кнопке на разрабатываемой панели инструментов. Раскроется диалоговое окно Toolbar Button Properties , (Свойства кнопки панели инструментов). Введите в качестве идентификатора в список ID (Идентификатор) ID_RECORD_APPEND.

4. В поле Prompt (Подсказка) наберите текст — Append a new record to the table. New Record (Добавление новой записи в таблицу Новая запись), как показано на рис. 13.

5. Нажмите кнопку закрытия в правом верхнем углу диалогового окна Toolbar Button Properties, и Visual C++ автоматически присвоит новой кнопке идентификатор ID_RECORD_APPEND.

Рис. 13.

Теперь необходимо сопоставить созданной кнопке некоторые действия. Откройте окно Class Wizard, выполнив команду View/ClassWizard, выберите вкладку Message Maps (карта сообщений) и в списке Object IDs выделите элемент ID_RECORD_APPEND, а в

раскрывающемся списке Class name выберите CPrimView — класс, который служит для просмотра базы данных. Именно для него потребуется добавить некоторый код. Далее в списке Message выделите элемент COMMAND и нажмите кнопку Add Function, в результате чего на экране появится диалоговое окно Add Member Function, в котором, уже предложено некоторое имя (рис. 14), базирующееся на идентификаторе кнопки (присваивайте всем идентификаторам содержательные имена).

Рис. 14. Если идентификатору присвоено содержательное имя, то можно просто нажать кнопку ОК

Нажмите кнопку ОК, и диалоговое окно MFC ClassWizard примет вид, показанный на рис. 15. В этом окне нажмите кнопку Edit Code (Редактировать код), чтобы сразу перейти к шаблону созданной функции, куда потребуется добавить код, приведенный ниже. Просмотрите примечание, расположенное после текста кода.

void CPrimView::OnRecordAppend()

{

// TODO: Add your command handler code here

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

// в режиме только для чтения. Если это так,

//то выводим предупреждение и выходим из функции

if(m_pSet->CanAppend() == 0)

MessageBox("Можно только просматривать записи ",

" Ошибка добавления записи ",

MB_OK | MB_ICONWARNING);

else

{

// Перемещаемся на первую запись

m_pSet->MoveFirst();

// Создаем пустую запись,

// в которую пользователь будет вводить значения

m_pSet->AddNew();

// Устанавливаем флаг перехода в режим добавления записи

m_bAdd = TRUE;

//  Обновляем поля формы

UpdateData(FALSE);

}

}

Рис. 15. Диалоговое окно MFC ClassWizard после проведенных действий

Примечание:

Для успешной работы приложения необходимо ввести новую переменную для фиксации режима добавления записиm_bAdd:

  1. В окне ClassView щелкните правой кнопкой на имени класса CPrimView и выберите в появившемся контекстном меню команду Add→AddVariable. Появится диалоговое окно Add Member Variable Wizard.
  2. В раскрывающемся списке Access выделите метод доступа protected, в текстовом поле раскрывающегося списка Variable type сохраните тип переменной bool, в текстовом поле Variable name введите идентификатор переменной m_bAdd и нажмите кнопку Finish.
  3. Проинициализируем добавленную переменную значением FALSE для нормальной работы в режиме просмотра записей.

CPrimView→CPrimView()

Вставьте код:

CDBView::CDBView() : CRecordView(CDBView::IDD)

{

m_bAdd = FALSE;

}

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

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

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

BOOLCPrimView::OnMove(UINTnIDMoveCommand)

{

// TODO: Add your specialized code here and/or call the base class

// Проверяем, в каком режиме — добавления или перемещения -

// мы находимся

if(m_bAdd){

// «Выходим из режима добавления»

m_bAdd=false;

UpdateData();

if(m_pSet->CanUpdate() )

m_pSet->Update() ;

m_pSet->Requery();

UpdateData(FALSE);

returnTRUE;

}

else

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

return CRecordView::OnMove(nIDMoveCommand);

}

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

DDX_FieldText(pDX, IDC_PROD, m_pSet->m_ProductName, m_pSet);

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

Вернемся к табл.1, где представлены поля таблицы jobs. Видим, что у столбца job_id установлен тип данных IDENTITY. Это означает, что система управления базами данных сама позаботится о присвоении значения этому столбцу. Чтобы решить эту проблему достаточно поменять переменную, с которой связан элемент управления IDC_EDIT4 (например, на m_job_Nom типа long), и тем самым переложить задачу на СУБД.

Кроме этого, в код требуется также добавить одну строку, чтобы можно было видеть информацию об идентификаторе job_id.

void CPrimView::DoDataExchange(CDataExchange* pDX)

{// Для отображения значений поля job_id таблицы

// необходимо выполнить присваивание

m_job_Nom= m_pSet->m_job_id ;

// Осуществляем обмен данными с элементами управления в форме

CRecordView::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CPrim_1View)

DDX_FieldText(pDX, IDC_EDIT3, m_pSet->m_min_lvl, m_pSet);

DDX_FieldText(pDX, IDC_EDIT2, m_pSet->m_max_lvl, m_pSet);

DDX_FieldText(pDX, IDC_EDIT1, m_pSet->m_job_desc, m_pSet);

DDX_Text(pDX, IDC_EDIT4, m_job_Nom);

//}}AFX_DATA_MAP

}

Уберем значение идентификатора с экрана на время вставки новой записи. Для этого немного модифицируем код в двух функциях OnRecordAppend и On Move, как

показанониже.

void CPrimView::OnRecordAppend()

{

// TODO: Add your command handler code here

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

// в режиме только для чтения. Если это так, 

//то выводим предупреждение и выходим из функции 

if(m_pSet->CanAppend() == 0)

MessageBox("Можно только просматривать записи",

"Ошибка добавления записи",

MB_OK | MB_ICONWARNING);

else

{

// Скрываем элементы управления на время ввода новой записи

GetDlgItem(IDC_EDIT4)->ShowWindow(SW_HIDE);

// Устанавливаем фокус на поле ввода названия

GetDlgItem(IDC_EDIT1)->SetFocus();

// Перемещаемся на первую запись

m_pSet->MoveFirst();

// Создаем пустую запись,

//в которую пользователь будет вводить значения

m_pSet->AddNew();

// Устанавливаем флаг перехода в режим добавления записи

m_bAdd = TRUE; 

// Обновляем поля формы

UpdateData(FALSE);}

}

BOOL CPrimView::OnMove(UINT nIDMoveCommand)

{

// TODO: Add your specialized code here and/or call the base class

// Проверяем, в каком режиме — добавления или перемещения -

// мы находимся

if(m_bAdd){

// «Выходим из режима добавления»

m_bAdd=false;

// После завершения ввода снова отображаем элементы управления

GetDlgItem(IDC_EDIT4)->ShowWindow(SW_SHOW);

UpdateData();

if(m_pSet->CanUpdate() )

m_pSet->Update() ;

m_pSet->Requery();

UpdateData(FALSE);

return TRUE;

}

else

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

return CRecordView::OnMove(nIDMoveCommand);

}

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

После нажатия кнопки New Record и ввода информации о новой должности основное окно будет иметь вид представленный на рис. 16.

Рис. 16. Так теперь выглядит основное окно приложения в режиме ввода новой записи в базу данных

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

4. Удаление записей из таблицы.

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

void CPrimView::OnRecordDelete()

{

// TODO: Add your command handler code here

// Последняявозможностьсохранитьзапись

if(MessageBox("Вы действительно хотите удалить эту запись?",

"Удаление записи",

MB_OKCANCEL|MB_ICONQUESTION|MB_DEFBUTTON2) == IDOK)

// Удаляем запись

m_pSet->Delete();

//Перечитываем информацию из БД.

m_pSet->Requery();

// Переходим к следующей записи

m_pSet->MoveNext ();

// Если мы переместилисьза последнюю запись,

//то устанавливаем указатель на последнюю запись

if(m_pSet->IsEOF())

m_pSet->MoveLast ();

// Если таблица стала пустой, то очищаем поля.

if(m_pSet->IsBOF ())

m_pSet->SetFieldNull(NULL);

 

// Обновляем поля формы

UpdateData(FALSE);

}

Правила хорошего тона требуют, чтобы перед удалением пользователю был задан вопрос, хорошо ли он подумал и не случайно ли нажал кнопку удаления. Если запись действительно надо удалить, то смело нажимайте кнопку ОК, после чего будет вызвана функция Delete класса CRecordset, которая проведет корректное удаление записи из базы данных. Чтобы обновить набор, с которым мы работаем, необходимо вызвать функцию Requery, иначе мы будем продолжать работать с записью, которой в базе данных уже нет. Этот шаг был бы не нужен, если бы мы работали с динамическим набором— как вы помните, в этом случае результирующий набор обновляется одновременно с обновлением базы данных. Новыми при удалении являются следующие два действия — проверка на выход за начало и конец записей. Действительно, если мы переместились (вызов функции MoveNext) за последнюю запись (проверка if (m_pSet->isEOF ())), то непонятно, на что в этом случае будет указывать указатель. Поэтому его нужно установить на последнюю запись результирующего набора (вызов m_pSet->MoveLast ()). Аналогичные действия необходимо предпринять для проверки на выход за "левую границу", т. е. за первую запись (вызов if (m_pSet->isBOF())). Эта ситуация возникает тогда, когда из таблицы удаляется последняя строка и требуется очистить поля единственной пустой строки (ВЫЗОВ m_pSet->SetFieldNull (NULL) ).

Как и в случае обновления и добавления новой записи, здесь также всю основную работу делает библиотека MFC, а точнее, ее класс CRecordset.

5. Сортировка записей.

Для демонстрации этой возможности создадим специальные элементы меню, внешний вид которых представлен на рис 17. Используем для организации сортировки два критерия: по идентификатору должности, по названию. Для создания элементов меню откройте окно ResourceView→Menuи дважды щелкните левой кнопкой мыши на идентификаторе ресурса IDR_MAINFRAIM. В строке заголовков меню введите текст &Sort (Сортировка) и добавьте подпункты - &ID_JOB и &JOB_NAME (с именами S_JOB_ID и S_JOB_DESC, а также соответствующими подсказками типа Sort by job_id JOB_ID sort).

Рис. 17.Окно свойств элементов меню

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

//Сортировка

void CPrimView::OnSortJobId()

{

OnSort("job_id");

}

void CPrimView::OnSortJobDesc()

{

OnSort("job_desc");

}

Добавьтев классCPrimView функцию OnSort, непосредственно осуществляющую сортировку по записям.

//Сортировка по записи

Рис. 18

void CPrimView::OnSort(CString szText)

{

m_pSet->Close ();

m_pSet->m_strSort=szText;

m_pSet->Open();

UpdateData(FALSE);

}

 

6. Поиск информации в базе данных.

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

Рис. 19. Свойства блока диалога

Наберите IDD_DLG_FIND в поле ID. Далее следует дать этому диалоговому окну более содержательный заголовок. Наберите "Поиск информации в базе данных" в поле ввода Caption (Заголовок), а затем нажмите кнопку Close, расположенную в правом верхнем углу окна, чтобы завершить, ввод изменений.

Теперь нам необходимо добавить один элемент управления Static Text и связанный с ним элемент управления Edit Box, чтобы завершить создание блока диалога. Последняя часть работы для этого диалогового окна — установить свойства элемента управления Edit Box. Щелкните правой кнопкой мыши на этомэлементе управления и выберите Properties из контекстного меню. Измените свойство ID в диалоговом окне EditProperties (Свойства текстового поля) на IDC_RECORD_FIND. Нажмите кнопку Close. Теперь при нажатой клавише дважды щелкните на элементе управления Edit Box. При этом вы увидите блок диалога Adding a Class (Добавить класс). Причина появления этого блока диалога заключается в том, что каждый диалог в вашей программе должен быть с чем-то связан. Так же, как окно About, имеющееся в любом приложении, каждое диалоговое окно должно иметь свой собственный класс. Нажмите кнопку ОК, чтобы создать новый класс для этого диалогового окна. Visual C++ отобразит диалоговое окно New Class, в котором нужно набрать имя класса в поле Name. Вы можете использовать любое имя класса, какое хотите. Например, выберем CFindJob. Нажмите кнопку ОК, чтобы завершить создание нового класса, после чего еще раз нажмите ОК, чтобы закрыть диалоговое окно MFC ClassWizard. Затем при нажатой клавише еще раз дважды щелкните на элементе управления Edit Box, появится диалоговое окно Add Member Variable, которое вы и ожидали увидеть. Наберите m_Job вполе Member variable name. Убедитесь, чтополе Category содержит Value, аполе Variable Type — CString. Нажмите кнопку ОК, чтобы добавить компонентную переменную.

Осталось создать оболочку и саму функцию OnRecordFind() (просмотритепримечания ниже по тексту):

voidCPrimView::OnRecordFind()

{CDBVariant varBookmark;

// Создаем экземпляр диалогового окна

CFindJobdlgFind;

// Нашли соответствующую запись?

BOOLbFound = FALSE;

// Расположение закладки CDBVariant.varBookmark; 

// Отображаем диалоговое окно и определяем,

// какую кнопку пользователю нажимать для выхода

if (dlgFind.DoModal () ==IDOK){

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

if(m_pSet->CanBookmark())

m_pSet->GetBookmark(varBookmark);

// Идем к началу

m_pSet->MoveFirst(); }

while (!m_pSet->IsEOF() ^ bFound)

// Проверяем, равны лизначения

if(m_pSet->m_job_desc.CompareNoCase(dlgFind.m_Job)==0)

bFound = TRUE;

 

else

// Переходим к следующей

m_pSet->MoveNext();

}

if (bFound)

{

//Отображаем данные о новой записи

UpdateData(FALSE);

} else

// Отображаем сообщение об ошибке, если запись не найдена

MessageBox("Запись не найдена",

   "Ошибка",

MB_OK | MB_ICONERROR) ;

// Восстанавливаем текущую позицию,

// если база данных поддерживает закладки

if(m_pSet->CanBookmark())

m_pSet->SetBookmark(varBookmark);

else

//В противном случае переходим к первой записи

m_pSet->MoveFirst();

UpdateData(FALSE);  

}}

Примечание:

Однако, прежде чем компилировать и выполнять наше приложение, необходимо внести еще одно дополнение.В начале листинга файла расположены директивы #include. Чтобы можно было работать с диалоговым окном FindJob, необходимо включить в модуль его заголовочный файл. Добавьте новую директиву #include, аналогичнуюпоказанной здесь:

// Для поддержки блока диалога FindJob,

//в начало файла PrimView.CPP необходимо

// самостоятельно включить строку

#include "FindJob.h"

Теперь, когда есть все фрагменты, скомпилируйте и выполните программу.

Рис. 20.

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

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

ЗАДАНИЕ:Используя классы MFC, рассмотреть принципы программирования клиентских приложений через интерфейс ODBC. Самостоятельно создать приложение для БД EDUCATION, реализующее функции просмотра, редактирования, удаления и добавления записей, а также поиска студента по егофамилии.