一、數據庫操作准備
// ---------------------------------------------------------------------------------------------------------------------------------
1、導入ADO動態鏈接庫
在工程的stdafx.h中加入如下語句:
#import "C:\Program Files\Common Files\System\ado\msado15.dll"\
no_namespace rename("EOF", "adoEOF")
這一語句有何作用呢?
其最終作用同我們熟悉的 #include 類似,編譯的時候系統會為我們生成msado15.tlh和ado15.tli兩個C++頭文件來定義ADO庫,
即加載ADO動態庫(msado15.dll)。
其中,no_namespace表明不使用命名空間,rename("EOF","adoEOF")表明把ADO中用到的EOF改為adoEOF,防止發生命名沖突。
注意:
該代碼需要在一行中完成,如果寫成兩行或者多行,行末要加上“\”符號,表示把這幾行看成一行,如本例。
// ---------------------------------------------------------------------------------------------------------------------------------
2、初始化OLE/COM庫環境
在基於MFC的應用中初始化OLE/COM庫環境的一個比較好的位置,是在應用程序類CXXXApp的InitInstance成員函數中直接調用AfxOleInit(),
而在退出應用時該函數也負責COM資源的釋放,將此函數添加在InitInstance中的如下位置:
BOOL CExpApp::InitInstance() { AfxEnableControlContainer(); // 初始化OLE DLL
if (!AfxOleInit()) { AfxMessageBox(_T("初始化OLE DLL失敗!")); return FALSE; } // 其他操作...
}
說明:
也可以在InitInstance中使用::CoInitialize初始化OLE/COM庫環境,
但須在ExitInitInstance中使用::CoUninitialize釋放占用的COM資源。
比較之下,顯然是使用AfxOleInit更為方便。
// ---------------------------------------------------------------------------------------------------------------------------------
3、連接數據庫
在Doc\View程序中,通常在應用程序類(CXXXApp)中進行數據庫的連接。
具體操作如下:
1)聲明一個Connection指針
_ConnectionPtr m_pConnection;
注:
ADO最重要的對象有三個:
連接對象(Connection)、命令對象(Command)和記錄集對象(RecordSet)。
在使用這三個對象的時候,需要定義與之相對應的智能指針,分別為_ConnectionPtr、_CommandPtr、_RecordsetPtr。
由上述ConnectionPtr指針的使用步驟可知,和C++中的類指針使用方法一樣,智能指針也要先定義指針變量、
創建其實例(實例化),然后就可以調用它的方法和屬性。 不同的是,該智能指針最后是自動進行內存釋放的。
所有的智能指針都是基於_com_ptr_t模板類的,該類封裝了IUnknow接口的3個方法:QueryInterface、Addref和Release。
它具有自動計數的機制,即在構造對象時,自動為該對象計數加1。析構對象時,自動調用Release方法。
(即該類型的指針在使用后不需要手動釋放內存)(但需要調用Close方法,關閉連接或者記錄集)
所以智能指針會使代碼更加簡潔並且不易出錯。
2)創建Connection對象
方法有如下兩種:
m_pConnection.CreateInstance(__uuidof(Connection));
m_pConnection.CreateInstance("ADODB.Connection");
注意:
上面調用_ConnectionPtr接口指針的方法CreateInstance時,用的是"."而非"->"。
3)設置連接字符串,以便指定需要的連接
對應不同的數據庫,其連接字庫串有所不同:
3.1) 使用JET數據庫引擎實現對Acess2000類型的數據庫info.mdb的連接
CString strSQ L= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=info.mdb;User ID=admin;Passward=;";
或者
CString strSQL = _T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=info.mdb;User ID=admin;Passward=;");
3.2) 使用OLE DB提供者實現對SQL Server的標准安全連接串
strConnect = _T("Provider=SQLOLEDB;Data Source=MyServerName;\
Initial Catalog=MyDateBaseName;\
User ID=MyUserName;Password=MyPassword;");
或者
strConnect = _T("Provider=SQLOLEDB;Server=MyServerName;\
Database=MyDateBaseName;\
Uid=MyUserName;Pwd=MyPassword;");
例程:
_bstr_t strConnect("Provider=SQLOLEDB;Server=MYSERVER;Database=MYDATADABE;Uid=sa;Pwd=12345678;");
m_pConnection->Open((_bstr_t)strSQL,"","",adModeUnknown);
或者, 是在此處不設置User ID和Password,而直接在Open函數的第2、3個參數中設置,這也可以。
_bstr_t strConnect("Provider=SQLOLEDB;Server=MYSERVER;Database=MYDATADABE;");
m_pConnection->Open((_bstr_t)strSQL,"sa","12345678",adModeUnknown);
注意:
上面設置連接字符串的時候,如果過長需要分行時,可用反斜杠"\"。
如果是本地服務器,則DataSource=local或本地服務器名均可。
若數據庫沒有設置密碼,在連接字符串中可以將其省略,但User ID不能省。
若數據庫和程序文件不在同一文件夾下,直接寫數據庫名即可,在InitialCatalog中不需加上該數據庫的存儲器地址。
3.3) 使用OLE DB提供者實現對遠程SQL Server的標准安全連接串
strConnec t= _T("Provider=sqloledb;Network Library=DBMSSOCN;\
Data Source=130.120.110.001,1433;
Initial Catalog=MyDateBaseName;
User ID=MyUserName;Password=MyPassword;");
4)、使用m_pConnection的Open方法實現對數據庫的連接
在ADO的操作中建議使用 try...catch( )來捕獲錯誤信息,因為它有時會經常出現一些意想不到的錯誤
try { m_pConnection-> //...
m_pRecordSet-> //...
} catch(_com_error e) //捕捉異常
{ CString strError; strError.Format( "連接數據庫發生異常! \r \n錯誤信息:%s", e.ErrorMessage( ) ); //顯示錯誤信息
AfxMessageBox(errormessage); }
// ---------------------------------------------------------------------------------------------------------------------------------
4、關閉連接
一般重載App類的ExitInstace( )函數實現。
調用m_pConnection的Close方法關閉連接即可。
代碼如下:
m_pConnection->Close( );
m_pConnection=NULL;
注意:
由於初始化COM庫調用的是AfxOleInit,這種方法初始化COM庫的優點就在於資源的釋放也是自動進行的,所以不必擔心資源泄漏的問題。
二、數據庫操作 ADO庫中包含的三個基本接口為: _ConnectionPtr接口、_CommandPtr接口、_RecordsetPtr接口。
// ---------------------------------------------------------------------------------------------------------------------------------
1、_ConnectionPtr接口
該接口返回一個記錄集或一個空指針。
通常用它來創建一個數據庫連接,或執行一條不返回任何結果的SQL語句,如一個存儲過程。
不推薦使用_ConnectionPtr接口返回一個記錄集,對於要返回記錄集的操作通常用_RecordsetPtr來實現。
而且使用_ConnectionPtr時要想得到記錄數目必須遍歷所有記錄,但使用_RecordsetPtr時則不需要。
// ---------------------------------------------------------------------------------------------------------------------------------
2、_CommandPtr接口
該接口返回一個記錄集。
它提供了一種簡單的方法來執行返回記錄集的存儲過程和SQL語句。
在使用_CommandPtr接口時,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr 接口里直接使用連接串。
如果只執行一次或者幾次數據庫訪問操作,后者是比較好的選擇。
但是,如果頻繁訪問數據庫,並要返回很多記錄集,那么應該使用全局_ConnectionPtr接口創建一個數據庫連接,然后使用_CommandPtr接口執行存儲過程和SQL語句。
// ---------------------------------------------------------------------------------------------------------------------------------
3、_RecordsetPtr接口
該接口是一個記錄集對象。
與前兩種對象相比,它對記錄集提供了更多的控制功能,如記錄鎖定、游標控制等。
同_CommandPtr接口一樣,它不一定使用一個已經創建的數據庫連接,可以用一個連接串代替連接指針賦給_RecordsetPtr的connection成員變量,讓它自己創建數據庫連接。
如果使用多個記錄集,最好的方法是同Command對象一樣使用已經創建了數據連接的全局_ConnectionPtr接口,然后使用_RecordsetPtr執行存儲過程和SQL語句。
注意:
可以使用Recordset對象來執行查詢命令,但如果查詢或者存儲過程是需要參數的,這時就只能使用Command對象。
使用Recordset對象操作數據庫:
假定已經成功使用Connection對象創建了數據源的連接,連接指針為m_pConnection
1)創建記錄集
_RecordsetPtr m_pRecordset; // 聲明記錄集指針
m_pRecordset.CreateInstance(__uuidof(Recordset)); // 創建記錄集
或者:
_RecordsetPtr m_pRecordset; // 聲明記錄集指針
m_pRecordset.CreateInstance("ADODB.Recordset"); // 創建記錄集
2)打開記錄集
記錄集指針創建完畢后,調用該指針的Open方法打開記錄集。
該函數聲明如下:
HRESULT Recordset15::Open(const _variant_t & Source,
const _variant_t & ActiveConnection,
enum CursorTypeEnum CursorType,
enum LockTypeEnum LockType,
long Options);
各個參數的含義如下:
參數Source:
為_variant_t類型的引用,可以為有效的Command對象、SQL語句、表名、存儲過程調用等。
參數ActiveConnection:
為_variant_t類型的引用,為已經建立好的連接。
參數CursorType:
用於設置在打開Recordset時提供者應使用的游標類型,它可取CursorTypeEnum 中的任一值,默認值為adOpenForwardOnly。
參數 LockType:
用於設置在打開Recordset時提供者應使用的鎖定類型,它可取枚舉LockTypeEnum中的任一值,默認值為adLockReadOnly。
參數 Options:
用於設置獲取Source(即Open第一個參數)的方式,其類型long。
例程1:
CString strSQL = "select * from mytablename";
m_pRecordset->Open(_variant_t (strSQL),
m_pConnection.GetInterfacePtr( ),
adOpenDynamic,
adLockOptimistic,
adCmdText);
使用SQL語句作為Open方法的第一個參數Source的值,此時Options為adCmdText
例程2:
m_pRecordset->Open(_variant_t ("tbDVDInfo"),
m_pConnection.GetInterfacePtr( ),
adOpenDynamic,
adLockOptimistic,
adCmdTable);
直接使用表名作為第一個參數,此時Options應為adCmdTable
3)遍歷記錄集
一般在返回記錄集時,通常要遍歷結果記錄集,以便查看或編輯某一條記錄,Recoreset指針提供了幾個用於實現遍歷的方法。
注意:
為了避免發生異常,一般在使用MoveFirst、MovePrev之前,需要使用記錄集的指針BOF屬性來檢測當前的記錄集指針是否位於第一條記錄之前;
在使用MoveLast、MoveNext之前需要使用記錄集指針的EOF屬性來檢測當前的記錄集指針是否位於最后一條記錄之后。
4)記錄集定位
記錄集接口類提供了兩種定位方法: 絕對定位和書簽定位
前者通過設置或者獲取AbsolutePosition屬性即可,其值從1開始,並且當前記錄為記錄集中第一條記錄時等於1
對於后者可以通過設置或獲取BookMark屬性即可。
5)訪問記錄集
讀寫記錄集中某一記錄中的一個字段,有具體有兩種方法:
一:
讀取操作:
_variant_t var = m_pResultSet->Fields->GetItem(_variant_t("字段名"))->Value;
也可用GetValue方法:
_variant_t var = m_pResultSet->Fields->GetItem(_variant_t("字段名"))->GetValue();
寫入操作:
_variant_t var = ..... // 初始化
m_pResultSet->Fields->GetItem(_variant_t("字段名"))->Value = var;
也可用PutValue方法:
m_pResultSet->Fields->GetItem(_variant_t("字段名"))->PutValue(var);
二:
最簡單的方法是直接使用如下語句:
讀取操作:
_variant_t var = m_pRecordset->GetCollect (_variant_t("字段名"));
寫入操作:
_variant_t var = ... // 初始化
m_pRecordset->PutCollect (_variant_t("字段名"), var);
兩個方法的原型:
_variant_t GetCollect(const _variant_t & Index )
void PutCollect(const _variant_t & Index, const _variant_t &pvar)
其中:
參數Index可以是字符串表示字段名,也可以是整型,表示字段對應的序號。
pvar表示要寫入的變量值。
例如:
_variant_t var;
var = m_pRentRecordset->GetCollect(_variant_t("ID"));
var = m_pRentRecordset->GetCollect(long(0)); // 都可以
6)記錄集更新
更新記錄集包括添加新的記錄、編輯當前記錄和刪除當前記錄
記錄集接口指針對這三種操作分別提供了相應的方法
添加新的記錄:AddNew
編輯當前記錄:Edit
刪除當前記錄:Delete
注意:
記錄集接口指針針對AddNew以及Edit方法提供了Update方法,用於在數據庫中更新新添加或者編輯后的記錄。
AddNew方法:
用於添加新紀錄(該添加是直接在表的末尾續加的),該方法可以使用參數,在參數中指定要添加的新紀錄;
也可以不使用參數,而在后面使用PutCollect方法,並需使用Update函數保存新紀錄。
Update方法:
用於保存從調用AddNew方法以來所作的任何更改。
例程:
//在表的末尾增加新紀錄
m_pRecordset->AddNew();
m_pRecordset->PutCollect(_variant_t("姓名"),
_variant_t(m_strName));
m_pRecordset->PutCollect(_variant_t("工作單位"), _variant_t(m_strComName));
m_pRecordset->PutCollect(_variant_t("單位地址"), _variant_t(m_strComAddr));
//更新數據庫-將新紀錄存入數據庫
m_pRecordset->Update();
7)記錄集關閉
在對記錄集的操作完成后,必須及時關閉記錄集。
if ( m_pRecordset != NULL )
{
m_pRecordset ->Close( );
m_pRecordset =NULL;
}
三、ADO中的數據類型
在使用ADO技術操作數據庫時,存取變量的數據類型都是COM類型,這就要求經常在COM類型和普通數據類型之間進行類型轉換。
ADO中特有的數據類型包括以下三種:
// ---------------------------------------------------------------------------------------------------------------------------------
1、Variant
該類型是結構化的數據類型,它包含值成員和數據類型成員。
Variant可以包含許多其他的數據類型:如Variant、BSTR、Boolean、IDispatch或者IUnknow指針、貨幣、日期等。
COM中由_variant_t 類來封裝和管理Variant數據類型。
在使用ADO對象模型操作數據庫時,如果對象的某個方法或者屬性操作數接受某個值,那么通常表明該值在_variant_t 中傳遞。
注意:
在利用ADO對象進行C++數據類型的相關數據庫操作(如將CString類型的值添加到數據庫)時,需要進行強制類型轉換
例如:
對於 CString m_strName;
m_pRecordset->PutCollect("姓名", _variant_t(m_strName));
// ---------------------------------------------------------------------------------------------------------------------------------
2、BSTR
該類型 ( Basic STRing )也是結構化的數據類型,它包含字符串和字符串的長度。
COM提供分配、處理和釋放BSTR的方法,由_bstr_t類來封裝和管理BSTR數據類型
在使用ADO對象模型操作數據庫時,如果對象的某個方法或者屬性操作數接受一個字符串值,那么通常表明該值的形式為_bstr_t,而非一般的CString。
// ---------------------------------------------------------------------------------------------------------------------------------
3、SafeArray 同樣是一種結構化的數據類型,包含其它數據類型的數組。
在使用ADO對象模型操作數據庫時,如果對象的某個方法或者屬性操作數接受或者返回一個數組,則數組類型只能是SafeArray,而非通常意義上的C/C++數組。
通常,從數據庫中取出的字段值大都在_variant_t中傳遞,下面給出從COM類型Variant向CString類型轉換的通用函數。
該函數將為數據庫的訪問和操作提供極大便利。
1 CString CLeftTreeView::VariantToCString(const _variant_t &var) 2 { 3 CString strValue; 4 switch (var.vt) 5 { 6 case VT_BSTR: //字符串
7 case VT_LPSTR: 8 case VT_LPWSTR: 9 strValue = (LPCTSTR)(_bstr_t)var; 10 break; 11 case VT_I1: //無符號字符
12 case VT_UI1: 13 strValue.Format("%d", var.bVal); 14 break; 15 case VT_I2: //短整型
16 strValue.Format("%d", var.iVal); 17 break; 18 case VT_UI2: //無符號短整型
19 strValue.Format("%d", var.uiVal); 20 break; 21 case VT_INT: //整型
22 strValue.Format("%d", var.intVal); 23 break; 24 case VT_I4: //整型
25 case VT_I8: //長整型
26 strValue.Format("%d", var.lVal); 27 break; 28 case VT_UINT: //無符號整型
29 strValue.Format("%d", var.uintVal); 30 break; 31 case VT_UI4: //無符號整型
32 case VT_UI8: //無符號長整型
33 strValue.Format("%d", var.ulVal); 34 break; 35 case VT_VOID: 36 strValue.Format("%8x", var.byref); 37 break; 38 case VT_R4: //浮點型
39 strValue.Format("%.4f", var.fltVal); 40 break; 41 case VT_R8: //雙精度型
42 strValue.Format("%.8f", var.dblVal); 43 break; 44 case VT_DECIMAL://小數
45 strValue.Format("%.8f", (double)var); 46 break; 47 case VT_CY: 48 { 49 COleCurrency cy = var.cyVal; 50 strValue = cy.Format(); 51 } 52 break; 53 case VT_BLOB: 54 case VT_BLOB_OBJECT: 55 case 0x2011: 56 strValue = "[BLOB]"; 57 break; 58 case VT_BOOL: //布爾型
59 strValue = var.boolVal ? "TRUE" : "FALSE"; 60 break; 61 case VT_DATE: //日期型
62 { 63 DATE dt = var.date; 64 COleDateTime da = COleDateTime(dt); 65 strValue = da.Format("%Y-%m-%d %H:%M:%S"); 66 } 67 break; 68 case VT_NULL: //NULL值
69 case VT_EMPTY: //空
70 strValue = ""; 71 break; 72 case VT_UNKNOWN://未知類型
73 default: 74 strValue = "UN_KNOW"; 75 break; 76 } 77 return strValue; 78 }