C++使用DCI连接和操作达梦数据库DM8简单示例

需求

近期有相关需求,需要一个DCI的使用示例。不知何原因,我从《DM8 - DCI Program》手册复制过来的示例无法在我本地正常编译,存在各种各样的报错和警告。折腾了几天总算有些收获,实现了简单的增删改查,在此记录以备自己查询,也为有需要的朋友提供参考。

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

环境

操作系统:Windows 10 家庭中文版

数据库版本:DM8.1.2.38

开发工具:Microsoft Visual Studio Community 2019(版本 16.11.2)

DM8相对较老的一些版本在安装数据库以后就可以在“安装目录/drivers”下看到dci、oci目录,但是新版本已经将oci单独打包,名字为xxxx_dmdci.zip,内有oci和occi所需的头文件和库文件。数据库的drivers目录仍然保留有dci目录与库文件。需要oci相关文件的朋友需要注意一下这里。

示例数据

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;

实现过程

准备C++程序

先在VS里新建一个空的控制台项目:

设置项目名称及路径:

创建项目demo后,再新建一个源文件,也就是我们的C++代码文件demo.cpp,如下:

在刚才创建的demo.cpp源文件中贴入以下代码(备注:与官方文档的示例代码略有区别):

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

// 声明句柄
OCIEnv* envhp;				/* 环境句柄 */
OCISvcCtx* svchp;			/* 服务环境句柄 */
OCIServer* srvhp;			/* 服务器句柄 */
OCISession* authp;			/* 会话句柄 */
OCIStmt* stmthp;			/* 语句句柄 */
OCIDescribe* dschp;			/* 描述句柄 */
OCIError* errhp;			/* 错误句柄 */
OCIDefine* defhp[3];		/* 定义句柄 */
OCIBind* 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;
}

配置项目调试参数

这时会出现很多波浪线提示错误信息,这是因为我们还没有配置好相关的文件路径参数。首先把DCI.h文件放到源文件所在路径下,即项目根目录下。

再到项目资源管理器中,添加头文件DCI.h(添加以后那些代码报错应该消失了):

再到项目目录下新建库文件目录lib,将dmoci.lib库文件和动态库文件(.dll文件)拷贝至lib下 。然后配置项目属性:

由于我使用的x64版本数据库的驱动,希望编译出在x64环境运行的程序,因此这里首先需要在配置属性里将平台改为x64:

 然后设置C++附加包含目录,绝对路径或者相对路径都可以,这里我设置相对路径.\lib:

 设置链接器附加库目录(这里存放链接时需要用到的动态库文件):

设置链接器输入附加依赖项,dmoci.lib,分号分隔,该文件需要提前存放在我们上一步选择的目录下。如下:

调试和生成可执行文件

开始调试,如果调试过程有问题,可以通过F11逐步或者F10逐过程调试程序,观察内存值的情况。

正常情况下会弹出以下控制台,可以看到程序运行成功了:

到这里,示例功能就算实现了。

如果需要可执行文件,我们可以在右侧解决方案管理器界面右键项目,点击重新生成。

这时在项目调试目录(我这里是D:\VS\demo\x64\Debug)下会生成一个demo.exe可执行文件。将我们在项目里建的lib目录中的所有dll文件拷贝至操作系统C:\Windows\System32下,准备好示例数据,就可以双击运行它了:

(我们编译的是x64程序,为什么拷贝到System32下呢?其实没错,System32是64位程序使用的目录,C:\Windows\SysWOW64是32位程序使用的目录)

遇到的问题

1、编译时错误:E0167与C2664

使用手册示例代码编译时报以下错误:

        E0167    "text *" 类型的实参与 "char *" 类型的形参不兼容

        C2664    “char *strcpy(char *,const char *)”: 无法将参数 1 从“text [2]”转换为“char *”

        C2664    “sword OCIBindByName(OCIStmt *,OCIBind **,OCIError *,const OraText *,sb4,void *,sb4,ub2,void *,ub2 *,ub2 *,ub4,ub4 *,ub4)”: 无法将参数 4 从“const char [5]”转换为“const OraText *” 

原手册示例代码在绑定输入列之前设置输入参数的时候,使用了strcpy函数,这里可能是常量和变量之间的关系问题。

处理办法:由于不知道怎么处理该报错,于是改用memcpy函数并定义了局部变量,绕过该问题。

原代码(部分列,其他列类似):

memset(szsex, 0, sizeof(szsex));
strcpy(szsex, "M");
OCIBindByName (stmthp, &bidhp[0], errhp, ":sex", 4, szsex,
strlen((char*)szsex), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);

修改后的代码(部分列,其他列类似):

memset(szsex, 0, sizeof(szsex));
memcpy(szsex, "M", strlen("M") + 1);
const OraText col_sex[] = ":sex";
OCIBindByName(stmthp, &bidhp[0], errhp, col_sex, 4, szsex,
strlen((char*)szsex), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);

2、编译时错误:LNK2019

最初我在链接器配置的输入文件为dmdci.lib,在编译时出现该错误,意思是我们源文件里main函数调用的这些内容在链接文件里没有被找到。

处理办法:将链接器输入文件名更换为dmoci.lib文件就可以了,其实官方DCI手册的内容写明了这里需要依赖dmoci.lib库。这里除了修改链接器输入文件名,还要注意需要拷贝oci目录下的dmoci.lib这个文件到链接器附加库目录下。

 3、编译时错误:C4996

由于代码中使用了strcpy函数,新开发工具编译器认为该函数存在安全问题,推荐使用strcpy_s函数。

处理办法:由于我不会改写,干脆屏蔽掉这个问题。两种方法任选其一:

方法一:在代码最上面(即:第一条#include代码的上方)添加以下信息:

#define _CRT_SECURE_NO_WARNINGS

方法二:如果不想动代码,也可以在项目 -> 属性 -> C/C++ -> 预处理器 -> 预处理器定义中添加 _CRT_SECURE_NO_WARNINGS 这个预定义,注意使用分号分隔:

4、运行时错误:找不到动态库

编译及生成可执行文件都能正常进行,但运行时出现以下系统错误:

这是因为没有将动态库文件拷贝到操作系统库目录去,操作系统在自己的环境变量目录中找不到需要的文件。

处理办法:将zip包解压出的oci目录下的所有dll文件拷贝到System32下即可(也可以通过配置系统环境变量实现)。

更多资讯请上达梦技术社区了解:https://eco.dameng.com

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个示例代码,展示了如何使用达梦数据库DCI(Database Connectivity Interface)库来连接达梦数据库: ```c #include <stdio.h> #include <stdlib.h> #include <dciapi.h> // 包含达梦数据库 DCI 头文件 #define MAX_BUFFER_SIZE 100 int main() { DCI_ENV* env; // DCI 环境变量 DCI_CONNECTION* conn; // DCI 连接 DCI_STATEMENT* stmt; // DCI 语句 char connStr[MAX_BUFFER_SIZE] = "dbname=mydatabase;host=myhost;port=myport;user=myuser;password=mypassword"; // 连接字符串,根据实际情况修改 // 初始化 DCI 环境 if (DCIAllocEnv(&env) != DCI_SUCCESS) { printf("Failed to allocate DCI environment.\n"); return 1; } // 连接达梦数据库 if (DCIConnect(env, connStr, &conn) != DCI_SUCCESS) { printf("Failed to connect to the database.\n"); DCIReleaseEnv(env); return 1; } // 执行查询语句 if (DCIExecDirect(conn, "SELECT * FROM mytable", &stmt) == DCI_SUCCESS) { // 获取查询结果 while (DCIFetch(stmt) == DCI_SUCCESS) { // 处理查询结果 // ... } // 释放语句资源 DCICloseCursor(stmt); } else { printf("Failed to execute the query.\n"); } // 断开与达梦数据库连接 DCIDisconnect(conn); // 释放 DCI 环境 DCIReleaseEnv(env); return 0; } ``` 在上述示例代码中,我们使用达梦数据库提供的 DCI 头文件,并通过 DCI 相关的函数来完成数据库连接、查询和断开连接操作。你需要根据实际情况修改连接字符串和查询语句,以适应你的数据库环境和需求。 请注意,以上代码仅为示例,实际使用时需要根据具体情况进行适当的错误处理和资源释放。 希望这个示例能对你有所帮助!如果还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

XJ_MrCat

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

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

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

打赏作者

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

抵扣说明:

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

余额充值