在 debian 上使用 qt 访问 oracle 数据库

之前的一个使用 oracle 的项目自从我把数据库装好之后就没有下文了,最近迎来了另一个也需要使用 oracle 的项目。由于 oracle 不是开源的,为了能在 Linux 下使用 qt 访问,只好自己编译对应的驱动。在网上找了一堆资料,又折腾了好几天,终于在今天晚上成功了,在这里记录一下。

先说一下环境:debian 6.0,qt sdk 4.8.1,oracle 11gR2。本文主要记录的是怎样编译驱动和怎样连接,关于 oracle 数据库服务器端安装中的一些问题可以参考之前的一篇 笔记

准备工作

下载 qt sdk。因为编译的时候 qt 工具链和源代码要保持一致,而 debian 6 源里的 qt 版本是 4.6.3,官网上已经找不到对应的源码包了,所以下载 sdk 安装(sdk 包含工具链和对应版本的源码),安装的时候要注意把安装 source code 的选项勾上。这里使用 QTDIR 表示 qt sdk 的安装目录(这个环境变量不是必须的,只是为了描述方便,因此在下面出现这个目录的时候把它替换成实际的安装目录)。

安装和 oracle 服务端对应版本的 oracle instant client(runtime 部分),安装目录为 ORACLE_CLIENT_HOME(这个环境变量也不是必须的,只是为了描述方便)。这个东东主要包含编译驱动所需的头文件和动态链接库。安装过程就不详细说了,出现的各种错误提示都被我跳过了。选择安装 InstantClient 能找到后面运行需要的 .so。

安装了 qt sdk 和 instant client 的机器以下简称 client,装有 oracle 数据库的机器为 server(当然这两个可以是同一台机器)。

编译 QOCI 驱动

编译工作在 client 上进行。

由于许可证的限制,qt 的开源版本并没有带编译好的 oracle 驱动,但是源代码中有编译的代码,位于(把其中的版本号“4.8.1”替换成实际使用的版本号):

$QTDIR/QtSources/4.8.1/src/plugins/sqldrivers/oci

这个目录下的 main.cpp 和 oci.pro 就是我们需要的代码。从 server 上的 $ORACLE_HOME/lib 中找到 libclntsh.so,libclntsh.so.10.1 和 libclntsh.so.11.1(其实这三个文件是同一个东西,前两个是链接到第三个的符号链接),把它们复制到这个目录,然后在 oci.pro 中添加头文件和库的位置(最后 3 行):

TARGET = qsqloci

SOURCES = main.cpp
include(../../../sql/drivers/oci/qsql_oci.pri)

include(../qsqldriverbase.pri)

INCLUDEPATH+=$ORACLE_CLIENT_HOME/rdbms/public
INCLUDEPATH+=$QTDIR/QtSources/4.8.1/include
LIBS+= -L. -lclntsh

然后使用下面的命令编译:

$QTDIR/Desktop/Qt/4.8.1/gcc/bin/qmake oci.pro
make

如果编译成功会生成一个 libqsqloci.so,这个就是我们需要的驱动。

连接数据库

测试是在 client 上进行的。测试程序放在 $ORACLE_TEST 目录。

把上面生成的 libqsqloci.so 放到下面的目录中:

$QTDIR/Desktop/Qt/4.8.1/gcc/plugins/sqldrivers

然后编写测试程序(把其中的 dbhost,dbusr 和 dbpasswd 替换成实际的名字):

#include <QSqlDatabase>
#include <QSqlError>
#include <QDebug>
#include <QStringList>

int main(void)
{
    QSqlDatabase db; 

    QStringList drivers = QSqlDatabase::drivers();
    foreach (QString driver, drivers) 
        qDebug() << driver;

    db = QSqlDatabase::addDatabase("QOCI");
    db.setDatabaseName("orcl"); /* default */
    db.setPort(1521);

    db.setHostName("<dbhost>");
    db.setUserName("<dbusr>");
    db.setPassword("<dbpasswd>");

    if (db.open())
        qDebug() << "connect ok";
    else
        qDebug() << db.lastError().text();

    return 0;
}

关键时刻到了。先看编译程序的 Makefile:

QTPATH := $(QTDIR)/Desktop/Qt/4.8.1/gcc
INCLUDE := -I$(QTPATH)/include -I$(QTPATH)/include/QtCore -I$(QTPATH)/include/QtSql
LIBS := -L$(QTPATH)/lib -lQtCore -lQtSql -L. -lclntsh

TARGET := oracletest

$(TARGET): oracletest.o
        g++ -o $@ $^ $(LIBS)

.cpp.o:
        g++ -c $< $(INCLUDE)

clean:
        rm -f *.o $(TARGET)

然后设置环境变量:

export ORACLE_HOME=$ORACLE_CLIENT_HOME
export LD_LIBRARY_PATH=$QTDIR/Desktop/Qt/4.8.1/gcc/lib:$ORACLE_HOME/lib:$LD_LIBRARY_PATH

虽然这是在 client 端,但是还是要设置 ORACLE_HOME(没错,client 端的环境变量也叫这个,如果不设置会出现“QOCIDriver: unable to create environment”的错误提示,解决这个问题花了我一下午的时间)。

(2012.09.05 补充)我安装的时候使用的是 oracle client 的完整安装包,安装界面上包含 4 个选项:InstantClient,Administrator,Runtime 和 Custom。如果选了 InstantClient 能找到上面提到的几个 .so,但是运行的时候同样会提示错误“QOCIDriver: unable to create environment”,还要重新装 Runtime,并且在设置环境变量的时候把 ORACLE_HOME 设为 Runtime 部分的安装目录而不是 InstantClient 的目录。

然后编译运行:

make && ./oracletest

如果一切顺利,最后看到的输出类似于下面这样:

"QSQLITE" 
"QOCI8" 
"QOCI" 
connect ok 

后记

写这篇笔记也就用了 1 小时多点,但是这几天的经历和心情却是很痛苦,其中走了多少弯路浪费了多少时间。作为一个使用过 LFS 和 FVWM 的用户,这两次折腾让我最不能明白的是,像 oracle 这种反社会反人类反科学的软件怎么会一直存在并活得好好的。

参考资料

[1] QOCI for the Oracle Call Interface (OCI)
[2] QOCI编译笔记

Comment (1)

  1. Oracle和微软策略类似,用准盗版方式培育市场(软件安装media可以从官网下载,并且没有多少限制,只是没有补丁,对行业标杆用户进行超低价重点培育等),形成了广泛的开发和使用人员队伍,并形成技术舆论影响,一旦有了大批靠他吃饭的,就成功了。
    BTW,目前我也靠他吃饭。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注