參考FROM:http://hi.baidu.com/sunkanghome/item/e1fda510b3186359f1090ee2
數據庫與數據庫編程:
- 當前各種主流數據庫有很多,包括Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, Interbase / Firebird, PostgreSQL, SQLite, SAP/DB, TimesTen, MS ACCESS等等。
- 數據庫編程是對數據庫的創建、讀寫等一列的操作。數據庫編程分為數據庫客戶端編程與數據庫服務器端編程。數據庫客戶端編程主要使用ODBC API、ADO、ADO.NET、OCI、OTL等方法;數據庫服務端編程主要使用OLE DB等方法。
- 數據庫編程需要掌握一些訪問數據庫技術方法,還需要注意怎么設計高效的數據庫、數據庫管理與運行的優化、數據庫語句的優化。
ADO編程的一般步驟:
- 創建一個Connection對象
- 打開數據源,建立同數據源的連接
- 執行一個SQL命令
- 使用結果集
- 終止連接
ADO最重要的三個對象:
- 連接對象(Connection)
- 命令對象(Command)
- 記錄集對象(RecordSet)
在使用這三個對象的時候,需要定義與之相對應的智能指針:_ConnectionPtr、_CommandPtr、_RecordsetPtr
使用智能指針要:定義指針變量、創建其實例(實例化)、調用方法和屬性。該智能指針在析構對象時,自動調用Release方法,即使用后不需要手動釋放內存,代碼更加簡潔。
但需要調用Close方法,關閉連接Connection或者記錄集RecordSet。
一、ADO編程預處理操作
1.1 導入ADO動態鏈接庫:
在工程的stdafx.h中加入如下語句:
#import "c:\\Program Files\\Common Files\\System\\ADO\\msado15.dll" rename_namespace("ADOCG") rename("EOF","adoEOF") //rename("BOF","adoBOF") no_namespace using namespace ADOCG;
注:import代碼要在一行中完成,換行需添加'\'
1.2 初始化OLE/COM庫環境:
在基於MFC的應用里,在應用類的InitInstance成員函數中初始化OLE/COM庫環境,直接使用AfxOleInit,在退出應用時,該函數自動負責COM資源的釋放,比較方便,不用在
ExitInitInstance中添加相關操作:
BOOL CYourApp::InitInstance() { AfxEnableControlContainer(); //初始化OLE DLLs if(!AfxOleInit()) { AfxMessageBox("初始化OLE DLL失敗!"); Return FALSE; } ...... }
二、ADO進行數據庫連接:
2.1 在App類的頭文件中定義變量:
_ConnectionPtr m_pConnection;
2.2 創建智能指針的實例:
在App類的cpp文件InitInstance方法中:
m_pConnection.CreateInstance("ADODB.Connection"); //或者m_pConnection.CreateInstance(__uuidof(Connection));
使用'.'而不是->創建m_Connection實例,然后m_pConnection->open方法創建連接。
2.3 設置連接字符串,以便指定需要的連接
2.3.1 使用JET數據庫引擎實現對Acess2000類型的數據庫info.mdb的連接
CString strSQL=_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=info.mdb;User ID=admin;Passward=;");
2.3.2 使用OLE DB提供者實現對SQL Server的標准安全連接串
CString str SQL=_T("Provider=SQLOLEDB;Data Source=local;Initial Catalog=DVDRentDB_Data.MDF;User ID=sa;Password=123456;");
或者是在此處不設置User ID和Password,而直接在Open的第2、3個參數中設置。
CString strConnection="Provider=SQLOLEDB;DataSource=local;Initial Catalog=DVDRentDB_Data.MDF"; m_pConnection->Open((_bstr_t)strSQL,"sa","820415",adModeUnknown);
注意:
- 上面設置連接字符串的時候,如果過長需要分行時,則每一行都要加上雙引號,在最后加上分號即可。
- 如果是本地服務器,則Data Source=loca l或 本地服務器名(主機名)
- 若數據庫沒有設置密碼,在連接字符串中可以將其省略,但User ID不能省
- 若數據庫和程序文件不在同一文件夾下,直接寫數據庫名即可,在InitialCatalog中不需加上該數據庫的存儲器地址
2.3.3 使用OLE DB提供者實現對遠程SQL Server的標准安全連接串
strConnect=_T("Provider=sqloledb;Network Library=DBMSSOCN;" "Data Source=130.120.110.001,1433;" "Initial Catalog=MyDateBaseName;" "User ID=MyUserName;Password=MyPassword;");
2.4 實現對數據庫的連接
在ADO的操作中建議使用try...catch( )來捕獲錯誤信息,因為它有時會經常出現一些意想不到的錯誤
try { m_pConnection->Open( (_bstr_t) strSQL," "," ",adModeUnknown); } catch(_com_error e) //捕捉異常 { CString strError; strError.Format( "連接數據庫發生異常! \r \n錯誤信息:%s",e.ErrorMessage( ) ); AfxMessageBox(errormessage); //顯示錯誤信息 }
綜上:InitInstance方法中可:
if(!AfxOleInit()) { AfxMessageBox("初始化OLE DLL失敗!"); Return FALSE; } m_pConnection.CreateInstance("ADODB.Connection"); //或者m_pConnection.CreateInstance(__uuidof(Connection)); try { m_pConnection->ConnectionTimeout = 3; //連接ACCESS2000 m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:/Attendence/AttendenceDB.mdb","","",adModeUnknown); } catch(_com_error e) { AfxMessageBox(e.Description() + _T("\n數據庫連接失敗")); }
2.5 關閉數據庫連接
一般重載App類的ExitInstace( )函數,調用m_pConnection的Close方法關閉連接即可:
m_pConnection->Close( );
m_pConnection=NULL;
注意:由於初始化COM庫調用的是AfxOleInit,這種方法初始化COM庫的優點就在於資源的釋放也是自動進行的,所以不必擔心資源泄漏的問題。
三、數據庫操作:
_CommandPtr接口
該接口返回一個記錄集, 在使用_CommandPtr接口時,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用連接串。如果只執行一次或者幾次數據庫訪問操作,后者是比較好的選擇。但是,如果頻繁訪問數據庫,並要返回很多記錄集,那么應該使用全局_ConnectionPtr接口創建一個數據庫連接,然后使用_CommandPtr接口執行存儲過程和SQL語句。
_RecordsetPtr接口
該接口是一個記錄集對象, 與前兩種對象相比,它對記錄集提供了更多的控制功能,如記錄鎖定、游標控制等。
3.1 使用Recordset對象操作數據庫:
假定已經使用連接指針m_pConnection對象創建了數據源的連接:
3.1.1 創建記錄集
_RecordsetPtr m_pRecordset; //聲明記錄集指針 m_pRecordset.CreateInstance(__uuidof(Recordset)); //創建實例
3.1.2 打開記錄集
記錄集指針創建完畢后,調用該指針的Open方法打開記錄集。
//讀取數據庫: CString sqlHasRecord; sqlHasRecord.Format("SELECT * FROM WorkUser WHERE MemberID = '%s'" , m_StringNumber); HRESULT hr = m_pRecordset->Open(
sqlHasRecord.GetBuffer(0),
_variant_t((IDispatch*)theApp.m_pConnection, true),
adOpenDynamic,
adLockPessimistic,
adCmdText
);
Open函數聲明如下:
HRESULT Recordset15::Open ( const _variant_t & Source, //sql語句、表名、command對象 const _variant_t & ActiveConnection, //已經建立好的連接 enum CursorTypeEnum CursorType, //用於設置在打開Recordset時提供者應使用的游標類型,默認值adOpenForwardOnly enum LockTypeEnum LockType, //用於設置在打開Recordset時提供者應使用的鎖定類型,默認值adLockReadOnly long Options ) ; //獲取Source(即Open第一個參數)的方式
如果第一個參數是sql語句則選擇adCmdText
如果第一個參數是表名則選擇adCmdTable
//讀取數據庫: CString sqlHasRecord; sqlHasRecord.Format("SELECT * FROM WorkUser WHERE MemberID = '%s'" , m_StringNumber); HRESULT hr = m_pRecordset->Open(sqlHasRecord.GetBuffer(0), _variant_t((IDispatch*)theApp.m_pConnection, true), adOpenDynamic, adLockPessimistic, adCmdText); if (SUCCEEDED(hr)) { while (!m_pRecordset->adoEOF || !m_pRecordset->BOF)//遍歷返回的每一條記錄 { CString m_StringID; m_StringID = (LPCSTR)_bstr_t(m_pRecordset->GetCollect("FeatureID"));//讀取id
m_pRecordset->MoveNext();
} } m_pRecordset->Close(); //記錄用完之后需要關閉
3.1.3 遍歷記錄集
一般在返回記錄集時,通常要遍歷結果記錄集,以便查看或編輯某一條記錄:
注:為了避免發生異常:
MoveFirst、MovePrev之前,需要使用記錄集的指針BOF屬性來檢測當前的記錄集指針 是否位於第一條記錄之前;
MoveLast、MoveNext之前需要使用記錄集指針的EOF屬性來檢測當前的記錄集指針 是否位於最后一條記錄之后.
記錄集定位
兩種定位方法:前者通過設置或者獲取AbsolutePosition屬性,其值從1開始,並且當前記錄為記錄集中第一條記錄時等於1, 后者通過設置或獲取BookMark屬性.
3.1.4 訪問記錄集
讀取字段值:
m_pRecordset->GetCollect (字段名); // 字符串 -> 字段名 or 整型(long) -> 字段對應的序號
設置字段值:
m_pRecordset->PutCollect (字段名,新值);
//兩個方法的原型: _variant_t GetCollect ( const _variant_t & Index ) void PutCollect ( const _variant_t & Index , const _variant_t &pvar )
3.1.5 記錄集更新(添加、編輯、刪除)
添加新的記錄:AddNew
編輯當前記錄:Edit
刪除當前記錄:Delete
AddNew方法:直接在表的末尾續加新記錄,該方法可以使用參數,在參數中指定要添加的新紀錄;
也可以不使用參數,而在后面使用PutCollect方法,並需使用Update函數保存新紀錄。
Update方法:用於保存從調用AddNew方法以來所作的任何更改。
//在表的末尾增加新紀錄 m_pRecordset->AddNew(); //------------------ m_pRecordset->PutCollect("姓名",_variant_t(m_strName)); m_pRecordset->PutCollect("工作單位",_variant_t(m_strComName)); m_pRecordset->PutCollect("單位地址",_variant_t(m_strComAddr)); //------------------ m_pRecordset->Update();//更新數據庫-將新紀錄存入數據庫
3.1.6 記錄集關閉
在對記錄集的操作完成后,必須及時關閉記錄集。
if ( m_pRecordset != NULL ) { m_pRecordset ->Close( ); m_pRecordset =NULL; }
3.2 使用Connection對象操作數據庫
1.編輯SQL語句
2.執行connection對象的Excute()方法
//_ConnectionPt m_pConnection 智能指針對象
strSql.Format(_T("UPDATE WorkUser SET MemberName = '%s', MemberPosition = '%s' WHERE MemberID = '%s' "),m_StringName, m_StringPosition, m_StringNumber); try { m_pConnection->Execute(_bstr_t(strSql), 0, adCmdText); } catch(_com_error e) { MessageBox(e.Description()); return; }
注:_variant_t和_bstr_t這兩個類分別封裝並管理VARIANT和BSTR這兩種數據類型,VARIANT和BSTR這兩種類型是COM中使用的數據類型。
四、實例演示
4.1 初始化引入相關的庫+Connection對象的創建和數據庫的連接
#import "c:\\Program Files\\Common Files\\System\\ADO\\msado15.dll" rename_namespace("ADOCG") rename("EOF","adoEOF") //rename("BOF","adoBOF") using namespace ADOCG;
//... ... _ConnectionPtr m_pConnection;
//... ... if(!AfxOleInit()) { AfxMessageBox("初始化OLE DLL失敗!"); Return FALSE; } m_pConnection.CreateInstance("ADODB.Connection"); try { m_pConnection->ConnectionTimeout = 3; //連接ACCESS2000 m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:/Attendence/AttendenceDB.mdb","","",adModeUnknown); } catch(_com_error e) { AfxMessageBox(e.Description() + _T("\n數據庫連接失敗")); }
4.2 Recordset對象的聲明與創建實例:
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset)); //創建實例
4.3 讀取記錄內容:
CString sqlHasRecord; sqlHasRecord.Format("SELECT * FROM WorkUser WHERE MemberID = '%s'" , m_StringNumber); HRESULT hr = m_pRecordset->Open(sqlHasRecord.GetBuffer(0), _variant_t((IDispatch*)theApp.m_pConnection, true), adOpenDynamic, adLockPessimistic, adCmdText); if (SUCCEEDED(hr)) { while (!m_pRecordset->adoEOF || !m_pRecordset->BOF)//遍歷返回的每一條記錄 { CString m_StringID; m_StringID = (LPCSTR)_bstr_t(m_pRecordset->GetCollect("FeatureID"));//讀取id m_pRecordset->MoveNext(); } } m_pRecordset->Close(); //記錄用完之后需要關閉
4.4 插入新記錄:
CString strSql; strSql.Format("INSERT INTO WorkUser(MemberName, MemberID, MemberPosition, FeatureID, BeDeleted, SendedToClient) VALUES('%s', '%s', '%s', %d, 0, 0)",“ZhangSan”,”14S051000”, “Student”, 16); try { (theApp.m_pConnection)->Execute(_bstr_t(strSql), 0, adCmdText); } catch(_com_error e) { MessageBox(e.Description()); return; }
4.5 更新記錄:
strSql.Format(_T("UPDATE WorkUser SET MemberName = '%s', MemberPosition = '%s' WHERE MemberID = '%s' "),m_StringName, m_StringPosition, m_StringNumber); try { (theApp.m_pConnection)->Execute(_bstr_t(strSql), 0, adCmdText); } catch(_com_error e) { MessageBox(e.Description()); return; }
4.6 刪除記錄:
CString strSql; strSql.Format(_T("DELETE FROM WorkUser WHERE FeatureID = %s "), FeatureID[i]); try { (theApp.m_pConnection)->Execute(_bstr_t(strSql), 0, adCmdText); } catch(_com_error e) { MessageBox(e.Description()); return FALSE; }
4.7 讀取字節流文件:
讀取圖像數據:
try { CString sql; sql.Format("select * from WorkUser where MemberID='%s'",MemberID); HRESULT hr = m_pRecordset->Open(sql.GetBuffer(0),_variant_t((IDispatch *)theApp.m_pConnection,true),adOpenDynamic,adLockPessimistic,adCmdText); if(SUCCEEDED(hr)) { if(m_pRecordset->adoEOF||m_pRecordset->BOF) { MessageBox(_T("數據庫中沒有相應的記錄")); return; } else { long lDataSize = m_pRecordset->GetFields()->GetItem(imgColName.GetBuffer(0))->ActualSize; if (lDataSize>0) { _variant_t varBLOB; varBLOB = m_pRecordset->GetFields()->GetItem(imgColName.GetBuffer(0))->GetChunk(lDataSize); if(varBLOB.vt == (VT_ARRAY | VT_UI1)) { if(buffer) ///重新分配必要的存儲空間 { char *pBuf = NULL; SafeArrayAccessData(varBLOB.parray,(void **)&pBuf); memcpy(buffer,pBuf,lDataSize); ///復制數據到緩沖區buffer SafeArrayUnaccessData (varBLOB.parray); } } } else { MessageBox(_T("數據庫中的圖像數據為空!")); return; } } } } catch (...) { MessageBox(_T("數據庫訪問出錯")); } m_pRecordset->Close();
4.8 保存字節流文件:
CString sql; sql.Format("select * from WorkUser where MemberID='%s'",MemberID); HRESULT hr = m_pRecordset->Open(sql.GetBuffer(0),_variant_t((IDispatch *)theApp.m_pConnection,true),adOpenDynamic,adLockPessimistic,adCmdText); if(SUCCEEDED(hr)) { if (!m_pRecordset->adoEOF||!m_pRecordset->BOF) { char *pBuf = buffer; VARIANT varBLOB; SAFEARRAY *psa; SAFEARRAYBOUND rgsabound[1]; if(pBuf) { rgsabound[0].lLbound = 0; rgsabound[0].cElements = bufLength; psa = SafeArrayCreate(VT_UI1, 1, rgsabound); //分配的數據類型為unsigned int (1 byte 長度的類型) for (long i = 0; i < (long)bufLength; i++) SafeArrayPutElement (psa, &i, pBuf++); varBLOB.vt = VT_ARRAY | VT_UI1; varBLOB.parray = psa; m_pRecordset->GetFields()->GetItem(imgColName.GetBuffer(0))->AppendChunk(varBLOB); } m_pRecordset->Update(); } else { MessageBox(_T("數據庫中沒有相應用戶的記錄!")); return; } } m_pRecordset->Close();
SafeArrayDestroy