Опубликован: 17.08.2010 | Доступ: свободный | Студентов: 999 / 59 | Оценка: 4.11 / 3.89 | Длительность: 29:38:00
Самостоятельная работа 10:

Поддержка баз данных с помощью API ODBC

Подключение к источнику данных

После настройки DSN с помощью утилиты ODBC Data Source Administrator теперь вместо конкретного файла с данными у нас есть именованная абстракция данные-драйвер, зарегистрированная в операционной системе и поэтому готовая к поддержке ее механизмом ODBC. Все готово для того, чтобы к базе данных можно было обратиться с помощью функций API ODBC как к абстрактным данным, которые теперь можно назвать данными ODBC.

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

  • Прежде всего следует инициализировать сам механизм ODBC и получить уникальный дескриптор среды ( unique environment handle ) henv. Этот дескриптор уникально идентифицирует в операционной системе поток, который будет использовать механизм ODBC. Следующий фрагмент кода резервирует в системе поток для ODBC
    SQLHENV henv;
    SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_ENV,
                                  SQL_NULL_HANDLE,
                                  &henv);
  • Получив дескриптор среды, следует установить версию ODBC, с которой совместимо приложение
    SQLSetEnvAttr(henv,
                  SQL_ATTR_ODBC_VERSION,
                  (SQLPOINTER) SQL_OV_ODBC3,
                  SQL_IS_INTEGER);
  • первый параметр указывает дескриптор потока
  • второй указывает необходимость регистрирования версии
  • третий устанавливает, что приложение будет поддерживать ODBC версии 3.0
  • Прежде, чем приложение сможет подключиться к источнику данных, оно должно получить от ODBC дескриптор подключения к базе данных ( data connection handle ) hdbc
    SQLHDBC hdbc;
    SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_DBC,
                                  henv,
                                  &hdbc);
  • Получив дескриптор hdbc, можно, наконец, подключиться к источнику данных, использовав любую из трех функций ODBC:
    • SQLConnect() - самый простой способ подключения к источнику данных. Ей достаточно передать DSN, идентификатор пользователя и пароль для подключения к источнику данных ODBC
    • SQLDriverConnect() - альтернативный вариант для драйверов ODBC. Она требует передачи более подробной информации, чем функция SQLConnect()
    • SQLBrowseConnect() - это средство подключения к источнику данных, обладающее наибольшими возможностями. При помощи одного или нескольких обращений к этой функции можно запросить и получить всю информацию, необходимую для подключения к источнику данных. Для обеспечения такого подхода функция возвращает значение SQL_NEEDS_DATA до тех пор, пока приложение не представит всю требуемую информацию, и только после этого она возвратит значение SQL_SUCCESS

Вот первый, наиболее простой, вариант использования функции подключения

SQLRETURN rc = SQLConnect(hdbc, // Дескриптор подключения
          (SQLCHAR*) "MyBase", SQL_NTS, // DSN
          (SQLCHAR*) "", SQL_NTS, // Идентификатор пользователя
          (SQLCHAR*) "", SQL_NTS); // Пароль для подключения
bool bIsConnected = TRUE; // Подняли флаг, что соединение существует

Аргумент SQL_NTS сообщает ODBC о том, что предыдущий переданный аргумент представляет собой строку с завершающим нулевым символом.

Получение информации о возможностях драйвера ODBC

Одной из наиболее существенных особенностей ODBC является то, что он позволяет запросить информацию о возможностях драйвера ODBC. Для этого используются API ODBC -функции SQLGetFunctions(), SQLGetInfo(), SQLGetTypeInfo(). Рассмотрим их...

  • SQLGetFunctions() - позволяет запросить драйвер ODBC о том, поддерживает ли он определенные функции или группы функций. В качестве аргументов необходимо передать дескриптор hdbc и числовое значение, идентифицирующее определенную функцию или группу функций API ODBC. Эти значения определены в файле заголовка sqlext.h, который устанавливается вместе с Microsoft SQL Server, SDK ODBC или Visual Studio .NET. Приведенный ниже фрагмент кода выясняет, поддерживает ли драйвер, ассоциированный с указанным DSN, API ODBC -функции SQLTables() и SQLColumns()
    SQLUSMALLINT bSQLTablesFunctionExists; // Объявили логическую величину
    SQLUSMALLINT bSQLColumnsFunctionExists; // Объявили логическую величину
    SQLGetFunctions(hdbc,
                    SQL_API_SQLTABLES,
                    &bSQLTablesFunctionExists);
    SQLGetFunctions(hdbc,
                    SQL_API_SQLCOLUMNS,
                    &bSQLColumnsFunctionExists);
    if(&bSQLTablesFunctionExists
       && bSQLColumnsFunctionExists){
       // Обе функции существуют
    }

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

  • SQLGetInfo() - возвращает информацию о драйвере ODBC и его возможностях. Например, с ее помощью можно выяснить версию драйвера ODBC, его название, а также уровни согласования API и SQL
  • SQLGetTypeInfo() - возвращает информацию о том, какие типы данных поддерживаются применяемым источником данных. Драйвер возвращает информацию в формате результирующего набора данных ( result set )
Подготовка к выполнению SQL-запросов

Последнее, что приложение должно сделать после подключения к источнику данных, но прежде, чем оно будет способно осуществлять запросы SQL, - это получить дескриптор оператора ( statement handle ) или hstmt. Чтобы получить дескриптор hstmt, достаточно выполнить такой код

SQLSTMT hstmt;
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_STMT,
                              hdbc,
                              &hstmt);

Как только приложение получит дескриптор hstmt, оно сможет передавать источнику данных операторы SQL.

SQL-запросы на получение выборки данных

Существуют два разных способа организации выполнения источником данных запроса SQL.

Способ 1
LPCSTR szSQL = "INSERT INTO MyTable "
               "VALUES('UserID', 'USER NAME', 0)";
SQLRETURN rc;         
rc = ::SQLExecDirect(hstmt, (unsigned char*)szSQL, SQL_NTS);

Данная функция добавит в таблицу MyTable базы данных MyBase новую строку данных со значениями полей ("UserID", "USER NAME", 0)

Способ 2

Здесь подразумевается предварительная подготовка SQL -запроса при помощи функции SQLPrepare() и последующее его выполнение при помощи функции SQLExecute().

LPCSTR szSQL = "INSERT INTO MyTable "
               "VALUES('UserID', 'USER NAME', 0)";
SQLRETURN rc;      
rc = ::SQLPrepare(hstmt, (unsigned char*)szSQL, SQL_NTS);   
if(rc != SQL_SUCCESS){
   // Плохо! Сообщить пользователю или принять решение! 
   // Дальше не идти!
}  
            
rc = ::SQLExecute(hstmt);   
if(rc != SQL_SUCCESS){
   // Плохо! Сообщить пользователю или принять решение! 
   // Дальше не идти!
}

Второй способ иногда может быть эффективней потому, что выражение SQL будет откомпилировано один раз, а затем может многократно использоваться для выполнения одного и того же запроса.

Получение данных

При выполнении SQL -запроса на получение данных приложению возвращается результирующий набор данных ( result set ), из которого приложение должно выбирать данные и передавать их в переменные для обработки и представления пользователю. Для решения этой задачи предусмотрен ряд API ODBC -функций, таких как

  • SQLNumResultCols() - определяет количество и тип полей возвращенных данных в результирующем наборе
  • SQLGetData() - извлекает значение определенного поля
  • SQLBindCol() - ассоциирует переменные приложения со столбцами базы данных
  • SQLFetch() - передает данные из набора в переменные и передвигает курсор результирующего набора данных на следующую запись

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

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

Отключение от источника данных

Как только подключение к источнику данных станет ненужным, от него необходимо отключиться при помощи функции SQLDisconnect(), а также освободить все дескрипторы, которые были для этого созданы. Освобождение дескрипторов должно выполняться в порядке, обратном их созданию. Соответствующий код может выглядеть так

if(henv){
  if(hdbc){
    if(bIsConnected){
      if(hstmt){
        ::SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
      }
      ::SQLDisconnect(hdbc);
      bIsConnected = FALSE;
    }
    ::SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
    hdbc = NULL;
  }
  ::SQLFreeHandle(SQL_HANDLE_ENV, henv);
  henv = NULL;
}

Настройки мастера создания приложения для автоматической поддержки ODBC

Правильная настройка мастера создания приложений с поддержкой ODBC позволяет сгенерировать значительное количество кода автоматически. Кратко рассмотрим некоторые параметры мастера.

Нужно создать новый проект на основе MFC


Установить тип документа SDI или MDI


Вкладка Database Support

None (Отсутствует) - мастер не будет создавать никакого кода, имеющего отношение к базам данных.

Header files only (Только файлы заголовка) - сделает доступным переключатель, позволяющий выбрать применяемую приложением технологию доступа к базе данных. Выбор параметра ODBC приведет к включению мастером в состав проекта заголовка AFXDB.H и добавит в проект все необходимые подключаемые баблиотеки. Выбор параметра OLE DB приведет к подключению таких файлов заголовка, как ATLBASE.H, AFXOLEDB.H, ATLPLUS.H.

Database view without file support (Представление базы данных без файловой поддержки) - приведет к включению в состав приложения соответствующих заголовочных файлов, подключаемых библиотек, представления записей и класса набора записей. Через кнопку Data Source (Источник данных) можно выбрать ODBC DSN или OLEDB Provider, а также используемую таблицу или представление. Параметры Dynaset (Динамический набор) или Snapshot (Моментальная выборка) позволяют выбирать тип логического курсора для результирующего набора данных. Параметр Bind all columns (Связать все поля) - включает поддержку механизма обмена данными RFX ( Record Field Exchange - обмен данными с полями записи) между переменными представления и полями базы. Это аналог DDX для баз данных.

Database view with file support (Представление базы данных с файловой поддержкой) - обеспечивает способность полученному коду сохранять документ.

Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .