在上一篇中我們簡單的介紹了通過Connection對象獲取數據,主要目的是為了演示如何連接數據庫。在此篇文章中我們將完整的介紹獲取數據的各種方法,包括通過Command和Recordset對象獲取數據。
獲取數據的一個完整步驟是通過Connection對象進行連接,然后使用Command對象發送指令,最后通過Recordset對象來接受數據。下面我們就先來學習如何連接數據庫,當然在上一篇中我們已經介紹了如何使用Connection對象連接數據庫但是考慮的知識的完整性我還是會在此列出來。
連接數據庫
1.使用Connection對象
1: _ConnectionPtr connObject = NULL;2: connObject.CreateInstance(__uuidof(Connection)); //創建一個實例3: //連接字符串,就是我們在步驟4中獲取的字符串4: _bstr_t strConnStr = "Provider=SQLOLEDB.1;Password=zw123;\5: Persist Security Info=True;User ID=sa;Initial Catalog=Communication;Data Source=DAVID-PC";6: TESTHR(connObject->Open(strConnStr, "", "", adConnectUnspecified));
2.使用Recordset對象隱式連接
1: _RecordsetPtr recordSet = NULL;2: recordSet.CreateInstance(__uuidof(Recordset));3: _bstr_t strConnStr = "Provider=SQLOLEDB.1;Password=zw123;\4: Persist Security Info=True;User ID=sa;Initial Catalog=Communication;Data Source=DAVID-PC";5: _bstr_t strSql = "SELECT * FROM UserInfo";6: TESTHR(recordSet->Open(strSql, strConnStr, adOpenStatic, adLockReadOnly, adCmdText));7: PrintTableHeader(recordSet);8: PrintTableRecords(recordSet);
在此我們就只有到一個重要的函數Open。由於函數參數比較多,我們還先對這些參數做一個簡單的介紹。
函數原型:
HRESULT Open (
const _variant_t & Source,
const _variant_t & ActiveConnection,
enum CursorTypeEnum CursorType,
enum LockTypeEnum LockType,
long Options );
Source:可以是一個有效的Command對象(下面會介紹),SQL語句,表名,存儲過程調用,URL,一個文件名或一個存在Recordset對象中的Stream對象。
ActiveConnection:一個Connection對象或者連接字符串。
CursorType:在一個打開的記錄集中確定提供着所提供的光標類型。在上一篇文章Recoordset對象的介紹時有介紹,在此不贅述。
LockType:指定記錄的鎖類型
LockTypeEnum
| 常量 |
值 |
說明 |
| adLockBatchOptimistic |
4 |
指示開放式批更新。需要批更新模式。 |
| adLockOptimistic |
3 |
指示逐個記錄開放式鎖定。提供者使用開放式鎖定,僅在調用 Update 方法時鎖定記錄。 |
| adLockPessimistic |
2 |
指示逐個記錄保守式鎖定。提供者要確保記錄編輯成功,通常在編輯之后立即在數據源鎖定記錄。. |
| adLockReadOnly |
1 |
指示只讀記錄。無法改變數據。 |
| adLockUnspecified |
-1 |
未指定鎖定類型。創建副本時,副本與源對象使用相同的鎖定類型。 |
開放式鎖定
並行更新數據的一種途徑是用開放式鎖定。開放式鎖定工作原理是通過應用程序來檢查數據是否被更新而實現的。一種更普通的實現開放式鎖定的方法是在每個表中添加一個“版本列”(version column),對每個表而言,程序每次改變其中一行的時候都會更新這個“版本列”。每個UPDATE語句中的WHERE語句會根據上次查詢的結果判斷這個版本號是不是被更改了。
保守式鎖定
對於開放式鎖定來說,另外一種途徑是使用保守式鎖定。當讀取某些行的數據時,他會對這些數據加鎖,這樣就防止其他的訪問這些數據了。具體的實現是需要數據庫支持的,然而不幸的是,不是所有的數據庫都支持保守式鎖定。如果你的數據庫支持話,那么你的應用程序直接執行SQL語句來實現保守式鎖定將非常容易。Options 指定命令類型(CommandTypeEnum )和執行選項(ExecuteOptionEnum)
| 常量 |
值 |
說明 |
| adCmdUnspecified |
-1 |
不指定命令類型。 |
| adCmdText |
1 |
按命令或存儲過程調用的文本定義計算 CommandText。 |
| adCmdTable |
2 |
指定CommandText 為一個表名,內部會組織一個查詢所有數據的SQL語句。 |
| adCmdStoredProc |
4 |
指定CommandText 為一個存儲過程。 |
| adCmdUnknown |
8 |
· 默認值,表示CommandText為一個未知值,ADO會嘗試各種方式去解析文本命令。 |
| adCmdFile |
256 |
這個常量只用於Recordset對象中的Open和ReQuery。 |
| adCmdTableDirect |
512 |
這個常量只用於Recordset對象中的Open和ReQuery。使用Seek函數時,Recordset對象必須以adCmdTableDirect方式打開,這個值不能和ExecuteOptionEnum中的adAsyncExecute同時使用。 |
| 常量 |
值 |
說明 |
| adAsyncExecute |
0x10 |
表示命令以異步方式執行它不能夠跟CommandTypeEnum的adCmdTableDirect一起使用。 |
| adAsyncFetch |
0x20 |
超過CacheSize屬性所指定的大小后,剩下的數據將以異步的方式獲取。 |
| adAsyncFetchNonBlocking |
0x40 |
表示主線程絕不阻塞,如果沒有檢索到數據當前行將被自動移動到文件結束。在一個流對象(Stream )中打開的記錄將不會受到adAsyncFetchNonBlocking的影響,這操作將還是會以同步阻塞的方式執行。adAsynchFetchNonBlocking 也不會影響到以adCmdTableDirect打開的記錄集 |
| adExecuteNoRecords |
0x80 |
表示一個命令或存儲過程的執行不返回任何記錄集數據,不如像插入數據或更新數據,adExecuteNoRecords 值能做為Command 對象的或者 Connection 對象的Execute方法的參數。 |
| adExecuteStream |
0x400 |
表示命令執行的結果流作為一個Stream對象返回。adExecuteStream 選項只能用於Command對象的Excute方法的參數。 |
| adExecuteRecord |
|
表示CommandText屬性所指定的命令或存儲過程只返回一行數據,將其作為一個Record對象。 |
| adOptionUnspecified |
-1 |
命令未指定。 |
Command對象的使用
使用Command對象我們可以做如下一些事情:
- 定義一個可以執行的命令文本,例如:SQL語句和存儲過程。
- 通過使用Parameter對象和Parameters定義參數化的查詢和帶參的存儲過程。
- 通過Execute函數執行一個命令並且返回一個記錄集(如果有)。
- 通過使用CommandType屬性指定命令的類型,用以優化命令的執行。
- 通過使用Command對象的Dialect屬性指定關於命令文本的特殊信息。
- 通過使用Prepared屬性控制提供者是否保存一個編譯好的版本。
- 通過CommandTimeout設置命令執行的超時值。
- 通過設置Command對象的ActiveConnection屬性來關聯一個Connection對象。
- 通過Name屬性設置一個命名對象並作為Connection的一個方法來調用。
- 為了獲取數據,可以設置Recordset對象的Source屬性為一個命令對象。
- 傳遞一個包含命令(例如:一個XML命令)的Stream對象到提供者,前提是提供者要支持它。
1. 創建一個簡單(不帶參數化的查詢或存儲過程)的命令。
可以使用3種方式:Connection,Recordset和Command對象,前面我們已經介紹了前兩個命名。現在我們再對Command對象進行介紹。
使用一個Command對象時,我們必須指定它的CommandText屬性,用以判斷執行的命令是什么。然后我們再設置它的命令類型,通過CommandType屬性。在執行命令之前我們當然還需要一個Connection對象,我們通過ActiveConnection屬性去指定。到現在我們應經把准備工作做好了,只需要調用Execute方法去執行命令了。
1: _bstr_t strConnStr = "Provider=SQLOLEDB.1;Password=zw123;\2: Persist Security Info=True;User ID=sa;Initial Catalog=Communication;Data Source=DAVID-PC";3: _ConnectionPtr connObject = NULL;4: _CommandPtr commandObject = NULL;5: _RecordsetPtr recordSetObject = NULL;6:7: TESTHR(connObject.CreateInstance(__uuidof(Connection)));8: TESTHR(commandObject.CreateInstance(__uuidof(Command)));9: TESTHR(recordSetObject.CreateInstance(__uuidof(Recordset)));10:11: connObject->Open(strConnStr, "", "", adConnectUnspecified);12: commandObject->ActiveConnection = connObject;13: commandObject->CommandText = "SELECT * FROM UserInfo";14: commandObject->CommandType = adCmdText;15:16: recordSetObject = commandObject->Execute(NULL, NULL, adCmdUnspecified);17:18: PrintTableHeader(recordSetObject);19: PrintTableRecords(recordSetObject);
在Execute方法中我們會發現最后一個參數也能夠用於指定CommandType當然Execute函數會以傳入的參數為准,所以我們在此指定為adCmdUnspecified,這樣它就會以我們CommandType中指定的命令類型為准了。更多的命令類型可參見上文中的CommandTypeEnum.
2.帶參數的命令對象
帶參數的命令對象,這就意味着我們可以傳遞參數來多次執行命令。在Command對象中有一個叫Prepared的屬性,可以幫助我們快速的執行命令,因為只要我們設置Prepared參數為true時,那么我們的命令將會在內存中保留一份編譯好的指令,當然如果我們只需要執行一次就沒有必要指定PrePared參數為真了。因為指定Prepared他會對命令做一次編譯會消耗時間,指定Prepared屬性只是為了加速第二次以及以后的執行。
1: _bstr_t strConnStr = "Provider=SQLOLEDB.1;Password=zw123;\2: Persist Security Info=True;User ID=sa;Initial Catalog=Communication;Data Source=DAVID-PC";3:4: _ConnectionPtr connObject = NULL;5: _CommandPtr commandObject = NULL;6: _RecordsetPtr recordSetObject = NULL;7:8: TESTHR(connObject.CreateInstance(__uuidof(Connection)));9: TESTHR(commandObject.CreateInstance(__uuidof(Command)));10: TESTHR(recordSetObject.CreateInstance(__uuidof(Recordset)));11:12: connObject->Open(strConnStr, "", "", adConnectUnspecified);13:14: commandObject->ActiveConnection = connObject;15: commandObject->CommandText = "SELECT * FROM UserInfo WHERE UserName = ? OR Sex = ?";16: commandObject->CommandType = adCmdText;17: commandObject->Prepared = true;18:19: _ParameterPtr param1 = commandObject->CreateParameter("@Name", adVarChar, adParamInput, 6, "Jack");20: _ParameterPtr param2 = commandObject->CreateParameter("@Sex", adVarChar, adParamInput, 6, "1");21: commandObject->Parameters->Append(param1);22: commandObject->Parameters->Append(param2);23:24:25: recordSetObject = commandObject->Execute(NULL, NULL, adCmdText);26:27: PrintTableHeader(recordSetObject);28: PrintTableRecords(recordSetObject);29:30: commandObject->Parameters->GetItem("@Name")->Value = "Mary";31: recordSetObject = commandObject->Execute(NULL, NULL, adCmdText);32:33: PrintTableHeader(recordSetObject);34: PrintTableRecords(recordSetObject);
3.使用命令對象調用存儲過程
掉用存儲過程,跟上面我們調用帶參數的命令對象過程都差不多。只不過我們把CommanType的值設置為了adCmdStoredProc,並且不用設置Prepared的屬性為true,因為存儲過程是由你的數據提供者創建並編譯好的,我們只需要調用就行了。還有一個不同的使用,我們不需要用CreateParameter創建參數對象了,只需要調用Parameters集合中的Refresh方法就能夠刷新參數了,當然這個刷新也是建立在消耗性能的基礎上的。
在調用存儲過程之前我先來創建一個簡單的存儲過程,我們使用MS Sql Server做為數據源來進行創建。
在Sql Server中新建一個查詢,然后輸入如下命令,就能創建成功了。
1: CREATE PROCEDURE UserProcedures @UserName nvarchar(10) AS2: SELECT *FROM UserInfo3: WHERE UserName = @UserName
下面我們就來看看,如何使用Command對象調用存儲過程。
1: //命令對象調用存儲過程2: //刪除先前創建的參數對象3: const int paramCount = commandObject->Parameters->Count;4: for (int i = 0; i < paramCount; ++i)5: {6: commandObject->Parameters->Delete((long)0);7: }8:9: commandObject->CommandText = "UserProcedures";10: commandObject->CommandType = adCmdStoredProc;11: commandObject->Parameters->Refresh();12: commandObject->Parameters->GetItem("@UserName")->Value = "Jack";13: recordSetObject = commandObject->Execute(NULL, NULL, adCmdUnspecified);14: PrintTableHeader(recordSetObject);15: PrintTableRecords(recordSetObject);

