Класс CODBCRecordset

В обычном MFC проекте, предназначенном для работы с базой данных, мы получаем множество классов, наследованных от CRecordset, которые генерирует для нас визард (ClassWizard). Представленный здесь класс CODBCRecordset легко может заменить все эти CRecordset классы. CODBCRecordset сам по себе наследуется от CRecordset ,   но не имеет жёско-заданных количества и типов полей базы данных.

CODBCRecordset имеет как минимум два преимущества:

  1. CODBCRecordset может использоваться не токо для получения значений из базы данных, но и для записи, используя MFC.
  2. Другие подобные примеры не могут одновременно открывать более одной записи через одно соединение с базой данных, в случае с базой данных MS SQL Server. В некоторых случаях это вызывает проблемы, так как открытие нового соединения довольно продолжительно по времени и требует значительных затрат ресурсов.

Так как CODBCRecordset наследуется от CRecordset и использует его механизм обмена данными, то он полностью совместим с MFC ODBC классом CDatabase .

Каждое значение поля хранится в объекте CDBField. Класс CDBField унаследован от CDBVariant и имеет дополнительные функции, которые упрощают его использование.

Классы CODBCRecordset и CDBField поддерживают все типы данных. CDBField делает неявное преобразование типов, в тех местах, где требуется представить данные в запрошенном формате.

Ниже представлен список методов CODBCRecordset и CDBField:

Класс CODBCRecordset
Конструктор Тоже что и для CRecordset принимая CDatabase*
BOOL Open( LPCTSTR lpszSQL, UINT nOpenType, DWORD dwOptions ); Открывает запись
lpszSQL - это SQL выражение, которое возвращает запись
т.е. SELECT * FROM tablename
nOpenType тип открытия, см. CRecordset::Open()
dwOptions опции для CRecordset, см. CRecordset::Open()

Обратите внимание, что lpszSQL и nOpenType поменялись местами, если сравнивать с CRecordset::Open()

short GetODBCFieldCount() Возвращает количество полей (колонок) в записи. Данный метод определён в CRecordset .
int GetFieldID( LPCTSTR ) Возвращает индекс поля для данного имени поля. Не чувствителен к регистру. Напротив, CRecordset::GetFieldIndexByName() чувствителен к регистру.
CString GetFieldName( int ) Возвращает имя поля для данного индекса поля
CDBField& Field( LPCTSTR )
CDBField& Field( int )
По средствам этого метода можно получить ссылку на внутренний объект CDBField, отвечающий за определённую колонку (См. класс CDBField ).

Данный метод может использоваться в двух вариациях - с аргументом типа LPCTSTR szName , указывающем имя колонки, и int nID , указывающем индекс колонки в записи.

CDBField& operator( LPCTSTR )
CDBField& operator( int )
Оператор функции служит для облегчения использования класса и для вызова соответствующего метода Field()

параметр типа LPCTSTR szName - указывает на имя колонки, а int nID - на индекс колонки.

bool GetBool()
unsigned char GetChar()
short GetShort()
int GetInt()
long GetLong()
float Getfloat()
double GetDouble()
COleDateTime GetDate()
CString GetString()
CLongBinary* GetBinary()
Каждый из этих методов делает соответствующее преобразование, в зависимости от возвращаемых значений и типов данных.

Эти методы вызывают Field().AsXXX()
(См. класс CDBField).параметр типа LPCTSTR szName - указывает на имя колонки, а int nID - на индекс колонки.

 

Класс CDBField
Конструкторы Здесь не присутствует конструкторов public. CDBField нельзя использовать без CODBCRecordset, так как внутренние структуры и объекты доступны через методы CODBCRecordset.
bool AsBool()
unsigned char AsChar()
short AsShort()
int AsInt()
long AsLong()
float AsFloat()
double AsDoble()
COleDateTime AsDate()
CString AsString()
CLongBinary* AsBinary()
Каждый из этих методов делает соответствующее преобразование, в зависимости от возвращаемых значений и типов данных. (См. таблицу методов AsXXX, для более детального ознакомления с правилами преобразования).

Здесь нет типа данных Int , а AsInt() эквивалентен AsLong()

операторы аргументов (См. таблицу операторов аргументов для ознакомления с правилами преобразования).
const CString& GetName() Возвращает имя поля, которому соответствует данный объект.
bool IsNull()
bool IsBool()
bool IsChar()
bool IsShort()
bool IsInt()
bool IsLong()
bool IsFloat()
bool IsDouble()
bool IsNumber()
bool IsDate()
bool IsString()
bool IsBinary()
Каждый из них возвращает true если поле содержит значение соответствует типу данных.

Здесь нет типа данных Number , а IsNumber() возвращает true если IsShort() || IsLong() || IsFloat() || IsDouble().

Здесь нет типа данных Int , а IsInt() возвращает true если IsLong() возвращает true.

 

Преобразования делаемые методами AsXXX

 

Значения в базе данных

  NULL BOOL UCHAR SHORT LONG SINGLE DOUBLE DATE STRING BINARY
AsBool false * * * * * *   *  
AsChar 0x32 * * * * * *   *  
AsShort 0 * * * * * *   *  
AsInt 0 * * * * * *   *  
AsLong 0 * * * * * *   *  
AsFloat 0.0 * * * * * *   *  
AsDouble 0.0 * * * * * *   *  
AsDate null invalid invalid * * * * * *  
AsString empty * * * * * * * * *
AsLongBinary NULL                 *
Пустые ячейки, указывают на то, что данное преобразование невозможно.
Ячейки, помеченные как * указывают на допустимость данной операции (См. таблицу алгоритмов преобразования).

 

Conversions made by assignment operators

Тип поля базы данных

Аргумент оператора

  bool unsigned char short int long float double COleDateTime String
NULL                  
BOOL * * * * * * *   *
UCHAR * * * * * * *   *
SHORT * * * * * * *   *
LONG * * * * * * *   *
SINGLE * * * * * * *   *
DOUBLE * * * * * * *   *
DATE               * *
STRING * * * * * * * * *
BINARY                  
Пустые ячейки, указывают на то, что данное преобразование невозможно.
Ячейки, помеченные как * указывают на допустимость данной операции (См. таблицу алгоритмов преобразования).

 

Алгоритмы преобразования
String в Bool Сравнение первого символа строки с 'T'
Char в Bool Сравнение символа с 'T'
Bool в String String = (bVal) ? 'T' : 'F'
Bool в Char Char = (bVal) ? 'T' : 'F'
String в Number использование функции atoX()
Number в String метод CString::Format(), используя соответствующий формат строки
String в Date метод COleDateTime::ParseDateTime()
Date в String метод COleDateTime::Format()

 

Пример использования CODBCRecordset

Вам необходимо включить в Ваш проект файлы ODBCRecordset.h и ODBCRecordset.cpp.

Обычно я добавляю следующую строчку в мой файл StdAfx.h.

#include "ODBCRecordset.h"

Далее следует простой код, показывающий как можно использовать CODBCRecordset.

/////////////////////////////////////////////////////////////////////////////
CDatabase db;
// Соединение с ODBC с всплявающим диалогом ODBC
CString cConnect = "ODBC;";
db.Open( NULL, // DSN
FALSE, // Эксклюзивно
FALSE, // Только чтение (ReadOnly)
cConnect, // строка ODBC Connect
TRUE // используем курсор
);

COleDateTime dOrderDate;

CODBCRecordset rs( &db );
rs.Open( "SELECT * FROM Orders \
WHERE ORDER_DATE > 'jan 1 2000' \
ORDER BY ORDER_DATE"
);
for( ; ! rs.IsEOF(); rs.MoveNext() )
{
// Далее на Ваш выбор. Вы можете выбрать способ,
// которым будете получать значения
//
// Здесь возвращается значение COleDateTime
dOrderDate = rs.GetDate( "ORDER_DATE" );
dOrderDate = rs.Field("ORDER_DATE").AsDate();

// Здесь делается неявный запрос в AsDate()
dOrderDate = rs("ORDER_DATE");
dOrderDate = rs.Field("ORDER_DATE");

// Теперь редактируем поля в записи
rs.Edit();
rs("ORDER_DATE") = "jan 1 1999"; // Неявное преобразование
rs.Field("ORDER_DATE") = "jan 1 1999"; // Неявное преобразование
rs.Update();
} // for(....
/////////////////////////////////////////////////////////////////////////////

Если ORDER_DATE хранится в базе данных как datetime либо совместимым типом данных, то значение будет браться напрямую.
Если ORDER_DATE хранится в базе данных как string либо совместимым типом данных (char, varchar), то значение будет преобразовано через метод COleDateTime::ParseDateTime(). Если преобразование не удалось, то dOrderDate будет установлен в COleDatetime::invalid.

При работе с базой данных, может возникнуть так, что 2 или более колонок могут иметь одинаковые имена. CODBCRecordset оставляет имя первого столбца нетронутым, но другие повторяющиеся колонки переименованы путём добавления номера этой колонки. Например:

SELECT * FROM Orders, Customers WHERE Orders.CUST_ID = Customers.ID

Если таблица Orders имеет колонку с именем ID , и Customers имеет колонку с именем ID , то CODBCRecordset переименует ID из Customers в ID2, а все остальные, неповторяющиеся столбцы останутся не тронутыми.

Например: Переименуем колонки в ручную, чтобы быть уверенными в именах

SELECT Orders.*, Customers.ID as CUSTOMERS_ID
FROM Orders, Customers
WHERE Orders.CUST_ID = Customers.ID