【原创】摸爬滚打之Linux下的OCCI编程

第一篇技术博客,终于有权说【转载请注明出处http://user.qzone.qq.com/329331481

 

Linux下的OCCI编程,也许您简简单单就是实现了,但对于一个从未接触过Oracle数据库,没有Linux下编程经验,一切从0学起的菜鸟新手来说,的确可谓摸爬滚打,当然,也学到了不少东西。

在这个过程中,遇到了太多的问题,在网上搜的时候感觉这方面的资料还不是很多,或者比较零散,所以作此总结。

 

u    开发环境

 

Red Hat Enterprise Linux AS 4.0

gcc 3.4.3

Oracle 10g Express Edition

 

u    开发步骤

 

n       安装Oracle

此部分本人没有参与,直接拿别人装好的虚拟机来用的,参见网上其他文章。

 

n       安装gcc 3.4.3

如果RedHat自带的gcc不是3.4.3版本,则需要更新到3.4.3版本以兼容Oracle提供的OCCI库。gcc 3.4.3可点击下面的连接下载:

http://download.chinaunix.net/down.php?id=6573&ResourceID=69&site=1

 

由于是编译安装,所以在安装之前,必须保证原系统中已存在某个版本的gcc

输入如下命令:

# cp gcc-3.4.3.tar.gz /usr/src

# cd /usr/src

# tar jxvf gcc-3.4.3.tar.bz2

(如果后缀是.gz,则命令修正为:# tar zxvf gcc-3.4.3.tar.gz

# cd gcc-3.4.3

# ./configure --prefix=/usr/local/src/gcc-3.4.3 --enable-threads=posix --disable-checking --enable--long-long --host=i386-redhat-linux --with-system-zlib --enable-languages=c,c++

# make

# make install

这样就将 gcc 3.4.3 安装到 /usr/local/src/gcc-3.4.3 目录下了。为了和原系统中的gcc共存,可作如下符号链接:

# cd /usr/bin

# ln -s /usr/local/src/gcc-3.4.3/bin/gcc gcc343

# ln -s /usr/local/src/gcc-3.4.3/bin/g++ g++343

最后需设置库的路径到环境变量中。修改 /etc/profile 文件,在尾部增加:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/src/gcc-3.4.3/lib

 

n       更新库文件

由于 Oracle 自带的库文件不支持 gcc 3.4.3,所以需要更新某些库文件。

Oracle 官方网站上提供更新文件的下载:occi_gcc343.tar,点击此处链接下载:http://www.oracle.com/technology/tech/oci/occi/occidownloads.html

将包中的两个库文件(libocci.so.10.1, libocci10.a)复制到下面的目录下:/app/oracle/product/10.2.0/db_1/lib

 

n       OCCI代码

这部分是在网上找的,转自:http://blog.163.com/kh-bright/blog/static/565271200974102737859/

感谢作者提过如此完整无误的代码,其中对ExcuteUpdate的返回值略作了修改,同时删掉了一个不知道做什么用的但牵涉到一个库的函数:

// dbAccessor.h头文件

#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <assert.h>
#include <occi.h>
#include <occiCommon.h>
#include <occiData.h>
#include <occiObjects.h>
#include <occiAQ.h>
#include <occiControl.h>

using namespace oracle::occi;

class dbAccessor
{
public:
 dbAccessor();
 ~dbAccessor();
 int get_dbstat();
 bool Connect_DB();
 void Disconn_DB();
 int ExecuteUpdate(const char* sql);
 ResultSet* ExecuteQuery(const char* sql);
 void ClearQuery(ResultSet* set);
 void Init(const char* dbName,const char* dbUser,const char* dbPass);

private:
 Environment* m_env;
 Connection*  m_conn;
 Statement*   m_stmt;
 const char *m_szUser;
 const char *m_szPassword;
 const char *m_szDbName;
 bool bConnected;

};

 

// dbAccessor.cpp文件

#include "oracle.h"

using namespace std;
using namespace oracle::occi;

dbAccessor::dbAccessor()
{
 bConnected=false;
 m_env=0;
 m_conn=0;
 m_stmt=0;
}
 
dbAccessor::~dbAccessor()
{
 Disconn_DB();
}

void dbAccessor::Init(const char* dbName,const char* dbUser,const char* dbPass)
{
 m_szDbName = dbName;
 m_szUser = dbUser;
 m_szPassword = dbPass;
}

bool dbAccessor::Connect_DB()
{
 if(m_env ==0)
  m_env = Environment::createEnvironment();

 if(m_conn==0){
  try {
   m_conn=m_env->createConnection(m_szUser,m_szPassword,m_szDbName);
    } catch (SQLException& ex) {
   m_conn = 0;
   cout << "connect to database failed: " << ex.getMessage() << endl;
   return false;
    }
 }

 return true;
}

void dbAccessor::Disconn_DB()
{
 if(m_stmt!=0 && m_conn!=0)
  m_conn->terminateStatement(m_stmt);

 if(m_conn!=0 && m_env!=0)
  m_env->terminateConnection(m_conn);
}

// 执行数据库更新操作,包括insertupdatedelete操作
int dbAccessor::ExecuteUpdate(const char* sql)
{
 int update_OK = 0;
 if(sql==0){
  exit(1);
 }

 if(m_conn==0&&m_env==0){
  exit(1);
 }

 try {
  m_stmt=m_conn->createStatement(sql);
 } catch (SQLException& ex) {
  m_stmt = NULL;
  cout << "ExecuteUpdate failed: " << ex.getMessage() << endl;
  exit(1);
 }

 update_OK = m_stmt->executeUpdate();
 

//返回更新的条数  
 return update_OK;
}

//执行数据库查询操作,即select操作
ResultSet* dbAccessor::ExecuteQuery(const char* sql)
{
 ResultSet* set=0;
 if(sql==0){
  exit(1);
 }

 if(m_conn==0&&m_env==0){
  exit(1);
 }

 try {
  m_stmt=m_conn->createStatement(sql);
 } catch (SQLException& ex) {
  m_stmt = NULL;
  cout << "ExecuteQuery failed: " << ex.getMessage() << endl;
  exit(1);
 }

 set=m_stmt->executeQuery();

 return set;
}


void dbAccessor::ClearQuery(ResultSet* set)
{
 if(m_stmt==0){
  exit(1);
 }

 if(set==0){
  exit(1);
 }

 m_stmt->closeResultSet(set);

// OracleTest.cpp文件
#include<string>
#include<string.h>
#include<stdlib.h>
#include<occi.h>
#include<occiCommon.h>
#include<occiData.h>
#include<occiObjects.h>
#include<occiAQ.h>
#include<occiControl.h>

#include "oracle.h"

using namespace std;
using namespace oracle::occi;


int main(void)
{
 dbAccessor obj;
 obj.Init("192.168.0.1:1521/test","user","pass");

bool conn = obj.Connect_DB();
 if(!conn) exit(1);

// 1、插入数据库操作
string sql = "insert into EMAIL0(emailid,checkintime,clientip,clientport,serverip,serverport,capturetime,type,username,password,sendaddress,receiveaddress,subject,ccaddress) ";
sql += "values(1238673850,to_date('04-3-2009','MM-DD-YYYY'),'127.0.0.1',4327,'serverip',110,to_date('04-3-2009','MM-DD-YYYY'),1,'user','pass','sendaddress','receiveaddress','subject','ccaddress')";
 obj.ExecuteUpdate(sql.c_str());

 

 // 2、查询数据库操作
 sql = "select emailid,to_char(checkintime,'YYYY-MM-DD HH24:MI:SS'),clientip from EMAIL0";
 //
第一列emailidInt型;第二列checkintimeDate类型,转换为字符串类型输出;第三列clientipstring字符串类型
 ResultSet* set=obj.ExecuteQuery(sql.c_str());
 while(set->next()){
  cout << set->getInt(1) << endl;   //
获取第一列值,int类型
  cout << set->getString(2) << endl; //
获取第二列值,date类型转换成了字符串类型
  cout << set->getString(3) << endl; //
获取第三列值,string类型
 }
 obj.ClearQuery(set);

 return 0;
}

 

n       编写makefile

这个过程犯了不少错误,原因是在没有搞清楚编译链接的原理之前就照猫画虎的去写,导致了很多编译链接错误。

建议在写makefile之前,先看一下这篇文章,理解了原理之后,再去写就容易多了:

菜鸟入门:LinuxMakefile概述【转自www.bitsCN.com

http://www.bitscn.com/linux/system_manage/200604/7068.html

下面是我写的makefile,不能跟高手写的比,但通俗易懂且经过了实践的检验。首先说明,我的oracle安装在了rootapp下,所以Includelib的路径分别是:/app/oracle/product/10.2/db_1/rdbms/public/app/oracle/product/10.2/db_1/lib

好了,贴代码:

ICINCHOME=/app/oracle/product/10.2/db_1/rdbms/public

SD = src

 

OBJS = OracleTest.o dbAccessor.o

 

oracletest: $(OBJS)

  g++ -o oracletest $(OBJS) -L/app/oracle/product/10.2/db_1/lib -locci -lclntsh /usr/lib/libstdc++.so.5

 

OracleTest.o: $(SD)/OracleTest.cpp

  g++ -c -g $(SD)/OracleTest.cpp -I $(ICINCHOME)

 

dbAccessor.o: $(SD)/dbAccessor.cpp

  g++ -c -g $(SD)/dbAccessor.cpp -I $(ICINCHOME)

 

clean:

  rm -f oracletest $(OBJS)

 

这样,在linuxmake,然后运行即可连接数据库完成查询和更新操作。

但是,绝大多数入门级选手是不能顺利运行的,在make和执行的时候总会出现各种各样的问题(所以称之为“摸爬滚打”),所以,进入最重要的一个小节:

 

u    问题及解决方案汇总

 

n       问题一

找不到函数实现,make后显示的错误信息类似下面的这段:

g++  -o oracletest OracleTest.o dbAccessor.o -I include -Llibocci.so.10.1 -Llibocci10.a

dbAccessor.o: In function `dbAccessor::Connect_DB()':

dbAccessor.cpp:(.text+0x707): undefined reference to `oracle::occi::Environment::createEnvironment(oracle::occi::Environment::Mode, void*, void* (*)(void*, unsigned int), void* (*)(void*, void*, unsigned int), void (*)(void*, void*))'

dbAccessor.o:(.gcc_except_table+0x50): undefined reference to `typeinfo for oracle::occi::SQLException'

dbAccessor.o:(.gcc_except_table+0xa4): undefined reference to `typeinfo for oracle::occi::SQLException'

dbAccessor.o:(.gcc_except_table+0xfc): undefined reference to `typeinfo for oracle::occi::SQLException'

collect2: ld 返回 1

make: *** [oracletest] 错误 1

解决办法:

如果你看过了《菜鸟入门:LinuxMakefile概述》这篇文章,那么一看到上述错误提示就知道,这些找不到实现的函数或参数肯定是由于它们的实现库没有连接进去。因此,你需要检查makefile中链接库的地方有没有写正确,还要检查这些库到底有没有存在你所指定的路径下。

在我解决的过程中,所犯的错误就是把makefile中的-locci –lclntsh,写成了-llibocci.so.10.1之类,因为我看到的目录下库的名字确实是带.so.a的,其实我到现在也没明白,为什么要那样写而不把库的名字写出来

n       问题二:

当我好不容易把makefile写对了之后,改对付那些“无关紧要”的warning了。

Warning虽然不影响程序的运行,但是最好都解决掉,下面遇到了三个warning

1、       Warning: File `Makefile' has modification time 2.5e+07 s in the future

出现该警告时,可以看一下linux虚拟机的系统时间,会发现跟当前时间相差很多,所以会提示此警告。

解决办法:

通过如下命令设置当前时间:

Date –s 03/12/2010

Date –s 20:41:00

2、       warning: libstdc++.so.5, needed by /oracle/product/10.2.0/db_1/lib/libocci.so, may conflict with libstdc++.so.6

这个警告还有可能导致运行问题。Oracle以及OCCI真是麻烦,linux版本低了不行,高了也不行。好在在大多数linux系统上,还保留有libstdc++5的库,自己手工在编译的时候加上去就好了,所以在我的makefile中有这么一句

    g++ -o oracletest $(OBJS) -L/app/oracle/product/10.2/db_1/lib -locci -lclntsh /usr/lib/libstdc++.so.5

n       问题三:

编译通过了,运行却出问题了:

libclntsh.so.10.1: cannot open shared object file: No such file or directory

libocci.so.10.1: cannot open shared object file: No such file or directory

可能是安装Oracle的时候没有配置好环境变量吧,找库一直是个问题,所以还是提醒大家,一定要把环境变量设置好:

export ORACLE_BASE=/app/oracle

export ORACLE_HOME=$ORACLE_BASE/product/10.2/db_1

export PATH=$PATH:$ORACLE_HOME/bin

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib

当时我还没意识到环境变量的问题,用下面两行命令做了个软链接就可以了:

 ln -s /app/oracle/product/10.2/db_1/lib/libocci.so.10.1 /lib/libocci.so.10.1

ln -s /app/oracle/product/10.2/db_1/lib/libclntsh.so.10.1 /lib/libclntsh.so.10.1

 

n       问题四:

程序终于可以运行了,但是,结果很简单:

段错误!!!

到底是哪一行出问题呢?如果你是崭新的linux用户,那么我来告诉你最简单的gdb调试方法:

首先在makefile中编译.cpp.o的时候要加一个-g选项;

然后不直接./oracletest执行程序而是gdb ./oracletest调试执行。

进入调试模式,可以设断点,比如在***.cpp文件的第10行设断点,可以输入命令:break ***.cpp10,然后输入run开始运行到断点处,再用next一步一步执行,或者用step进入函数内部查找,最终,是不是看到段错误就发生在创建环境函数,也就是env=Environment::createEnvironment(Environment::DEFAULT);之处?

如果是,那你可能就跟我一样一直没有完成对环境变量的正确配置:

export ORACLE_BASE=/app/oracle

export ORACLE_HOME=$ORACLE_BASE/product/10.2/db_1

export PATH=$PATH:$ORACLE_HOME/bin

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib

输入上面几句设置环境变量的命令之后,段错误应该是没有了。

 

n       问题五:

接下执行连接数据库的代码最有可能出错的地方就是连接数据库指之处了

如果数据库连接错误,有确认以下几点:

1、          数据库是开启了监听程序的,且连接测试是成功的。

2、          连接数据库函数的参数需要数据库名(格式为:192.168.0.1:1521/test),用户名和密码,请确认这些参数正确。

3、          数据库所在的主机和访问程序所在的主机防火墙可能会阻止改访问,请关闭防火墙或者将端口号55601521都加入信任列表。

如果上述这些步骤没做好,则会出现“无监听程序”或“监听程序当前无法识别连接描述符中请求的服务”之类的错误。

但是上述步骤做好之后,还会在黎明前有一个最黑暗的错误:

Oracle ORA-12154: TNS:could not resolve service name

网上搜,都讲的很复杂,但我试了一个最简单的方法就搞定了:

打开/app/oracle/product/10.2/db_1/network/admin下的sqlnet.ora文件,里面可能只有一句:NAMES.DIRECTORY_PATH= (ONAMES),只要改为NAMES.DIRECTORY_PATH= (TNSNAMES, HOSTNAME, ONAMES),就OK了!

 

相信当你看到Oracle中的数据打印到屏幕上的那一刻,应该是成就满满,收益多多吧!加油!

 

菜鸟新手的摸爬滚打之经验,希望能对大家有帮助~欢迎交流和指正~

 

【转载请注明出处http://user.qzone.qq.com/329331481

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值