ODBC第一次接触。
先写一下收获:) 再写一下憋闷的接触过程.
动态的获得数据
1 不加载游标库 也就是可以直接创建一个CDatabase m_db; m_db.OpenEx("conn str...",这个参数不要CDatabase::useCursorLib);因为加载游标库后,必须进行数据绑定(我原来的CMyRecordSet就是自己写的方法根据m_rgODBCFieldInfos中记录的类型动态创建变量进行动态绑定 晕````呼,还高兴了半天)
2 可以直接用CRecordSet 来执行 SQL 语句(不过是通过Open函数,把已经打开的数据库和待执行的SQL作为参数即可)---不用再为每个表对应一个CRecordSet派生类拉 哈~
3 直接调用 MoveNext 和 GetFieldValue (如果是设置了多行读取,默认情况下将取得第一行的数据,需要调用 SetRowsetCursorPosition来指明哪行)
使用多行同时读取
1 设置数据集CRecordset 选项加上 CRecordset::useMultiRowFetch
2 设置同时读取的行数 SetRowsetSize( DWORD dwNewRowsetSize ); 默认读取25行。
通过GetRowsetSize()可得到读取的行数
3 调用GetFieldValue前,先调用SetRowsetCursorPosition 设置获取哪行
4 还有值得注意的是,使用了多行读取,将不能使用Double Buffer ,类似MFC文档中使用的CMirrorFile 对原始文件的保护吧 ,具体没咋看。以后再说拉……
看了好几本的书都没看到这些,还是MSDN好, 憋闷. 浪费俺时间的书
OK 上面是刚看MSDN的.
接下来说说我原来对数据绑定的处理。一开始看了几本书,都是直接使用向导生成一个和表对应的类(派生于 CRecordset),并且表中的数据域分别在类中有对应的变量。在数据交换函数中,硬编码进行地址映射。(当然了那时候没用过ODBC,图快直接看书,看例子,没看MSDN)。 就想咋的这么死呢。于是跟踪了一下CRecordset 的Open代码。
//-@ CRecordset::Open中部分代码 //-@ 创建SQL语句,并解析后执行或者直接执行 BuildSQL(lpszSQL); PrepareAndExecute();
//-@ 获得并保存各字段的相关信息,(在这里便可以知道result集各个域的信息) AllocAndCacheFieldInfo(); //-@ 如果是多行读取时分配rowset(否则只标记行数为1)) AllocRowset();
//-@ 延迟绑定数据时,在这里进行分配字段状态数组空间 if (bUnbound && (m_nFields > 0 || m_nParams > 0)) AllocStatusArrays();
//-@ 给派生类定制一些操作(在数据绑定之前) //-@ 我重载了这个函数,并根据上面记录的 result集中各个域的信息 //-@(类型,域名等)动态创建变量。然后在数据交换(绑定)函数中使用 //-@ 我自己的函数 PreBindFields();
//-@ 获得第一行数据(第一次调用时,将进行变量地址同结果集各域的绑定) //-@ 如果没有进行绑定那么这里将会出错,既然是在这里才绑定那就好办了 //-@ 我就来个动态绑定(不是硬编码),在这之前,又刚好有一个 PreBindFields //-@ 给我定制,嘿嘿(不用重写一个Open 并 copy paste了) MoveNext();
//-@ CMyRecordset 内部辅助类 DataExchangeHelper DataExchangeHelper m_dataExchHelp; //-@ 重写的PreBindFields //-@ 该函数根据结果集域信息动态创建变量并保存到CPtrArray中 m_dataExchHelp.AllocData(this->m_rgODBCFieldInfos, this->GetODBCFieldCount()); //-@ 重写的数据交换函数 void CMyRecordSet::DoFieldExchange(CFieldExchange* pFX) { pFX->SetFieldType(CFieldExchange::outputColumn);
//-@ CFieldExchange::Name用来生成SQL语句 if(pFX->m_nOperation != CFieldExchange::Name) { m_dataExchHelp.DoDataExchange(pFX); } } //-@ 实际完成数据交换函数(当然数据库的字段类型只是根据MS的 Access分析 //-@ 后简易的实现了一下,测试不多,可以肯定的是只能偶尔玩玩时候用) 哈~ void CMyRecordSet::DataExchangeHelper::DoDataExchange(CFieldExchange* pDX) { for(int i=0; i<m_typeList.size(); i++) { this->DoDataExchange(pDX,m_typeList[i],m_pDataList[i]); } } //-@ 根据参数进行调用 void CMyRecordSet::DataExchangeHelper::DoDataExchange(CFieldExchange* pDX, int type, void* pData) { ASSERT(pData);
switch(type) { case SQL_LONGVARCHAR: case SQL_CHAR: case SQL_VARCHAR: RFX_Text(pDX,_T(""),*(CString*)pData); break; case SQL_C_LONG: RFX_Long(pDX,_T(""),*(long*)pData); break; case SQL_C_SHORT: RFX_Int(pDX,_T(""),*(int*)pData); break; case SQL_C_FLOAT: RFX_Single(pDX,_T(""),*(float*)pData); break; case SQL_C_DOUBLE: RFX_Double(pDX,_T(""),*(double*)pData); break; case SQL_C_TIME: case SQL_C_TYPE_TIME: RFX_Date(pDX,_T(""),*(CTime*)pData); break; case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: RFX_Date(pDX,_T(""),*(TIMESTAMP_STRUCT*)pData); break; case SQL_C_BINARY: RFX_Binary(pDX,_T(""),*(CByteArray*)pData); break; case SQL_C_BIT: RFX_Byte(pDX,_T(""),*(BYTE*)pData); break; } } |
下面是内部类的声明
class DataExchangeHelper { public: DataExchangeHelper(); ~DataExchangeHelper();
//-@ 调用数据交换 void DoDataExchange(CFieldExchange* pDX);
//-@ 根据参数进行调用 void DoDataExchange( CFieldExchange* pDX, int type, void* pData);
//-@ 创建变量 void AllocData( CODBCFieldInfo *pFieldInfo, int count);
//-@ 获得变量 int GetFieldsCount()const; void* GetData(int idx); void* GetData(const CString& name, CODBCFieldInfo *pFieldInfo, int count); CString GetDataString(int idx); CString GetDataString( const CString& name, CODBCFieldInfo *pFieldInfo, int count);
//-@ 清空数据链表 bool ClearDataList();
protected: void FieldToString( int idx, CString& outStr);
//-@ 数据交换时按照顺序排列的(函数类型和绑定的数据指针) vector<int > m_typeList; CPtrArray m_pDataList; }m_dataExchHelp; |