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_1026_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@','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. 新建项目
打开Qt Creator软件 --> new Project -->Rmpty qmake Project 新建项目名称,并指定目录。(注意目录一定不可以包含中文)建议保持跟我的一致,特别是创建路径,否则 第四小节 .pro文件的相对路径会不对导致找不到文件。
确定软件跟C编译器版本
3. 拷贝OCI库
将oci包中dmoci目录下的内容拷贝到C:\Windows\SysWOW64
我本地的话就是将 D:\dm8_1026_x86_win_32_ent_8.1.2.84_dmdci\dmoci 拷贝到C:\Windows\SysWOW64
说明
32位的DLL是在C:\Windows\SysWOW64目录下
64位DLL的是在C:\Windows\System32目录下
注意:拷贝过程中会有两个文件提示是否覆盖,这里选否即可
三、调试环境
1. 新建主文件
到项目上–》右键新建添加新文件 新建一个C文件 编辑名称 下一步后目录结构已经变更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@", strlen("zhangsan@") + 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@',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文件
点击Headers–》右键在Explorer中显示 将OCi的Include目录下的两个文件移到项目的目录下 右键—》添加现有文件–》将DCI.h添加进去 注意文件路径 DCI.h这时已经引进来了4. 修改.pro文件
点击项目.pro文件–>添加库文件 点击外部库 勾选oci32位的lib库 会发现.pro文件,出现如下内容 保留INCLUDEPATH跟DEPENDPATH的内容。并进行调整添加QT、QMAKE_LFLAGS、LIBS三个变量的内容。QMAKE_LFLAGS是我的oci的解压路径需要替换成自己的说明
HEADERS += \oci_connect.h \DCI.hSOURCES += \oci_connect.cpp## 添加的内容如下QT += networkQMAKE_LFLAGS=D:\dm8_1026_x86_win_32_ent_8.1.2.84_dmdci\dmoci\dmoci.dllINCLUDEPATH += $$PWD/../../dm8_1026_x86_win_32_ent_8.1.2.84_dmdci/dmociDEPENDPATH += $$PWD/../../dm8_1026_x86_win_32_ent_8.1.2.84_dmdci/dmociLIBS += -L$$PWD/../../dm8_1026_x86_win_32_ent_8.1.2.84_dmdci/dmoci/dmoci.lib
四、运行项目
注意:运行后会出现黑窗口。窗口关了以后,会出现如下信息。
附录
1. Qt Creator 右击添加库无反应解决方案
想向工程添加外部库,但点击添加库无反应
打开 pro 文件 在 pro 文件界面内,右击鼠标,选择添加库 添加库的 UI 弹出 总结解决方案
Qt 5.9.0 Creator 添加库 UI 弹出有 bug
如果觉得《QT Create OCI方式连接达梦数据库》对你有帮助,请点赞、收藏,并留下你的观点哦!