Студопедия

Главная страница Случайная страница

КАТЕГОРИИ:

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Загальні відомості про ODBC






Використання баз даних та основаних на них інформаційних систем є чи не найпопулярнішою галузею використання комп’ютерів. Звичайно, кожен з програмістів може стверджувати, що будь-яка мова програмування здатна у власний спосіб подавати та зберігати дані. І це правда, бо під базами даних розуміють масиви інформації, структурованої у той чи інший спосіб. Далі можна додати, що з часом виникла проблема більш стандартизованого підходу до створення баз даних аж до створення стандартних форматів зберігання інформації і розробки спеціального типу програмного забезпечення – систем керування базами даних. Більш того, для забезпечення створення, зберігання та обробки даних у спосіб, однаковий для різних системних платформ розроблена спеціальна мова – SQL (Stuctured Query Language – мова структурованих запитів).

Системи керування базами даних є корисним видом забезпечення. Однак, інколи необхідно забезпечувати створення власних інтерфейсів обробки баз даних або включати БД, як елемент власного програмного забезпечення. З цієї причини, сучасні системи містять засоби підтримки стандартизованих інтерфейсів баз даних. Зокрема система програмування Borland С++ Builder включає Borland Database Engine (BDE) із відповідним набором програм та бібліотек. У свою чергу, Microsoft Visual С++ може використати базовий для Microsoft Windows відкритий інтерфейс доступу до баз даних – ODBC (Open Database Connectivity), також систему класів DAO (Data Access Objects) та ADO (ActiveX Data Objects).

Метою цього розділу є розгляд прикладу простої програми, що забезпечуватиме доступ до бази даних.

ODBC є програмним інтерфейсом, що дозволяє програмі звертатися до файлів баз даних різного типу, використовуючи структуровану мову запитів SQL. Застосовуючи ODBC, розробник може створювати програми, незалежні від архітектури БД. Згідно з MSDN, ODBC забезпечує роботу з такими типами баз даних:

− SQL Server;

− Microsoft Access;

− Microsoft FoxPro;

− Microsoft Excel;

− dBASE;

− Paradox;

− Microsoft Oracle ODBC;

− Text files.

Архітектура ODBC складається з чотирьох компонентів: програма користувача, менеджер драйверів ODBC, драйвер, джерело даних. Менеджер драйверів написано у вигляді динамічно-зв’язуваної бібліотеки (DLL), що завантажується програмою користувача і здійснює перенаправлення викликів функцій ODBC API до потрібного драйвера (стандартне вікно адміністратора наведено на рисунку 10.1). У свою чергу драйвер виконує основну роботу із виконання запитів до бази даних.

 

Рисунок 10.1 – Вигляд вікна програми “Адмініністратор ODBC”

 

Джерело даних (Data Source) є логічним ім’ям бази даних, що використовується для звернення до неї засобами ODBC.

 

10.2 Основні класи і функції забезпечення роботи з БД

 

Типова схема взаємодії програми з базою даних складається з трьох кроків:

1) встановлення з’єднання з БД;

2) виконання запитів на вибір або зміну даних БД;

3) роз’єднання.

MFC містить набір класів, що значно спрощують роботу з ODBC API. Два з них мають найважливіше значення: CDatabase та CRecordset. Хоча ці класи виконують усі основні операції з вибору та модифікації даних, інколи їх можливостей недостатньо. У цьому випадку доводиться викликати функції ODBC API у прямий спосіб (усі вони мають спеціальний префікс SQL) [6].

Під час використання ODBC API програміст має аналізувати значення функцій, що повертаються у процесі роботи, звертаючись за більш детальною інформацією до функції SQLError. MFC надає таку можливість, хоча і приховує деталі реалізації. У випадку виникнення помилки, MFC створює виключення, що наша програма має перехоплювати. Обробник виключення отримує покажчик на структуру CDBException, що містить усю необхідну інформацію. Так, зокрема поле m_strError містить опис помилки у зрозумілому вигляді, а поле m_strStateNativeOrigin містить п’ятисимвольний код стану ODBC. Типовий приклад коду обробки виключних ситуацій виглядає у такий спосіб:

 

try { // відкриття та обробка БД}

catch(CDBException *pException)

{ AfxMessageBox(pException-> m_strError); // повідомлення про помилку

pException-> Delete(); // вилучення структури з пам’яті

}

 

Реєстрація джерел даних здійснюється за допомогою вже згадуваної програми – адміністратора даних ODBC.

З’єднання з базою даних у MFC здійснюється за допомогою об’єкта класу CDatabase. Для встановлення з’єднання слід використати функцію CDatabase:: OpenEx(). Вона має такий прототип:

 

BOOL CDatabase:: OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions = 0);

 

Тут lpszConnectString – рядок підключення, у якому пари “параметр-значення” розділені знаком “; ”. Імена параметрів не залежать від регістру. До стандартного набору параметрів входять: DSN (data source name – ім’я джерела даних), UID (ім’я користувача), PWD (пароль) та DRIVER (драйвер ODBC). Більшість драйверів розпізнає ряд додаткових параметрів. Наприклад, якщо у явний спосіб задати lpszConnectString як рядок " DSN=База данных MS Access" (для російськомовного варіанта MS Access) або " DSN=MS Access Database" (для англомовного) відбуватиметься стандартний діалог відкриття файлів цієї популярної СКБД. Якщо ж рядок не вказувати, ODBC-адміністратор запитає про тип DNS та про розташування файла БД.

Другий параметр dwOptions функції OpenEx – є набором бітових прапорців, об’єднаних логічним “Або”. Серед них такі:

CDatabase:: openReadOnly – відкрити БД у режимі " тільки для читання";

CDatabase:: noOdbcDialog – ніколи не виводити діалог, що запитує додаткову інформацію про з’єднання;

CDatabase:: forceOdbcDialog – завжди виводити діалог, що запитує інформацію про з’єднання. Цей режим підходить, якщо під час написання програми невідомо з якою саме базою даних вона взаємодіятиме.

За замовчуванням (якщо другий параметр OpenEx () не заданий або дорівнює 0) база даних відкривається у режимі “читання та запис”, а діалог з’являється тільки у випадку, коли в рядку стану з’єднання відсутні необхідні пара-метри (наприклад, ім’я джерела даних).

Використовуючи ODBC, програма отримує дані з БД, у вигляді множини записів (recordset). Кожен запис містить набір полів. У будь-який заданий момент часу програма може опрацьовувати лише один запис (що називається поточним). Використовуючи функції ODBC, можна переміщуватися від одного запису до іншого. Для забезпечення ефективної роботи програми кожне поле зазвичай пов’язують зі змінною, що використовується для читання та модифікації значення відповідного поля, хоча цей спосіб не є обов’язковим.

У MFC робота з множиною записів забезпечується за допомогою класу CRecordset. Як правило, цей клас не використовується безпосередньо. Зазвичай необхідно створювати свій власний клас записів, змінні-члени якого зв’язуються з полями множини записів.

Множина записів створюється функцією CRecordset:: Open(), а переміщення від одного запису до іншого здійснюється за допомогою функцій Move(), MoveNext(), MovePrev(), MoveLast(), MoveFirst().

Зазначимо, що для забезпечення роботи з базою даних необхідно встановити з’єднання з нею. Цей процес відбувається із використанням вже згадуваної функції CDatabase:: OpenEx(). Другим кроком необхідно поєднати покажчик на відкриту базу даних із об’єктом класу записів. Третім кроком і є використання функції CRecordset:: Open(). Використання функції Open() демонструє приклад 10.1.

 

Приклад 10.1 – Послідовність відкриття бази даних

// Виклик діалогу пошуку файла бази даних

if(m_db.OpenEx(" DSN=База данных MS Access")) // Російськомовний MSAccess

{rec.m_pDatabase=& m_db; // зв’язування запису та об’єкта БД

try {rec.m_nFields=8; // вказується кількість полів БД

rec.Open(CRecordset:: dynaset, " SELECT * FROM TABLE1");

rec.Move(0);

}

catch(CDBException *cdb) { AfxMessageBox(cdb-> m_strError); cdb-> Delete(); }

}

 

Функція CRecordset:: Open() має такий прототип:

 

BOOL CRecordset:: Open(UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE,

LPCTSTR lpszSQL = NULL, DWORD dwOptions = none);

 

Перший параметр nOpenType задає тип результуючої множини записів. Зокрема, задаються такі типи:

CRecordset:: forwardOnly – множина записів, доступна тільки для читання, у якій можна переміщуватися тільки вперед;

CRecordset:: snapshot – множина записів, у якій можна переміщуватися у будь-якому напрямку; зміни, внесені у БД після створення такої множини записів, не відображуються;

CRecordset:: dynaset – схожий не попередній, але будь-які зміни записів БД будуть наявні тільки після повторного звернення до запису, а нові записи, додані у БД після створення такої множини, не відображуються.

CRecordset:: dynamic – найбільш ресурсоємний варіант, у якому зміни внесені у БД після її відкриття, одразу ж і будуть відображені. Не підтримується багатьма драйверами.

Якщо драйвер не підтримує запитуваний тип множини записів, MFC створює ситуацію обробки виключної ситуації.

Другий параметр lpszSQL функції CRecordset:: Open() використовується для передавання імені таблиці або запиту до БД, на основі якого будується необхідна множина записів. MFC самостійно визначає, які саме параметри передані. Зокрема у прикладі 10.1 рядок " SELECT * FROM TABLE1" означає SQL-запит на вибір усіх полів таблиці Table1. Параметр lpszSQL можна використати для розміщення усіх необхідних SQL-запитів.

Третій параметр функції Open() зазвичай не вказується, хоча за його допомогою можна встановити режим роботи із записами БД, наприклад CRecordset:: appendOnly – дозволити тільки додавання записів (редагування і вилучення забороняються), CRecordset:: readOnly – лише читання записів.

Після того як множину записів створено, її використання виглядає досить простим. Для звернення до полів поточного запису використовуються змінні-члени похідного від СRecordset класу, наприклад класу CMRecordset, (наведено у прикладі 10.2), що містить дані про БД персоналу - m_Code – номер запису, m_Name – ім’я, m_Age – вік, m_Occupation – рід занять (місце роботи):

 

Приклад 10.2 – Реалізація класу записів БД персоналу

class CMRecordset: public CRecordset

{ public: long m_Code; // поле номерів записів

CString m_Name; // поле із іменем

int m_Age; // поле віку

CString m_Occupation; // поле із описом місця роботи

void DoFieldExchange(CFieldExchange *pFX);

CMRecordset();

};

 

Доступ до наступного, попереднього, першого та останнього записів БД може забезпечуватися за допомогою однієї з Move() -функцій:

 

void CRecordset:: MoveNext(); // перехід до наступного запису

void CRecordset:: MovePrev(); // перехід до попереднього запису

void CRecordset:: MoveFirst(); // перехід до першого запису

void CRecordset:: MoveLast(); // перехід до останнього запису

 

Коли усі записи вичерпано, функція CRecordset:: IsEOF() повертає TRUE, і цикл обробки переривається.

Зв’язування бази даних та класу запису відбувається за допомогою вірту-альної функції CRecordset:: DoFieldExchange(), що залежить від структури полів бази даних і тому має перевизначитися у класі запису користувача. Ця функція, власне, здійснює обмін даними між полями запису та змінними класу.

 

void CRecordset:: DoFieldExchange(CFieldExchange* pFX);

 

Не випадково, що приклад 10.2 також містить оголошення прототипу цієї функції. Реалізація DoFieldExchange() наведена у прикладі 10.3.

 

Приклад 10.3 – Реалізація функції DoFieldExchange()

void CMRecordset:: DoFieldExchange(CFieldExchange *pFX)

{ pFX-> SetFieldType(CFieldExchange:: outputColumn);

RFX_Long(pFX, _T(" [Сode]"), m_Code);

RFX_Text(pFX, _T(" [Name]"), m_Name);

RFX_Int(pFX, _T(" [Age]"), m_Age);

RFX_Text(pFX, _T(" [Oppupation]"), m_Occupation); }

Першим рядком функції є виклик SetFieldType() з параметром CFieldExchange:: outputColumn. Цей виклик завжди передує операціям зв’язування. Сам механізм нагадує механізм обміну даними між програмою та елементами керування діалогового вікна. В обох випадках використовується набір спеціальних макросів, які у залежності від контексту (що визначається об’єктом класі CFieldExchange) і виконують різні дії – у нашому випадку формують елементи запиту, пов’язують змінні з полями множини записів та здій-снюють обмін між ними.

Власне, операції обміну у DoFieldExchange() здійснюються за допомогою спеціальних макросів. Кожен макрос має префікс " RFX_". Існує декілька версій цих макросів – по одному на кожен основний тип. Набори параметрів є дещо відмінними, але перші три параметри співпадають в усіх макросах: покажчик на об’єкт класу CFieldExchange (передається покажчик, який надійшов у функцію DoFieldExchange()), ім’я поля у множині записів, посилання на змінну, що надалі зв’язуватиметься з цим полем. Наприклад рядок:

 

RFX_Long(pFX, _T(" [Code]"), m_Code);

 

Означає, що обмін даними типу long (це видно з суфікса _Long) здійснюється між полем БД “Code” та змінною m_Code класу CMRecordset. Інші типи функцій обміну даними наводяться у таблиці 10.1.

 

Таблиця 10.1 – Функції обміну даними та їх відповідні типи

Функція Тип даних
RFX_Bool BOOL
RFX_Byte BYTE
RFX_Binary CByteArray
RFX_Double double
RFX_Single float
RFX_Int Int
RFX_Long long
RFX_LongBinary CLongBinary
RFX_Text CString
RFX_Date CTime

 

Таким чином, за допомогою функції DoFieldExchange() програма здійснює обмін між полями бази даних та змінними класу CRecordset.

Обмін даними є необхідним у декількох випадках: під час відкриття бази даних, під час зчитування, редагування та додавання даних.

За допомогою методів класу CRecordset можна змінювати записи у таблиці БД і додавати нові записи. Насамперед слід упевнитися, що відкрита множина записів допускає саму операцію додавання. Це можна перевірити за допомогою функції CRecordset:: CanUpdate(). Сама модифікація починається викликом функції CRecordset:: Edit() і завершується викликом функції CRecordset:: Update(). Між цими двома викликами слід змінити значення змінних, пов’язаних з полями множини записів. Наприклад:

 

if(rec.CanUpdate())

{ rec.Edit();

rec.m_Code = 1;

rec.m_Name = “Мирослава Сопілка”;

rec.m_Age = 35;

rec.m_Occupation = “поетеса”;

rec.Update(); }

 

В аналогічний спосіб можна додавати і нові записи, але при цьому замість функції Edit() використовувати AddNew(). Також не слід зневажати перевірку підтримки додавання множиною записів, що можна перевірити за допомогою функції CRecordset:: CanAppend(). Наприклад:

 

if(rec.CanAppend())

{ rec.AddNew();

rec.m_Code = 2;

rec.m_Name = “Петро Могила”;

rec.m_Age = 65;

rec.m_Occupation = “Митрополит Київський”;

rec.Update(); }

 

Для оновлення множини записів після внесення змін до БД, слід викликати функцію CRecordset:: Requery().

Вилучення записів відбувається також у дуже простий спосіб. Знаходячись у поточному записі, достатньо викликати функцію CRecordset:: Delete():

 

rec.Delete();

 

Важливим кроком роботи з БД є виведення записів на екран. При цьому можна використовувати доступ до полів запису у спосіб, наведений вище, також можна використати властивості спеціального класу СDBVariant.

Клас СDBVariant не має базового класу і містить лише конструктор та поля, що за змістом відповідають основним типам даних, що використовуються у БД. Дію об’єкта класу можна пояснити у такий спосіб: якщо дані поля БД отримані об’єктом типу СDBVariant, доступ до них можна здійснити, звернувшись до змінної-члена класу, відповідного до типу отриманих даних. Перелік полів наведено у таблиці 10.2.

 

Таблиця 10.2 – Змінні-члени класу СDBVariant

Ім’я змінної-члена Коментар до вмісту поля
m_dwType містить тип даних значення, що зберігається, тип DWORD.
m_boolVal містить значення типу BOOL.
m_chVal містить значення типу unsigned char
m_iVal містить значення типу short.
m_lVal містить значення типу long.
m_fltVal містить значення типу float.
m_dblVal містить значення типу double
m_pdate містить покажчик на об’єкт типу TIMESTAMP_STRUCT
m_pstring містить покажчик на об’єкт типу CString.
m_pbinary містить покажчик на об’єкт типу CLongBinary

Отримання доступу до полів БД здійснюється за допомогою функції GetFieldValue(). Вона має декілька прототипів:

 

void CRecordset:: GetFieldValue(LPCTSTR lpszName, CDBVariant& varValue,

short nFieldType = DEFAULT_FIELD_TYPE);

void CRecordset:: GetFieldValue (short nIndex, CDBVariant& varValue,

short nFieldType = DEFAULT_FIELD_TYPE);

void CRecordset:: GetFieldValue(LPCTSTR lpszName, CString& strValue);
void CRecordset:: GetFieldValue(short nIndex, CString& strValue);

 

Параметрами тут є lpszName – ім’я поля БД; varValue – посилання на об’єкт типу CDBVariant, що зберігатиметься у цій змінній; nFieldType – ODBC тип поля (за замовчуванням тип поля визначається запитом SQL під час відкриття БД); nIndex – числовий індекс поля; strValue – посилання на об’єкт типу CString. Програмно доступ до поля поточного запису rec БД може виглядати так:

 

char str[255]; // оголошення рядка тексту

CDBVariant var; // оголошення змінної CDBVariant типу

short index=0; // встановлення індексу поля - 0

rec.GetFieldValue(index, var); // отримання значення var поля index запису rec

// отримання значення типу long з var та копіювання його до рядка str

wsprintf(str, " %d ", var.m_lVal);

IdEd-> SetWindowText(str); // копіювання рядка до елемента CEdit

 

Останнім етапом роботи з БД є розрив з’єднання. Це простий, але дійсно необхідний етап. Після закінчення роботи з джерелом даних, програма має розірвати з’єднання викликом CDatabase:: Close(). Перед цим необхідно також закрити усі набори записів, використовуючи CRecordset:: Close(). Обидві функції не потребують параметрів:

 

rec.Close();

m_db.Close();

 

10.3 Приклад розробки програми для роботи з базою даних

 

З точки зору користувача програми байдуже яким чином програміст здобуває інформацію з БД і які функції при цьому використовує. Головна мета програміста в тому, щоб користувач мав якісні та зручні засоби створення, редагування та обробки даних і не мав ніяких неприємностей через те, що певні функції програми з незрозумілих причин спрацьовують невдало. Інтерфейс програми має бути простим, прозорим і ефективним.

З практичної точки зору, розробка програм обробки даз даних не є надто важкою. Щоправда, ваша програма таки міститиме увесь необхідний для розробки набір класів: клас бази даних, клас запису, клас прикладки, клас головного або діалогового вікна.

Під час розробки бази даних нам доводиться упевнитися у корисності концепції “документ - вигляд”. У випадку бази даних ця концепція демонструється у дуже простий спосіб: інформація, що міститься у базі даних міститься у зовнішньому файлі, тобто є окремим документом; з іншого боку виведення інформації бази даних у вікно програми є засобом подання документа у певній послідовності, тобто має на меті забезпечення певного вигляду документа. В програмі, що описується нижче, концепція “документ - вигляд” присутня як ідея, хоча класи – нащадки CDocument та CView не використовуються.

Як приклад розглянемо програму, що здійснює відкриття бази даних “Країни світу” і містить відповідну довідкову інформацію.

Для побудови програми із обробки бази даних виконаємо таку послідовність кроків:

1) за допомогою програми MS Access створити базу даних нескладної структури (приклад наведено на рисунку 10.2) та задати ім’я таблиці „Table1”; як видно з рисунка у базі даних містяться поля: ID (порядковий номер, тип – long), Country (назва країни, тип – рядок символів), Location (частина світу, де розташовується країна, тип – рядок символів), Square (площа території у км2, тип – long), Population (кількість населення, тип – long), Capital (столиця, тип – рядок символів), OfficialLanguage (державна мова, тип – рядок символів) та PersonIncom (дохід на душу населення, тип – long);

2) створити новий проект типу “Win32 Application” із опцією “Empty project”, задати ім’я проекту;

3) у звичному режимі створити класи головного та діалогового вікон програми, прикладки (із функцією InitInstance);

4) для відображення полів бази даних створити ресурс діалогового вікна із кнопками навігації, що забезпечуватимуть перехід від одного запису бази даних до іншого та елементами редагування, які виводитимуть інформацію відповідних полів бази даних (вигляд шаблону ресурсу наведено на рисунку 10.3);

5) створити клас бази даних (похідний від СDatabase, наприклад CMDatabase), клас відображення записів (похідний від CRecordset, наприклад CMRecordset); оголосити члени класу записів, відповідні полям баз даних та функцію DoFieldExchange() обміну між базою даних та записом відповідно прикладам 10.4 та 10.5.

 

Приклад 10.4 – Класи бази даних та запису програми обробки БД

 

class CMDatabase: public CDatabase

{public: CMDatabase();

virtual ~CMDatabase(); };

 

class CMRecordset: public CRecordset

{public: long m_Id, m_Square, m_Population, m_PersonIncome; // поля типу long

CString m_Country, m_Location, m_Capital, m_OfficialLanguage; // поля рядків

virtual void DoFieldExchange(CFieldExchange *pFX); // функція обміну

CMRecordset(CMDatabase *pDatabase=NULL): CRecordset(pDatabase){}

};

6) реалізувати функцію DoFieldExchange() обміну класу CMRecordSet програми із полями БД у такий вигляд:

 

Приклад 10.5 – Організація обміну між програмою та БД

 

void CMRecordset:: DoFieldExchange(CFieldExchange *pFX)

{ pFX-> SetFieldType(CFieldExchange:: outputColumn);

RFX_Long(pFX, _T(" [ID]"), m_Id);

RFX_Text(pFX, _T(" [Country]"), m_Country);

RFX_Text(pFX, _T(" [Location]"), m_Location);

RFX_Long(pFX, _T(" [Square]"), m_Square);

RFX_Long(pFX, _T(" [Population]"), m_Population);

RFX_Text(pFX, _T(" [Capital]"), m_Capital);

RFX_Text(pFX, _T(" [OfficialLanguage]"), m_OfficialLanguage);

RFX_Long(pFX, _T(" [PersonIncome]"), m_PersonIncome); }

 

 

Рисунок 10.2 – Вигляд таблиці бази даних у програмі Microsoft Access.

Вміст бази основано на інформації, взятої з [11]

 

 

Рисунок 10.3 – Вигляд шаблону діалогового вікна програми обробки БД

 

7) для забезпечення роботи ODBS додати директиву препроцесора

#include < afxdb.h>

8) оголосити глобальними змінні класів CMRecordset та CMDatabase:

 

CMRecordset rec;

CMDatabase m_db;

 

9) за допомогою пункта “Open” меню головного вікна програми забезпечити виведення на екран модального діалогового вікна, що міститиме інформацію про поля бази даних:

 

void CMainWin:: OnOpen()

{CSample countryTable(IDD_DIALOG, this);

countryTable.DoModal();

rec.Close(); m_db.Close(); // закриття БД

}

 

10) безпосереднє відкриття бази даних реалізувати під час ініціалізації діалогового вікна одночасно із ініціалізацією необхідних елементів керування діалогового вікна (кнопки навігації та редагування БД, елементи реданування – виведення полів БД) у спосіб, показаний у прикладі 10.6;

 

Приклад 10.6 – Відкриття бази даних у функції ініціалізації діалогу

 

BOOL CSample:: OnInitDialog()

{ CDialog:: OnInitDialog();

IdEd=(CEdit *)GetDlgItem(IDC_ID); // поле номеру запису

CountryEd=(CEdit *)GetDlgItem(IDC_COUNTRY); // поле назви країни

LocationEd=(CEdit *)GetDlgItem(IDC_LOCATION); // поле розташування країни

SquareEd=(CEdit *)GetDlgItem(IDC_SQUARE); // поле площі території

PopulationEd=(CEdit *)GetDlgItem(IDC_POPULATION); // поле кількості населення

CapitalEd=(CEdit *)GetDlgItem(IDC_CAPITAL); // поле назви столиці

OfficialLanguageEd=(CEdit *)GetDlgItem(IDC_OFFICIALLANGUAGE); // поле держ. мови

PersonIncomeEd=(CEdit *)GetDlgItem(IDC_PERSONINCOME); // поле доходу населення

bt1=(CButton*)GetDlgItem(IDC_BUTTON1); // ідентифікатори кнопок

………………………………………………..

bt7=(CButton*)GetDlgItem(IDC_BUTTON7);

if(m_db.OpenEx(" DSN=База данных MS Access")) // Російськомовний варіант MS Access

// if(m_db.OpenEx(" DSN=MS Access Database")) // Англомовний MS Access

{ rec.m_pDatabase=& m_db;

try { rec.m_nFields=8; // пропуск указання кількості полів є частою помилкою!

rec.Open(CRecordset:: dynaset, " SELECT * FROM TABLE1");

rec.Move(0); // перехід до запису “0” БД

ButtonLock(false); // блокування клавіші підтвердження додавання запису

ShowRecord(); // виведення запису “0”

}

catch(CDBException *cdb) {AfxMessageBox(cdb-> m_strError); cdb-> Delete(); }

} return TRUE;

}

Додатково зазначимо, що для забезпечення відкриття бази, закритої раніше із певним паролем (у монопольному режимі відкриття (див. пункт “Open” MS Access)) можна застосувати таку конструкцію:

 

if(m_db.OpenEx(" DSN=База даних MS Access; PWD=1122; UID=admin"))

 

де “ PWD ” – пароль, “ UID ” – ім’я користувача, m_db – глобальна змінна класу CMDatabase.

11) у класі діалогового вікна оголосити прототипи функцій ОnPrev(); OnNext(); OnTop(); OnEnd(); для переходу до попереднього, наступного, початкового та кінцевого записів, наприклад у такий спосіб:

 

void CSample:: OnTop(){rec.MoveFirst(); ShowRecord(); }

 

12) для відображення записів БД у діалоговому вікні, забезпечити створення спеціальної функції ShowRecord(), основний зміст якої зводиметься до звернення за вмістом поточного запису поля БД та виведення отриманої інформації у відповідне поле; приклад частини коду цієї функції наведено у прикладі 10.7:

 

Приклад 10.7 – Реалізація функції ShowRecord()

 

void CSample:: ShowRecord()

{ char str[255];

CDBVariant var; // змінна, здатна містити значення усіх типів полів БД

short index=0; // початок блоку коду обробки поля із індексом 0 (номер запису)

rec.GetFieldValue(index, var); // отримання даних, що містяться у полі 0

wsprintf(str, " %d ", var.m_lVal); // перетворення значення типу long (m_lVal) у рядок

IdEd-> SetWindowText(str); // копіювання рядка у поле IdEd

index=1; // початок обробки поля із індексом 1

rec.GetFieldValue(index, var); // отримання даних, що містяться у полі 1

CountryEd-> SetWindowText(*var.m_pstring); // копіювання текстового вмісту поля у CountryED

……………………………..

index=7;

rec.GetFieldValue(index, var);

wsprintf(str, " %d", var.m_lVal);

PersonIncomeEd-> SetWindowText(str);

}

 

13) для забезпечення повноцінної роботи описати функцію додавання записів у кінець БД; зазначимо, що додавання запису рекомендується розділити на дві частини: 1) заповнення полів редагування у діалоговому вікні – функція OnAdd(); 2) підтвердження додавання, що забезпечується натиснення кнопки „Підтвердити” із формальним присвоєнням значень полів редагування відпо-відним полям поточного запису – функція OnSubmit(); вказані функції можна реалізувати у спосіб, показаний у прикладі 10.8:

 

Приклад 10.8 – Реалізація функцій додавання записів у БД

 

void CSample:: OnAdd()

{rec.MoveLast(); // перехід в останній запис

if(rec.CanAppend()) // перевірка, чи можна додавати записи

{ ButtonLock(true); // блокування клавіш навігації

IdEd-> SetWindowText(" "); CapitalEd-> SetWindowText(" "); // очищення елементів редагування

CountryEd-> SetWindowText(" "); LocationEd-> SetWindowText(" ");

OfficialLanguageEd-> SetWindowText(" "); PersonIncomeEd-> SetWindowText(" ");

PopulationEd-> SetWindowText(" "); SquareEd-> SetWindowText(" ");

char str0[255];

while(! rec.IsEOF())rec.MoveNext(); // примусовий перехід в останній запис

kk=rec.m_Id+1; wsprintf(str0, " %d", kk); // присвоєння нового номера

IdEd-> SetWindowText(str0); // відображення номера в елементі редагування IdEd

MessageBox(" Заповніть кожне поле");

}}

void CSample:: OnSubmit()

{if(! CapitalEd-> GetWindowText(str1, sizeof(str1))||! CountryEd-> GetWindowText(str2, sizeof(str2))||

! LocationEd-> GetWindowText(str3, sizeof(str3))|| // перевірка наповненості елемента LocationEd

! OfficialLanguageEd-> GetWindowText(str4, sizeof(str4))||

! PopulationEd-> GetWindowText(str5, sizeof(str5))||! SquareEd-> GetWindowText(str6, sizeof(str6))||

! PersonIncomeEd-> GetWindowText(str7, sizeof(str7))) MessageBox(" Одне з полів не заповнено");

rec.AddNew(); // додавання нового запису

rec.m_Id=kk; rec.m_Capital=str1; rec.m_Country=str2; rec.m_Location=str3; // заповнення полів

rec.m_OfficialLanguage=str4; rec.m_Population=atoi(str5); // заповнення полів

rec.m_Square=atoi(str6); rec.m_PersonIncome=atoi(str7); // заповнення полів

rec.Update(); // оновлення записів БД

MessageBox(" Запис додано"); ButtonLock(false);

}

14) реалізувати функцію OnDelete() вилучення записів з БД:

 

void CSample:: OnDelete()

{ CapitalEd-> SetWindowText(" "); CountryEd-> SetWindowText(" "); LocationEd-> SetWindowText(" ");

OfficialLanguageEd-> SetWindowText(" "); PersonIncomeEd-> SetWindowText(" ");

PopulationEd-> SetWindowText(" "); SquareEd-> SetWindowText(" ");

rec.Delete(); MessageBox(" Запис вилучено");

}

Вигляд вікна розробленої програми наведено на рисунку 10.4, а повний її текст – у прикладі 10.9.

 

 

Рисунок 10.4 – Вигляд вікна програми обробки бази даних “Країни світу”

 

Приклад 10.9 – Повний текст програми обробки БД

 

// файл MainWin.h

class CMDatabase: public CDatabase

{public: CMDatabase();

virtual ~CMDatabase(); };

 

class CMRecordset: public CRecordset

{public: long m_Id, m_Square, m_Population, m_PersonIncome;

CString m_Country, m_Location, m_Capital, m_OfficialLanguage;

virtual void DoFieldExchange(CFieldExchange *pFX);

CMRecordset(CMDatabase *pDatabase=NULL): CRecordset(pDatabase){}

};

 

class CMainWin: public CFrameWnd

{public: void OnExit();

void OnOpen();

CMainWin();

DECLARE_MESSAGE_MAP() };

 

class CApp: public CWinApp

{public: BOOL InitInstance(); };

 

class CSample: public CDialog

{private: CEdit *IdEd, *SquareEd, *PopulationEd, *PersonIncomeEd, *CountryEd, *LocationEd, *CapitalEd, *OfficialLanguageEd;

CButton *bt1, *bt2, *bt3, *bt4, *bt5, *bt6, *bt7;

public: void ButtonLock(bool);

void OnSubmit();

void OnDelete();

void OnEnd();

void OnTop();

void OnNext();

void OnPrevious();

void ShowRecord();

BOOL OnInitDialog();

CSample(UINT id, CWnd *Owner): CDialog(id, Owner){ };

void OnAdd();

DECLARE_MESSAGE_MAP()

};

 

// файл MainWin.cpp

 

#include < afxwin.h>

#include < afxdb.h>

#include " MainWin.h"

#include " resource.h"

 

CMRecordset rec;

CMDatabase m_db;

 

CMainWin:: CMainWin()

{RECT r; r.top=r.left=100; r.bottom=r.right=400;

Create(NULL, " Країни світу”, WS_OVERLAPPEDWINDOW, r, NULL, MAKEINTRESOURCE(IDR_MENU));

}

CMainWin:: ~CMainWin(){ }

 

char str1[255], str2[255], str3[255], str4[255], str5[255], str6[255], str7[255];

long kk=0;

 

BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)

ON_COMMAND(IDM_FILE_OPEN, OnOpen)

ON_COMMAND(IDM_FILE_EXIT, OnExit)

END_MESSAGE_MAP()

 

BOOL CApp:: InitInstance()

{ m_pMainWnd=new CMainWin;

m_pMainWnd-> ShowWindow(m_nCmdShow);

m_pMainWnd-> UpdateWindow();

return TRUE; }

 

 

BEGIN_MESSAGE_MAP(CSample, CDialog)

ON_COMMAND(IDC_BUTTON1, OnTop) // Additional

ON_COMMAND(IDC_BUTTON2, OnPrevious) // Additional

ON_COMMAND(IDC_BUTTON3, OnNext) // Additional

ON_COMMAND(IDC_BUTTON4, OnEnd) // Additional

ON_COMMAND(IDC_BUTTON5, OnAdd) // Additional

ON_COMMAND(IDC_BUTTON6, OnDelete) // Additional

ON_COMMAND(IDC_BUTTON7, OnSubmit) // Additional

END_MESSAGE_MAP()

 

void CSample:: OnAdd()

{rec.MoveLast();

if(rec.CanAppend())

{ButtonLock(true);

IdEd-> SetWindowText(" "); CapitalEd-> SetWindowText(" "); CountryEd-> SetWindowText(" ");

LocationEd-> SetWindowText(" "); OfficialLanguageEd-> SetWindowText(" ");

PersonIncomeEd-> SetWindowText(" "); PopulationEd-> SetWindowText(" "); SquareEd-> SetWindowText(" ");

char str0[255];

while(! rec.IsEOF())rec.MoveNext();

kk=rec.m_Id+1;

wsprintf(str0, " %d", kk);

IdEd-> SetWindowText(str0);

MessageBox(" Заповніть кожне поле ");

}}

 

CApp App;

void CMainWin:: OnOpen()

{CSample countryTable(IDD_DIALOG, this); countryTable.DoModal();

rec.Close(); m_db.Close(); }

 

void CMainWin:: OnExit() { SendMessage(WM_CLOSE); }

 

BOOL CSample:: OnInitDialog()

{ CDialog:: OnInitDialog();

IdEd=(CEdit *)GetDlgItem(IDC_ID);

CountryEd=(CEdit *)GetDlgItem(IDC_COUNTRY);

LocationEd=(CEdit *)GetDlgItem(IDC_LOCATION);

SquareEd=(CEdit *)GetDlgItem(IDC_SQUARE);

PopulationEd=(CEdit *)GetDlgItem(IDC_POPULATION);

CapitalEd=(CEdit *)GetDlgItem(IDC_CAPITAL);

OfficialLanguageEd=(CEdit *)GetDlgItem(IDC_OFFICIALLANGUAGE);

PersonIncomeEd=(CEdit *)GetDlgItem(IDC_PERSONINCOME);

bt1=(CButton*)GetDlgItem(IDC_BUTTON1); // Additional

bt2=(CButton*)GetDlgItem(IDC_BUTTON2); // Additional

bt3=(CButton*)GetDlgItem(IDC_BUTTON3); // Additional

bt4=(CButton*)GetDlgItem(IDC_BUTTON4); // Additional

bt5=(CButton*)GetDlgItem(IDC_BUTTON5); // Additional

bt6=(CButton*)GetDlgItem(IDC_BUTTON6); // Additional

bt7=(CButton*)GetDlgItem(IDC_BUTTON7); // Additional

if(m_db.OpenEx(" DSN=База данных MS Access")) // Russian Language MS Access

// if(m_db.OpenEx(" DSN=MS Access Database")) // English Language MS Access

{ rec.m_pDatabase=& m_db;

try { rec.m_nFields=8; // missing that string is a popular mistake

rec.Open(CRecordset:: dynaset, " SELECT * FROM TABLE1");

rec.Move(0);

ButtonLock(false);

ShowRecord(); }

catch(CDBException *cdb) { AfxMessageBox(cdb-> m_strError); cdb-> Delete(); }

}

return TRUE;

}

 

CMDatabase:: CMDatabase(){ }

CMDatabase:: ~CMDatabase(){ }

 

void CSample:: ShowRecord()

{ char str[255]; CDBVariant var;

short index=0;

rec.GetFieldValue(index, var); wsprintf(str, " %d ", var.m_lVal); IdEd-> SetWindowText(str);

index=1; rec.GetFieldValue(index, var); CountryEd-> SetWindowText(*var.m_pstring);

index=2; rec.GetFieldValue(index, var); LocationEd-> SetWindowText(*var.m_pstring);

index=3; rec.GetFieldValue(index, var);

wsprintf(str, " %d", var.m_lVal); SquareEd-> SetWindowText(str);

index=4; rec.GetFieldValue(index, var);

wsprintf(str, " %d", var.m_lVal); PopulationEd-> SetWindowText(str);

index=5; rec.GetFieldValue(index, var); CapitalEd-> SetWindowText(*var.m_pstring);

index=6; rec.GetFieldValue(index, var); OfficialLanguageEd-> SetWindowText(*var.m_pstring);

index=7; rec.GetFieldValue(index, var);

wsprintf(str, " %d", var.m_lVal); PersonIncomeEd-> SetWindowText(str);

}

 

void CMRecordset:: DoFieldExchange(CFieldExchange *pFX)

{ pFX-> SetFieldType(CFieldExchange:: outputColumn);

RFX_Long(pFX, _T(" [ID]"), m_Id);

RFX_Text(pFX, _T(" [Country]"), m_Country);

RFX_Text(pFX, _T(" [Location]"), m_Location);

RFX_Long(pFX, _T(" [Square]"), m_Square);

RFX_Long(pFX, _T(" [Population]"), m_Population);

RFX_Text(pFX, _T(" [Capital]"), m_Capital);

RFX_Text(pFX, _T(" [OfficialLanguage]"), m_OfficialLanguage);

RFX_Long(pFX, _T(" [PersonIncome]"), m_PersonIncome); }

 

void CSample:: OnPrevious() {if(! rec.IsBOF())rec.MovePrev(); ShowRecord(); }

void CSample:: OnNext() {if(! rec.IsEOF())rec.MoveNext(); ShowRecord(); }

void CSample:: OnTop() {rec.MoveFirst(); ShowRecord(); }

void CSample:: OnEnd() {rec.MoveLast(); ShowRecord(); }

 

void CSample:: OnDelete()

{ CapitalEd-> SetWindowText(" "); CountryEd-> SetWindowText(" "); LocationEd-> SetWindowText(" ");

OfficialLanguageEd-> SetWindowText(" "); PersonIncomeEd-> SetWindowText(" ");

PopulationEd-> SetWindowText(" "); SquareEd-> SetWindowText(" ");

rec.Delete(); MessageBox(" Запис вилучено"); }

 

void CSample:: OnSubmit()

{ if(! CapitalEd-> GetWindowText(str1, sizeof(str1))||! CountryEd-> GetWindowText(str2, sizeof(str2))||

! LocationEd-> GetWindowText(str3, sizeof(str3))||! OfficialLanguageEd-> GetWindowText(str4, sizeof(str4))||

! PopulationEd-> GetWindowText(str5, sizeof(str5))||! SquareEd-> GetWindowText(str6, sizeof(str6))||

! PersonIncomeEd-> GetWindowText(str7, sizeof(str7))) MessageBox(" Одне з полів не заповнено");

rec.AddNew();

rec.m_Id=kk; rec.m_Capital=str1; rec.m_Country=str2; rec.m_Location=str3;

rec.m_OfficialLanguage=str4; rec.m_Population=atoi(str5); rec.m_Square=atoi(str6);

rec.m_PersonIncome=atoi(str7); rec.Update();

MessageBox(" Запис додано"); ButtonLock(false);

}

 

void CSample:: ButtonLock(bool lock)

{if(lock){bt1-> EnableWindow(FALSE); bt2-> EnableWindow(FALSE);

bt3-> EnableWindow(FALSE); bt4-> EnableWindow(FALSE);

bt5-> EnableWindow(FALSE); bt6-> EnableWindow(FALSE); bt7-> EnableWindow(); }

else {bt1-> EnableWindow(); bt2-> EnableWindow(); bt3-> EnableWindow(); bt4-> EnableWindow();

bt5-> EnableWindow(); bt6-> EnableWindow(); bt7-> EnableWindow(FALSE); }

}

 

10.4 Контрольні завдання

 

1. Пояснити схему взаємодії MFC-програми з базою даних.

2. Пояснити програмні особливості відкриття бази даних.

3. Пояснити реалізацію функції DoFieldExchange ().

4. Пояснити реалізацію функції ShowRecord (), наведеної у даному розділі.

5. Розробити програму, що забезпечує ведення бази даних про поточну успішність студентів.

6. Розробити програму, що забезпечує реалізацію запитів до бази даних із відомостями про країни світу на основі програми, наведеної у даному розділі.



Поделиться с друзьями:

mylektsii.su - Мои Лекции - 2015-2024 год. (0.118 сек.)Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав Пожаловаться на материал