1. 程式人生 > >Windows Service開發系列(ODBC開發)(一) -- ODBC簡介與一般操作流程

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;