Введение
Тема нашего сегодняшнего разговора - ODBC. Если вы хоть раз писали СУБД (системы
управления базами данных), то вам, наверное, знакома эта аббревиатура. Расшифровывается
ODBC как Open DataBase Connectivity (вольный перевод - открытая система связи
с базами данных). Open (открытая) в данном случае означает "расширяемая",
"легко наращиваемая", "открытая для модификаций". Вы сами
это поймете, если посмотрите на архитектуру построения ODBC:
Как легко заметить, она действительно легко наращиваемая. Для добавления нового
типа БД нужно лишь написать драйвер и зарегистрировать его. Еще одно преимущество,
вытекающее из такого построения ODBC - пользовательское приложение общается
с физической БД через менеджер драйверов, фактически ничего не зная о типе используемой
БД (общение менеджера драйверов и самих драйверов более менее стандартно, так
что с точки зрения пользователя все источники данных обладают практически одинаковыми
свойствами). Таким образом, вы легко можете поменять физический тип базы данных,
а приложение даже не узнает об этом (конечно, существуют исключения из-за особенностей
поддержки языка SQL различными типами БД, но они несущественны).
Физически ODBC представляет собой набор динамических библиотек DLL, которые
обслуживают подключение и работу с конкретным типом базы данных.При запросе
на подключение к определенной, заранее описанной базе "активизируется"
определенная DLL - драйвер этого типа БД. Обращение к определенной базе данных
происходит по имени так называемого источника данных ODBC (или DSN - data source
name). Можно представить DSN как своего рода объявление БД на данном компьютере,
причем DSN может быть пользовательским, т.е. находится в области видимости только
одного польователя, в сеансе которого был создан этот DSN, и системным, т.е.
видимым всеми пользователями данного компьютера.
Управление источниками данных ODBC (да и вообще настройкой всей системы ODBC)
осуществляется с помощью специальной программы - ODBC-администратора. Давайте
разберем, где его можно найти в различных версиях Windows:
- Windows 9х - сам исполняемый файл odbcad32.exe лежит в каталоге Windows\System.
Запускать его можно напрямую либо через Панель управления (значок "Источники
данных ODBC (32-бит)"). Если на компьютере установлен BDE, запустите администратор
BDE и из меню Object выберите пункт ODBC Administrator.
- Windows 2000 - исполняемый файл odbcad32.exe лежит в каталоге WinNT\System32,
а запускать его можно через Панель управления -> Администрирование ->
Источники данных ODBC. К сожалению, у меня нет возможности проверить, как с
ODBC в NT3.51 / 4 - не думаю, что там ребята из Microsoft сильно его спрятали.
Почему ODBC?
Ведь есть же BDE, скажете вы. Да, возможно вы правы. Однако здесь есть "но".
У обоих движков есть свои преимущества и недостатки. ODBC намного лучше работает
с "родными, от Microsoft" источниками данных продуктов Microsoft Office
- типа Access, Excel, MS SQL Server (вообще-то из-за Access я и затеял всю эту
писанину). Кроме того, в стандартный дистрибутив BDE, который можно на каждом
шагу найти в Интернете, я что-то ни разу не встречал SQL Links, впрочем, как
я ни разу не встречал и отдельного дистрибутива SQL Links. А именно после установки
SQL Links в BDE появлется возможность создавать алиасы к Access, MS SQL Server,
Oracle и др. типам баз данных. Устанавливать же полную версию C++Builder или
Delphi на компьютер пользователя может не каждый. А ODBC на "голой"
машине уже содержит драйверы Access, Excel, FoxPro и SQL Server. В общем, для
общего развития не помешает освоить и ODBC, тем более что access'овские базы,
с которыми мы будем работать через ODBC, очень надежны (не в пример стандартным
BDE'шным paradox'овым, у которых то и дело сыпятся индексы, а про такое поле
как счетчик я вообще молчу), практически на любом компьютере пользователя установлен
Office, что позволяет вносить коррективы "на месте", да и удобство
всей базы в одном файле тоже иногда сказывается.
Фнукции, с которыми нам придется работать
Естественно, я не буду рассказывать обо всех функциях, с помощью которых можно
сделать с ODBC все, что угодно. Поверьте, их очень много. Нам же для нашей практической
задачи достаточно будет всего четырех:
1. Функция SQLDataSources возвращает список всех источников данных ODBC.
RETCODE SQLDataSources(HENV hEnv, UWORD fDirection, UCHAR * szDSN,
SWORD cbDSNMax, SWORD *pcbDSN, UCHAR * szDescription,
SWORD cbDescriptionMax, SWORD * pcbDescription );
hEnv - хэндл, возвращаемый функцией SQLAllocEnv();
fDirection - задает режим работы. Может принимать значения SQL_FETCH_FIRST
- вернуть первую запись и SQL_FETCH_NEXT - вернуть следующую запись. Логика
работы аналогична функциям FindFirst и FindNext (помните DOS?);
szDSN - указатель на буфер длиной не менее cbDSNMax, куда будет записано имя
источника данных;
cbDSNMax - длина буфера szDSN;
pcbDSN - указатель на целое, куда записывается реальная длина (не включая завершающий
\0) принятого имени источника данных;
szDescription - указатель на буфер длиной как минимум 255 символов, куда запистывается
описание принятого источника данных;
cbDescriptionMax - длина буфера szDescription;
pcbDescripton - указатель на целое, куда записывается реальная длина описания
источника данных.
2. Функция SQLConfigDataSources добавляет, изменяет или удаляет указанный источник
данных.
BOOL SQLConfigDataSources(HWND hwndParent, WORD fRequest,
LPCSTR lpszDriver, LPCSTR lpszAttributes)
hwndParent - хэндл родительского окна. Если не равен NULL, отображается стандартный
для данного типа источника данных диалог настройки.
fRequest - тип запрашиваемой операции. Может принимать следующие значения:
ODBC_ADD_DSN (ODBC_ADD_SYS_DSN) - запрос на создание нового
пользовательского (системного) источника данных;
ODBC_CONFIG_DSN (ODBC_CONFIG_SYS_DSN) - запрос на изменение
существующего пользовательского (системного) источника данных;
ODBC_REMOVE_DSN (ODBC_REMOVE_SYS_DSN) - удаление пользовательского
(системного) источника данных.
lpszDriver - указатель на строку, содержащую имя драйвера, ассоциированного
с конкретным типом БД;
lpszAttributes - указатель на строку атрибутов в форме ключевое слово - значение.
Каждая такая пара должна заканчиваться символом \0, в конце строки атрибутов
должен стоять двойной символ \0. Приведем несколько примеров строки атрибутов,
чтобы вам было понятно, что это такое:
"DSN=MySource\0UID=MyLogin\0PWD=MyPass" - источник данных с именем
MySource, логин - MyLogin, пароль - MyPass
Каждый драйвер "понимает" свои ключевые слова, чаще всего применяются:
PWD - пароль
UID - логин
DSN - имя источника данных
DATABASE - имя базы данных
SERVER - имя сервера, на котором находится база данных
Для баз данных Microsoft Access для указания имени БД вместо DATABASE используется
ключевое слово DBQ.
3. Функция SQLAllocEnv создает новый хэндл для использования в других функциях
и инициализирует интерфейс для работы с ODBC
RETCODE SQLAlocEnv(HENV * hEnv)
hEnv - указатель на переменную, которая будет хранить созданный хэндл в случае
удачного выполнения функции
4. Функция SQLFreeEnv удаляет заданный хэндл и освобождает всю память, ассоциированную
с ним.
RETCODE SQLFreeEnv(HENV hEnv)
hEnv - хэндл, который нужно удалить
Исходный код
Сначала несколько замечаний. В C++Builder есть заголовочные файлы для работы
с ODBC. Нам нужно два из них - odbcinst.h и sql.h. Добавьте строки
#include <odbcinst.h>
#include <sql.h>
в начало вашего файла. Реализация функций, описанных в этих файлах, находится
в нескольких DLL, лежащих в каталоге Windows\System (или WinNT\System32). В
наших примерах будут использоваться две такие DLL, а именно odbc32.dll и odbccp32.dll.
Чтобы подключить их к проекту, нужно проделать следующие шаги:
1. Запускаем любой досовский файл-менеджер или командную строку Windows и переходим
в каталог Windows\System (WinNT\System32) и находим эти две библиотеки.
2. Если каталог BIN С++Builder'а прописан в переменной окружения PATH, просто
набираем
c:\windows\system\> implib odbc32.lib odbc32.dll
иначе набираем полностью путь к файлу implib.exe, либо копируем его в каталог,
прописанный в PATH.
3. То же самое проделываем с odbccp32.dll:
c:\windows\system\> implib odbccp32.lib odbccp32.dll
4. Полученные .lib - файлы копируем в каталог с проектом
5. Из меню Project выбираем пункт Add to Project... и поочередно добавляем
odbc32.lib и odbccp32.lib.
Все, теперь система сама загрузит эти DLL во время запуска программы.
При
мер 1:
Получаем список уже созданных источников данных ODBC.
const short SQL_MAX_DSN_LENGTH=255;
UCHAR szDSN[ SQL_MAX_DSN_LENGTH+1 ];
UCHAR szDescription[ 256 ];
short wDSNLen;
short wDesLen;
int retCode;
SQLHENV hEnv=NULL;
AnsiString DSNName;
SQLAllocEnv( &hEnv );
retCode = SQLDataSources( hEnv, SQL_FETCH_FIRST,
szDSN, SQL_MAX_DSN_LENGTH+1, &wDSNLen,
szDescription, 256, &wDesLen );
while( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
{
DSNName = (AnsiString)((char *)szDSN);
// -- получили имя DSN в переменной DSNName
retCode = SQLDataSources( hEnv, SQL_FETCH_NEXT,
szDSN, SQL_MAX_DSN_LENGTH+1, &wDSNLen,
szDescription, 256, &wDesLen );
}
SQLFreeEnv( hEnv );
Этот фрагмент кода просто перебирает все уже созданные источники данных. В
первой строке фрагмента после объявления переменных создается хэндл текущего
сеанса работы с ODBC, затем выбирается первое имя DSN. Если первое имя успешно
прочитано (retCode содержит константу SQL_SUCCESS), то в цикле выбирается следующий
DSN и т.д. до тех пор, пока не будет прочитан последний DSN. Если на место комментария
вставить код (ListBox1 -> Items -> Add(DSNName)), добавляющий прочитанное
имя в ListBox, например, то вы сможете просмотреть весь этот список. А если
на место комментария вставить что-нибудь типа
if (DSNName=='OurDSN') ...
то мы проверим, сможет ли наша программа работать нормально.
Пример 2:
Создаем новый источник данных.
В этом примере вы узнаете, как создать системный DSN для базы данных Access.
Предположим, файл с БД лежит в том же каталоге, что и программа, а его имя -
db1.mdb.
#include <sysutils.hpp>
WORD fRequest = ODBC_ADD_SYS_DSN;
UCHAR lpszDriver[] = "Microsoft Access Driver (*.mdb)";
UCHAR lpszAttributes[255] = "DSN=c2a;DBQ=";
bool retCode;
AnsiString CurDir;
CurDir = GetCurrentDir();
StrCat(lpszAttributes, CurDir.c_str());
StrCat(lpszAttributes, "\\db1.mdb");
retCode = SQLConfigDataSource(NULL, fRequest, lpszDriver, lpszAttributes);
if (retCode == true)
Application -> MessageBox("DSN успешно создан", "Сообщение", MB_OK);
else
{
Application -> MessageBox("Произошла ошибка во время создания DSN",
"Ошибка", MB_OK);
}
Как видите, здесь уже не нужны хэндлы и код достаточно ясный. В первых трех
строках - дописываем в строку атрибутов путь к файлу БД. Затем собственно создаем
источник. Обработка ошибок тривиальна.
Важное замечание: При создании DSN если такое имя уже есть, то не возвращается
ошибка, а создается DSN с текущими параметрами.
Пример 3:
Как все это собрать воедино и использовать в программе
Здесь кода не будет, здесь будет описание. Итак, у нас есть программа, которой
нужно "присоединиться" к базе Microsoft Access, лежащей совсем рядом,
в текущем каталоге. На машине пользователя нам не нужно ничего, кроме стандартного
набора bpl и BDE. Но - теперь не нужны SQL Links, можно использовать, например,
Install Shield, можно взять отдельный дистрибутив BDE. Поехали:
1. На основную форму проекта кидаем компоненту TDatabase. Устанавливаем ее
свойства: LoginPrompt - false, DatabaseName - dbMain, AliasName - c2a (имя источника
данных ODBC). Во всех компонентах TQuery или TTable свойство Database Name будет
равно dbMain. Конечно, можно не использовать компоненту TDatabase, а в TQuery
или TTable прямо указывать имя источника данных ODBC, но представьте, если вы
написаи проект формочек на двадцать-тридцать, а потом решили, что имя источника
'MySuperDSN' не в полной мере отражает всю вашу крутость :-) Изменять свойство
у полусотни компонент - очень муторное занятие, поверьте.
2. В конструкторе основной формы прописываем код примера ?1, вставляя на место
комментария следующий код:
if (t == "c2a")
{
Exists = true;
break;
}
а после цикла - такой фрагмент:
if (!Exists)
{
NewDSN();
}
3. Функция NewDSN() полностью повторяет код примера ?2.
Теперь при первом запуске программы будет создан источник данных ODBC Microsoft
Access с именем c2a, ссылающийся на файл db1.mdb, лежащий в том же каталоге,
что и программа.
4. На всякий непредвиденный случай (если вы переместили программу в другой
каталог, поменялась буква диска или сетевое имя, где лежит база данных) можно
в программе предусмотреть немедленное перезаписывание DSN по горячей клавише,
что-то типа такого:
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if (Shift.Contains(ssAlt) && Shift.Contains(ssCtrl))
if (Key==VK_F1)
{
if (Application -> MessageBox("Перезаписать DSN?",
"Вопрос",MB_YESNO)==IDYES)
{
NewDSN();
}
}
}
Здесь для перезаписи DSN используется комбинация Alt-Ctrl-F1
Ну, вот и все. Надеюсь, я чем-то вам помог.
Удачного программирования!
Литература по C & C++
|