天天看點

MFC 使用 ADO技術連接配接Access資料庫

學了很久的MFC,于是想着使用MFC的ADO技術連接配接Access資料庫。

記錄一下,以備後面複習。

1.首先需要了解MDB資料庫檔案,本次示例代碼連接配接的是MDB資料庫檔案。*.MDB檔案是(Microsoft Database) 檔案的縮寫,是Access資料庫檔案的一種格式。就像是Word文檔的.doc檔案一樣。

2.下面開始講一下連接配接的具體步驟:

(1)建立一個對話框工程,添加相對應的控件,如下圖所示:

MFC 使用 ADO技術連接配接Access資料庫

首先最關鍵的步驟如下:

在stdafx.h頭檔案中添加如下代碼,注意工程運作的x86平台下才不會出錯。如果運作在x64平台下,可能會報錯。筆者使用VS2022時運作在x64平台下,報錯無法打開檔案。可以參考該說明:https://blog.csdn.net/hbspring007/article/details/107467823

#import "C:\\Program Files (x86)\\Common Files\\System\\ado\\msado15.dll" no_namespace rename("EOF","adoEOF"),rename("BOF","adoBOF")
#import "C:\\Program Files (x86)\\Common Files\\System\\ado\\msjro.dll" no_namespace rename("ReplicaTypeEnum","_ReplicaTypeEnum")  
           

msado15.dll:主要用于對資料庫進行操作。

msjro.dll:主要用于對資料庫進行壓縮。

第二步:

1.首先需要将msado15.tlh檔案和msado15.tli檔案添加到工程中。如果找不到,可從筆者工程中查找。

2.在工程的*App.h頭檔案中添加:連接配接資料庫的代碼如下:

_ConnectionPtr :為指向Connect 類對象的指針,用于連接配接資料庫。

在工程的*App.cpp的檔案的InitInstance()函數中的建立對話框之前添加如下代碼:

// 初始化COM庫
if (!AfxOleInit())
{
	AfxMessageBox(_T("初始化OLE DLL失敗"));
}
HRESULT hr;
try
{
	// 建立Connection對象 
     hr = m_pConnnect.CreateInstance(_T("ADODB.Connection"));
     if (SUCCEEDED(hr))
     {   // 下面的連接配接字元串中Provider是針對Access2000環境的,對于Access97環境,需要改為Microsoft.Jet.OLDDB.3.51.
     hr = m_pConnnect->Open(_T("Provider =   Microsoft.Jet.OLEDB.4.0;Data Source= AdoData.mdb"), "", "", adModeUnknown);  // 連接配接資料庫
     }
}
catch (_com_error e) // 捕捉異常
{
     CString errormessage;
	 errormessage.Format(_T("連接配接資料庫失敗!\r\n錯誤資訊:%s"), e.ErrorMessage());
	 AfxMessageBox(errormessage);///顯示錯誤資訊
}
           

注意:上面的m_pConnect及使用了 “.” 操作符,又指針的行為間接引用"->“,是因為_ConnectionPtr類重載了”->"操作符。

首先,我們需要先初始化COM庫。

其中的Data Source = AdoData.mdb資料庫是預先做好的資料庫。使用_ConnectionPtr::Open()函數打開資料庫。

第三步:在***Dlg.h 檔案中添如下代碼:

為對話框關聯成員變量如下:

_RecordsetPtr m_pRecordset;
    UINT m_nID;
	UINT m_nAge;
	CString m_strUserName;
	// DataTime 控件成員變量
	COleDateTime m_tBirthday;
	// CListCtrl 控件
	CListCtrl m_UserList;
	// 用于判斷是否成功讀取資料庫資料
	BOOL m_bSuccess;
	// 擷取CListCtrl 控件中目前選擇的行
	int m_nCurSelect;
	// 判斷在添加資料或者選擇資料後是否儲存資料
	BOOL m_bAutoSave;

	// 定義四個 _variant_t 類對象
	_variant_t m_vID, m_vUserName, m_vAge, m_vBirthday;
           

_RecordsetPtr: 用于通路資料庫中的資料。具體的用法可以參考MSDN,網址如下:MSDN 關于ADO 的介紹

其中使用了_variant_t類,關于這個類的介紹,大家也可以參考前面的MSDN.

_variant_t類:封裝和管理了Variant類型的資料。

另外再添加兩個函數用于儲存修改和删除原來的資料。

在工程對話框的***::OnInitialDialog()函數中添加如下代碼:

// 為清單控件添加列
	m_UserList.SetExtendedStyle(m_UserList.GetExtendedStyle()|LVS_EX_FULLROWSELECT| LVS_EX_GRIDLINES);
	m_UserList.InsertColumn(0,_T("ID"),LVCFMT_LEFT,60);
	m_UserList.InsertColumn(1, _T("使用者名"), LVCFMT_LEFT,100);
	m_UserList.InsertColumn(2, _T("年齡"), LVCFMT_LEFT,60);
	m_UserList.InsertColumn(3, _T("生日"), LVCFMT_LEFT,120);
    try
	{
		// 建立一個新的連接配接資料庫執行個體
	m_pRecordset.CreateInstance(_T("ADODB.Recordset"));
		// 使用SQL語句,用來查詢users這個表的内容
		m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch*)theApp.m_pConnnect,TRUE),adOpenStatic,adLockOptimistic,adCmdText);
		m_bSuccess = TRUE;
		// EOF(End of File:訓示目前位置位于Recordset對象的最後一個記錄之後)
		// 使用BOF(Before of File):bof表示rs目前的指針是指在了資料集的前面;使用BOF和EOF可用于判斷Recordset對象是否包含記錄
		// 或者從一個記錄移到另一個記錄時是否超出Recordset對象的限制。
		while(!m_pRecordset->adoEOF)  
		{
			// GetCollect()函數的作用:用來擷取收集的字元串,例如就是用來擷取_T("")字元串的
			vID = m_pRecordset->GetCollect(_T("id"));
			vUserName = m_pRecordset->GetCollect(_T("username"));
			vAge = m_pRecordset->GetCollect(_T("age"));
			vBirthdayr = m_pRecordset->GetCollect(_T("birthday"));
			// 在控件中插入新行
			nItem = m_UserList.InsertItem(0xffff,(_bstr_t)vID);
			// 在已經存在的行中設定資訊
			m_UserList.SetItem(nItem, 1, 1, (_bstr_t)vUserName,NULL,0,0,0);
			m_UserList.SetItem(nItem, 2, 1, (_bstr_t)vAge, NULL, 0, 0, 0);
			m_UserList.SetItem(nItem, 3, 1, (_bstr_t)vBirthdayr, NULL, 0, 0, 0);
			m_pRecordset->MoveNext(); 
		}
	}
	catch (_com_error e)
	{
		AfxMessageBox(_T("讀取資料庫失敗!"));
	}
           

上面對話框中,添加行按鈕的實作代碼如下:

UpdateData();
	if (m_strUserName.GetLength() > 0)
	{
		// 建立可更新Recordset對象的新紀錄
		m_pRecordset->AddNew();
		// 在65535行中插入_T("")
		m_nCurSelect = m_UserList.InsertItem(0xffff,_T(""));
		SaveData();
		m_UserList.SetItemState(m_nCurSelect,LVIS_SELECTED| LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
		m_UserList.SetHotItem(m_nCurSelect);
		m_UserList.SetFocus();
	}
	else
	{
		AfxMessageBox(_T("請輸入使用者名"));
	}
           

調用的SaveData()函數的代碼如下:

if (!m_pRecordset->adoEOF && m_nCurSelect >= 0 && m_bAutoSave)
	{
		m_vID = (long)m_nID;
		m_vUserName = m_strUserName;
		m_vAge = (long)m_nAge;
		m_vBirthday = m_tBirthday;
		// PutCollect(A,B)函數的作用是将B中的資料添加到資料庫A中
		m_pRecordset->PutCollect(_T("id"),m_vID);
		m_pRecordset->PutCollect(_T("username"), m_vUserName);
		m_pRecordset->PutCollect(_T("age"), m_vAge);
		m_pRecordset->PutCollect(_T("birthday"), m_vBirthday);
		// 将目前修改的值插入到目前選擇行中
		m_UserList.SetItem(m_nCurSelect,0,LVIF_TEXT,(_bstr_t)m_vID,NULL,0,0,0);
		m_UserList.SetItem(m_nCurSelect, 1, LVIF_TEXT, (_bstr_t)m_vUserName, NULL, 0, 0, 0);
		m_UserList.SetItem(m_nCurSelect, 2, LVIF_TEXT, (_bstr_t)m_vAge, NULL, 0, 0, 0);
		m_UserList.SetItem(m_nCurSelect, 3, LVIF_TEXT, (_bstr_t)m_vBirthday, NULL, 0, 0, 0);
	}
           

與之相對的LoadData()函數的代碼如下:

m_pRecordset->Move(m_nCurSelect, _variant_t((long)adBookmarkFirst));

	m_vID = m_pRecordset->GetCollect(_T("id"));
	m_vUserName = m_pRecordset->GetCollect(_T("username"));
	m_vAge = m_pRecordset->GetCollect(_T("age"));
	m_vBirthday = m_pRecordset->GetCollect(_T("birthday"));

	m_nID = m_vID.lVal;
	m_strUserName = (LPCTSTR)(_bstr_t)m_vUserName;
	m_nAge = m_vAge.lVal;
	m_tBirthday = m_vBirthday;
	UpdateData(FALSE);
           

前面對話框中删除按鈕的實作代碼如下:

m_bAutoSave = FALSE;
	if (m_nCurSelect >= 0)
	{
		// 删除目前選中行
		m_UserList.DeleteItem(m_nCurSelect);
		int nCount = m_UserList.GetItemCount();
		if (nCount <= m_nCurSelect)
			m_nCurSelect = nCount - 1; // 删除之後重新設定選中行
		// 設定目前指針指向的記錄會被删除
		m_pRecordset->Delete(adAffectCurrent);
		// 将目前記錄集指針移到下一個記錄
		m_pRecordset->MoveNext();
		if (nCount > 0)
			LoadData();
		// 設定目前選中行,為選中狀态
		m_UserList.SetItemState(m_nCurSelect, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
		m_UserList.SetFocus();
	}
	m_bAutoSave = TRUE;
           

點選ListCtrl清單控件通知消息的代碼如下:

void CMFCTESTADODlg::OnLvnItemchangedUserList(NMHDR* pNMHDR, LRESULT* pResult)
{
	// NMHDR:包含有關此通知消息的資訊結構體
	// LPNMLISTVIEW:專屬于ListView類型的通知消息結構體,包含了NMHDR,換一個控件會有不同的形式
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO: 在此添加控件通知處理程式代碼
	// 判斷是否選擇目前控件的行
	if (pNMLV->uNewState & LVIS_SELECTED)
	{
		UpdateData();
		SaveData();
		// 設定目前行為選中行
		m_nCurSelect = pNMLV->iItem;
		LoadData();
	}
	*pResult = 0;
}
           

最後确定按鈕的實作代碼如下:

if (m_bSuccess)
{
	// Update:儲存對于對于目前記錄集對象,即m_pRecordset資料的修改
	m_pRecordset->Update();
	// 關閉一個對象
	m_pRecordset->Close();
}
           

該Demo中用到的主要技術有:

_ConnectPtr類和_RecordsetPtr類。

Connect::CreateInstance;

Connect::Open;

Recordset::CreateInstance;

Recordset::Open;

Recordset的adoEOF和adoBOF成員變量;

Recordset::GetCollect();

Recordset::MoveNext();

Recordset::AddNew();

Recordset::Delete();

Recordset::Update();

Recordset::Close();

Recordset::Move();

這些函數的具體用法可以參考微軟的官方文檔:

Recordset Methods

本Demo的源碼下載下傳如下:Gitee