Windows Service开发系列(ODBC开发)(一) -- ODBC简介与一般操作流程

Windows Service开发系列(ODBC开发)(一) – ODBC简介与一般操作流程

【1】ODBC简介
开放数据库互连(ODBC)是微软提出的数据库访问接口标准。开放数据库互连定义了访问数据库的API一个规范,这些API独立于不同厂商的DBMS(数据库管理系统),也独立于具体的编程语言。通过使用ODBC,应用程序能够使用相同的源代码和各种各样的数据库进行交互。这使得开发者不需要以特殊的数据库管理系统DBMS为目标,或者了解不同支撑背景的数据库的详细细节,就能够开发和发布客户/服务器应用程序。
【2】ODBC的句柄
应用程序运行后,为维护执行的状态,ODBC 管理器和ODBC 驱动程序中必须保持足够的控制信息。应用程序要求ODBC 管理器和ODBC 驱动程序为ODBC环境、每个连接以及每个SQL语句分配描述/控制信息存储空间,并返回指向各个存储区的句柄供其使用。
(1)环境句柄:整个ODBC上下文的根句柄。标识全程数据访问控制信息的内存结构,包括有效连接句柄以及当前活动连接句柄。ODBC将环境句柄定义为HENV类型的变量。应用程序使用单一的环境句柄,在连接到数据源以前必须申请该句柄。
(2)连接句柄:管理有关数据库会话的所有信息。连接句柄标识每个特定的连接信息的内存结构。ODBC将连接句柄定义为HDBC类型的变量。应用程序在连接数据源之前申请连接句柄。每个连接句柄与环境句柄有关,环境句柄上可以有多个与其有关的连接句柄。
(3)语句句柄:ODBC语句包括应用访问数据源的SQL语句和语句相关的管理信息,语句句柄标识每个语句管理信息的内存结构。ODBC将语句句柄定义为HSTMT类型的变量。应用程序在提交SQL请求之前也必须申请语句句柄。每个语句句柄与一个连接句柄有关,每个连接句柄上可以有多个与其有关的语句句柄。
【3】ODBC操作数据库的流程
在这里插入图片描述
【4】对应流程常用的API函数
分配环境句柄
初始化环境句柄; SQLAllocHandle
设置环境选项; SQLSetEnvAttr

分配连接句柄
初始化连接句柄; SQLAllocHandle

建立数据源
建立连接; SQLConnect

分配语句句柄
初始化语句句柄; SQLAllocHandle
设置语句选项; SQLSetStmtAttr
设置 事务处理方式(提交或回滚);SQLEndTran
注:回滚,在事务提交之前将数据库数据恢复到事务修改之前数据库数据状态

执行SQL语句
执行特定的SQL语句; SQLExecDirect
绑定数据; SQLBindCol
移动游标,取得下一个数据; SQLFetch

结束应用程序
释放语句句柄; SQLFreeHandle
关闭连接; SQLDisconnect
释放连接句柄; SQLFreeHandle
释放环境句柄; SQLFreeHandle

【5】常用函数及其参数详解
SQLAllocHandle
原型:
SQLRETURN SQLAllocHandle( SQL SMALLINT HandleType,
SQLHANDLE InputHandle,
SQLHANDLE *OutputHandlePtr );
输入参数:
HandleType ,取值说明:

  • SQL_HANDLE_ENV 分配的是环境句柄
  • SQL_HANDLE_DBC 分配的是数据库连接句柄
  • SQL_HANDLE_STMT 分配的是语句句柄
  • SQL_HANDLE_DESC 分配的是描述句柄
    InputHandle,取值说明:输入参数,指定分配新句柄的相关联的句柄具体值与HandleType相对应
    即:
  • SQL_HANDLE_ENV SQL_NULL_HANDLE
  • SQL_HANDLE_DBC 连接所属的环境句柄
  • SQL_HANDLE_STMT 语句所属的连接句柄
  • SQL_HANDLE_DESC 描述所属的句柄连接
    OutputHandlePtr,取值说明:输出参数,返回分配的句柄
    返回值:
  • 若返回SQL_SUCCESS 表示成功执行
  • 若返回SQL_SUCCESS_WITH_INFO 表示成功执行并有返回信息
  • 若返回SQL_INVALIB_HANDLE 表示无效句柄

SQLSetEnvAttr
原型:
SQLRETURN SQLSetEnvAttr( SQOHENV EnvironmentHandle,
SQLINTEGER Attribute,
SQLPOINTER ValuePtr,
SQLINTEGER StringLength );
输入参数:
EnvironmentHandle,取值说明:指定设置环境属性的环境句柄
Attribute,取值说明:指定设置的环境属性
ValuePtr,取值说明:指定要设置的环境属性值
StringLength ,取值说明:指定ValuePtr指向字符串或二进制缓冲区数据的长度,如果ValuePtr指向其他数据类型,则忽略此参数

SQLConnect
原型:
SQLRETURN SQLConnect( SQLHDBC ConnectionHandle. SQLCHAR* ServerName,
SQLSMALLINT NameLength1,
SQLCHAR* UserName,
SQLSMALLINT NameLength2,
SQLCHAR* Authentication,
SQLSMALLINT NameLength3 );
输入参数:
ConnectionHandle ,取值说明:指定建立连接的连接句柄
ServerName ,取值说明:指定建立连接的数据源名称
NameLength1 ,取值说明:指定ServerName的长度
UserName ,取值说明:指定建立连接采用的用户ID
NameLength2 ,取值说明:指定
UserName的长度
Authentication ,取值说明:指定此用户的密码
NameLength3 ,取值说明:指定*Authentication的长度

SQLEndTran
原型:
SQLRETURN SQLEndTran(
SQLSMALLINT HandleType,
SQLHANDLE Handle,
SQLSMALLINT CompletionType);
输入参数:
HandleType,取值说明:句柄类型(SQL_HANDLE_ENV或SQL_HANDLE_DBC);
Handle,取值说明:具体的处理句柄;
CompletionType,取值说明:SQL_COMMIT(提交操作) SQL_ROLLBACK(回滚操作)

SQLExecDirect
原型:
SQLRETURN SQLExecDirect( SQLHSTMT StatementHandle,SQLCHAR StatementText,SQLINTEGER TextLength );
输入参数:
StatementHandle ,取值说明:设置要执行的语句
StatementText ,取值说明:设置要执行的SQL语句
TextLength ,取值说明:指定*StatementText缓冲区的长度

SQLBindCol
原型:
SQLRETURN SQLBindCol( SQLHSTMT StatementHandle,
SQLUSMALLINT ColumnNumber,
SQLSMALLINT TargetType,
SQLPOINTER TargetValuePtr,
SQLINTEGER BufferLength,
SQLLEN * StrLen_or_Ind );
输入参数:
StatementHandle ,取值说明:语句句柄
ColumnNumber ,取值说明:列的位置,如果用户使用书签(BookMark),也就是设置语句句柄属性为SQL_ATTR_USE_BOOKMARKS时,列号的基号为0,0表示书签,如果用户不使用书签,设置语句句柄为SQL_UB_OFF时,其基值为1,它们的顺序是按记录集中返回列的顺序递增的
TargetType ,取值说明:指用于参数绑定C语言数据类型,当调用SQLFetch,SQLFetchScroll,SQLBulkOperator,SQLSetPos等函数从数据源检索数据时,驱动程序将数据类型换行为此类型;当调用SQLBulkOperator,SQLSetPos等函数将数据发送到数据源时,驱动程序将数据装潢成数据源对应的数据类型
TargetValuePtr ,取值说明:延迟输入/输出参数,为绑定列数据缓冲区指针,如果其值为空指针,则驱动程序会解除列于数据缓冲区的绑定,但长度/指示器缓冲区与此列绑定,也就是说,如果TargeValuePtr为空指针,后面的参数StrLen_or_IndPtr仍然保存为此列的长度/指示器缓冲区指针
BufferLength ,取值说明:指明参数指针所指向的缓冲区的字节数大小.对于字符串和结构需要指明大小,而对于普通的变量如SQLINTEGER,SQLFLOAT等设置为0就可以了
StrLen_or_IndPtr ,取值说明:延迟输入/输出参数,返回拷贝的缓冲区的数据的字节数

SQLFetch
原型:
SQLRETURN SQLFetch(SQLHSTMT StatementHandle);
输入参数:
StatementHandle ,取值说明:STMT句柄
在你调用SQLExecDirect执行SQL语句后,你需要遍历结果集来得到数据.StatementHandle是STMT句柄,此句柄必须是被执行过.
当调用SQLFetch 函数后,光标会被移动到下一条记录处,当光标移动到记录集的最后一条,函数将会返回SQL_NO_DAT

SQLFreeHandle
原型:
SQLRETURN SQLFreeHandle( SQLSMALLINT HandleType,SQLHANDLE Handle );
输入参数:
HandleType ,取值说明:要分配的句柄类型.UltraLite 支持以下句柄类型:
SQL_HANDLE_ENV
SQL_HANDLE_DBC
SQL_HANDLE_STMT

SQLDisconnect
原型:
SQLRETURN SQLDisconnect(SQLHDBC ConnectionHandle );
输入参数:
ConnectionHandle,取值说明:要关闭的连接的句柄

【6】ODBC操作数据库示例程序
postgresql.h

#define insert_type 0
#define delete_type 1
#define update_type 2
#define select_type 3

#define FILE_PATH "F:\\log.txt"  //LOG文件的存放路径

struct Member{
 SQLLEN id;
 SQLCHAR username[255];
 SQLCHAR passwad[255];
}member;

struct ConnetctInfo{
 SQLHENV henv;//SQL环境句柄
    SQLHDBC hdbc;//数据库连接句柄
    SQLHSTMT hstmt;//执行语句句柄
 SQLCHAR* DataSourceName;
 SQLCHAR* UserName;
 SQLCHAR* PassWadName;
}connectInfo;

主程序引入头文件

#include "stdafx.h"
#include<stdio.h>
#include<windows.h> 
#include<iostream>
//SQL相关头文件
#include<sql.h> 
#include<sqlext.h> 
#include<sqltypes.h>
#include<odbcinst.h>
//博主自定义的头文件
#include"postgresql.h"

#pragma comment(lib,"odbc32.lib")
#pragma comment(lib,"odbccp32.lib")

using namespace std;

WriteToLog函数

int WriteToLog(char* str)     //自定义的写日志函数
{
    FILE* pfile;
    fopen_s(&pfile,FILE_PATH,"a+"); //已追加的方式打开文件;fopen_s函数的安全性更高;
    if (pfile==NULL)     
    {
        return -1; 
    }
    fprintf_s(pfile,"%s\n",str);  //向文件中写入字符串
    fclose(pfile);               //关闭打开的文件
    return 0;
}

init_ODBC_function函数

SQLRETURN init_ODBC_function(ConnetctInfo* pconnectInfo){
	SQLRETURN ret;
	ret=SQLAllocHandle(SQL_HANDLE_ENV,NULL,&(*pconnectInfo).henv);//申请环境句柄
	if(ret==SQL_SUCCESS || ret==SQL_SUCCESS_WITH_INFO){ 
  		WriteToLog("Regist the Env_Handle Success!");
 	}
 	else{
  		WriteToLog("Regist the Env_Handle Failed!");
	}
	ret=SQLSetEnvAttr((*pconnectInfo).henv,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC2,SQL_IS_INTEGER);//设置环境属性
	if(ret==SQL_SUCCESS || ret==SQL_SUCCESS_WITH_INFO){
  		WriteToLog("Regist the Env_Attr Success!");
	}
	else{
  		WriteToLog("Regist the Env_Attr Failed!");
	}
	ret=SQLAllocHandle(SQL_HANDLE_DBC,(*pconnectInfo).henv,&(*pconnectInfo).hdbc);//申请数据库连接句柄
	if(ret==SQL_SUCCESS || ret==SQL_SUCCESS_WITH_INFO){ 
  		WriteToLog("Regist the SQL_HANDLE_DBC Success!");
	}
	else{
  		WriteToLog("Regist the SQL_HANDLE_DBC Failed!");
	}
	ret=SQLConnect((*pconnectInfo).hdbc,(*pconnectInfo).DataSourceName,SQL_NTS,(*pconnectInfo).UserName,SQL_NTS,(*pconnectInfo).PassWadName,SQL_NTS);//连接数据库
	 if(ret==SQL_SUCCESS || ret==SQL_SUCCESS_WITH_INFO){ 
  		WriteToLog("Link the DataBase Success!");
	}
 	else{
  		WriteToLog("Link the DataBase Failed!");
	}
	ret = SQLAllocHandle(SQL_HANDLE_STMT, (*pconnectInfo).hdbc, &(*pconnectInfo).hstmt);
	if(ret==SQL_SUCCESS || ret==SQL_SUCCESS_WITH_INFO){ 
  		WriteToLog("Regist the SQL_HANDLE_STMT Success!");
	}
 	else{
  		WriteToLog("Regist the SQL_HANDLE_STMT Failed!");
	}
	return ret;
}

relase_ODBC_function函数

SQLRETURN relase_ODBC_function(ConnetctInfo* pconnectInfo){
	SQLRETURN ret;
 	/** 释放各个句柄,注意顺序,不要改变**/ 
 	ret=SQLFreeHandle(SQL_HANDLE_STMT, (*pconnectInfo).hstmt);  //释放执行句柄
 	ret=SQLDisconnect((*pconnectInfo).hdbc);      //断开连接句柄
 	ret=SQLFreeHandle(SQL_HANDLE_DBC, (*pconnectInfo).hdbc);  //释放连接句柄
 	ret=SQLFreeHandle(SQL_HANDLE_ENV, (*pconnectInfo).henv);  //释放环境句柄
 	return ret;
}

select_ODBC_function函数

SQLRETURN select_ODBC_function(ConnetctInfo* pconnectInfo,Member* pmember,char* strSQL,int sqlEndTranType){
	int set_num=0;
 	SQLRETURN ret;
 	SQLSMALLINT col_num;
 	ret = SQLEndTran(SQL_HANDLE_DBC,(*pconnectInfo).hdbc,sqlEndTranType);
 	 if(ret != SQL_SUCCESS){
  		WriteToLog("Sever Select Error!");
	}
 	else{
  		WriteToLog("Sever Select Success!");
	}
	SQLExecDirect((*pconnectInfo).hstmt,(SQLCHAR*)strSQL,SQL_NTS);
 	SQLNumResultCols((*pconnectInfo).hstmt,&col_num);
 	SQLBindCol((*pconnectInfo).hstmt,col_num-2,SQL_C_ULONG,&(*pmember).id,0,NULL);  
 	SQLBindCol((*pconnectInfo).hstmt,col_num-1,SQL_C_CHAR,(*pmember).username,sizeof((*pmember).username),NULL);  
 	SQLBindCol((*pconnectInfo).hstmt,col_num,SQL_C_CHAR,(*pmember).passwad,sizeof((*pmember).username),NULL);
	 while(SQL_NO_DATA != SQLFetch((*pconnectInfo).hstmt)) 
	 { 
  		set_num++;
  		printf("%d, %s, %s\n",(*pmember).id,(*pmember).username,(*pmember).passwad);
 	} 
  		printf("the numbers of set is: %d\n",set_num);
	return ret;
}

sqlexecute_ODBC_function函数

SQLRETURN sqlexecute_ODBC_function(ConnetctInfo* pconnectInfo,Member* pmember,char* strSQL,int executeType,int sqlEndTranType){
	SQLRETURN ret = 0;
	switch(executeType){
		case select_type:  //Select
  			ret=select_ODBC_function(pconnectInfo,pmember,strSQL,sqlEndTranType);
  			break;
  		 case insert_type:  //Insert
 		case delete_type:  //Delete
 		case update_type:  //Update
  			ret=SQLExecDirect((*pconnectInfo).hstmt,(SQLCHAR*)strSQL,SQL_NTS);
  			break;
  		default:
  		break;
	}
	return ret;
}

SQL_combination_function函数

char* SQL_combination_function(char* tableName,Member* pmember,int SQLType){
	char strSQL[255];
 		memset(strSQL,0x00,sizeof(strSQL));
 		//SQL Combination
 		switch(SQLType){
 		case insert_type:
  		sprintf_s(strSQL,sizeof(strSQL),"insert into %s values(%d,'%s','%s')",tableName,(*pmember).id,(*pmember).username,(*pmember).passwad);
  		break;
 		case delete_type:
 		sprintf_s(strSQL,sizeof(strSQL),"delete from %s where id=%d",tableName,(*pmember).id);
  		break;
  		case update_type:
 		sprintf_s(strSQL,sizeof(strSQL),"update %s set username='%s',passwad='%s' where id=%d",tableName,(*pmember).username,(*pmember).passwad,(*pmember).id);
  		break;
 		case select_type:
  		sprintf_s(strSQL,sizeof(strSQL),"select * from %s where id=%d",tableName,(*pmember).id);
  		break;
  		default:
  		break;
  	}
  	return strSQL;
}

主函数

int _tmain(int argc, _TCHAR* argv[])
{
 	SQLRETURN ret;
 	char strSQLExecute[255];
 	WriteToLog("Link the DataBase Start!");
 	//ConnetctInfo structure init
 	ConnetctInfo connectInfo = {NULL,NULL,NULL,(SQLCHAR*)"PostgreSQL35W",(SQLCHAR*)"postgres",(SQLCHAR*)"abc123"};
 	ConnetctInfo *pconnectInfo;
 	pconnectInfo = &connectInfo;
 	//Member structure init
	Member member = {6,"User1","Pass1"};	
	Member *pmember;
	pmember = &member;
	//init ODBC connect
	ret=init_ODBC_function(pconnectInfo);
	//Delete
 	memset(strSQLExecute,0x00,sizeof(strSQLExecute));
 	memcpy(strSQLExecute,SQL_combination_function("member",pmember,delete_type),255);
 	ret = sqlexecute_ODBC_function(pconnectInfo,pmember,strSQLExecute,delete_type,SQL_COMMIT);
 	//Insert
	memset(strSQLExecute,0x00,sizeof(strSQLExecute));
	memcpy(strSQLExecute,SQL_combination_function("member",pmember,insert_type),255);
 	ret = sqlexecute_ODBC_function(pconnectInfo,pmember,strSQLExecute,insert_type,SQL_COMMIT);
 	 //Update
	Member membernew = {6,"UserName6","PassWad6"};
	pmember = &membernew;
	memset(strSQLExecute,0x00,sizeof(strSQLExecute));
	memcpy(strSQLExecute,SQL_combination_function("member",pmember,update_type),255);
	ret = sqlexecute_ODBC_function(pconnectInfo,pmember,strSQLExecute,update_type,SQL_COMMIT);
	//Select
 	memset(strSQLExecute,0x00,sizeof(strSQLExecute));
 	memcpy(strSQLExecute,SQL_combination_function("member",pmember,select_type),255);
 	ret = sqlexecute_ODBC_function(pconnectInfo,pmember,strSQLExecute,select_type,SQL_COMMIT);
 	//Disable the Connection
 	ret = relase_ODBC_function(pconnectInfo);
 	return 0;

参考致谢
本博客为本人的学习笔记总结,其中参考了网上众多博主的博客分享,在此向广大博主表示感谢
【1】博文 【ODBC】ODBC连接数据库详细说明
【2】ODBC 数据类型和API(VC)

猜你喜欢

转载自blog.csdn.net/qq_27788177/article/details/82347409