10. Лекция: Реализация доступа к базам данных в среде VISUAL STUDIO.NET на языке С++ с применением MFC

Описываются классы библиотеки MFC, используемые для доступа к данным, и средства, применяемые для создания приложений баз данных.

Библиотека MFC

Среда Visual Studio.NET предоставляет различные подходы для реализации работы с базами данных:

  • Применение библиотеки MFC (Microsoft Foundation Class Library).
  • Применение ATL (Active Template Library).
  • Использование библиотек Framework.

Библиотека MFC реализует поддержку доступа к базам данных, основанную на двух механизмах:

  • через ODBC-драйверы;
  • с применением провайдеров данных OLE DB.

Приложения, использующие доступ к базам данных с применением ODBC-драйверов, в обязательном порядке используют класс CDatabase для создания объекта "база данных" и класс CRecordset для создания объекта "результирующий набор".

Библиотека MFC предоставляет для работы с базами данных через ODBC-драйверы следующие классы:

  • CDatabase – класс, инкапсулирующий соединение с источником данных;
  • CRecordset – класс, инкапсулирующий работу с результирующим набором, включая переход между записями, добавление, удаление и изменение записей, задание фильтров и выполнение параметризированных запросов;
  • CRecordView – класс, предоставляющий средства визуального отображения данных из созданного результирующего набора;
  • CDBException – класс, предназначаемый для обработки ошибок, которые возникают при работе с источником данных;
  • CFieldExchange – класс, который поддерживает RFX обмен данными между полями – членами класса результирующего набора и столбцами таблицы источника данных.

Библиотека MFC предоставляет для работы с базами данных с применением провайдеров OLE DB класс COleDBRecordView, используемый для отображения данных из результирующего набора (представляемого классом CRowset) в элементах управления шаблона диалога. Этот класс объявляется в заголовочном файле afxoledb.h.

Для реализации клиентов и серверов OLE DB можно использовать ATL, предоставляющую OLE DB шаблоны как С++ шаблоны. Более подробно шаблоны ATL будут рассмотрены в следующей лекции.

Библиотека MFC предоставляет следующие два класса, упрощающие обмен данными:

  • CLongBinary – класс, предоставляющий средства работы с BLOB-объектами (Binary Large OBject), такими как bitmap-изображения, хранимыми в базе данных;
  • CDBVariant – класс, реализующий тип Variant, который позволяет сохранять информацию в базе данных, используя текущее значение, хранимое в переменной типа union.

Для доступа к конкретной базе данных требуется, чтобы был установлен соответствующий драйвер ODBC или OLE DB провайдер.

Приложения, работающие с базами данных, могут быть реализованы как консольные приложения, приложения с архитектурой документ-отображение (SDI- и MDI-приложения), приложения-диалоги, серверные приложения.

Классы для работы с базами данных

Класс CDATABASE

Класс CDatabase предоставляет средства подключения к источнику данных. В приложении может использоваться несколько активных объектов CDatabase.

Для использования класса CDatabase следует подключить заголовочный файл afxdb.h.

Член класса CDatabase::m_hdbc является указателем на подсоединенный ODBC источник данных. Он может использоваться как дескриптор соединения при непосредственном вызове функций ODBC.

Например:

// Использование m_hdbc для непосредственного вызова 
// ODBC API.
// m_db – объект типа CDatabase,
// m_hdbc – член класса CDatabase
nRetcode = ::SQLGetInfo( m_db.m_hdbc, 
                        SQL_ODBC_SQL_CONFORMANCE,
 	                &nValue, sizeof( nValue ), 
                        &cbValue );

Класс CDatabase имеет один конструктор без параметров и набор методов, включая следующие:

  • OpenEx (LPCTSTR lpszConnectString, DWORD dwOptions = 0);throw (CDBException, CMemoryException ); - метод, выполняющий открытие базы данных. Первый параметр определяет ODBC строку подключения, описывающую источник данных, и некоторую дополнительную информацию, такую как идентификатор пользователя и пароль.

    Например: "DSN=SQLServer_Source;UID=Us1;PWD=pwd1".

    Второй параметр по умолчанию равен нулю, что предполагает открытие базы данных с разделяемым доступом и с правами записи, без загрузки ODBC Cursor Library DLL и без принудительного отображения диалога ODBC, определяющего информацию для подключения к базе данных. Этот параметр может быть задан как битовая маска, определяемая комбинацией следующих значений:

    • CDatabase::openReadOnly – открытие источника данных только для чтения;
    • CDatabase::useCursorLib – указывает загрузку библиотеки ODBC Cursor Library DLL, маскирующей некоторую функциональность ODBC-драйвера;
    • CDatabase::noOdbcDialog – предотвращает появление диалога для подсоединения источника данных;
    • CDatabase::forceOdbcDialog – обеспечивает отображение диалога с целью определения информации для ODBC-соединения.

    Например:

    // Объявление объекта типа CDatabase 
    CDatabase m_db1;
    // Объект CDatabase: открытие источника данных 
    // "только на чтение"
    m_db1.OpenEx( _T( "DSN=MYDATASOURCE;UID=U1" ),
                   CDatabase::openReadOnly | 
                   CDatabase::noOdbcDialog ) );
  • Close – метод, закрывающий соединение с базой данных. После закрытия соединения объект типа CDatabase можно использовать для открытия другого источника данных;
  • IsOpen – метод, определяющий, установлено ли соединение с базой данных для объекта CDatabase;
  • GetConnect – метод, который возвращает строку подключения, использованную для открытия источника данных;
  • GetDatabaseName – метод, возвращающий имя текущей подсоединенной базы данных (это не источник данных DSN, указываемый для методов OpenEx и Open, и зависит от ODBC);
  • CanTransact – метод, позволяющий определить, можно ли использовать транзакции для базы данных;
  • SetLoginTimeout – метод, устанавливающий интервал времени, по истечении которого попытка подсоединения к источнику данных будет отменена;
  • BeginTrans – метод, открывающий транзакцию. Не все ODBC-драйверы поддерживают применение транзакций;
  • CommitTrans – метод, выполняющий завершение транзакции;
  • Rollback – метод, выполняющий откат транзакции;
  • ExecuteSQL – метод, выполняющий SQL-оператор без возвращения результирующего набора.

    Например:

    CString strCmd = "UPDATE TBL1 SET F1 = 124";
    TRY
    {   m_db1.ExecuteSQL( strCmd );}
    CATCH(CDBException, e)
    {   // Код ошибки в e->m_nRetCode
    }
    END_CATCH

Класс CRECORDSET

Класс CRecordset реализует операции над результирующим набором, получаемым из источника данных при выполнении SQL-оператора SELECT.

Результирующий набор может использоваться в двух режимах:

  • динамический набор записей (dynasets), состояние которого синхронизируется с изменениями, сделанными другими пользователями;
  • статический набор записей (snapshots), не отражающий изменения, производимые другими пользователями.

Класс CRecordset позволяет:

  • просматривать записи результирующего набора;
  • изменять записи;
  • выполнять блокировку записей;
  • сортировать записи;
  • определять фильтр для выбора записей.

Для использования набора записей из базы данных следует:

  1. Создать объект класса CRecordset, передав конструктору в качестве параметра указатель на объект CDatabase или NULL;
  2. Вызвать метод Open и определить режим использования набора данных: динамический или статический.

Метод Open создает (открывает) результирующий набор.

Член класса CRecordset::m_hstmt содержит указатель на структуру данных типа HSTMT (дескриптор оператора).

Класс CRecordset содержит переменные и методы для работы с результирующим набором, включая следующие:

  • CRecordset::m_nFields – переменная, содержащая количество полей в результирующем наборе.
  • CRecordset::m_nParams – переменная, содержащая количество параметров, используемых в запросе.
  • CRecordset::m_pDatabase – переменная, содержащая указатель на объект CDatabase посредством которого для данного результирующего набора было установлено соединение с базой данных.
  • CRecordset::m_strFilter – переменная, содержащая условие, указываемое во фразе WHERE SQL-оператора (условие должно быть определено до создания результирующего набора вызовом метода Open).

    Например:

    CMySet rsMySet( NULL );  // Класс CMySet наследуется от CRecordset
    // Определение фильтра (условия во фразе WHERE)
       rsMySet.m_strFilter = "field2 > 123";
    // Выполнение запроса – открытие результирующего набора
       rsMySet.Open( CRecordset::snapshot, "MyTbl1" );
  • CRecordset::m_strSort – переменная, содержащая значение, указываемое во фразе ORDER BY SQL-оператора;
  • CRecordset ( CDatabase* pDatabase = NULL); – конструктор;
  • Open – метод, создающий результирующий набор;
  • CanAppend – метод, определяющий, разрешено ли в данный открытый набор записей добавлять новые записи;
  • GetSQL – метод, возвращающий SQL-оператор, использованный для получения открытого в данный момент результирующего набора;
  • IsEOF – метод, определяющий, достигнут ли конец результирующего набора;
  • IsBOF – метод, определяющий, достигнуто ли начало набора записей.

    Например:

    rsSet.Open( ); // Результирующий набор открыт: 
                   // текущая запись - первая
    if( rsSet.IsBOF( ) )    return;    	// Результирующий набор пуст
    while ( !rsSet.IsEOF( ) )		// Просмотр всех 
                                      // записей от начала до конца
        rsSet.MoveNext( );
    rsSet.MoveLast( );		      // Переход к последней записи
    while( !rsSet.IsBOF( ) )	      // Просмотр всех 
     			         // записей от конца до начала
        rsSet.MovePrev( );
    rsSet.MoveFirst( );	            // Переход к первой записи
    
  • AddNew – метод, используемый для добавления новой записи (только для режима выборки по одной записи);
  • CancelUpdate – метод, используемый для отмены изменений, произведенных операциями Edit или AddNew с момента последнего вызова метода Update;
  • Delete – метод, используемый для удаления текущей записи;
  • Edit – метод, вызываемый для включения режима редактирования текущей записи. В режиме редактирования можно выполнять методы AddNew и Delete. Для внесения изменений, сделанных этими методами, в базу данных следует вызвать метод Update.

    Например:

    rsSet.Edit( );		// Начало операций изменения записи
    rsSet.m_dwF1 = 123;
    rsSet.m_strF1 = "abc";
    if( !rsCustSet.Update( ) )    // Внесение изменений
    
  • Update – метод, возвращающий ненулевое значение в том случае, если запись источника данных была успешно обновлена;
  • MoveFirst – метод, перемещающий указатель текущей записи на первую запись результирующего набора;
  • MoveLast – метод, перемещающий указатель текущей записи на последнюю запись результирующего набора;
  • MoveNext – метод, перемещающий указатель текущей записи на следующую запись результирующего набора;
  • MovePrev – метод, перемещающий указатель текущей записи на предыдущую запись результирующего набора;
  • DoFieldExchange – метод, вызываемый для автоматического обмена данными между членами класса – полями результирующего набора (текущей записи) и соответствующими полями текущей записи в источнике данных. Метод DoFieldExchange доступен только для объектов класса, производного от класса CRecordset. Если набор записей создан непосредственно как объект класса CRecordset, то следует использовать метод GetFieldValue. Обмен данными с источником данных, называемый RFX-обменом (record field exchange), работает в двух направлениях: из полей объекта "результирующий набор" в поля источника данных и обратно. Для того чтобы использовать этот метод, достаточно в производном классе результирующего набора определить имена и тип полей данных – членов класса. ClassWizard самостоятельно вставит код в переопределяемый метод DoFieldExchange.

Например:

void CSet::DoFieldExchange(CFieldExchange* pFX)
{
   pFX->SetFieldType(CFieldExchange::outputColumn);
   RFX_Int(pFX, "F1", m_wF1);   // Вызов RFX-метода
   RFX_Text(pFX, "F2", m_strF2);
}

Класс CRECORDVIEW

Объект "отображение" типа CRecordView предоставляет средства для просмотра полей базы данных в элементах управления.

Отображение создается на основе шаблона документа, используя элементы управления, добавленные в ресурс шаблона диалога.

Объект CRecordView использует DDX-обмен данными и RFX-обмен данными для реализации обмена между тремя наборами данных: элементами управления, полями результирующего набора и записями источника данных. На следующей схеме проиллюстрировано взаимодействие этих трех наборов данных.

Элементы управления (определяются как ресурсы)DDX-методыПоля результирующего набора (определяются как переменные члены класса, наследуемого от CRecordset)RFX-методыПоля источника данных (указываются оператором SELECT)

Класс CRecordView по умолчанию поддерживает реализацию просмотра набора записей и перехода к первой, последней, следующей и предыдущей записям в текущем отображении.

При использовании для создания шаблона приложения MFC Application Wizard автоматически будут созданы ресурс меню и панели инструментов, содержащей кнопки прокрутки набора записей. При программировании вручную, используя ClassWizard, эти ресурсы надо создавать самостоятельно в редакторе меню и в редакторе битовых изображений.

Класс CRecordView имеет два конструктора и набор методов, включая следующие:

  • CRecordView – конструктор, параметр которого указывает используемый ресурс шаблона диалога;
  • OnGetRecordset – метод, возвращающий указатель на "объект результирующий набор", ассоциированный с отображением, при его успешном создании, и NULL — в противном случае;
  • OnMove – метод, используемый для перехода к другой записи результирующего набора. Направление перехода задается параметром, который может принимать следующие значения: ID_RECORD_FIRST; ID_RECORD_LAST; ID_RECORD_NEXT; ID_RECORD_PREV. По умолчанию, OnMove обновляет текущую запись источника данных, если пользователь внес изменения в отображение записи (элементы управления);
  • IsOnFirstRecord – метод, возвращающий ненулевое значение, если текущей записью является первая запись результирующего набора.

Для использования базы данных следует:

  1. Создать объект класса, наследуемого от CDatabase.
  2. Установить соединение, открыв базу данных (вызвав метод CDatabase::OpenEx или CDatabase::Open).
  3. Создать объект класса, наследуемого от Crecordset, для операций над подсоединенным источником данных, передав конструктору указатель на CDatabase. Как любой класс, реализующий визуализацию данных, этот класс связывается с шаблоном диалога (шаблон диалога – это ресурс, описывающий диалоговое окно и все располагаемые в нем элементы управления). Используя DDX-механизм (Dialog Data eXchange) обмена данными между результирующим набором и элементами управления, класс CRecordView реализует отображение и изменение данных.
  4. Для завершения работы закрыть базу данных, вызвав метод CDatabase::Close. Это также закроет все наборы записей.

Создание приложений баз данных

Средства MFC Application Wizard позволяют практически без всякого программирования разрабатывать приложения, выполняющие подключение к таблице базы данных и отображение содержимого ее полей. При этом для реализации доступа к базе данных используется или механизм ODBC, или OLE DB.

Для того чтобы использовать ODBC-драйверы, первоначально следует создать источник данных DSN (Data Source Name). Это можно сделать как в момент формирования проекта с помощью MFC Application Wizard, так и используя ODBC32 панели управления Windows.

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

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

  1. Создайте новый проект. Выберите в качестве шаблона создаваемого проекта MFC Application.
  2. Автоматически формируемые приложения, реализующие работу с базой данных, должны поддерживать архитектуру "документ-отображение". (Выберите, например, тип приложения Single Document).
  3. Перейдите на страницу Database Support (рис. 10.1) и определите поддержку работы с базой данных. Опция Database view without file support обеспечивает формирование кода, выполняющего подключение к источнику данных, но без поддержки механизма сериалицации. Опция Bind all columns доступна только для ODBC и обеспечивает автоматическое связывание полей результирующего набора с переменными членами класса, наследуемого от CRecordset.

    Страница Database Support мастера MFC Application Wizard
    Рис. 10.1.  Страница Database Support мастера MFC Application Wizard

  4. Выберите механизм доступа (Client type):
    • опция ODBC обеспечивает включение в проект заголовочного файла AFXDB.H и линкуемых библиотек;
    • опция OLE DB обеспечивает включение в проект заголовочных файлов ATLBASE.H, AFXOLEDB.H и ATLPLUS.H.
    Если для Database Support была установлена опция Database view without file support или Database view with file support, то для механизма ODBC создаваемый класс отображения будет наследоваться от CRecordView и будет ассоциирован с классом результирующего набора, наследуемого от CRecordset. Для механизма OLE DB класс отображения наследуется от COleDBRecordView и ассоциируется с классом, наследуемым от CTable или CCommand.
  5. Далее, если используется механизм ODBC, то следует выбрать источник данных, с которым устанавливается соединение. По щелчку мышью на командной кнопке Data Source отображается диалог для выбора источника данных (рис. 10.2).

    Диалог для выбора источника данных (для ODBC)
    Рис. 10.2.  Диалог для выбора источника данных (для ODBC)

  6. При использовании механизма OLE DB также следует определить источник данных, используя или имя источника данных, или строку подключения (рис. 10.3)

    Диалог для выбора источника данных (для OLE DB)
    Рис. 10.3.  Диалог для выбора источника данных (для OLE DB)

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

    Диалог Select Database Object
    Рис. 10.4.  Диалог Select Database Object

  8. На странице Generated Classes отображается список классов, автоматически создаваемый MFC Application Wizard. Класс отображения будет наследован от класса СRecordView, а класс результирующего набора – от класса CRecordset.

Созданное AppWizard приложение будет содержать панель инструментов с кнопками для перехода между записями таблицы базы данных, но не будет содержать элементов управления для отображения полей таблицы базы данных.

Мастер MFC Application Wizard создает в классе, наследуемом от Crecordset, переменные члены класса для каждого поля таблицы подключаемой базы данных.

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

  1. Расположить в редакторе ресурсов для каждого поля таблицы соответствующий ему элемент управления.
  2. Вставить в метод DoDataExchange класса отображения вызовы методов DDX_FieldText (или DDX_FieldCheck, DDX_FieldRadio, DDX_FieldSlider и т.п.), выполняющие связь между идентификатором ресурса и переменной – членом класса результирующего набора.

    Например:

    void CP2View::DoDataExchange(CDataExchange* pDX)
    {
    CRecordView::DoDataExchange(pDX);
    DDX_FieldText(pDX, 
             IDC_EDIT1, // Идентификатор ресурса
     	      m_pSet->m_f1, // Переменная — член 
     			   // класса, наследуемого 
     	    		   // от CRecordset
          m_pSet); // Член класса, наследуемого 
                    // от CRecordView:   CP2Set* m_pSet;
                    // Член класса, наследуемого от 
                    // CDocument: CP2Set m_P2Set;
                    // Метод OnInitialUpdate класса  
                    // наследуемого от CRecordView :
                    // CDocument: CP2Set m_P2Set;
                    // m_pSet = &GetDocument()->m_P2Set;
       DDX_FieldText(pDX, 
     		IDC_EDIT2, 
          m_pSet->m_f2, 
          m_pSet);
    }

В результате выполненных действий записи базы данных будут отображаться в окне документа в соответствующих элементах управления.