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操作資料庫的流程
分配連線控制代碼 初始化連線控制代碼; 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;