学会使用ODBC API

学会使用ODBC API

  虽说以前也接触过一些数据库的应用,但是由于当时只是抱着非常浅显的数据库知识,做了一些SQL语句上的操作,安装-配置-连接这一系列的流程全是自己照着例子实现的,大概记住的也就是ODBC、ADO之类,也简单的理解了一下,当共享使用数据库时最好使用ODBC的方式,而本地的数据库就可以使用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能不能及时的接收到,而且责任分明,也方便维护,但是现在考虑这种方式的缺点就是,我们的代码有一定的重复,他们的客户端就有一些根据数据条件进行“分类”的意思,我们这边则要根据数据进行不同的显示效果。

posted @ 2013-03-13 16:01  DageKing  阅读(758)  评论(0编辑  收藏  举报