ADOを使ったデータベースアクセス

注意:ADO関連の処理はVC++のバージョンやEditionによっては実装できないこともあるらしいです。
ADOとは?
ADO(ActiveX Data Objects)は、OLE DBをラッピングしたインターフェイスである。 OLE DBはMicrosoft社が提唱したデータベースアーキテクチャであり、ODBCのインターフェイスを利用することで、 ODBCドライバが提供されるデータベースへのアクセスを可能にしている。 ADOもODBCと同様、ODBCドライバが提供されるデータベースであればどんな種類であっても同一の方法でアクセスすることができる。
ちなみにADOのインターフェイスはCOMを使用しているので、COMについてある程度の知識があったほうが理解しやすいだろう。

アプリケーションの作成
  1. テキストデータベースMyData.csvを用意する。データベースの内容や、 ODBCアドミニストレータでのデータソース登録方法は ODBCを使ったデータベースアクセスで示したものと全く同じである。

  2. ダイアログベースのプロジェクト「UKAdoTest」を新規に作成する。
    ダイアログリソースの内容や、コントロールのDDX変数についてはODBCを使ったデータベースアクセス と全く同じようにすること。

  3. UKAdoTestDlg.hファイルを開き、以下の文を追加してタイプライブラリ情報をインポートする。
    #define INITGUID		// これはADOを定義するための定数(GUID)の初期化
    #import "C:\Program Files\Common Files\System\ADO\msado15.dll" rename_namespace("ADOCG") rename("EOF", "EndOfFile")
    using namespace ADOCG;	// 名前空間
    #include "icrsint.h"	// ADOを使って取得したフィールドのデータを変換するマクロなどが定義されたヘッダー
  4. UKAdoTestDlg.hファイル内に記述されたCUKAdoTestDlgクラスにメンバ変数を追加する。
    ここで追加する3つのメンバ変数は、ADOが持つオブジェクトである。
    /////////////////////////////////////////////////////////////////////////////
    // CUKAdoTestDlg ダイアログ
    
    class CUKAdoTestDlg : public CDialog
    {
    ……
    private:
    	_ConnectionPtr m_pConnect;   // Connectionオブジェクト
    	_CommandPtr m_pCommand;      // Commandオブジェクト
    	_RecordsetPtr m_pRecordset;  // Recordsetオブジェクト
    };
    
  5. UKAdoTestDlg.cppファイルを開き、OnInitDialog関数を修正する。 また、WM_DESTROYメッセージのハンドラ関数OnDestroyを追加し、処理を入れる。
    BOOL CUKAdoTestDlg::OnInitDialog()
    {
    	CDialog::OnInitDialog();
    ……
    	try {
    		// COMの初期化処理
    		::CoInitialize(NULL);
    
    		// ADOオブジェクトの生成
    		m_pConnect.CreateInstance(__uuidof(Connection));
    		m_pCommand.CreateInstance(__uuidof(Command));
    		m_pRecordset.CreateInstance(__uuidof(Recordset));
    
    		// データベースへの接続
    		m_pConnect->Open(
    			L"Provider=MSDASQL.1;Data Source=TEXTDATABASE",
    			L"", L"", adOpenUnspecified);
    
    		// SQLの設定
    		m_pCommand->ActiveConnection = m_pConnect;
    		m_pCommand->CommandText = "SELECT * FROM MyData.csv";
    		m_pRecordset->PutRefSource(m_pCommand);
    
    		// レコードセットの取得
    		_variant_t vNull;  // VARIANT型のNULLとして使う
    		vNull.vt = VT_ERROR;
    		vNull.scode = DISP_E_PARAMNOTFOUND;
    		m_pRecordset->Open(vNull, vNull, adOpenDynamic, adLockOptimistic, adCmdUnknown);
    
    		// 先頭レコードへ移動し、フィールドの値を取得
    		m_pRecordset->MoveFirst();
    		DisplayField();
    	}
    	catch(_com_error &e)
    	{
    		MessageBox(e.Description());
    		return TRUE;
    	}
    
    	return TRUE;  // TRUE を返すとコントロールに設定したフォーカスは失われません。
    }
    
    // WM_DESTROYメッセージハンドラ
    void CUKAdoTestDlg::OnDestroy() 
    {
    	CDialog::OnDestroy();
    	
    	// TODO: この位置にメッセージ ハンドラ用のコードを追加してください
    	m_pRecordset->Close();
    	m_pConnect->Close();
    	m_pConnect = 0;
    	m_pCommand = 0;
    	m_pRecordset = 0;
    	::CoUninitialize();
    }
    
  6. UKAdoTestDlgクラスにDisplayFieldというメンバ関数を追加し、次のように処理を入れる。
    void CUKAdoTestDlg::DisplayField()
    {
    	try {
    		_variant_t vString;
    
    		vString = m_pRecordset->GetCollect(_variant_t("ID"));
    		vString.ChangeType(VT_I4);
    		m_iID = vString.iVal;
    
    		vString = m_pRecordset->GetCollect(_variant_t("RaceName"));
    		vString.ChangeType(VT_BSTR);
    		m_strRaceName = vString.bstrVal;
    
    		vString = m_pRecordset->GetCollect(_variant_t("Distance"));
    		vString.ChangeType(VT_BSTR);
    		m_strDistance = vString.bstrVal;
    
    		UpdateData(FALSE);
    	}
    	catch(_com_error &e)
    	{
    		MessageBox(e.Description());
    	}
    }
    
  7. ダイアログ下部の「<<」ボタンと「>>」ボタン(それぞれのリソースIDをIDC_BUTTON_PREV、IDC_BUTTON_NEXTとする) を押したときのメッセージハンドラを以下のように記述する。
    void CUKAdoTestDlg::OnButtonNext() 
    {
    	// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
    
    	// ここでもtry〜catchで例外処理をおこなうべき!
    	m_pRecordset->MoveNext();
    	if( m_pRecordset->GetEndOfFile() )
    	{
    		m_pRecordset->MoveLast();
    	}
    	DisplayField();
    }
    
    void CUKAdoTestDlg::OnButtonPrev() 
    {
    	// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
    
    	// ここでもtry〜catchで例外処理をおこなうべき!
    	m_pRecordset->MovePrevious();
    	if( m_pRecordset->GetBOF() )
    	{
    		m_pRecordset->MoveFirst();
    	}
    	DisplayField();
    }
    
    これで、「<<」ボタンと「>>」ボタンを使ってデータベースの各レコードを順次表示することが可能となる。

データベースのレコードを更新、追加、削除する
注意:テキストデータベースの場合、レコードの更新や削除はできないようです。
データベースを更新する場合、レコードセットの更新と同時にデータベースに反映させたり、 あるいはレコードセットのみをあらかじめ更新し、後で一括してデータベースへ反映させることが可能である。
後者の方法をとる場合、レコードセットのオープン時の引数を下記のようにする。
m_pRecordset->Open(vNull, vNull, adOpenStatic, adLockBatchOptimistic, adCmdUnknown);
レコードセットに対する処理はそれぞれ以下のようになる。
// カレントレコードの削除
// 削除後はカレントレコードを他のレコードへ移動させること
m_pRecordset->Delete(adAffectCurrent);

// カレントレコードの"RaceName"フィールドを更新する
UpdateData(TRUE);
_variant_t vName, vValue;
vName.SetString("RaceName");
vValue.SetString(m_strRaceName);
m_pRecordset->Update(vName, vValue);

// レコードを新規作成する
// 他のフィールドもUpdateを使って値を設定すること(更新時の方法で)
_variant_t vName, vValue;
vName.SetString("RaceName");
vValue.SetString("(新規レコード)");
m_pRecordset->AddNew(vName, vValue);
データベースを一括して更新する方法の場合、上記の方法ではレコードセットが更新されるだけである。 更新したレコードセットの内容をデータベースに反映させるには、以下の処理をおこなう。
m_pRecordset->UpdateBatch(adAffectAllChapters);

// 新規作成時は以下の処理もおこなうこと
m_pRecordset->Requery(adCmdUnknown);
m_pRecordset->MoveLast();

目次へ