学会使用ODBC API
学会使用ODBC API
虽说以前也接触过一些数据库的应用,但是由于当时只是抱着非常浅显的数据库知识,做了一些SQL语句上的操作,安装-配置-连接这一系列的流程全是自己照着例子实现的,大概记住的也就是ODBC、ADO之类,也简单的理解了一下,当共享使用数据库时最好使用ODBC的方式,而本地的数据库就可以使用ADO。
具体区别这些连接方式倒是就可以好好学习一下了:
- MDAC:http://zh.wikipedia.org/wiki/MDAC
- ODBC:http://zh.wikipedia.org/zh-cn/ODBC
- ADO:http://zh.wikipedia.org/wiki/ADO
简单的来说,ODBC和ADO都是微软提供的数据库访问方式,ADO是一个COM组件,而ODBC则是一种标准的访问方式,结束了数据库访问和数据库相关的局面,只不过两者实现的不同,所以在不同应用的表象上不同罢了(不对的话请指出,只看了Wiki上的一点介绍,没有系统学习)。
使用
通过分析考虑到将来可能会采用共享数据库的方式,此外对于COM自己也不是很熟悉,所以还是使用ODBC的方式比较好,使用ODBC的方式就很多了,但是由于我并不是程序开发人员,现在负责维护,不大敢做大手术,所以就选择了ODBC API,这就相当于使用WINDOS API和MFC一样,虽然API复杂,但是相对自由很多。
首先要在程序中添加几个依赖项
#include <sql.h> #include <sqlext.h> #include <odbcinst.h> #pragma comment(lib,"odbc32.lib") #pragma comment(lib,"odbccp32.lib")
前两个头文件分别代表基本的ODBC API和高级的ODBC API,最后一个好像是包含了更高级的API(暂时这么理解吧),一般用到前两个即可,odbc32.lib对应sql.h和sqlext.h,odbccp32.lib对应odbcinst.h。
所有使用数据库的过程无非是这样一个流程:
1. 想办法建立数据库的连接
2. 想办法执行SQL语句,并使用结果
3. 不用时关闭连接,以免下次访问报错
对于像我这样不常用数据库的菜鸟来说,这样的方式足够完成我所面临的问题了,诸如互斥访问、分布式之类的问题还是暂时留给高手们考虑吧。
建立数据库连接
在我们要连接数据库的类中,或者全局变量下建立相关的变量
SQLHENV m_hEnviroment;//数据库环境句柄,属于老大级别的
SQLHDBC m_hDatabaseConnection;//数据库连接句柄,老大以后就是他了,有了他数据库就连接上了
SQLHSTMT m_hStatement;//执行语句句柄,最终执行SQL于句的句柄
使用ODBC API建立数据库连接分为3部分:申请环境句柄,使用环境句柄申请连接句柄、使用连接句柄连接数据库。
/* 申请环境变量 */ SQLRETURN l_uiReturn = SQLAllocHandle(SQL_HANDLE_ENV,NULL,&m_hEnviroment); //申请各种句柄都靠这个函数,参数1是要申请句柄的类型,参数2为申请该句柄依靠的句柄(老大没依靠,所以是NULL),申请结果在参数3中保存 //返回值代表着执行的意义,如下面判断,SUCCESS_WITH_INFO相当于是警告,虽然成功了,但是可能有问题 if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO) { return false; } SQLSetEnvAttr(m_hEnviroment,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,SQL_IS_INTEGER); l_uiReturn = SQLAllocHandle(SQL_HANDLE_DBC,m_hEnviroment,&m_hDatabaseConnection); if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO) { return false; } SQLWCHAR * l_swcaDsnName = _T("");//数据源名称 SQLWCHAR * l_swcaUserName = _T("");//用户名称 SQLWCHAR * l_swcaPassWord = _T("");//密码 l_uiReturn = SQLConnect(m_hDatabaseConnection,l_swcaDsnName,SQL_NTS ,l_swcaUserName,SQL_NTS ,l_swcaPassWord,SQL_NTS); if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO) { return false; }
使用SQL语句
/* 申请于句句柄 */ SQLRETURN l_uiReturn = SQLAllocHandle(SQL_HANDLE_STMT,m_hDatabaseConnection,&m_hStatement); if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO) { return 0; } /* 构造SQL语句 */ CString l_cstrSql; l_cstrSql.Format(_T("SELECT * FROM 数据表 ")); /* 执行SQL语句 */ l_uiReturn = SQLExecDirect(m_hStatement,l_cstrSql.GetBuffer(),SQL_NTS); if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO) { return 0; } /* 获得返回结果的行数 */ SQLINTEGER l_siIdCount = 0; l_uiReturn = SQLRowCount(m_hStatement,&l_siIdCount); /* 开始读取结果 *///读取第一行时要调用,以后依次调用就可以下移行数,直到不返回SQL_SUCCESS l_uiReturn = SQLFetch(m_hStatement); if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO) { return 0; } SQLINTEGER l_siID; SQLINTEGER l_siIDLength = 0; /* 获得数据 */ SQLGetData(m_hStatement,1,SQL_C_ULONG,&l_siID,0,&l_siIDLength); //参数1为执行语句的句柄,参数2为所要得到的数据位于的列数(SQL语句中),参数3为数据类型,这个比较多,需要看一下MSDN //参数4为保存的位置(地址),参数5为参数4可用的位置,既然参数3已设定为长整型,所以这里可使用0 //参数6为实际返回的长度 /* 释放语句句柄 */ SQLFreeHandle(SQL_HANDLE_STMT,m_hStatement);
断开连接
SQLFreeHandle(SQL_HANDLE_STMT,m_hStatement);
SQLFreeHandle(SQL_HANDLE_DBC,m_hDatabaseConnection);
SQLFreeHandle(SQL_HANDLE_ENV,m_hEnviroment);
后记:客户需求与实现方式的思考
客户来了新的需求-交换:当时的原型为,他们那里有一套嵌入式的系统负责从传感器上获得数据,他们也有一套相应的管理程序,但是但是那套管理程序缺乏显示效果,所以需要利用我们所做的系统进行显示。
根据这个需求,我们(其实只有我一个人)进行了分析,当时客户提出的最好使用SOCKET变成来实现,按说这也是一个不错的主意,自由且不是很复杂,但是带来的后果就可能是以后如果各种交互多了以后,极难维护。所以觉得使用一个共同的数据库比较好,他们负责生成数据,我们负责读取并显示,这种好处是可以最大的运用程序的效率,不用总是考虑SOCKET能不能及时的接收到,而且责任分明,也方便维护,但是现在考虑这种方式的缺点就是,我们的代码有一定的重复,他们的客户端就有一些根据数据条件进行“分类”的意思,我们这边则要根据数据进行不同的显示效果。