2009-04-23

C/C++调用OCCI方法全解析

    C/C++操作Oracle数据库的方式核心有两种:C的OCI与C++的OCCI,OCCI的语法近于 JDBC,简单易学,但OCI接口复杂,难以学习、理解和使用,因此Oracle提出了折中方案:Pro*C,在C语言文件里嵌入SQL语句,通过proc预编译产生包含OCI函数的C文件,但另外的麻烦又来了,预编译文件.pc,由于嵌入了SQL语句,并不符合C的语法,IDE会产生警告,自然,如果你习惯于使用vi来编辑,则令当别论了。如果在C源文件中直接使用OCCI的方法,使用C++编译器来编译所有的C文件,也是一种简化问题的方法,但将大量C文件使用C++编译器编译让人感觉不是那么痛快。
    如果能封装OCCI对数据库的操作,并生成C语言的动态库或静态库,那就可以继续使用C编译器来编译其他的源文件了,最终动态链接或通过连接器静态链接到一起就行了。

一、 C++的OCCI操作数据库
    要用C封装OCCI,首先是要知道如果用C++通过OCCI来操作数据库
/*
 * occi.h
 *
 *  Created on: Apr 22, 2009
 *      Author: hanchen
 */

#ifndef OCCI_H_
#define OCCI_H_

#include <occi.h>

using namespace std;
using namespace oracle::occi;

class OcciDemo {
private:
        Environment *env;
        Connection *conn;
public:
        OcciDemo(string user, string passwd, string dbname);
        ~OcciDemo();
        void displayTable();
};

#endif /* OCCI_H_ */


/*
 * occi.cpp
 *
 *  Created on: Apr 22, 2009
 *      Author: hanchen
 */

#include <iostream>
#include <algorithm>
#include "occi.h"

OcciDemo::OcciDemo(string user, string passwd, string dbname) {
        env = Environment::createEnvironment(Environment::OBJECT);
        conn = env->createConnection(user, passwd, dbname);
}

OcciDemo::~OcciDemo() {
        cout << "delete OcciDemo" << endl;
        env->terminateConnection(conn);
        Environment::terminateEnvironment(env);
}

void OcciDemo::displayTable() {
        Statement *stmt = conn->createStatement();
        ResultSet *rs = stmt->executeQuery("select id from usr");
        try {
                while (rs->next()) {
                        cout << rs->getInt(1) << endl;
                }
        } catch (SQLException se) {
                cout << "Error number: " << se.getErrorCode() << endl;
                cout << se.getMessage() << endl;
        }
}



/*
 * t_occi.cpp
 *
 *  Created on: Apr 22, 2009
 *      Author: hanchen
 */
#include "occi.h"

int main(void) {
        OcciDemo *occi = new OcciDemo("hanchen", "123456", "orcl");
        occi->displayTable();
        delete (occi);
        return 0;
}

二、网上关于C++导出成为C的so文件例子很多,照葫芦画瓢即可自然,实际上并不需要使用occi.cpp来操作,直接在C源文件里操作OCCI导出即可。
/*
 * e_occi.h
 *
 *  Created on: Apr 22, 2009
 *      Author: hanchen
 */

#ifndef E_OCCI_H_
#define E_OCCI_H_

#ifdef _cplusplus
extern "C"
{
#endif
void eocci_test();
#ifdef _cplusplus
}
#endif
#endif /* E_OCCI_H_ */


/*
 * e_occi.cpp
 *
 *  Created on: Apr 22, 2009
 *      Author: hanchen
 */
#ifndef _cplusplus
#define _cplusplus
#endif

#include "occi.h"
#include "e_occi.h"

void eocci_test() {
        OcciDemo occi("hanchen", "123456", "orcl");
        occi.displayTable();
}


/*
 * t_e_occi_so.c
 *
 *  Created on: Apr 22, 2009
 *      Author: hanchen
 */

#include <stdio.h>
#include <dlfcn.h>
#include "e_occi.h"

int main(void) {
        void (*df)();
        void *hl;

        hl = dlopen("./e_occi.so", RTLD_LAZY);
        df = dlsym(hl, "eocci_test");
        (*df)();
        dlclose(hl);

        return 0;
}

三、动态链接虽然简单,但操作麻烦,如果程序员自己能控制代码的话,做静态链接则是一种更好的选择
/*
 * t_e_occi_a.c
 *
 *  Created on: Apr 22, 2009
 *      Author: hanchen
 */

#include <stdio.h>
#include <dlfcn.h>
#include "e_occi.h"

int main(void) {
        eocci_test();

        return 0;
}


四、makefile,想必您看到这里已经很厌烦了,光看代码不一定能实际运用,最后是三个单元的makefile集合,什么,你说你不知道makefile是什么东西?。。。如果你是windows用户,并且在windows下开发C程序的话,那么您没必要看下去了,如果你是linux C程序员,那我建议你还是先学习下linux C开发的基础。。。废话少说。。。看文件
ORACLE_HOME=/opt/oracle/ora11

TARGET=t_occi t_e_occi_so t_e_occi_a

all:$(TARGET)

occi.o:occi.cpp
        g++ -o $@ $^ -c -I$(ORACLE_HOME)/rdbms/public
        
t_occi.o:t_occi.cpp
        g++ -o $@ $^ -c -I$(ORACLE_HOME)/rdbms/public
        
e_occi.o:e_occi.cpp
        g++ -o $@ $^ -c -I$(ORACLE_HOME)/rdbms/public
        
e_occi.so:e_occi.o occi.o
        g++ -o $@ $^ -shared
        
t_e_occi_a.o:t_e_occi_a.c
        gcc -o $@ $^ -c

t_occi:t_occi.o occi.o
        g++ -o $@ $^ -L$(ORACLE_HOME)/lib -locci -lclntsh
        
t_e_occi_so:e_occi.so
        gcc -o $@ t_e_occi_so.c -ldl -L$(ORACLE_HOME)/lib -locci -lclntsh
        
t_e_occi_a:t_e_occi_a.o e_occi.o occi.o
        gcc -o $@ $^ -L$(ORACLE_HOME)/lib -locci -lclntsh
        
clean:
        rm -rf *.o *.so
        rm -rf $(TARGET)

    makefile写的很复杂,一会是gcc,一会儿是g++,您耐心慢慢读,如果你无法编译出C++版本通过OCCI操作数据的程序,那么建议您读读另外一篇文章 linux下使用occi链接oracle11



2009-04-19

linux下使用occi链接oracle11

系统环境:
OS: Linux Windows 2.6.27-12-generic #1 SMP Thu Feb 5 09:26:35 UTC 2009 i686 GNU/Linux
Oracle:Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production
GCC:gcc (Ubuntu 4.3.2-1ubuntu12) 4.3.2

用户环境:
export JAVA_HOME=/usr/share/java
export ORACLE_BASE=/opt/oracle
export ORACLE_HOME=/opt/oracle/ora11
export ORACLE_OWNER=oracle
export ORACLE_SID=tmp
export ORACLE_TERM=xterm
export PATH=$JAVA_HOME/bin:$ORACLE_HOME/bin:$PATH

步骤:
1.建立数据库表usr
2.建立C++源文件,occi.cpp
#include <iostream>
#include <algorithm>
#include <occi.h>

using namespace std;
using namespace oracle::occi;

class OcciDemo {
private:
    Environment *env;
    Connection *conn;
public:
    OcciDemo(string user, string passwd, string dbname) {
        env = Environment::createEnvironment(Environment::OBJECT);
        conn = env->createConnection(user, passwd, dbname);
    }

    ~OcciDemo() {
        env->terminateConnection(conn);
        Environment::terminateEnvironment(env);
    }

    void displayTable(string tablename) {
        cout << "Displaying the table " << tablename << endl;
        Statement *stmt = conn->createStatement("select id from usr");
        ResultSet *rs = stmt->executeQuery();
        try {
            while (rs->next()) {
                cout << rs->getInt(1) << endl;
            }
        } catch (SQLException se) {
            cout << "Error number: " << se.getErrorCode() << endl;
            cout << se.getMessage() << endl;
        }
    }
};

int main(void) {
    OcciDemo *occi = new OcciDemo("hanchen", "123456", "orcl");
    occi->displayTable("usr");
    return 0;
}

3.建立makefile
CC
=g++
ORACLE_HOME=/opt/oracle/ora11
INCLUDES=$(ORACLE_HOME)/rdbms/public
LIBS=$(ORACLE_HOME)/lib

CXXFLAGS=-O2 -Wall -I$(INCLUDES)

TARGET=occi

all:$(TARGET)

occi:occi.o
    $(CC) -o $@ $^ -L$(LIBS) -locci -ldl -lclntsh
    
hash:hash.o
    $(CC) -o $@ $^ -L$(LIBS)
    
clean:
    rm -rf *.o
    rm -rf $(TARGET)

4. 如果编译错误,请检查-I参数,如果链接错误,请检查参数-L与-ldl -lclntsh,如果运行错误,请继续5
5.编辑/etc/ld.so.conf文件
加入/opt/oracle/ora11/lib,该目录下有libocci.so文件
然后运行ldconfig -v使库链接生效
6.运行./occi即可显示结果

如有问题,或错误,请联系我,请高手不吝赐教,谢谢!