在VC中使用OTL访问Oracle和程序发布

在VC中使用OTL访问Oracle和程序发布

 

       在VC中访问Oracle,可以使用ADO或ODBC,如果你比较强大,也可以直接使用OCI API,但我个人认为OTL是最佳选择,它是一套数据库访问C++模板库,全部代码都在otlv4.h头文件中,通过OTL不但可以访问Oracle数据库(使用OCI API),还可以访问DB2,或者使用ODBC连接字符串访问其他数据库。

       本文是总结作者在使用OTL访问Oracle过程中遇到的问题和解决办法,方便以后查阅,如果你正准备在项目中使用OTL,也希望对你有所帮助。

如何使用OTL

       整个OTL只的一个otlv4.h头文件(对于OTL V4.xx)版本,使用OTL只需要include该头文件就可以,但要编译则需要OCI的头文件和Lib文件。

 

#define OTL_ORA9I // Compile OTL 4.0/OCI9i
#include "otlv4.h"

以上声明表示使用Oracle 9i的OCI,需要从安装有Oracle 9i的机器上拷贝oci的头文件和Lib文件,并将路径加入到项目工程中。

 

初始化OTL

在使用OTL连接Oracle服务器之前,需要对OTL进行初始化,主要是初始化OCI库。

 

otl_connect m_db;
 
_putenv(const_cast<char*>("NLS_LANG=SIMPLIFIEDCHINESE_CHINA.ZHS16GBK"));
m_db.otl_initialize(TRUE);// 以线程安全模式初始化OCI环境
 

_putenv()函数用于设置Oracle的NLS_LANG环境变量,这样可以避免读取Oracle的中文数据记录时出现乱码。在网上的许多例子代码中,都是使用db.otl_initialize()无参数方式进行初始化的,因为这些例子都是写在控制台程序的main()函数中,以单线程方式运行,这样是没有问题的。可能你的连接是多线程的,或者创建了多个Oracle连接,这样的代码无法正常工作。给出TRUE参数强制OTL以多线程安全方式初始化OCI库,才可以在多线程环境下正常工作。

 

使用连接字符串

OTL完成初始化后,就可以连接Oracle数据库了,连接Oracle数据库需要为rlogon()函数提供一个连接字符串,一般是这样的:

[User Name]/[Password]@[TNS Alias]

这里的TNS Alias指在tnsnames.ora中配置TNS名,比如我的tnsnames.ora里有以下的配置

 

# Generated by Oracleconfiguration tools.
myora =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.1)(PORT= 1521))
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = ora)
    )
  )
 

哪么TNS Alias就是myora。

 

CString szConnectString = _T("user/xxx@myora");
 
try
{
     m_db.rlogon(szConnectString,1);
}
catch (otl_exception& p)
{
     TRACE(_T("Oracle connecterror(msg:%s, stm_text: %s, sqlstate: %s, var_info: %s)"), p.msg, p.stm_text, p.sqlstate, p.var_info);
}
 

使用该方式连接Oracle需要用户配置tnsnames.ora,比较麻烦并且容易出错。还有一种更简洁的方法是直接使用tnsnames.ora中myora等号后面的字符串作为TNS Alias,这样可以使得Oracle连接配置也可以像MySQL哪样,直接指定Oracle服务IP地址、端口号、服务名、用户和密码就可以连接。为此我写了如下的函数来实现对两种连接字符串方式的支持:

 

BOOL COCIConnect::Connect(LPCTSTRlpszUserName, LPCTSTRlpszPassword, BOOLbUseTNS/* = TRUE*/,LPCTSTR lpszTNSAlias/* = NULL*/,
                              LPCTSTR lpszHost/* =_T("127.0.0.1")*/, WORD wPort/* = 1521*/,BOOL bAutoCommit/* = TRUE*/)
{
     m_bAutoCommit= bAutoCommit;
 
     CString szConnectString;
     if(!lpszTNSAlias || !*lpszTNSAlias)
         szConnectString.Format(_T("%s/%s"), lpszUserName,lpszPassword);
     else{
         CStringszTNSAlias;
         if(bUseTNS)
              szTNSAllas = lpszTNSAlias;
         else
              szTNSAllas.Format(_T("(DESCRIPTION= (ADDRESS = (PROTOCOL = TCP)(HOST = %s)(PORT = %d)) (CONNECT_DATA = (SERVER =DEDICATED) (SERVICE_NAME = %s)))"), lpszHost,(int)wPort, lpszTNSAlias);
        
         szConnectString.Format(_T("%s/%s@%s"), lpszUserName,lpszPassword, szTNSAlias);
     }
 
     try
     {
         m_db.rlogon(szConnectString,(int)bAutoCommit);
 
         returnIsConnected();
     }
     catch(otl_exception& p)
     {
         TRACE(_T("COCIConnect::Connect error(msg:%s, stm_text: %s, sqlstate: %s, var_info: %s)"), p.msg, p.stm_text, p.sqlstate, p.var_info);
         return FALSE;
     }
     catch(...)
     {
         TRACE(_T("COCIConnect::Connect error: Unknown error"));
         return FALSE;
     }
}

 

执行无返回数据集的SQL

DELETE、UPDATE操作都是无返回数据集的操作,好像没什么好说的,直接上代码:

 

BOOL COCIConnect::ExecSQL(LPCTSTRlpszSQL, int*pRowsAffected/* =NULL*/)
{
     long lret = otl_cursor::direct_exec(
         m_db,                                // OCI连接器
         lpszSQL,                         // SQL语句
         otl_exception::disabled          // 禁止OTL异常
         );
 
     if(pRowsAffected)
         *pRowsAffected= lret;
 
     return(lret >= 0);
}
 

这里的pRowsAffected可以返回SQL执行所影响的记录数,比如执行DELETE时返回删除了多少行,执行UPDATE操作时返回更新了多少行。

 

流化读取数据记录

OTL的亮点就在于可以像C++的Stream操作那样读写数据记录,以下代码演示如何执行参数化的查询语句,并使用流化方式读取返回的数据记录集:


CString szSQL = _T("SELECTid,user,age FROM users WHERE age >= :age1<LONG> AND age <=:age2<LONG>");
 
try
{
     otl_stream o(1000, szSQL,m_db);
     o<< 25 << 45;
 
 
     longlID, lAge;
     charszUser[80];
 
     while(!o.eof())
     {
 
         o>> lID >> szUser >> lAge;
 
         //...
     }
 
     return TRUE;
}
catch (otl_exception& p)
{
     TRACE(_T("SELECT error(msg:%s, stm_text: %s, sqlstate: %s, var_info: %s)"), p.msg, p.stm_text, p.sqlstate, p.var_info);
     return FALSE;
}
catch(...)
{
     TRACE(_T("SELECT error:Unknown error"));
     return FALSE;
}


  代码就贴到这里了,快到年关了,人有点懒,还有其他比如参数化执行SQL语句,读写BLOB数据等调用方法可以google到许多。如果还有问题请联系我。我们接着说下一个问题。

程序发布

    由于OTL访问Oracle实际是使用OCI API,所以一般来说需要用户安装Oracle Client,这里提供更简洁的发布方案,用户不需要另行安装OracleClient,而是直接将Oracle Client随你的程序一起发布。为此,你需要到Oracle网站上下载一份InstantClient,我下载的是10.2.0.4 版的,只需要oci.dll和oraociei10.dll动态库文件,为了保证oci.dll在所有的计算机上可以使用,还需要MSVCR71.DLL动态库,这三个文件和你的应用程序放在同一个目录下即可。

    如果想使用tnsnames.ora配置Oracle数据库连接,即可以将该文件和oci.dll动态库放在同一目录下,用户可以手工编辑该文件来配置Oracle连接。

    当OCI访问异常时,会在当前目录生成一个sqlnet.log日志文件,而且这个文件会一直增长,变得非常庞大。我们可以在oci.dll动态库文件所在目录配置一个sqlnet.ora文件,内容如下:

 

# Generated by Oracleconfiguration tools.

LOG_DIRECTORY_CLIENT = NULL

SQLNET.AUTHENTICATION_SERVICES=(NTS)

NAMES.DIRECTORY_PATH=(TNSNAMES, ONAMES, HOSTNAME)

 

       以上红色部分用于禁止输出sqlnet.log日志文件。我们总结一下,发布我们的应用程序需要额外为Oracle Client发布以下的文件:

  •  oci.dll
  • oraociei10.dll
  • MSVCR71.DLL
  • tnsnames.ora
  • sqlnet.ora

将这些文件和你的应用程序一起打成安装包,就可以实现真正的All in one了。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值