在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(); } |