Рассмотрим небольшой пример. Допустим, в базе данных содержится таблица tPeople с полями Name (типа строка из 50 символов) и DateOfBirth (типа дата/время), и мы хотим напечатать на экране её содержимое. Сперва создаём новый класс, порождённый от CRecordset:
class CPeople : public CRecordset {
public:
CPeople(CDatabase *pDatabase = NULL) : CRecordset(pDatabase) {
m_nFields = 2;
};
CString m_Name;
CTime m_DateOfBirth;
void DoFieldExchange(CFieldExchange *pFX);
};
Для каждого поля в таблице tPeople мы объявили переменную соответствующего типа. Так, поле Name хранится как строка, поэтому m_Name имеет тип CString. DateOfBirth - это дата, поэтому m_DateOfBirth - переменная типа CTime. Обратите внимание, что в переменную m_nFields (CPeople наследует её от CRecordset) необходимо записать количество полей таблицы (это значение необходимо MFC, чтобы правильно построить запрос). Теперь реализуем функцию DoFieldExchange, в которой происходит связывание полей таблицы с переменными нашего класса CPeople.
void CPeople::DoFieldExchange(CFieldExchange *pFX) {
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Text(pFX, "Name", m_Name, 50);
RFX_Date(pFX, "DateOfBirth", m_DateOfBirth);
}
Вызов SetFieldType с параметром CFieldExchange::outputColumn должен всегда предшествовать операциям связывания. Сам механизм связывания напоминает механизм обмена данными с элементами управления диалога. Тут и там используется набор специальных макросов, которые в зависимости от контекста (который определяется объектом класса CFieldExchange) выполняют различные действия – в нашем случае формируют элементы запроса, связывают переменные с полями множества записей и осуществляют обмен данными между ними.
Каждый из макросов, используемых в DoFieldExchange, имеет префикс "RFX_". Существует несколько версий этих макросов – по одному на каждый основной тип. Наборы параметров у них несколько отличаются, но первые три параметра совпадают у всех макросов: указатель на объект класса CFieldExchange (нужно просто передать указатель, полученный от MFC), имя поля во множестве записей и ссылка на переменную, которая будет с этим полем связана.
Итак, создание класса CPeople закончено, и можно использовать его для доступа к таблице tPeople. Вот фрагмент, который печатает на экране её содержимое.
:
CDatabase Db;
Db.OpenEx("DSN=db;UID=sa;PWD=");
CPeople Rs(&Db);
Rs.Open(CRecordset::dynaset, "tPeople");
while(!Rs.IsEOF) {
printf("%s\t%s\n", Rs.m_Name, Rs.m_DateOfBirth.Format("%d of %B %Y"));
Rs.MoveNext;
}
:
Здесь нужно обратить внимание на несколько моментов. Во-первых, как мы помним, прежде чем работать с базой данных, необходимо установить соединение с ней. Это делается с использованием уже знакомой нам функцией CDatabase::OpenEx. Во-вторых, указатель на соединение нужно передать конструктору класса CPeople, чтобы данные извлекались именно из нужной нам базы данных.
Теперь рассмотрим параметры функции CRecordset::Open. Первый параметр задаёт тип результирующего множества записей. Можно задавать следующие типы:
• CRecordset::forwardOnly – множество записей, доступное только для чтения и по которому можно перемещаться только вперёд.
• CRecordset::snapshot – множество записей, по которому можно перемещаться в любом направлении. Изменения, внесённые в БД после создания такого множества, в нём не отражаются.
• CRecordset::dynaset – похоже на предыдущее, но любые изменения записи в БД будут видны после повторной выборки этой записи. Новые записи, добавленные в БД после создания такого множества, в нём не отражаются.
• CRecordset::dynamic – самое ресурсоёмкое множество. Любые изменения, внесённые в БД после его открытия, будут в нём отражены. Не поддерживается многими драйверами.
Если драйвер не поддерживает запрошенный тип множества записей, MFC возбудит исключение.
Второй параметр функции CRecordset::Open используется для передачи имени таблицы или запроса, на основе которого будет построено множество записей. MFC сама определит, что именно ей передали. У функции CRecordset::Open есть также третий параметр – который во многих случаях можно не указывать. За его описанием можно обратиться к документации.
После того как множество записей создано, пользоваться им достаточно просто. Для обращения к полям текущий записи мы используем переменные-члены класса CPeople, а к следующей записи перемещаемся при помощи функции CRecordset::MoveNext. Когда все записи исчерпаны, функция CRecordset::IsEOF возвращает TRUE, и цикл прерывается.