|
在Unix環境下,常用的數據庫開發方法有ODBC、調用層接口(CLI)、SQL內嵌C語言或利用其他特定的開發工具等。由不同的開發方法提供給C語言訪問數據庫的方法及書寫方式各不相同,所以如此開發出的數據庫應用程序難以在不同開發環境下通用,導致移植性較差,如採用SQL內嵌C語言開發的程序要改為調用層接口(CLI)來編寫時,程序中所有的C程序都得進行修改。為此,筆者設計了一套Unix環境下數據庫系統的C語言通用接口函數,利用這種通用接口函數,在數據庫系統的多種開發環境中移植程序也無需再修改代碼,改善了程序的通用性,提高了開發效率。本文介紹這種通用接口函數的設計思想、函數原型設計和應用示例。
設計思想
利用C語言模塊化編程方法,將與數據庫訪問有關的函數(主要是標準的SQL語句)集中一個模塊(DBADM)中,即將所有有關接口的函數“封裝”在此模塊中,把該模塊作為數據訪問的“中間件”,提供對通用接口函數的支持。這樣,在別的應用模塊中訪問數據庫時,只調用這些通用接口函數即可,避免了直接調用數據庫系統提供的數據庫訪問函數時接口不一致的問題,將數據庫應用的C語言源程序部分與數據實際訪問部分(DBADM)隔離開來。
同時,採用緩存交換技術,為每個打開的表申請一塊內存(全局變量),作為訪問數據庫的SQL語句進行數據交換的記錄緩存區。當使用SQL語句讀出數據庫中的數據時,先把數據存放在記錄緩存區(其值皆轉換為字符型)。同樣,當將數據寫入數據庫中時,也先在記錄緩存區中進行更新,再利用記錄緩存區組裝Insert、Update等SQL語句,從而避免了直接使用SQL語句。
這樣,我們可為數據庫系統的常用開發環境編寫相應的數據庫接口模塊(DBADM),如果要改變開發環境,只需改變MAKEFILE文件的數據庫接口模塊即可,其他模塊可原封不動。函數功能的擴充、修改及完善也只需改動數據庫接口模塊。這樣,不但提高了程序的通用性,而且把程序員從考慮不同接口的重復工作中解脫出來,把精力放在功能應用的設計開發上,從而提高了開發效率。
函數設計
數據庫系統的數據訪問主要是使用SQL查詢語言來進行,相關的數據訪問語句主要有Select(查詢)、Insert(插入)、Update(更新)、Delete(刪除)、RollBack(回滾),Commit(提交)等。通過對幾種常用的開發環境對SQL語句調用方法的共性與特性的比較,我們可以將它們定義成一套“通用”的數據庫訪問函數,供其他程序調用。下面是筆者定義的兩個接口函數的主要代碼(其他接口函數代碼略):
/* dbadm.ec Informix-esql */
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <decimal.h>
include sqlca;
include sqlda;
include sqltypes;
/*最大的可打開表的數目*/
#define MXFILES 20
/*當前打開的表句柄,表記錄的長度*/
int iFrx,iSQLRecordLength[MXFILES];
/*表記錄的存儲緩沖區,已打開的表*/
char szSQLRecordBuffer[MXFILES],szSQLfhl[MXFILES];
/*已打開表各字段類型區*/
char szSQLField_lx[MXFILES];
/*表結構*/
struct sqlda uDesc[MXFILES];
/*SQL語句組裝區*/
char szSQLbuf[1024];
/*打開表*/
DbTableOpen(char *szSelectSQL)
{
int iIdx,iPostion;
/*字段變量指針*/
struct sqlvar_struct uFieldVar;
/*參數裡沒有select,則按標準組裝select 語句*/
if (substr(szSelectSQL,“select ”)<0)
sprintf(szSQLbuf,“select * from %s”,szSelectSQL);
else
strcpy(szSQLbuf,szSelectSQL);
/*查找可打開的句柄*/
for (iFrx=0;iFrx<MXFILES;iFrx++)
if (szSQLfhl[iFrx]==NULL)
break;
if (iFrx==MXFILES)
{
printf(“ 打開的表太多\n”);
exit(-1);
}
/*申請保留select語句區*/
if ((szSQLfhl[iFrx]=(char)malloc(strlen
(szSQLbuf)))==NULL)
return(-1);
strcpy(szSQLfhl[iFrx],szSQLbuf);
/* 申請表結構保留區*/
uDesc[iFrx]=(struct sqlda )malloc(sizeof(struct sqlda));
/*定義select語句遊標,其具體實現略*/
if (declaredb(iFrx)<0)
exit(-1);
iPostion=0; /*記錄長度*/
/*申請字段類型區*/
if ((szSQLField_lx[iFrx]=(char )malloc(uDesc
[iFrx]->sqld+1))==NULL)
exit(-1);
/*對所有字段進行處理*/
for (uFieldVar=uDesc[iFrx]->sqlvar,iIdx=0;iIdx<uDesc[iFrx]->sqld;uFieldVar++,iIdx++)
{
switch (uFieldVar->sqltype)
{
case SQLMONEY: /*金額類型*/
case SQLDECIMAL: /*十進制數*/
/*計算出該字段的偏移*/
iPostion=rtypalign(iPostion,CDECIMALTYPE);
/*類型定義*/
uFieldVar->sqltype=CDECIMALTYPE;
/*位置移一個十進制數所佔字節數*/
iPostion+=rtypmsize(CDECIMALTYPE);
/*字段長度*/
uFieldVar->sqllen=rtypmsize(CDECIMALTYPE);
break;
default:
/*計算出該字段位置*/
iPostion=rtypalign(iPostion,CCHARTYPE);
/* 類型定義為字符型*/
uFieldVar->sqltype=CCHARTYPE;
uFieldVar->sqllen=rtypwidth(uFieldVar
->sqltype,uFieldVar->sqllen)+3;
/*位置移*/
iPostion+=uFieldVar->sqllen;
}
}
iPostion++;
/*申請表記錄的存儲區*/
if ((szSQLRecordBuffer[iFrx]=(char *)malloc
(iPostion))==NULL)
exit(-1);
iSQLRecordLength[iFrx]=iPostion;
/*給表中各字段在記錄存儲緩沖區中定位*/
for (uFieldVar=uDesc[iFrx]->sqlvar,iIdx=0;iIdx<uDesc[iFrx]->sqld;iIdx++,uFieldVar++)
{
/*計算出該字段偏移*/
iPostion=rtypalign(iPostion ,uFieldVar->sqltype);
/*字段在記錄緩沖區的位置*/
uFieldVar->sqldata=szSQLRecordBuffer[iFrx]+iPostion ;
iPostion+=uFieldVar->sqllen;
}
/*打開遊標函數,其具體實現略*/
if (opencuser(iFrx)<0)
exit(-1);
return(iFrx);
}
/*插入函數*/
DbInsertRecord(int iTableNo)
{
struct sqlvar_struct *uFeildVar;
int iIdx;
char szTableName[60];
/*從szSQLfhl[iTableNo]中取得表名*/
createtableName(szSQLfhl[iTableNo],szTableName);
/*組裝insert 語句 */
sprintf(szSQLbuf,“insert into %s values (”,
szTableName);
for (iIdx=0; iIdx<uDesc[iTableNo]->sqld;
uFieldVar++,iIdx++)
{
strcat(szSQLbuf,“?”);
if (iIdx!=uDesc[iTableNo]->sqld-1)
strcat(szSQLbuf,“,”);
}
strcat(szSQLbuf,“)”);
/*預定義*/
prepare insertid from szSQLbuf;
/*失敗退出*/
if (sqlca. sqlcode!=0)
return(-1);
/*執行 insert 語句 */
execute insertid using descriptor uDesc[iTableNo];
*失敗退出*/
if (sqlca.sqlcode!=0) /
return(-1);
return(0);
}
應用示例
在使用通用接口函數編程時,首先調用DbsOpen(),連接數據庫,成功使用數據庫接口函數,進行數據庫數據的讀寫。退出系統時,用DbCloseBase()函數關閉數據庫即可。test.c是筆者編寫的一個簡單的C語言應用程序,其功能是顯示數據庫qsxt [清算系統]的lsz[流水賬]中lb[類別]為02的TCHH[提出交換號]、TRHH[提入交換號]內容,並將02改為05。下面是主要代碼:
#include <stdio.h>
/*數據庫函數原型頭文件*/
#include “dbutil.h”
#define TRUE 1
#define FALSE 0
main()
{
/*流水賬表句柄*/
int iLsz_no;
/*流水號*/
long lRecord_no;
char szSQL[256];
/*連接失敗,退出*/
if (DbsOpen(“qsxt”,“qsuser”,“admin”)==FALSE)
{
printf(“數據庫打開失敗\n”);
exit(-1);
}
/*打開lsz表失敗,退出*/
if ((iLsz_no= DbTableOpen(“select * from lsz
where lb=’02’”))<0)
{
/*關閉數據庫*/
DbCloseBase();
printf(“lsz表打開失敗\n”);
exit(-1);
}
lRecord_no=1;
/*表沒讀完*/
while (DbGetRecord(iLsz_no,lRecord_no++)==TRUE)
{
/*按字段名顯示提出行號、提入行號*/
printf(“提出行號:%-6s 提入行號:%-6s\n”,
DbGetColDataByName(iLsz_no,“TCHH”),
DbGetColDataByName(iLsz_no,“TRHH”) );
}
/*將類別號改為05 */
strcpy(DbGetColDataByName(iLsz_no,“LB”),“05”);
/* 設置更改條件 */
Sprintf(szSQL, “where lb=‘02’”);
/* 表示更新事務開始*/
DbBegin_work();
/*更新記錄失敗,返回 */
If (DbUpdateRecord(iLsz_no,szSQL)==FALSE)
{
/*表示返回*/
DbRollBack_work();
break;
}
/* 提交 */
DbCommit_work();
/*關閉流水賬表*/
DbCloseTable (iLsz_no);
/*關閉數據庫*/
DbCloseBase();
}
(計算機世界報 第33期 C16、C17)
|