通过OTLV4分别使用ODBC和OCI链接达梦数据库方法对比

背景

在公司的一个项目中,要使用达梦替换Oracle数据库。除了对数据库中对象的迁移外还需要对应用进行迁移,一般企业中数据库应用通常通过JDBC,OCI(如果是Oracle)/ODBC等方式链接数据库。JDBC不讲了,一般换上对应的Driver就可以搞定,问题不大,但C/C++应用往往就未必那么容易。及时不考虑版权问题,目前国产数据库直接可以兼容OCI的并不多,一般MySQL系和PostgreSQL系的数据库会显得力不从心,但一些自研的国产数据库这方便往往兼容起来会相对容易些,目前我接触的国产数据库中达梦和OceanBase是可以做到兼容OCI接口的。其他的数据库就需要使用ODBC的方式进行迁移。今天以达梦为例,分别介绍如何使用OCI和ODBC进行C/C++数据库应用的迁移。以及两者在迁移过程中的区别。

技术栈简介

  • OTL
    做过OCI/OCCI应用开发的小伙伴应该感触最深的就是接口太复杂,参数太多,生产效率低。(OCCI会少好一些),在企业中一般都会对OCI的接口进行二次封账(要不代码真的很难看)。之前很多项目都是基于公司自己封装的接口进行开发。当然如果你的公司没有这方面的积累也不用慌,有很多这样的开源框架可以选择(包括一些ORM框架),我个人比较倾向使用OTLv4,也是我们正在使用的integrated library。它兼容大部分主流的数据,包括:Oracle,mysql,MSSQL,POSTGRESQL等;
  • OCI
    Oracle Call Interface,提供了一组可对ORACLE数据库进行存取的接口子例程(函数);
  • ODBC
    Open Database Connectivity,是为解决异构数据库间的数据共享的开放式数据库通用接口;

测试环境

软件版本
操作系统CentOS7
数据库达梦DM8
DBIOTLv4

获取otl 4.0 http://otl.sourceforge.net/otl3_compile.htm

代码准备

测试表

CREATE TABLE gchdb.company
(
            id integer NOT NULL,
            name character varying(20),
            age integer NOT NULL,
            address character(25),
            salary numeric(18,2)
);

ODBC环境搭建

参考:c++ 通过ODBC访问达梦数据库DM8

OCI链接达梦

-CMakeLists.txt

cmake_minimum_required (VERSION 2.8)
project (otloci)

set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS_DEBUG "-g3")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
set(CMAKE_BUILD_TYPE Debug)

include_directories(./)
include_directories(/usr/local/include)
include_directories(/home/dmdba/dmoci/include)

add_executable(otloci otloci.cpp)
link_directories("/home/dmdba/dmoci")
target_link_libraries(otloci /home/dmdba/dmoci/libdmoci.so)

关键配置:

  • target_link_libraries(otloci /home/dmdba/dmoci/libdmoci.so)dmoci的库文件;
  • /home/dmdba/dmoci/include接口所需头文件路径

核心源码

/// 主要宏定义
#define OTL_ORA12C // 适配Oracle 12C
#define OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE  // 8i以上需要加这个

/// 保护otlv4头文件
#include "otlv4.h" // include the OTL 4.0 header file

/// 部分测试代码
    try
    {
        otl_nocommit_stream o;
        o.open(50, // buffer size
        "merge INTO SYSDBA.COMPANY "
        "using DUAL on (id = :id<int>) "
        "when NOT MATCHED THEN "
        "insert (id,name,age,address,salary) values (:id<int>,:name<char[21]>,:age<int>,:address<char[26]>,:salary<float>) "
        "when matched then "
        "update set name=:name<char[21]>, age=:age<int>, address=:address<char[26]>,salary=:salary<float>",
            // SQL statement
            db // connect object
        );

        o.set_flush(false);
        o.set_commit(1);
        char name[50] = {"小明"};
        name[20] = '\0';
        o << 103 << name << 18 << "dalian" << static_cast<float>(10000);

        o.flush();
        // db.commit();
    }
    catch(otl_exception& p)
    {
        cout<<"otl_exception:"<<endl;
        cerr<<p.msg<<endl; // print out error message
        cerr<<p.stm_text<<endl; // print out SQL that caused the error
        cerr<<p.var_info<<endl; // print out the variable that caused the error
    }

测试

使用OCI的测试结果

id=103
age=18
name=小明
address=dalian

使用ODBC的测试结果

otl_exception:
Incompatible data types in stream operation
merge INTO SYSDBA.COMPANY using DUAL on (id = ?       ) when NOT MATCHED THEN insert (id,name,age,address,salary) values (?        ,?              ,?        ,?                 ,?             ) when matched then update set name=?               , age=?         , address=?                  ,salary=?
Variable: :id1<INT>, datatype in operator <</>>: CHAR

看报错应该是类型不匹配,但实际上是绑定的参数个数错误

分析

首先在OTL官网找到一些线索

大概意思是:OCI可以按名字绑定,而ODBC是按位置进行绑定。

以这个线索进行分析,同名的变量例如:name<char[21]>在OCI中出现了两次,实际上只需要进行一次赋值。而ODBC中,首先不允许同名变量出现多次,且要对每一个:VAR进行赋值。因此绑定语句如下:

        otl_nocommit_stream o;
        o.open(50, // buffer size
        "merge INTO SYSDBA.COMPANY "
        "using DUAL on (id = :id<int>) "
        "when NOT MATCHED THEN "
        "insert (id,name,age,address,salary) values (:id1<int>,:name<char[21]>,:age<int>,:address<char[26]>,:salary<float>) "
        "when matched then "
        "update set name=:name1<char[21]>, age=:age1<int>, address=:address1<char[26]>,salary=:salary1<float>",
            // SQL statement
            db // connect object
        );

:name和:name1进行了区分

下面开始修改赋值,要与绑定字段的位置和数量匹配。

o << 106 << 106 << name << 16 << "dalian"  << static_cast<float>(10000) << name << 16 << "dalian" << static_cast<float>(1000);

验证

修改后重新编译,运行,结果如下:

id=106
age=16
name=小红
address=dalian
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏 克

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值