源程序: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来自动实现配置呢?答案是肯定的。配置数据源的代码如下:
- SQLRETURN retcode;
- retcode = SQLConfigDataSource(NULL,ODBC_ADD_SYS_DSN,"SQL Server","DSN=master/0Server=(local)/0Database=master/0/0");
- if(!retcode)
- {
- AfxMessageBox("系统数据源配置失败!");
- return FALSE;
- }
2、连接数据源
在Visual C++程序中使用刚才建立的数据源之前,还必须建立一个到数据源的连接。以连接master数据库为例,实现一个数据源的连接。代码如下:
- BOOL CDbLink::OpenDatabase()
- {
- SQLINTEGER cbLenth = 0 ;
- SQLRETURN retcode;
- retcode = SQLConfigDataSource(NULL,ODBC_ADD_SYS_DSN,"SQL Server","DSN=master/0Server=(local)/0Database=master/0/0");
- if(!retcode)
- {
- AfxMessageBox("系统数据源配置失败!");
- return FALSE;
- }
- retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv) ;
- if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
- if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
- if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- retcode = SQLConnect(hdbc, (SQLCHAR*)(LPCTSTR)m_strDSN, SQL_NTS, (SQLCHAR*)(LPCTSTR)m_strUSER, SQL_NTS,
- (SQLCHAR*)(LPCTSTR)m_strPWD, SQL_NTS);
- if (!(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO))
- {
- AfxMessageBox("数据库连接失败!") ;
- SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
- SQLFreeHandle(SQL_HANDLE_ENV, henv);
- return FALSE;
- }
- else
- {
- m_bLink = TRUE;
- return TRUE;
- }
- }
- else
- {
- AfxMessageBox("连接句柄分配出错") ;
- SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
- SQLFreeHandle(SQL_HANDLE_ENV, henv);
- return FALSE;
- }
- }
- else
- {
- AfxMessageBox("属性设置出错!") ;
- SQLFreeHandle(SQL_HANDLE_ENV, henv);
- return FALSE;
- }
- }
- else
- {
- AfxMessageBox("环境变量分配出错!") ;
- SQLFreeHandle(SQL_HANDLE_ENV, henv);
- return FALSE;
- }
- }
其中,连接数据源中比较关键的是:数据源名DSN、用户名和密码。
3、动态创建数据库
这里先动态创建一个数据库,然后我们接下来以这个数据库来分析数据库的表的操作过程。那如何动态创建数据库呢?这个可能有点挑战吧!master数据库控制SQL Server的所有方面,这个数据库中包括所有的配置信息、用户登录信息、当前正在服务器中运行的过程的信息,当然要想创建数据库还是得靠他了,这个数据库中有一张系统表sysdatabases就是用来专门负责登记所有数据库的情况。所以你需要为master配置一个数据源,然后直接连接到这个数据源后,执行创建数据库的SQL语句就可以创建一个数据库了,当然在创建数据库之前你必须得到系统表sysdatabases查询看看,你的数据库是不是已经存在了,如果已经存在了就不必再创建了,明白了原理后,其实也很简单的,现在让我们踏上创建数据库的美丽旅程吧!代码如下:
- BOOL CDbLink::IsDatabaseExisted(CString strDbName)
- {
- SQLHSTMT hstmt ;
- SQLRETURN retcode;
- SQLINTEGER cbLenth = 0 ;
- CString strSQL;
- strSQL.Format("SELECT * FROM sysdatabases WHERE name='%s'", strDbName);
- retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
- if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) == SQL_ERROR)
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- if((SQLFetch(hstmt) == SQL_SUCCESS) || (SQLFetch(hstmt) == SQL_SUCCESS_WITH_INFO))
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return TRUE;
- }
- else
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
- else
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
- BOOL CDbLink::CreateDatabase(CString strDbName)
- {
- SQLHSTMT hstmt ;
- SQLRETURN retcode;
- SQLINTEGER cbLenth = 0 ;
- BOOL bIsExisted = IsDatabaseExisted(strDbName);
- if(bIsExisted)
- {
- return TRUE;
- }
- else
- {
- CString strSQL;
- 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",
- strDbName,strDbName,strDbName,strDbName,strDbName);
- retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
- if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) != SQL_ERROR)
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return TRUE;
- }
- else
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
- else
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
- }
4、创建表
上面我们已经在SQL SERVER中创建了一个新的数据库,当然这个数据库除了里面的系统表以外,别无它表。我们得自己在里面创建一个表。如何创建一个表呢?同样,这个时候我们得连接到我们新创建的数据库的数据源中,然后再执行创建表的SQL语句就可以创建一张表了,当然在创建表之前同样得查询看看这个表是否已经存在了。配置数据源的部分仿照上面的步骤进行即可。创建表的SQL语句如下:
- CREATE TABLE [Obj_User]
- (
- [User_Iden] [int] NOT NULL ,
- [User_Nina] [varchar] (32) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [User_Pawo] [varchar] (32) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [User_RoId] [int] NOT NULL ,
- [User_Name] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL ,
- [User_OfPh] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL ,
- [User_MoPh] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL ,
- [User_Mail] [varchar] (64) COLLATE Chinese_PRC_CI_AS NULL ,
- [User_QQ] [varchar] (16) COLLATE Chinese_PRC_CI_AS NULL ,
- [User_Addr] [varchar] (256) COLLATE Chinese_PRC_CI_AS NULL ,
- [User_Phot] [varchar] (256) COLLATE Chinese_PRC_CI_AS NULL ,
- [User_Born] [datetime] NULL ,
- [User_Job] [varchar] (64) COLLATE Chinese_PRC_CI_AS NULL ,
- [User_ShId] [int] NULL ,
- CONSTRAINT [PK_Obj_User] PRIMARY KEY CLUSTERED ([User_Iden]) ON [PRIMARY]
- ) ON [PRIMARY]
创建的表格名为Obj_User,如下:
字段名 |
数据类型 |
主键 |
允许空 |
备注 |
User_Iden |
int |
* |
*(*代表不允许) |
用户ID |
User_Nina |
varchar(32) |
|
* |
用户昵称 |
User_Pawo |
varchar(32) |
|
* |
用户密码 |
User_RoId |
int |
|
* |
用户角色 |
User_Name |
varchar(32) |
|
|
用户名称 |
User_OfPh |
varchar(32) |
|
|
办公号码 |
User_MoPh |
varchar(32) |
|
|
手机号码 |
User_Mail |
varchar(64) |
|
|
用户邮箱 |
User_QQ |
varchar(16) |
|
|
用户QQ |
User_Addr |
varchar(256) |
|
|
用户地址 |
User_Phot |
varchar(256) |
|
|
用户相片 |
User_Born |
datetime |
|
|
用户生日 |
User_Job |
varchar(32) |
|
|
用户工作 |
User_ShId |
int |
|
|
用户店铺 |
创建表代码如下:
- BOOL CDbOperator::IsTableExisted(CString strTableName)
- {
- SQLHSTMT hstmt ;
- SQLRETURN retcode;
- SQLINTEGER cbLenth = 0 ;
- CString strSQL;
- strSQL.Format("SELECT * FROM sysobjects WHERE name='%s'", strTableName);
- retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
- if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) == SQL_ERROR)
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- if((SQLFetch(hstmt) == SQL_SUCCESS) || (SQLFetch(hstmt) == SQL_SUCCESS_WITH_INFO))
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return TRUE;
- }
- else
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
- else
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
- BOOL CDbOperator::CreateSQL(CString strSQL)
- {
- SQLHSTMT hstmt ;
- SQLRETURN retcode;
- SQLINTEGER cbLenth = 0 ;
- retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
- if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) != SQL_ERROR)
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return TRUE;
- }
- else
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
- else
- {
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
4、访问表
更新、插入、删除语句都比较简单,直接执行SQL语句即可实现,现在就Select情况做如下说明:
1、准备SQL语句。
2、将指定的变量与列绑定,记住你成功的关键在于你对SQL中绑定类型的熟练程度。
3、提取数据,每提取一个记录后,游标自动向后移动,直到移动到最后时返回。
为了提高代码的可重用性,应该声明一个的头文件并提供统一数据库接口。根据上面的表模型,设计的结构体USER,代码如下:
- #ifndef __DBDEFINE_H__
- #define __DBDEFINE_H__
- typedef struct _USER
- {
- int Iden;
- char Nina[32];
- char Pawo[32];
- int RoId;
- char Name[32];
- char OfPh[32];
- char MoPh[32];
- char Mail[64];
- char QQ[16];
- char Addr[256];
- char Phot[256];
- SQL_TIMESTAMP_STRUCT Born;
- char Job[32];
- int ShId;
- }USER, *PUSER;
- #endif
- BOOL CDbOperator::GetUserByUserID(int nUserId, USER* pUser)
- {
- SQLHSTMT hstmt ;
- SQLRETURN retcode;
- SQLINTEGER cbLenth = 0 ;
- ZeroMemory(pUser, sizeof(USER));
- pUser->Iden = nUserId;
- CString strSQL;
- strSQL.Format("SELECT User_Nina, User_Pawo, User_RoId, User_Name, User_OfPh, User_MoPh, User_Mail, User_QQ, User_Addr,/
- User_Phot, User_Born, User_Job, User_ShId FROM Obj_User WHERE User_Iden=%d", nUserId);
- retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
- if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Nina, 32, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Pawo, 32, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_ULONG, (SQLPOINTER)&pUser->RoId, 4, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Name, 32, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->OfPh, 32, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->MoPh, 32, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Mail, 64, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->QQ, 16, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Addr, 256, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Phot, 256, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_TYPE_TIMESTAMP, (SQLPOINTER)&pUser->Born, sizeof(SQL_C_TYPE_TIMESTAMP), &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER)pUser->Job, 32, &cbLenth);
- SQLBindCol(hstmt, 1, SQL_C_ULONG, (SQLPOINTER)&pUser->ShId, 4, &cbLenth);
- if (SQLExecDirect(hstmt, (SQLCHAR*)(LPCTSTR)strSQL, SQL_NTS) == SQL_ERROR)
- {
- AfxMessageBox(strSQL) ;
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- retcode = SQLFetch(hstmt);
- if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
- {
- }
- else
- {
- AfxMessageBox(strSQL) ;
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return TRUE;
- }
- else
- {
- AfxMessageBox(strSQL) ;
- SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
- return FALSE;
- }
- }
总结:
用ODBC API来访问数据库其实是一件比较简单的事情,做完一次后,以后复制就可以了。设计上时候,数据库的部分应该先设计好各种共同的结构声明,然后实现对数据库的接口,以DLL方式提供给使用者。使用者不必再考虑数据库的访问问题,若数据库接口出现错误,直接跟数据库编写的人联系就OK。这样数据库的访问就统一了,避免了各个程序员都要写数据库访问接口的麻烦。但调用时候必须有共同的数据库的结构声明,以及你提供的数据库接口函数的说明。调用者只要嵌入头文件后,产生数据库对象即可访问,因为我的设计是将数据库的连接写在构造函数里,调用代码如下:
- #include "DbOperator/DbOperator.h"
- CDbOperator dbOperator;
- if(!dbOperator.m_bLink)
- {
- AfxMessageBox("数据库连接不成功!");
- return FALSE;
- }
- dbOperator.InitAllTable();
- dbOperator.InitTableContent();