基于ODBC API实现对数据库的访问

来源:互联网 发布:sql去空格的函数 编辑:程序博客网 时间:2024/06/10 01:39
 源程序:http://download.csdn.net/user/kissyfish

Visual C++提供了多种多样的数据库访问技术,ODBC API,MFC ODBC,DAO,OLEDB、ADO等。这些技术各有自己的特点,他们提供了简单、灵活、访问速度快、可扩展性强的开发技术,而这些正是Visual C++和其他开发工具相比优势所在。ODBC API是为客户应用程序访问关系数据库时提供了一个的标准接口,对不同的数据库,ODBC提供了一套统一的API,使得应用程序可以应用所提供的API,访问任何提供了ODBC驱动程序的数据库。而且,ODBC已经成为了一种标准,所以现在几乎所有的关系型数据库都提供了ODBC驱动程序,从而使得ODBC的应用更加广泛。ODBC API可以进行一些底层的数据库操作,访问速度快捷,使用灵活,但编码相对来说比较复杂。对于一个刨根究底的研究型爱好者来说,却是一个很好的选择。同时也让大家对于ADO、DAO的底层封装机制有个初步的了解和认识。

1、ODBC数据源的创建

从控制面板中双击管理工具图标,然后在新出现的窗口中双击数据源(ODBC)。在弹出的对话框中选择不同的选项卡来确定建立数据源的类型。这样手工配置数据源我个人感觉比较麻烦,是不是可以用API来自动实现配置呢?答案是肯定的。配置数据源的代码如下:

  1.     SQLRETURN retcode;
  2.     retcode = SQLConfigDataSource(NULL,ODBC_ADD_SYS_DSN,"SQL Server","DSN=master/0Server=(local)/0Database=master/0/0");
  3.     if(!retcode)
  4.     {
  5.         AfxMessageBox("系统数据源配置失败!");
  6.         return FALSE;
  7.     }

2、连接数据源

在Visual C++程序中使用刚才建立的数据源之前,还必须建立一个到数据源的连接。以连接master数据库为例,实现一个数据源的连接。代码如下:

  1. // 连接数据源
  2. BOOL CDbLink::OpenDatabase()
  3. {
  4.     SQLINTEGER cbLenth = 0 ;    
  5.     SQLRETURN retcode;
  6.     retcode = SQLConfigDataSource(NULL,ODBC_ADD_SYS_DSN,"SQL Server","DSN=master/0Server=(local)/0Database=master/0/0");
  7.     if(!retcode)
  8.     {
  9.         AfxMessageBox("系统数据源配置失败!");
  10.         return FALSE;
  11.     }
  12.     retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv) ;
  13.     if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
  14.     {
  15.         retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); 
  16.         if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
  17.         {
  18.             retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
  19.             if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
  20.             {
  21.                 retcode = SQLConnect(hdbc, (SQLCHAR*)(LPCTSTR)m_strDSN, SQL_NTS, (SQLCHAR*)(LPCTSTR)m_strUSER, SQL_NTS, 
  22.                     (SQLCHAR*)(LPCTSTR)m_strPWD, SQL_NTS);
  23.                 if (!(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO))
  24.                 {
  25.                     AfxMessageBox("数据库连接失败!") ;
  26.                     SQLFreeHandle(SQL_HANDLE_DBC, hdbc); 
  27.                     SQLFreeHandle(SQL_HANDLE_ENV, henv);
  28.                     return FALSE;
  29.                 }
  30.                 else
  31.                 {
  32.                     m_bLink = TRUE;
  33.                     return TRUE;
  34.                 }
  35.             } 
  36.             else
  37.             {
  38.                 AfxMessageBox("连接句柄分配出错") ;
  39.                 SQLFreeHandle(SQL_HANDLE_DBC, hdbc); 
  40.                 SQLFreeHandle(SQL_HANDLE_ENV, henv);
  41.                 return FALSE;
  42.             }
  43.         }
  44.         else
  45.         {
  46.             AfxMessageBox("属性设置出错!") ;
  47.             SQLFreeHandle(SQL_HANDLE_ENV, henv);
  48.             return FALSE;
  49.         }
  50.     }
  51.     else
  52.     {
  53.         AfxMessageBox("环境变量分配出错!") ;
  54.         SQLFreeHandle(SQL_HANDLE_ENV, henv);
  55.         return FALSE;
  56.     }
  57. }

其中,连接数据源中比较关键的是:数据源名DSN、用户名和密码。

3、动态创建数据库

这里先动态创建一个数据库,然后我们接下来以这个数据库来分析数据库的表的操作过程。那如何动态创建数据库呢?这个可能有点挑战吧!master数据库控制SQL Server的所有方面,这个数据库中包括所有的配置信息、用户登录信息、当前正在服务器中运行的过程的信息,当然要想创建数据库还是得靠他了,这个数据库中有一张系统表sysdatabases就是用来专门负责登记所有数据库的情况。所以你需要为master配置一个数据源,然后直接连接到这个数据源后,执行创建数据库的SQL语句就可以创建一个数据库了,当然在创建数据库之前你必须得到系统表sysdatabases查询看看,你的数据库是不是已经存在了,如果已经存在了就不必再创建了,明白了原理后,其实也很简单的,现在让我们踏上创建数据库的美丽旅程吧!代码如下:

  1. // 判断我们所创建的数据库是否已经存在
  2. BOOL CDbLink::IsDatabaseExisted(CString strDbName)
  3. {
  4.     SQLHSTMT hstmt ;
  5.     SQLRETURN retcode;
  6.     SQLINTEGER cbLenth = 0 ;
  7.     CString strSQL;
  8.     strSQL.Format("SELECT * FROM sysdatabases WHERE name='%s'", strDbName);
  9.     retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);    
  10.     if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
  11.     {   
  12.         if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) == SQL_ERROR)
  13.         {
  14.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  15.             return FALSE;
  16.         }
  17.         if((SQLFetch(hstmt) == SQL_SUCCESS) || (SQLFetch(hstmt) == SQL_SUCCESS_WITH_INFO))
  18.         {       
  19.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);  
  20.             return  TRUE;
  21.         }
  22.         else
  23.         {
  24.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);  
  25.             return FALSE;
  26.         }           
  27.     }
  28.     else
  29.     {   
  30.         SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  31.         return FALSE;   
  32.     }
  33. }
  34. // 创建数据库,其中strDbName代表数据库名
  35. BOOL CDbLink::CreateDatabase(CString strDbName)
  36. {
  37.     SQLHSTMT hstmt ;
  38.     SQLRETURN retcode;
  39.     SQLINTEGER cbLenth = 0 ;
  40.     BOOL bIsExisted = IsDatabaseExisted(strDbName);
  41.     if(bIsExisted)
  42.     {
  43.         return TRUE;
  44.     }
  45.     else
  46.     {
  47.         CString strSQL;
  48.         strSQL.Format("CREATE DATABASE [%s]  ON (NAME = N'%s_dat', FILENAME = N'D://%s.mdf' ,SIZE = 39, FILEGROWTH = 2) LOG ON (NAME = N'%s_log', FILENAME = N'D://%s.ldf' , SIZE = 2, FILEGROWTH = 1) COLLATE Chinese_PRC_CI_AS"
  49.             strDbName,strDbName,strDbName,strDbName,strDbName);
  50.         retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);    
  51.         if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
  52.         {   
  53.             if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) != SQL_ERROR)
  54.             {
  55.                 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  56.                 return TRUE;
  57.             }
  58.             else
  59.             {
  60.                 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);  
  61.                 return FALSE;
  62.             }           
  63.         }
  64.         else
  65.         {   
  66.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  67.             return FALSE;   
  68.         }
  69.     }
  70. }

4、创建表

上面我们已经在SQL SERVER中创建了一个新的数据库,当然这个数据库除了里面的系统表以外,别无它表。我们得自己在里面创建一个表。如何创建一个表呢?同样,这个时候我们得连接到我们新创建的数据库的数据源中,然后再执行创建表的SQL语句就可以创建一张表了,当然在创建表之前同样得查询看看这个表是否已经存在了。配置数据源的部分仿照上面的步骤进行即可。创建表的SQL语句如下:

  1. CREATE TABLE [Obj_User] 
  2. (
  3. [User_Iden] [int] NOT NULL ,
  4. [User_Nina] [varchar] (32) COLLATE Chinese_PRC_CI_AS NOT NULL ,
  5. [User_Pawo] [varchar] (32) COLLATE Chinese_PRC_CI_AS NOT NULL ,
  6. [User_RoId] [int] NOT NULL ,
  7. [User_Name] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL ,
  8. [User_OfPh] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL ,
  9. [User_MoPh] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL ,
  10. [User_Mail] [varchar] (64) COLLATE Chinese_PRC_CI_AS NULL ,
  11. [User_QQ] [varchar] (16) COLLATE Chinese_PRC_CI_AS NULL ,
  12. [User_Addr] [varchar] (256) COLLATE Chinese_PRC_CI_AS NULL ,
  13. [User_Phot] [varchar] (256) COLLATE Chinese_PRC_CI_AS NULL ,
  14. [User_Born] [datetime] NULL ,
  15. [User_Job] [varchar] (64) COLLATE Chinese_PRC_CI_AS NULL ,
  16. [User_ShId] [int] NULL ,
  17. CONSTRAINT [PK_Obj_User] PRIMARY KEY  CLUSTERED ([User_Iden])  ON [PRIMARY] 
  18. ) ON [PRIMARY]

创建的表格名为Obj_User,如下:

字段名数据类型主键允许空备注User_Idenint**(*代表不允许)用户IDUser_Ninavarchar(32) *用户昵称User_Pawovarchar(32) *用户密码User_RoIdint *用户角色User_Namevarchar(32)  用户名称User_OfPhvarchar(32)  办公号码User_MoPhvarchar(32)  手机号码User_Mailvarchar(64)  用户邮箱User_QQvarchar(16)  用户QQUser_Addrvarchar(256)  用户地址User_Photvarchar(256)  用户相片User_Borndatetime  用户生日User_Jobvarchar(32)  用户工作User_ShIdint  用户店铺创建表代码如下:

  1. // 先判断表是否已经存在,dbmarket数据库的系统表sysobjects记录了当前所创建的所有用户表。 
  2. BOOL CDbOperator::IsTableExisted(CString strTableName)
  3. {
  4.     SQLHSTMT hstmt ;
  5.     SQLRETURN retcode;
  6.     SQLINTEGER cbLenth = 0 ;
  7.     CString strSQL;
  8.     strSQL.Format("SELECT * FROM sysobjects WHERE name='%s'", strTableName);
  9.     retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);    
  10.     if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
  11.     {   
  12.         if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) == SQL_ERROR)
  13.         {
  14.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  15.             return FALSE;
  16.         }
  17.         if((SQLFetch(hstmt) == SQL_SUCCESS) || (SQLFetch(hstmt) == SQL_SUCCESS_WITH_INFO))
  18.         {       
  19.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);  
  20.             return  TRUE;
  21.         }
  22.         else
  23.         {
  24.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);  
  25.             return FALSE;
  26.         }           
  27.     }
  28.     else
  29.     {   
  30.         SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  31.         return FALSE;   
  32.     }
  33. }
  34. // 执行创建表操作
  35. BOOL CDbOperator::CreateSQL(CString strSQL)
  36. {
  37.     SQLHSTMT hstmt ;
  38.     SQLRETURN retcode;
  39.     SQLINTEGER cbLenth = 0 ;
  40.     retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);    
  41.     if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
  42.     {   
  43.         if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) != SQL_ERROR)
  44.         {
  45.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  46.             return TRUE;
  47.         }
  48.         else
  49.         {
  50.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);  
  51.             return FALSE;
  52.         }           
  53.     }
  54.     else
  55.     {   
  56.         SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  57.         return FALSE;   
  58.     }   
  59. }

4、访问表

更新、插入、删除语句都比较简单,直接执行SQL语句即可实现,现在就Select情况做如下说明:

1、准备SQL语句。

2、将指定的变量与列绑定,记住你成功的关键在于你对SQL中绑定类型的熟练程度。

3、提取数据,每提取一个记录后,游标自动向后移动,直到移动到最后时返回。

为了提高代码的可重用性,应该声明一个的头文件并提供统一数据库接口。根据上面的表模型,设计的结构体USER,代码如下:

  1. #ifndef __DBDEFINE_H__
  2. #define __DBDEFINE_H__
  3. typedef struct _USER
  4. {
  5.     int Iden;
  6.     char Nina[32];
  7.     char Pawo[32];
  8.     int RoId;
  9.     char Name[32];
  10.     char OfPh[32];
  11.     char MoPh[32];
  12.     char Mail[64];
  13.     char QQ[16];
  14.     char Addr[256];
  15.     char Phot[256];
  16.     SQL_TIMESTAMP_STRUCT Born;
  17.     char Job[32];
  18.     int ShId;
  19. }USER, *PUSER;
  20. #endif
  21. // 根据用户ID获取用户信息
  22. BOOL CDbOperator::GetUserByUserID(int nUserId, USER* pUser)
  23. {
  24.     SQLHSTMT hstmt ;
  25.     SQLRETURN retcode;
  26.     SQLINTEGER cbLenth = 0 ;
  27.     ZeroMemory(pUser, sizeof(USER));
  28.     pUser->Iden = nUserId;
  29.     CString strSQL;
  30.     strSQL.Format("SELECT User_Nina, User_Pawo, User_RoId, User_Name, User_OfPh, User_MoPh, User_Mail, User_QQ, User_Addr,/
  31.                   User_Phot, User_Born, User_Job, User_ShId FROM Obj_User WHERE User_Iden=%d", nUserId);
  32.     retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);    
  33.     if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
  34.     {   
  35.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Nina, 32, &cbLenth);    
  36.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Pawo, 32, &cbLenth);    
  37.         SQLBindCol(hstmt, 1, SQL_C_ULONG, (SQLPOINTER)&pUser->RoId, 4, &cbLenth);   
  38.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Name, 32, &cbLenth);    
  39.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->OfPh, 32, &cbLenth);    
  40.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->MoPh, 32, &cbLenth);    
  41.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Mail, 64, &cbLenth);    
  42.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->QQ, 16, &cbLenth);  
  43.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Addr, 256, &cbLenth);   
  44.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Phot, 256, &cbLenth);   
  45.         SQLBindCol(hstmt, 1, SQL_C_TYPE_TIMESTAMP, (SQLPOINTER)&pUser->Born, sizeof(SQL_C_TYPE_TIMESTAMP), &cbLenth);   
  46.         SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Job, 32, &cbLenth); 
  47.         SQLBindCol(hstmt, 1, SQL_C_ULONG, (SQLPOINTER)&pUser->ShId, 4, &cbLenth);   
  48.         if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) == SQL_ERROR)
  49.         {
  50.             AfxMessageBox(strSQL) ;
  51.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  52.             return FALSE;
  53.         }
  54.         retcode = SQLFetch(hstmt);
  55.         if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
  56.         {       
  57.         }
  58.         else
  59.         {
  60.             AfxMessageBox(strSQL) ;
  61.             SQLFreeHandle(SQL_HANDLE_STMT, hstmt);  
  62.             return FALSE;
  63.         }
  64.         SQLFreeHandle(SQL_HANDLE_STMT, hstmt);  
  65.         return  TRUE;       
  66.     }
  67.     else
  68.     {   
  69.         AfxMessageBox(strSQL) ;
  70.         SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  71.         return FALSE;   
  72.     }
  73. }

总结:

用ODBC API来访问数据库其实是一件比较简单的事情,做完一次后,以后复制就可以了。设计上时候,数据库的部分应该先设计好各种共同的结构声明,然后实现对数据库的接口,以DLL方式提供给使用者。使用者不必再考虑数据库的访问问题,若数据库接口出现错误,直接跟数据库编写的人联系就OK。这样数据库的访问就统一了,避免了各个程序员都要写数据库访问接口的麻烦。但调用时候必须有共同的数据库的结构声明,以及你提供的数据库接口函数的说明。调用者只要嵌入头文件后,产生数据库对象即可访问,因为我的设计是将数据库的连接写在构造函数里,调用代码如下:

  1. #include "DbOperator/DbOperator.h"
  2. CDbOperator dbOperator;// 构造函数实现连接
  3. if(!dbOperator.m_bLink)
  4. {
  5.     AfxMessageBox("数据库连接不成功!");
  6.          return FALSE;
  7. }
  8. dbOperator.InitAllTable(); // 统一的数据库接口函数,实现从文件中读取SQL语句,执行创建比操作。
  9. dbOperator.InitTableContent();// 从文件中读取你想要初始化的内容写入到我们所创建的表中。

至于其他的访问,比如修改表的字段、Select一个image的内容等相对复杂操作,大家可以查阅相关资料获取。

动态创建数据库的实现效果如下:

原创粉丝点击