QT Create OCI方式连接达梦数据库

QT OCI方式连接达梦数据库

一、背景

用户使用QT Create开发集成软件 OCI的方式连接数据库。

需要说明的是,本人非开发人员,所以在C++代码编写思路或者使用方式上难免有些低级操作,调试中也是参考了网上很多朋友发的帖子,如有错误或者修改建议,欢迎指正。谢谢!

环境

操作系统:Window 10 64位

数据库版本:Dm8.1.2.18

开发工具:QT 5.9.0 MinGw 5.3.0 32bit

OCI的库:QT环境下 C编译器的是32位的,现有提供的OCI64位的是不配套的需要单独申请32位包,本次使用的版本是dm8_20211026_x86_win_32_ent_8.1.2.84_dmdci

主要是分享QT Create软件使用C语言的OCI去连接达梦数据库

二、初始化环境

1. 新建测试SQL

DROP TABLE "SYSDBA"."PERSON" CASCADE;
 
CREATE TABLE "SYSDBA"."PERSON"
(
 "PERSONID" INT IDENTITY(1,1) NOT NULL,
 "SEX" CHAR(1) NOT NULL,
 "NAME" VARCHAR(50) NOT NULL,
 "EMAIL" VARCHAR(50) NULL,
 "PHONE" VARCHAR(25) NULL,
  CLUSTER PRIMARY KEY("PERSONID") ENABLE 
);
 
SET IDENTITY_INSERT "SYSDBA"."PERSON" ON;
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(1,'F','李丽','lily@sina.com','02788548562');
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(2,'M','王刚','','02787584562');
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(3,'M','李勇','','02782585462');
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(4,'F','郭艳','','02787785462');
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(5,'F','孙丽','','13055173012');
SET IDENTITY_INSERT "SYSDBA"."PERSON" OFF;
 
COMMIT;

2. 新建项目

  1. 打开Qt Creator软件 --> new Project -->Rmpty qmake Project

请添加图片描述

  1. 新建项目名称,并指定目录。(注意目录一定不可以包含中文)

建议保持跟我的一致,特别是创建路径,否则 第四小节 .pro文件的相对路径会不对导致找不到文件。

请添加图片描述

  1. 确定软件跟C编译器版本

请添加图片描述

3. 拷贝OCI库

将oci包中dmoci目录下的内容拷贝到C:\Windows\SysWOW64

我本地的话就是将 D:\dm8_20211026_x86_win_32_ent_8.1.2.84_dmdci\dmoci 拷贝到C:\Windows\SysWOW64

说明

32位的DLL是在C:\Windows\SysWOW64目录下

64位DLL的是在C:\Windows\System32目录下

请添加图片描述

注意:拷贝过程中会有两个文件提示是否覆盖,这里选否即可

三、调试环境

1. 新建主文件

  1. 到项目上–》右键新建添加新文件

请添加图片描述

  1. 新建一个C文件

请添加图片描述

  1. 编辑名称

请添加图片描述

  1. 下一步后目录结构已经变更

请添加图片描述

2. 调试代码

将下方的代码 放到ociconnect.cpp文件中


/************************************************************************/
/* DCI编程实例 (较DCI手册示例有改动)									*/
/************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include "DCI.h"

// 声明句柄
DCIEnv* envhp;				/* 环境句柄 */
DCISvcCtx* svchp;			/* 服务环境句柄 */
DCIServer* srvhp;			/* 服务器句柄 */
DCISession* authp;			/* 会话句柄 */
DCIStmt* stmthp;			/* 语句句柄 */
DCIDescribe* dschp;			/* 描述句柄 */
DCIError* errhp;			/* 错误句柄 */
DCIDefine* defhp[3];		/* 定义句柄 */
DCIBind* bidhp[4];			/* 绑定句柄 */
sb2 ind[3];					/* 指示符变量 */

// 绑定select结果集的参数
text szpersonid[11];		/* 存储personid列 */
text szsex[2];				/* 存储sex列 */
text szname[51];			/* 存储name列 */
text szemail[51];			/* 存储mail列 */
text szphone[26];			/* 存储phone列 */
char sql[256];				/* 存储执行的sql语句 */


int main(int argc, char* argv[])
{
    char strServerName[50];
    char strUserName[50];
    char strPassword[50];
    int ret;
    text errbuf[100];

    // 设置服务器,用户名和密码
    strcpy(strServerName, "localhost");
    strcpy(strUserName, "SYSDBA");
    strcpy(strPassword, "SYSDBA");

    // 初始化OCI应用环境
    OCIInitialize(OCI_DEFAULT, NULL, NULL, NULL, NULL);

    // 初始化环境句柄
    OCIEnvInit(&envhp, OCI_DEFAULT, 0, 0);

    // 分配句柄
    OCIHandleAlloc(envhp, (dvoid**)&svchp, OCI_HTYPE_SVCCTX, 0, 0);			/* 服务器环境句柄 */
    OCIHandleAlloc(envhp, (dvoid**)&srvhp, OCI_HTYPE_SERVER, 0, 0);			/* 服务器句柄 */
    OCIHandleAlloc(envhp, (dvoid**)&authp, OCI_HTYPE_SESSION, 0, 0);		/* 会话句柄 */
    OCIHandleAlloc(envhp, (dvoid**)&errhp, OCI_HTYPE_ERROR, 0, 0);			/* 错误句柄 */
    OCIHandleAlloc(envhp, (dvoid**)&dschp, OCI_HTYPE_DESCRIBE, 0, 0);		/* 描述符句柄 */

    // 连接服务器
    OCIServerAttach(srvhp, errhp, (text*)strServerName,
        (sb4)strlen(strServerName), OCI_DEFAULT);

    // 设置用户名和密码
    OCIAttrSet(authp, OCI_HTYPE_SESSION, (text*)strUserName,
        (ub4)strlen(strUserName), OCI_ATTR_USERNAME, errhp);
    OCIAttrSet(authp, OCI_HTYPE_SESSION, (text*)strPassword,
        (ub4)strlen(strPassword), OCI_ATTR_PASSWORD, errhp);

    // 设置服务器环境句柄属性
    OCIAttrSet((dvoid*)svchp, (ub4)OCI_HTYPE_SVCCTX,
        (dvoid*)srvhp, (ub4)0, OCI_ATTR_SERVER, errhp);
    OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, (dvoid*)authp, 0,
        OCI_ATTR_SESSION, errhp);

    // 创建并开始一个用户会话
    OCISessionBegin(svchp, errhp, authp, OCI_CRED_RDBMS, OCI_DEFAULT);
    OCIHandleAlloc(envhp, (dvoid**)&stmthp, OCI_HTYPE_STMT, 0, 0);			/* 语句句柄 */

        /************************************************************************/
        /* 查询person表															*/
        /************************************************************************/

    strcpy(sql, "select personid, name, phone from sysdba.person");

    // 准备SQL语句
    OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

    // 绑定输出列
    OCIDefineByPos(stmthp, &defhp[0], errhp, 1, (ub1*)szpersonid,
        sizeof(szpersonid), SQLT_STR, &ind[0], 0, 0, OCI_DEFAULT);
    OCIDefineByPos(stmthp, &defhp[1], errhp, 2, (ub1*)szname,
        sizeof(szname), SQLT_STR, &ind[1], 0, 0, OCI_DEFAULT);
    OCIDefineByPos(stmthp, &defhp[2], errhp, 3, (ub1*)szphone,
        sizeof(szphone), SQLT_STR, &ind[2], 0, 0, OCI_DEFAULT);

    // 执行SQL语句
    ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)0, 0, NULL, NULL, OCI_DEFAULT);
    if (ret != 0)
    {
        OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
        printf("\n%s\n", errbuf);
    }
    printf("%-10s%-10s%-10s\n", "PERSONID", "NAME", "PHONE");
    while ((OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT)) != OCI_NO_DATA)
    {
        printf("%-10s", szpersonid);
        printf("%-10s", szname);
        printf("%-10s\n", szphone);
    }

    /************************************************************************/
    /* 向person表插入一条数据												*/
    /************************************************************************/

    memset(sql, 0, sizeof(sql));
    strcpy(sql, "insert into sysdba.person(sex, name, email, phone) values(:sex,:name,:email,:phone)");

    // 准备SQL语句
    OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

    // 设置输入参数
    memset(szsex, 0, sizeof(szsex));
    memcpy(szsex, "M", strlen("M") + 1);
    memset(szname, 0, sizeof(szname));
    memcpy(szname, "张三", strlen("张三") + 1);
    memset(szemail, 0, sizeof(szemail));
    memcpy(szemail, "zhangsan@dameng.com", strlen("zhangsan@dameng.com") + 1);
    memset(szphone, 0, sizeof(szphone));
    memcpy(szphone, "02712345678", strlen("02712345678") + 1);

    // 绑定输入列
    const OraText col_sex[] = ":sex";
    const OraText col_name[] = ":name";
    const OraText col_email[] = ":email";
    const OraText col_phone[] = ":phone";
    OCIBindByName(stmthp, &bidhp[0], errhp, col_sex, 4, szsex,
        strlen((char*)szsex), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
    OCIBindByName(stmthp, &bidhp[1], errhp, col_name, 5, szname,
        strlen((char*)szname), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
    OCIBindByName(stmthp, &bidhp[2], errhp, col_email, 6, szemail,
        strlen((char*)szemail), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
    OCIBindByName(stmthp, &bidhp[3], errhp, col_phone, 6, szphone,
        strlen((char*)szphone), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);

    // 执行SQL语句
    ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0,
        (CONST OCISnapshot*) 0, (OCISnapshot*)0, (ub4)OCI_DEFAULT);
    if (ret != 0)
    {
        OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
        printf("\n%s\n", errbuf);
    }
    else
    {
        printf("\n新增了 name = 张三 的记录。\n");
    }

    // 提交到数据库
    OCITransCommit(svchp, errhp, OCI_DEFAULT);


    /************************************************************************/
    /* 更新person表															*/
    /************************************************************************/

    memset(sql, 0, sizeof(sql));
    strcpy(sql, "update sysdba.person set sex='M',name='Liuhuan',email='12345678@qq.com',phone='13399990000' WHERE personid='1'");

    // 准备SQL语句
    OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

    // 执行SQL语句
    ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0,
        (CONST OCISnapshot*)0, (OCISnapshot*)0, (ub4)OCI_DEFAULT);
    if (ret != 0)
    {
        OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
        printf("\n%s\n", errbuf);
    }
    else
    {
        printf("\n更新了 personid = 1 的记录。\n");
    }

    // 提交到数据库
    OCITransCommit(svchp, errhp, OCI_DEFAULT);


    /************************************************************************/
    /* 删除person表的ID为5的记录,首先要在数据库中存在这条记录				*/
    /************************************************************************/

    memset(sql, 0, sizeof(sql));
    strcpy(sql, "delete from sysdba.person WHERE personid=:1");

    // 准备SQL语句
    OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

    // 绑定输入参数
    memset(szpersonid, 0, sizeof(szpersonid));
    memcpy(szpersonid, "5", strlen("5") + 1);
    OCIBindByPos(stmthp, &bidhp[0], errhp, 1, szpersonid, strlen((char*)szpersonid),
        SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);

    // 执行SQL语句
    ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0,
        (CONST OCISnapshot*) 0, (OCISnapshot*)0, (ub4)OCI_DEFAULT);
    if (ret != 0)
    {
        OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
        printf("\n%s\n", errbuf);
    }
    else
    {
        printf("\n删除了 personid = 5 的记录。\n");
    }

    // 提交到数据库
    OCITransCommit(svchp, errhp, OCI_DEFAULT);


    /************************************************************************/
    /* 再次查询person表														*/
    /************************************************************************/

    strcpy(sql, "select personid, name, phone from sysdba.person");

    // 准备SQL语句
    OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

    // 绑定输出列
    OCIDefineByPos(stmthp, &defhp[0], errhp, 1, (ub1*)szpersonid,
        sizeof(szpersonid), SQLT_STR, &ind[0], 0, 0, OCI_DEFAULT);
    OCIDefineByPos(stmthp, &defhp[1], errhp, 2, (ub1*)szname,
        sizeof(szname), SQLT_STR, &ind[1], 0, 0, OCI_DEFAULT);
    OCIDefineByPos(stmthp, &defhp[2], errhp, 3, (ub1*)szphone,
        sizeof(szphone), SQLT_STR, &ind[2], 0, 0, OCI_DEFAULT);

    // 执行SQL语句
    ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)0, 0, NULL, NULL, OCI_DEFAULT);
    if (ret != 0)
    {
        OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
        printf("\n%s\n", errbuf);
    }
    printf("\n%-10s%-10s%-10s\n", "PERSONID", "NAME", "PHONE");
    while ((OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT)) != OCI_NO_DATA)
    {
        printf("%-10s", szpersonid);
        printf("%-10s", szname);
        printf("%-10s\n", szphone);
    }


    //结束会话
    OCISessionEnd(svchp, errhp, authp, (ub4)0);

    //断开与数据库的连接
    OCIServerDetach(srvhp, errhp, OCI_DEFAULT);

    //释放OCI句柄
    OCIHandleFree((dvoid*)dschp, OCI_HTYPE_DESCRIBE);
    OCIHandleFree((dvoid*)stmthp, OCI_HTYPE_STMT);
    OCIHandleFree((dvoid*)errhp, OCI_HTYPE_ERROR);
    OCIHandleFree((dvoid*)authp, OCI_HTYPE_SESSION);
    OCIHandleFree((dvoid*)svchp, OCI_HTYPE_SVCCTX);
    OCIHandleFree((dvoid*)srvhp, OCI_HTYPE_SERVER);
    system("pause");
    return 0;
}

3. 添加Headers文件

  1. 点击Headers–》右键在Explorer中显示

请添加图片描述

  1. 将OCi的Include目录下的两个文件移到项目的目录下

请添加图片描述

  1. 右键—》添加现有文件–》将DCI.h添加进去

请添加图片描述

请添加图片描述

  1. 注意文件路径 DCI.h这时已经引进来了

请添加图片描述

4. 修改.pro文件

  1. 点击项目.pro文件–>添加库文件

请添加图片描述

  1. 点击外部库

请添加图片描述

  1. 勾选oci32位的lib库

请添加图片描述

  1. 会发现.pro文件,出现如下内容

请添加图片描述

  1. 保留INCLUDEPATHDEPENDPATH的内容。并进行调整

说明

  1. 添加QT、QMAKE_LFLAGS、LIBS三个变量的内容。
  2. QMAKE_LFLAGS是我的oci的解压路径需要替换成自己的
HEADERS += \
    oci_connect.h \
    DCI.h

SOURCES += \
    oci_connect.cpp

## 添加的内容如下
QT += network
QMAKE_LFLAGS=D:\dm8_20211026_x86_win_32_ent_8.1.2.84_dmdci\dmoci\dmoci.dll
INCLUDEPATH += $$PWD/../../dm8_20211026_x86_win_32_ent_8.1.2.84_dmdci/dmoci
DEPENDPATH += $$PWD/../../dm8_20211026_x86_win_32_ent_8.1.2.84_dmdci/dmoci
LIBS += -L$$PWD/../../dm8_20211026_x86_win_32_ent_8.1.2.84_dmdci/dmoci/dmoci.lib

四、运行项目

注意:运行后会出现黑窗口。窗口关了以后,会出现如下信息。

请添加图片描述

附录

1. Qt Creator 右击添加库无反应解决方案

想向工程添加外部库,但点击添加库无反应

请添加图片描述

解决方案

  1. 打开 pro 文件

请添加图片描述

  1. 在 pro 文件界面内,右击鼠标,选择添加库

请添加图片描述

  1. 添加库的 UI 弹出

请添加图片描述

  1. 总结
    Qt 5.9.0 Creator 添加库 UI 弹出有 bug
  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值