На этом шаге мы рассмотрим алгоритм создания приложения, использующего базу данных.
Чтобы получить наглядное представление о взаимодействии классов базы данных, набора записей и представления данных, создадим приложение для работы с базой данных средствами мастера AppWizard. Оно будет основано на формах и предназначено для выборки и изменения данных базы pubs на SQL Server через ODBC-источник данных, созданный на 119 шаге. Хотя во всех дальнейших примерах применяются ODBC-классы, демонстрируемые методы работают и для DAO-классов.
Рис.1. Варианты поддержки баз данных в AppWizard
В окне 2 Вам предлагается указать один из четырех возможных вариантов поддержки БД в Вашем приложении. Выбрав первый вариант, Вы откажетесь от такой поддержки. Второй вариант позволяет включить в проект заголовочные файлы БД, в результате чего Вы сможете применять классы базы данных и вручную создавать собственные наборы записей. Третий и четвертый варианты предназначены для конструирования полноценного приложен архитектуры "документ/вид", в котором класс документа содержит набор записей, а класс вида является производным от CRecordView или CDaoRecordView. В третьем варианте не поддерживается сериализация, и поэтому его обычно применяют для простых приложений на базе форм, предназначенных для просмотра и/или модификации данных в БД. Выбирайте четвертый вариант, если Вам требуется поддержка сериализации.
Рис.2. Диалоговое окно Database Options
Рис.3. Окно Select Database Tables
После этого в редакторе диалогов откроется шаблон диалога IDD_MYDBAPP_FORM, на котором основан класс представления записей CMyDBAppView. Это напоминание Вам о том, что прежде чем отображать в представлении какие-либо данные, Вы должны создать элементы управления.
До создания шаблона диалога просмотрите сгенерированный мастером AppWizard код, чтобы понять, как реализуется приложение с поддержкой БД.
CString CMyDBAppSet::GetDefaultSQL()
{
return _T("[dbo].[authors]");
}
Обратите внимание, что по умолчанию в сгенерированном коде имена ODBC-объектов заключаются в квадратные скобки, хотя это требуется, только когда они содержат пробелы. Чтобы задать фильтр для набора записей, задайте строку с выражением WHERE в переменной-члене m_strFilter Вашего класса набора записей. Кроме того, указав строку с выражением ORDER BY в переменной-члене m_strSort, Вы отсортируете данные требуемым образом.
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 пароль не задан
Вы можете повысить производительность Вашего класса набора записей, удалив переменные и функции-члены RFX для тех столбцов таблицы авторов, которые не используются приложением. В следующем упражнении показано, как это сделать средствами ClassWizard.
Рис.4. Результат удаления переменных
Теперь вернитесь к шаблону диалога IDD_MYDBAPP_FORM и создайте элементы управления для отображения данных, хранящих в переменных набора записей.
Рис.5. Шаблон диалога IDD_MYDBAPP_FORM
Далее средствами мастера ClassWizard Вы свяжете их с переменными-членами класса набора записей.
Рис.6. Связывание IDC_AU_ADDRESS с m_pSet->m_address
Рис.7. Результат связывания
Откройте файлы 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 Кб).
На следующем шаге мы рассмотрим задание фильтров в наборах записей.