На этом шаге мы рассмотрим алгоритм создания приложения, использующего базу данных.
Чтобы получить наглядное представление о взаимодействии классов базы данных, набора записей и представления
данных, создадим приложение для работы с базой данных средствами мастера AppWizard. Оно будет основано
на формах и предназначено для выборки и изменения данных базы pubs на SQL Server через
ODBC-источник данных, созданный на 119 шаге. Хотя во всех дальнейших
примерах применяются ODBC-классы, демонстрируемые методы работают и для DAO-классов.
- Создание приложения MyDBApp.
- В меню File выберите пункт New и создайте проект MFC AppWizard (ехе), назвав его MyDBApp.
- В окне 1 мастера AppWizard выберите Single document и щелкните Next, чтобы перейти к окну 2 (рисунок 1).
Рис.1. Варианты поддержки баз данных в AppWizard
В окне 2 Вам предлагается указать один из четырех возможных вариантов поддержки БД в Вашем приложении. Выбрав
первый вариант, Вы откажетесь от такой поддержки. Второй вариант позволяет включить в проект заголовочные файлы БД,
в результате чего Вы сможете применять классы базы данных и вручную создавать собственные наборы записей.
Третий и четвертый варианты предназначены для конструирования полноценного приложен архитектуры "документ/вид",
в котором класс документа содержит набор записей, а класс вида является производным от CRecordView или
CDaoRecordView. В третьем варианте не поддерживается сериализация, и поэтому его обычно применяют для
простых приложений на базе форм, предназначенных для просмотра и/или модификации данных в БД. Выбирайте
четвертый вариант, если Вам требуется поддержка сериализации. - Щелкните Database view without file support. Поскольку Вы будете просматривать базу данных, то нужно выбрать источник данных
для созданного в проекте класса набора записей. Щелкните Data Source. Откроется диалоговое окно Database Options (рисунок 2).
Рис.2. Диалоговое окно Database Options - Убедитесь, что выбран пункт ODBC. Откройте список зарегистрированных источников данных и выберите MyDSN.
- Убедитесь, что указан тип набора записей Snapshot, и щелкните ОК. Имейте в виду, что при этом
должны быть доступны службы SQL Server, иначе выполнить операцию Вам не удастся. В результате будет открыто диалоговое окно Select Database Tables. - Щелкните dbo.authors и затем - OK (dbo - это имя владельца, присвоенное таблице SQL Server).
Рис.3. Окно Select Database Tables - Щелкайте Finish во всех остальных окнах AppWizard, чтобы принять значения по умолчанию. Завершите процесс создания
пректа, щелкнув ОК в диалоговом окне New Project Information.
После этого в редакторе диалогов откроется шаблон диалога IDD_MYDBAPP_FORM, на котором основан
класс представления записей CMyDBAppView. Это напоминание Вам о том, что прежде чем отображать в представлении
какие-либо данные, Вы должны создать элементы управления.
До создания шаблона диалога просмотрите сгенерированный мастером AppWizard код, чтобы понять, как
реализуется приложение с поддержкой БД.
- Просмотр кода, сгенерированного AppWizard.
- Откройте файл MyDBAppSet.h и взгляните на определение класса набора записей. Обратите внимание, что AppWizard добавил в него
группу переменных-членов RFX - по одной на каждый столбец таблицы авторов. - Откройте файл MyDBAppSet.cpp и найдите функцию CMyDBAppSet::DoFieldExchange(). Обратите
внимание, что она похожа на DDX-функцию DoDataExchange(). Для каждого столбца таблицы авторов AppWizard
добавляет вызов соответствующей RFX-функции. - Функция-член GetDefaultSQL(), расположенная перед функцией DoFieldExchange(), задает выражение
FROM оператора SELECT, который, собственно, и создает набор записей:CString CMyDBAppSet::GetDefaultSQL() { return _T("[dbo].[authors]"); }
Обратите внимание, что по умолчанию в сгенерированном коде имена ODBC-объектов заключаются в квадратные скобки, хотя это требуется,
только когда они содержат пробелы. Чтобы задать фильтр для набора записей, задайте строку с выражением WHERE в переменной-члене
m_strFilter Вашего класса набора записей. Кроме того, указав строку с выражением ORDER BY в переменной-члене m_strSort,
Вы отсортируете данные требуемым образом. - Функция-член GetDefaultConnect(), расположенная перед функцией GetDefaultSQL(), возвращает строку подключения, в
которой задан источник данных для Вашего набора записей.CString CMyDBAppSet::GetDefaultConnect() { return _T("ODBC;DSN=MyDSN"); }
Каркас не создает для Вашего проекта объект, производный от СDatabase. Вместо этого конструктору объекта набора записей
передается параметр NULL, что приводит к созданию временного объекта CDatabase.
Если для доступа к источнику данных требуется авторизация SQL Server, Вы можете предотвратить вывод диалогового окна SQL Server Login
во время выполнения приложения, создав такую строку подключения:
CString CMyDBAppSet::GetDefaultConnect(){ return _T("ODBC; DSN=MyDSN; UID=sa; PWD="); }
Здесь предполагается, что для учетной записи sa пароль не задан - И наконец, открыв файл MyDBAppDoc.h, просмотрите определение класса документа. Обратите внимание, что в классе
СМуDBAppDoc есть переменная-член m_myDBAppSet. Приложение CMyDBApp полностью соответствует
архитектуре "документ/вид" - класс документа содержит данные приложения, отображаемые классом представления
(CMyDBAppView).
Вы можете повысить производительность Вашего класса набора записей, удалив переменные и функции-члены RFX для тех столбцов таблицы авторов,
которые не используются приложением. В следующем упражнении показано, как это сделать средствами ClassWizard.
- Удаление лишних полей из класса набора записей.
- Нажмите CTRL+W, чтобы открыть ClassWizard, и щелкните вкладку Member Variables.
- Выберите класс CMyDBAppSet. В списке переменных-членов щелкните m_au_id, соответствующую столбцу [au_id], и затем -
Delete Variable. - Точно так же удалите переменную m_contract, соответствующую столбцу [contract]. Щелкните ОК, чтобы сохранить изменения.
Рис.4. Результат удаления переменных
Теперь вернитесь к шаблону диалога IDD_MYDBAPP_FORM и создайте элементы управления для отображения данных, хранящих в переменных набора записей.
- Изменение шаблона диалога представления записей.
- В редакторе диалогов добавьте в шаблон диалога IDD_MYDBAPP_FORM три статических текстовых строки и семь полей ввода,
как показано на рисунке 5.
Рис.5. Шаблон диалога IDD_MYDBAPP_FORM - Полям ввода присвойте следующие идентификаторы:
- IDC_AU_FNAME;
- IDC_AU_LNAME;
- IDC_AU_ADDRESS;
- IDC_AU_CITY;
- IDC_AU_STATE;
- IDC_AU_ZIP;
- IDC_AU_PHONE.
Далее средствами мастера ClassWizard Вы свяжете их с переменными-членами класса набора записей.
- Связывание переменных класса набора записей с идентификаторами элементов управления диалога представления записей.
- На вкладке Member Variables выберите класс CMyDBAppView.
- В поле Control IDs щелкните IDC_AU_ADDRESS и затем - Add Variable.
- Раскройте список Member variable name и просмотрите все члены-данные класса, которые можно связать с элементами
управления в представлении записей. Выберите переменную m_pSet->m_address.
Рис.6. Связывание IDC_AU_ADDRESS с m_pSet->m_address - Убедитесь, что в поле Category указано значение Value, а в поле Variable - CString, и щелкните ОК.
- Таким же образом свяжите остальные идентификаторы элементов управления с соответствующими переменными класса набора записей.
Для IDC_AU_STATE задайте ограничение на ввод не более двух символов.
Рис.7. Результат связывания - Щелкните ОК, чтобы закрыть ClassWizard и сохранить все изменения.
Откройте файлы MyDBAppView.h и MyDBAppView.cpp и посмотрите на изменения, внесенные
ClassWizard. Обратите внимание, что в классе CMyDBAppView не появилось никаких новых переменных-членов - просто созданы связи
между элементами управления и существующими переменными класса CMyDBAppSet. Это связывание
реализуется мастером ClassWizard путем вызова функции DDX_FieldText() из функции представления
записей DoDataExchange(). Вот как это выглядит:
void CMyDBAppView::DoDataExchange(CDataExchange* pDX) { CRecordView::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMyDBAppView) DDX_FieldText(pDX, IDC_AU_ADDRESS, m_pSet->m_address, m_pSet); DDX_FieldText(pDX, IDC_AU_CITY, m_pSet->m_city, m_pSet); DDX_FieldText(pDX, IDC_AU_FNAME, m_pSet->m_au_fname, m_pSet); DDX_FieldText(pDX, IDC_AU_LNAME, m_pSet->m_au_lname, m_pSet); DDX_FieldText(pDX, IDC_AU_PHONE, m_pSet->m_phone, m_pSet); DDX_FieldText(pDX, IDC_AU_STATE, m_pSet->m_state, m_pSet); DDV_MaxChars(pDX, m_pSet->m_state, 2); DDX_FieldText(pDX, IDC_AU_ZIP, m_pSet->m_zip, m_pSet); //}}AFX_DATA_MAP }
Имейте в виду, что можно также прибегнуть к стандартной DDV-функции проверки.
Соберите и запустите приложение MyDBApp. Оно должно выглядеть, как показано на рисунке 8.
Рис.8. Приложение MyDBApp
Обратите внимание, что класс CRecordView предоставляет панель передвижения по набору записей с кнопками
вперед, назад, перемещение в начало и в конец. Каркас связывает идентификаторы элементов управления этих кнопок с
функцией представления записей OnMove(). В базовом классе при переходе к другой записи эта функция вызывает
CWnd::UpdateData(TRUE) для сохранения изменений текущей записи и CWnd::UpdateData(FALSE) - для
отображения другой записи из набора. UpdateData обновляет представление, вызывая DoDataExchange().
Текст этого приложения можно взять здесь (41,7 Кб).
На следующем шаге мы рассмотрим задание фильтров в наборах записей.