Робота з СУБД Oracle використовуючи інтерфейс OCCI (исходники), Інтеграція додатків і даних, Бази даних, статті


Мінімальне використання OCCI


OCCI – розшифровується як Oracle C + + Call Interface і являє собою спеціалізоване апі для роботи з СУБД Oracle використовуючи C + + що в загальному то виявляється з назви. Для використання необхідно підключити заголовний файл “occi.h”.

Прийняті іменування об’єктів Environment * env;
Connection* conn;
Statement* stmt;
ResultSet* rs;
SQLException &sqlExcp;


Давайте розглянемо приклад невеликий тестової програми використовує інтерфейс OCCI:





#include 
#include “occi.h”

using namespace std;
using namespace oracle::occi;
#define db_user_name “test”
#define db_password “test”
#define db_conn_str “service”

main(int argc,char* argv[])
{
try
{
/* Створення середовища управління пам’яттю і ресурсами для об’єктів OCCI. Передбачається використання об’єктних розширень – Environment :: OBJECT
*/
Environment* env = Environment::createEnvironment(Environment::OBJECT);
/* Створення з’єднання з базою даних
*/
Connection* conn = env->createConnection(db_user_name, db_password, db_conn_str);
/* Робота із запитом
*/
Statement* stmt = conn->createStatement(“Select 1 from dual”);
/* Отримання результатів запиту rs-> getInt (номер_поля_начинается_единицы)
*/
ResultSet *rs = stmt->executeQuery();
int res = 0;
while (rs->next())
{
res = rs->getInt(1);
}
/* Звільнення ресурсів
*/
stmt->closeResultSet(rs);
env->terminateConnection(conn);
Environment::terminateEnvironment(env);
}
catch(SQLException &sqlExcp)
{
cerr << sqlExcp.getErrorCode() << ” ” << sqlExcp.getMessage() << endl;
}
return 0;
}


Збірка програми для Linux, зверніть увагу на шляху до заголовками (-I) та бібліотекам (-L)





g++ -L/opt/oracle/product/9ir2/lib -I/opt/oracle/product/9ir2/rdbms/demo  -I/opt/oracle/product/9ir2/rdbms/public
test.cpp -locci -lclntsh -o test

Наводжу бо на власному досвіді переконався що цей момент може викликати питання. Бібліотеки потрібні для складання програми:


Більш детальну інформацію можна отримати тут: Instant client.
Розглянемо базові об’єкти використовуються програмою.

Environment – призначений для створення середовища управління пам’яттю і ресурсами для інших об’єктів OCCI. Об’єкти створюються в рамках середовища, програма може мати кілька середовищ, середа може мати кілька з’єднань (навіть до різних баз) або пулів з’єднань. Доступні режими створення





enum Mode
{
DEFAULT = OCI_DEFAULT,
OBJECT = OCI_OBJECT,
SHARED = OCI_SHARED,
NO_USERCALLBACKS = OCI_NO_UCB,
THREADED_MUTEXED = OCI_THREADED,
THREADED_UNMUTEXED = OCI_THREADED / OCI_ENV_NO_MUTEX
};

Параметри:


Statement– Створення курсора для виконання запиту до бази, методи виконання запитів:


Дозволяє виконувати не тільки SQL-вирази але і PL / SQL (обрамлений BEGIN / END 😉

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


Робота з параметризованих запитами



Зазвичай запити містять змінні в своєму складі, наприклад у реченні WHERE. Для ефективної роботи з такими запитами використовуються параметризрвані запити. Звичайно, запит може бути і не параметризованих – Збиратися програмою динамічно, а потім подаватися для створення Statement – однак це не самий легкий (стосовно С + +) і що найголовніше далеко не оптимальний підхід.


Якщо ви розробляєте програми під Oracle то швидше за все в курсі деяких питань підвищення продуктивності додатків, одним з яких є застосування пов’язують змінних. Використання пов’язуються змінних зменшує кількість жорстких розборів sql-виразів, знижує навантаження на розділяється пул і в результаті може значно збільшити швидкість роботи. Аналогічна ситуація з багаторазовим використанням курсора – зменшується число м’яких розборів і т.д. – Мова зараз не про це. Повинен додати щойно у випадку якщо ви дізналися про роботу з єднальним змінними з того, що я написав вище – швидше за все вам слід почитати Т. Кайта “Oracle для професіоналів”, главу “Стратегії та засоби налаштування”.

Повернемося до справи – параметризований запит це sql-вираз на місці змінних (не обов’язково всіх) стоять ідентифікатори підставляємо значення – “: 1”, “: var”, “: data1”. Перед використанням їх треба зв’язати з конкретними величинами – це виробляється викликом методу stmt-> setXXX (setString, setInt, ….). Для більш швидкісний обробки можна використовувати stmt-> setDataBuffer, відмінність цього методу від setXXX в тому що методи setXXX копіюють вміст джерела в виділяється OCCI буфер що може знизити продуктивність додатків при великих розмірах джерела даних. На противагу setXXX setDataBuffer використовує в якості джерела безпосередньо переданий параметр, без виділення пам’яті і копіювання. Якщо ви використовуєте setXXX – відразу після виклику методу можна міняти джерело, що неприпустимо при використанні setDataBuffer


Ось ділянку коду ілюструє вищесказане:





/ / Деякі оголошення параметрів NLS
#define number_template “99999999999999999999”
#define number_nls “NLS_NUMERIC_CHARACTERS = “dg””
#define dur_number_template “99999999.999”
#define dur_number_nls “NLS_NUMERIC_CHARACTERS = “.,””
#define date_template “yyyymmddhh24miss”
#define date_nls “NLS_DATE_LANGUAGE = American”
/* Раніше введені об’єкти
Environment* env;
Connection* conn;
*/
if (conn)
{
try
{
/* Створюємо вираз
*/
Statement *stmt = conn->createStatement(“INSERT /*+ APPEND */ INTO cdrs
(CDRsequenceNumber,CallingAddress,CallingAddress_NAI,IncomingCalledAddress,IncomingCalledAddress_NAI,
OutgoingCalledAddress,OutgoingCalledAddress_NAI,StartTimeStamp,AlertingTimeStamp,AnswerTimeStamp,ReleaseTimeStamp,
CallDuration,CauseIndicator,InSigAddr_OPC,InSigAddr_DPC,InSigAddr_CIC,InSigAddr_slote,InSigAddr_ds,InSigAddr_card,
InSigAddr_timeslote,InSigAddr_gateway,OutSigAddr_OPC,OutSigAddr_DPC,OutSigAddr_CIC,OutSigAddr_slote,OutSigAddr_ds,
OutSigAddr_card,OutSigAddr_timeslote,OutSigAddr_gateway,OctSent,OctRxd,PktLost,
Jitter,Latency,CallingPartyCategory,ConnectedNumber,ConnectedNumber_NAI)
VALUES(:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15,:16,:17,:18,:19,:20,:21,:22,:23,
:24,:25,:26,:27,:28,:29,:30,:31,:32,:33,:34,:35,:36,:37)”); / / Відключаємо автоматичну фіксацію транзакції, вставляємо все в рамках однієї транзакції
stmt->setAutoCommit(false);
/* Нові об’єкти OCCI (представляють типи даних бази)
*/
Number CDRsequenceNumber = 0;
Number CallingAddress = 0;
Number IncomingCalledAddress = 0;
Number OutgoingCalledAddress = 0;
Date StartTimeStamp(env);
Date AlertingTimeStamp(env);
Date AnswerTimeStamp(env);
Date ReleaseTimeStamp(env);
/ / Контейнер з покажчиками на структури даних які будуть поміщатися в базу
deque::iterator p = c_cdrs.begin();
while (p != c_cdrs.end())
{
/* Обнуляємо значення чисел
*/
CDRsequenceNumber.setNull();
CallingAddress.setNull();
IncomingCalledAddress.setNull();
OutgoingCalledAddress.setNull();


if (strlen((*p)->CDRsequenceNumber) > 0)
{
CDRsequenceNumber = 0;
CDRsequenceNumber.fromText(env,(*p)->CDRsequenceNumber,number_template,number_nls);
}

if (strlen((*p)->StartTimeStamp) > 0)
{
StartTimeStamp.setDate();
StartTimeStamp.fromText((*p)->StartTimeStamp,date_template,date_nls,env);
}



/* Пряме призначення об’єкту Number значення через оператор =
*/
OctSent = (*p)->OctSent;
OctRxd = (*p)->OctRxd;
PktLost = (*p)->PktLost;

/* Встановлюємо змінні в курсор
*/
stmt->setNumber(1,CDRsequenceNumber);
stmt->setNumber(2,CallingAddress);
stmt->setInt(3,(*p)->CallingAddress_NAI);
stmt->setNumber(4,IncomingCalledAddress);
stmt->setInt(5,(*p)->IncomingCalledAddress_NAI);
stmt->setNumber(6,OutgoingCalledAddress);
stmt->setInt(7,(*p)->OutgoingCalledAddress_NAI);

stmt->setDate(8,StartTimeStamp);
stmt->setDate(9,AlertingTimeStamp);
stmt->setDate(10,AnswerTimeStamp);
stmt->setDate(11,ReleaseTimeStamp);

stmt->setDouble(12,(*p)->CallDuration);
stmt->setInt(13,(*p)->CauseIndicator);

stmt->setInt(22,(*p)->OutSigAddr_OPC);
stmt->setInt(23,(*p)->OutSigAddr_DPC);
/ * Виконання курсора і повторне його використання * /
stmt->executeUpdate();
p++;
} // while
conn->commit();
conn->terminateStatement(stmt);
} / / Ловимо виключення при виникли в процесі роботи з базою
catch(SQLException &sqlExcp)
{
conn->rollback();
cerr << sqlExcp.getMessage() << endl;
}


Деякі примітки за кодом:


Робота зі змінними IN / OUT, OUT



Ми розглянули тільки змінні передаються в режимі IN, але OCCI дозволяє працювати з OUT & IN / OUT. Такі змінні актуальні у разі використання так званих callable statemens – мається увазі виклик процедур і функцій на PL / SQL.


Розглянемо роботу з такими запитами. Припустимо, в базі є функція на PL / SQL, от її прототип:





CREATE OR REPLACE FUNCTION insclient(ContractNumber IN VARCHAR,
ClientPasswd IN OUT VARCHAR,
Action IN VARCHAR
) RETURN INTEGER;

Приклад роботи з нею:






string passwd;
int res;

Statement *stmt = conn->createStatement(“BEGIN :v1 := insclient(:v2,:v3,:v4); END;”); / / Встановлюємо параметри
stmt->setString(2,login); / / Реєструємо вихідні параметри
stmt->registerOutParam(1,OCCIINT,sizeof(res));
stmt->registerOutParam(3,OCCISTRING,pass_length+2); / / Встановлюємо значення параметрів для IN / OUT після реєстрації!
stmt->setString(3,pass_syms.c_str());
stmt->setString(4,command);
stmt->execute(); / / Отримуємо результат виконання
res = stmt->getInt(1); / / Отримуємо покажчик на результат
passwd = stmt->getString(3); / / Звільнення ресурсів
conn->terminateStatement(stmt);
env->terminateConnection(conn);

Примітки:


Підвищення продуктивності


Як згадувалося раніше для додатків, в яких швидкість виконання запитів є критичною можна використовувати функцію setDataBuffer.





void setDataBuffer(
int paramIndex,
void *buffer,
Type type,
sb4 size,
ub2 *length,
sb2 *ind = NULL,
ub2 *rc = NULL);


paramIndex
Номер параметра
buffer
покажчик на буфер з даними
type
Тип даних в буфері
size
Розмір елемента в буфері
length
Поточна довжина даних у поточній комірці буфера
ind
Індикатор. Вказує коли дані порожні, в разі якщо -1 було вставлено NULL – значення, у разі виклику підпрограми в запиті -1 вказує що повернуто NULL.
rc
Код повернення. Цей параметр непридатний до даних передаються методами Statement-a, але для даних повертаються з викликів процедур повертає параметр-залежний код помилки.

При ініціалізації даних використовуючи setDataBuffer дані зчитуються послідовно з пам’яті, тобто після кожної ітерації (кількість заздалегідь вказується) відбувається зміщення покажчика на адресу наступного елемента. Метод setDataBuffer можна використовувати спільно з Statement * stmt-> setXXX або без нього. Розглянемо обидва варіанти використання цього методу


Припустимо, в базі створена таблиця наступного вигляду:





CREATE TABLE tb01
(
id INTEGER,
data NUMBER(10),
val VARCHAR(20)
)
NOLOGGING

Код виконує вставку в таблицю:





/* Масиви даних для вставки
*/
int ids[] = {1,2,3,4};
ub2 ids_rc[] = {0,0,0,0};
unsigned long datas[] = {1000,2000,3000,4000};
/* Строкові змінні, в документації по OCCI не вказана 2-я розмірність масиву (10) – її потрібно вказувати
*/
char vals[4][10] = {“Value”, “Value666”, “Value677”, “Val4545”};
stmt->setSQL(“Insert into tb01(id,data,val) values(:1,:2,:3)”);
int i = 0;
/* Визначаємо масив довжин значень у масиві строкових даних і заповнюємо довжини
*/
ub2 valsLen[4];
for (i = 0; i < 4; i++)
{
valsLen[i] = strlen(vals[i]) + 1;
}
/* Встановлюємо максимальну кількість ітерацій
*/
stmt->setMaxIterations(4);
/* Заповнення буфера, викликається 1 раз на все значення. Вказується номер параметра, адреса даних, типу даних, розмір осередку даних (щоб OCCI знав на скільки пересунути вказівник для позиціонування на наступну комірку), покажчик на довжини даних в осередках. Передбачається що дані йдуть в пам’яті последоваетельно – за кінцем одного рядка початок следуюшей
*/
stmt->setDataBuffer(3,vals,OCCI_SQLT_STR, sizeof(vals[0]),valsLen);
/* sizeof (vals [0]) == 10 для цього випадку
*/
for (i = 0; i < 4; i++)
{
stmt->setInt(1,ids[i]);
stmt->setInt(2,datas[i]);
/* Додавання ітерації + фактичне зміщення покажчика даних і накопичення даних з setInt
*/
if (i != 3) stmt->addIteration();
}
/* Виконання всіх доданих ітерацій
*/
stmt->executeUpdate();

Важливо розуміти, що в даному випадку оголошення char vals [4] [10] = {“Value”, “Value666”, “Value677”, “Val4545”}; не можна замінити на char * vals [4] – і заповнити адреси довільно, дані повинні йти послідовно. Тепер розглянемо той же варіант, але коли всі дані містяться в масивах:





/* Масив id, а також масив довжин елементів і масив кодів повернення (для прикладу). Природно sizeof (ids [0]) == sizeof (ids [1]) == sizeof (ids [2]) == sizeof (ids [3]) – оголошено таким чином для наочності
*/
int ids[] = {1,2,3,4};
ub2 ids_len[] = {sizeof(ids[0]),sizeof(ids[1]),sizeof(ids[2]),sizeof(ids[3])};
ub2 ids_rc[] = {0,0,0,0};
unsigned long datas[] = {1000,2000,3000,4000};
ub2 datas_len[] = {sizeof(datas[0]),sizeof(datas[1]),sizeof(datas[2]),sizeof(datas[3])};
char vals[4][10] = {“Value”, “Value666”, “Value677”, “Val4545”};
ub2 valsLen[4];
for (i = 0; i < 4; i++)
{
valsLen[i] = strlen(vals[i]) + 1;
}
stmt->setSQL(“Insert into tb01(id,data,val) values(:1,:2,:3)”);
int i = 0;
/* Установка змінних
*/
stmt->setDataBuffer(1,ids,OCCIINT,sizeof(ids[0]),ids_len,NULL,ids_rc);
stmt->setDataBuffer(2,datas,OCCIINT,sizeof(datas[0]),datas_len);
stmt->setDataBuffer(3,vals,OCCI_SQLT_STR, sizeof(vals[0]),valsLen);
/* Виконати 4 ітерації
*/
stmt->executeArrayUpdate(4);

У використанні stmt-> setMaxIterations (4); немає необхідності Подібним чином можна не тільки вставляти дані, але і витягувати їх. Код з документації по OCCI ілюструє це:





int empno[5];
char ename[5][11];
ub2 enameLen[5];
ResultSet *resultSet = stmt->executeQuery(“select empno, ename from emp”);
resultSet->setDataBuffer(1, &empno, OCCIINT);
resultSet->setDataBuffer(2, ename, OCCI_SQLT_STR, sizeof(ename[0]), enameLen); rs-> next (5); / / зливаємо 5 рядків, enameLen [i] зберігає довжину ename [i]

Висновок



Загалом, це все що я хотів написати – сподіваюся наведені тут приклади допоможуть вам швидко почати використання Oracle у ваших проектах на C + +. OCCI дуже простий і потужний інтерфейс роботи з Oracle. Тут не порушені питання використання BLOB, використання потоків Stream, об’єктне програмування та Object Type Translator (OTT), отримання метаданих, розробка багатопоточних додатків наприклад використовуючи ConnectionPool і т.д. З усіх цих питань варто звернутися до документації по Oracle – Oracle C + + Call Interface Programmer’s Guide посилання на яку я наводив на початку статті.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*