Робота з DB Oracle з Xcode

Я хотів навчитися працювати з oracle з xcode, оскільки не виявив готового фреймворку від apple для роботи з базою даних oracle безпосередньо.
Для початку, я спробував створити тестовий проект і підключити до нього instantclient від oracle. Також я використовував тестовий приклад від Oracle (cdemo81.c), включив його код в проект на xcode і протестував роботу. Так, все працює, хоча довелося трошки побити в шаманський бубон від google. Проте, використовувати безпосередньо oci бібліотеку важко, так як потрібно реалізувати інтерфейс, а це схоже на винахід велосипеда.
Тоді я вирішив спробувати використовувати крос-платформену бібліотеку ocilib (http://orclib.sourceforge.net )
Далі в тексті покрокова інструкція про те, як зробити тестовий проект на cocoa і використовувати цю бібліотеку. Мета тестового проекту — підключивши бібліотеку отримати дані з сервера oracle.
 
1) Качаємо саму бібліотеку http://orclib.sourceforge.net/download/
2) Качаємо instantclient від oracle для macosx http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html , Мене в даному випадку цікавить Version 11.2.0.3.0 (64-bit), Instant Client Package — Basic: All files required to run OCI, OCCI, and JDBC-OCI applications (http://download.oracle.com/otn/mac/instantclient/11203/instantclient-basic-macos.x64-11.2.0.3 .0. zip ) Для скачування потрібно реєстрація, але вона безкоштовна. Також будуть потрібні header файли для компіляції ocilib, тому качаємо «Instant Client Package — SDK: Additional header files and an example makefile for developing Oracle applications with Instant Client», в моєму випадку ось цей — instantclient-sdk-macos.x64-11.2.0.3 .0. zip (http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html )
3) розпакуємо instantclient в папку, наприклад / usr/local/lib/instantclient_11_2
4) Також, розпакуємо SDK c header файлами у всередину папки з instantclient, наприклад ось так — / usr/local/lib/instantclient_11_2/sdk
5) Тепер, необхідно створити симлінк для бібліотеки в папці instantclient, в консолі переходимо в папку / usr/local/lib/instantclient_11_2, далі робимо симлінк — «ln-s libclntsh.dylib.11.1 libclntsh.dylib»
6) Далі, розпаковуємо архів з бібліотекою ocilib, наприклад в папку / Users/username/Downloads/ocilib-3.12.1
7) Відкриваємо термінал і переходимо в папку з ocilib, наприклад так —
cd /Users/username/Downloads/ocilib-3.12.1

8) У консолі послідовно cобіраем бібліотеку
 
 
./configure --with-oracle-lib-path=/usr/local/lib/instantclient_11_2/ —with-oracle-headers-path=/usr/local/lib/instantclient_11_2/sdk/include
make
sudo make install

 
Після цього, бібліотека должа бути встановлена ​​в папці / usr / local / lib, перевірте наявність файлів у папці, таких як libocilib *
Перший етап підготовки бібліотек виконаний. Тепер спробуємо створити тестовий проект в XCode
 
1) Створюємо в Xcode новий проект (OS X -> Cocoa Application)
За умовчанням в Xcode (у мене встановлена ​​версія 5.1) створюється порожній проект з MainMenu.xib в якому є пусте view.
2) Для початку додамо в проект header файл бібліотеки ocilib. Відкриваємо папку / Users/username/Downloads/ocilib-3.12.1/include і копіюємо звідти файл ocilib.h в папку нашого проекту для XCode, і додаємо цей файл в наш проект (в xcode, всередині папки проекту робимо file-> add files to «project name» і вибираємо наш ocilib.h
3) Далі потрібно додати в проект посилання лінковки для бібліотек, для цього відкриваємо Build Settings проекту xcode, шукаємо Other Linker Flags і додаємо туди два параметри — «-locilib» і «-lclntsh» — перший, посилання на бібліотеку ocilib, другий на instantclient. Якщо зараз спробувати скомпілювати проект, отримаємо помилку — «ld: library not found for-lclntsh». Справа в тому, що xcode при компіляції не знає, де саме лежить instantclient oracle, потрібно вказати шлях до нього.
4) Вкажемо шлях до бібліотек — для цього відкриваємо Build Settings проекту xcode, шукаємо «Library Search Path» і додаємо шлях до instantclient і ocilib, тиснемо два параметри «/ usr/local/lib/instantclient_11_2» і «/ usr / local / lib »
 
Другий етап підготовки виконаний, проект побачив бібліотеки. Спробуємо скомпілювати проект, все має заробити без помилок. Якщо помилок немає, вітаю, вде третини шляху позаду. Залишилися дрібниці — з'єднатися з базою даних і отримати будь-які відомості.
 
1) У AppDelegate.h пропишемо
#include "ocilib.h"

2) У AppDelegate.m змінимо код
  
 
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    
    OCI_Connection* cn;
    OCI_Statement* st;
    OCI_Resultset* rs;
    
    OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
    cn = OCI_ConnectionCreate("orcl", "USERNAME", "PASSWORD", OCI_SESSION_DEFAULT);
    st = OCI_StatementCreate(cn);
    
    OCI_ExecuteStmt(st, "select id,name from test_encoding_table");
    rs = OCI_GetResultset(st);
    
    while (OCI_FetchNext(rs)) {
        printf("%i - %s\n", OCI_GetInt(rs, 1), OCI_GetString(rs,2));
    }
    OCI_Cleanup(); 
}

 
Де username, password це ім'я і пароль для підключення до нашої бази даних, а orcl це ім'я instance прописаного в tnsnames.ora (як прописати, про це трохи пізніше)
Якщо зараз спробувати запустити проект, то отримаємо помилку, ось таку — «dyld: Library not loaded: / ade/b/2649109290/oracle/rdbms/lib/libclntsh.dylib.11.1
 Referenced from: /Users/chepil/Library/Developer/Xcode/DerivedData/test3-hdexygntscvmwahcgsfolpqrkldi/Build/Products/Debug/test3.app/Contents/MacOS/test3
 Reason: image not found »
 
Ця помилка говорить про те, що при запуску проекту (так, він скомпилірувався нормально), додаток не знає, де ж лежить бібліотека на яку ми посилаємося. Адже одна річ скомпілювати проект, інша справа його виконання.
Для виправлення помилки або запустимо проект з консолі, встановивши потрібні змінні оточення, або додамо потрібні змінні прямо в xcode.
Для цього клацнемо мишкою на найменуванні проекту, в лівому верхньому кутку вікна проекту (на жаль, не знаю як зробити це через меню), і виберемо «Edit Scheme ...»
Виберемо схему запуску (в моєму випадку «Run test3.app») і відкриємо вкладку Arguments, в якій пропишемо кілька «Environment Variables» — змінні оточення. Потрібно розуміти, що ці змінні діють тільки при запуску проекту з Xcode, і припинять діяти відразу, як тільки ми пустимо наш app файл у відкрите плавання…
 
3) Додамо змінну оточення DYLD_LIBRARY_PATH зі значенням / usr / local / lib :/ usr/local/lib/instantclient_11_2. Як Ви бачите, для додавання кількох значень, потрібно використовувати двокрапку. Спробуємо запустити проект. Проект запустився, але в консоль нічого не видалося. Логічно, так як у перших, не відбулося з'єднання з базою даних, у других, у Вашій базі даних напевно відсутній таблиця test_encoding_table (я сподіваюся, що Ви здогадалися замінити запит на своє значення). Проте, чого не вистачає для з'єднання з базою даних? Правильно, змінної оточення ORACLE_HOME, в якій повинен лежати network / admin / tnsnames.ora
 
4) створимо папки / Users / username / oracle_home / network / admin
5) створимо файл / Users / username / oracle_home / network / admin / tnsnames.ora
вміст файлу tnsnames.ora:
 
 
orcl=
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp) (HOST=orclserver.myaddress.com) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl)))

 
У вашому випадку підключення може бути іншим, налаштування підключення до oracle сервера виходять за рамки цієї статті. Підключення вище наведене просто як приклад.
 
6) Пропишемо для нашого застосування нову змінну оточення для запуску (там же, де ми прописували DYLD_LIBRARY_PATH). Нова змінна буде мати ім'я ORACLE_HOME і мати значення / Users / username / oracle_home
Після цього, під час запуску програми і створенні коректного запиту (до вашої таблиці), Ви почнете отримувати дані від Оракл сервера. Також, можна було прописати значення змінної не ORACLE_HOME, а TNS_ADMIN і вказати шлях до файлу tnsnames.ora (Оракл адміністратори і програмісти знають про що йде мова, не буду розжовувати)
 
7) У моєму випадку, таблиця test_encoding_table містить тестові записи російською та японською мовами, в UTF8 кодуванні. При запиті цих даних в xcode, я отримав ось такий результат —
1 -???????????? ???????? ????

 
Зрозуміло, що це вопросики, а не потрібні нам дані, а значить що то працює не так.
Для коректного повернення даних, потрібно вказати, в якому кодуванні ми працюємо
 
8) Пропишемо чергову змінну оточення для запуску програми. Також як і DYLD_LIBRARY_PATH, ORACLE_HOME, створимо змінну NLS_LANG і пропишемо їй значення (наприклад RUSSIAN_CIS.UTF8)
Запустимо проект і отримаємо висновок в консоль проекту —
 
1 — тестування руссккой імен
2 — 日本語 で 利用 し たい の です が
 
Що й потрібно було довести.
 
Залишилося — дрібниці. Давайте тепер виведемо результат в NSTextView в головному вікні програми. Для цього
Змінимо файл MainMenu.xib створені за замовчуванням, покладемо на window новий NSTextView
Змінимо AppDelegate.h
  
  
#import <Cocoa/Cocoa.h>
	#include "ocilib.h"
	@interface AppDelegate : NSObject <NSApplicationDelegate> {
    		IBOutlet NSTextView *textView;
	}
	@property (assign) IBOutlet NSWindow *window;
	@property (nonatomic,retain) IBOutlet NSTextView *textView;
	@end

 
3) Змінимо AppDelegate.m
 
 
#import "AppDelegate.h"
@implementation AppDelegate
@synthesize textView;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
   NSString *str = @"";
   
    OCI_Connection* cn;
    OCI_Statement* st;
    OCI_Resultset* rs;
    
    OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
    
    cn = OCI_ConnectionCreate("orcl", "username", "password", OCI_SESSION_DEFAULT);
    st = OCI_StatementCreate(cn);
    OCI_ExecuteStmt(st, "select id,name from test_encoding_table");
    rs = OCI_GetResultset(st);
    
    while (OCI_FetchNext(rs))
    {
        printf("%i - %s\n", OCI_GetInt(rs, 1), OCI_GetString(rs,2));
        NSString *str1 = [NSString stringWithUTF8String:OCI_GetString(rs,2)];
        str = [NSString stringWithFormat:@"%@\n%i: %@",str,OCI_GetInt(rs, 1),str1];
    }
    OCI_Cleanup();
   [textView setString:str];
}
@end

 
4) на MainMenu.xib відкриємо AppDelegate і зв'яжемо outlet textView на наш новий NSTextView, який ми поклали в наше віконце.
 
Запустимо додаток
Тепер висновок запиту до бази даних дублюється в консоль проекту і в NSTextView.
 
Останнє уточнення. Якщо ми захочемо запустити програму (в моєму випадку test3.app) з консолі, а не з xcode, то ми повинні будемо прописати змінні оточення.
Для тесту створюємо файл test.sh, даємо йому права
chmod +x test.sh
і пишемо у всередину що те типу:
 
 
#!/bin/sh
export DYLD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib/instantclient_11_2
export ORACLE_HOME=/Users/chepil/oracle_home
export NLS_LANG=RUSSIAN_CIS.UTF8

open test3.app

 
запускаємо проект з консолі —
 
 
./test3.sh

 
відкривається додаток, в ньому вікно, робиться запит до бази і результат виводиться в textview на екрані. Все працює, чого і потрібно було довести.
 
Наостанок хотілося б поцікавитися, можливо існують інші Біблітека для роботи з Оракл. Я перебуваю на початку розробки проекту і мені необхідно прийняти рішення про доцільність роботи саме з цією бібліотекою. Фактично, бібліотека ocilib є обгорткою навколо OCI від Оракл, і не хочеться винаходити свій велосипед. У кого який досвід роботи з Оракл з xcode? Які зауваження є до тексту статті?
Спасибі за конструктивну критику!

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.