行セットの更新
データベースの基本の操作は、データ ストアの更新、つまりデータの書き込みです。 OLE DB の更新機構は単純です。コンシューマー アプリケーションは、連結されたデータ メンバーの値を設定し、これらの値を行セットに書き込みます。その後、コンシューマーはプロバイダーにデータ ストアの更新を要求します。
コンシューマーが行セット データに対して実行できる更新の種類は、行内の列値の設定、行の挿入、および行の削除です。 これらの操作を実行するために、CRowset OLE DB テンプレート クラスは、IRowsetChange インターフェイスを実装し、次のインターフェイス メソッドをオーバーライドします。
SetData は行セットの行の列値を変更します。これは SQL UPDATE コマンドと等価です。
Insert は行を行セットに挿入します。これは SQL INSERT コマンドと等価です。
Delete は行セットから行を削除します。これは SQL DELETE コマンドと等価です。
更新操作のサポート
ATL OLE DB コンシューマー ウィザードを使用してコンシューマーを作成するときに、[変更]、[挿入]、および [削除] の 3 つのチェック ボックスのうちの 1 つ以上をオンにして、更新操作をサポートできます。 これらのチェック ボックスをオンにすると、ウィザードによってコードが適宜変更され、選択した変更の種類がサポートされます。 ただし、ウィザードを使用しない場合は、次の行セット プロパティを VARIANT_TRUE に設定して更新をサポートする必要があります。
DBPROPVAL_UP_CHANGE は行のデータ値を変更できます。
DBPROPVAL_UP_INSERT は行を挿入できます。
DBPROPVAL_UP_DELETE は行を削除できます。
プロパティは次のように設定します。
CDBPropSet ps(DBPROPSET_ROWSET);
ps.AddProperty(DBPROP_IRowsetChange, true)
ps.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE)
1 つ以上の列が書き込み禁止になっている場合は、変更、挿入、または削除の操作が失敗することがあります。 これを修正するにはカーソル マップを変更します。
行のデータの設定
CRowset::SetData は、現在の行の 1 つ以上の列にデータ値を設定します。 次のコードは、Products テーブルの列 "Name" と "Units in Stock" に連結されたデータ メンバーの値を設定し、SetData を呼び出して、行セットの 100 行目にこれらの値を書き込みます。
// Instantiate a rowset based on the user record class
CTable<CAccessor<CProductAccessor> > product;
CSession session;
// Open the rowset and move to the 100th row
product.Open(session, "Product", &ps, 1); // ps is the property set
product.MoveToBookmark(&bookmark, 0); // Assume that bookmark is set to 100th row
// Change the values of columns "Name" and "Units in Stock" in the current row of the Product table
_tcscpy_s( product.m_ProductName, product.m_sizeOfProductName,
_T( "Candle" ) );
product.m_UnitsInStock = 10000;
// Set the data
HRESULT hr = product.SetData( );
行セットへの行の挿入
CRowset::Insert は、アクセサーのデータを使用して新規の行を作成し、初期化します。 Insert は、現在の行の後に新規の行全体を作成します。現在行を次の行にインクリメントするか、または変更しないままにするかを指定する必要があります。 これを指定するには、bGetRow パラメーターを設定します。
HRESULT Insert(int nAccessor = 0, bool bGetRow = false)
false (既定値) は、現在行を次の行にインクリメントします。この場合、現在行は挿入された行を指します。
true は、現在行をそのままにします。
次のコードは、Products テーブルの列に連結されたデータ メンバーの値を設定し、Insert を呼び出して、行セットの 100 行目の後にそれらの値を持つ新規の行を挿入します。 新規の行に未定義データが含まれないように、すべての列値を設定することをお勧めします。
// Instantiate a rowset based on the user record class
CTable<CAccessor<CProductAccessor> > product;
CSession session;
// Open the rowset and move to the 100th row
product.Open(session, "Product", &ps, 1); // ps is the property set
product.MoveToBookmark(&bookmark, 0); // Assume that bookmark is set to 100th row
// Set the column values for a row of the Product table, then insert the row
product.m_ProductID = 101;
_tcscpy_s( product.m_ProductName, product.m_sizeOfProductName,
_T( "Candle" ) );
product.m_SupplierID = 27857;
product.m_CategoryID = 372;
_tcscpy_s( product.m_QuantityPerUnit, product.m_sizeOfQuantityPerUnit,
_T( "Pack of 10" ) );
product.m_UnitPrice = 20;
product.m_UnitsInStock = 10000;
product.m_UnitsOnOrder = 5201;
product.m_ReorderLevel = 5000;
product.m_Discontinued = false;
// You must also initialize the status and length fields before setting/inserting data
// Set the column status values
m_dwProductIDStatus = DBSTATUS_S_OK;
m_dwProductNameStatus = DBSTATUS_S_OK;
m_dwSupplierIDStatus = DBSTATUS_S_OK;
m_dwCategoryIDStatus = DBSTATUS_S_OK;
m_dwQuantityPerUnitStatus = DBSTATUS_S_OK;
m_dwUnitPriceStatus = DBSTATUS_S_OK;
m_dwUnitsInStockStatus = DBSTATUS_S_OK;
m_dwUnitsOnOrderStatus = DBSTATUS_S_OK;
m_dwReorderLevelStatus = DBSTATUS_S_OK;
m_dwDiscontinuedStatus = DBSTATUS_S_OK;
// Set the column length value for column data members that are not fixed-length types.
// The value should be the length of the string that you are setting.
m_dwProductNameLength = 6; // "Candle" has 6 characters
m_dwQuantityPerUnitLength = 10; // "Pack of 10" has 10 characters
// Insert the data
HRESULT hr = product.Insert( );
詳細な例については、「CRowset::Insert」を参照してください。
ステータスと長さのデータ メンバーの設定の詳細については、「ウィザードで生成されたアクセサーのフィールド ステータスのデータ メンバー」を参照してください。
行セットからの行の削除
CRowset::Delete は、行セットから現在行を削除します。 次のコードは、Delete を呼び出して行セットの 100 行目を削除します。
// Instantiate a rowset based on the user record class
CTable<CAccessor<CProductAccessor> > product;
CSession session;
// Open the rowset and move to the 100th row
product.Open(session, "Product", &ps, 1); // ps is the property set
product.MoveToBookmark(&bookmark, 0); // Assume that bookmark is set to 100th row
// Delete the row
HRESULT hr = product.Delete( );
即時更新と遅延更新
特に指定しない限り、SetData、Insert、および Delete の各メソッドを呼び出すと、データ ストアがすぐに更新されます。 ただし、コンシューマーがローカル キャッシュにすべての変更を格納し、次のいずれかの更新メソッドがユーザーによって呼び出されたときにそれらの変更をデータ ストアに転送するように、更新を遅延させることができます。
CRowset::Update は、最後のフェッチまたは Update 呼び出し以降に行われた現在行に対する保留中の変更を転送します。
CRowset::UpdateAll は、最後のフェッチまたは Update 呼び出し以降に行われた、すべての行に対する保留中の変更を転送します。
更新メソッドで使用される場合の更新には、"コマンドで変更を加える" という特有の意味があります。これを SQL UPDATE コマンドと混同しないようにしてください。SetData は SQL UPDATE コマンドに相当します。
遅延更新は、銀行の一連のトランザクションなどで役立ちます。最後の変更がコミットされるまでは一連の変更を送信しないため、1 つのトランザクションがキャンセルされた場合に変更を元に戻すことができます。 プロバイダーは、変更を 1 つのネットワーク呼び出しにまとめることもできます。このようにすると効率が良くなります。
遅延更新をサポートするには、「更新操作のサポート」で説明したプロパティに加えて、DBPROP_IRowsetChange プロパティを設定する必要があります。
pPropSet->AddProperty(DBPROP_IRowsetUpdate, true);
Update または UpdateAll を呼び出すと、これらのメソッドはローカル キャッシュからデータ ストアに変更を転送し、ローカル キャッシュをクリアします。 更新は現在の行に関する変更だけを転送するため、アプリケーションでどの行をどの時点で更新するかを追跡することが重要になります。 2 つの連続する行を更新する方法を次の例に示します。
// Instantiate a rowset based on the user record class
CTable<CAccessor<CProductAccessor> > product;
CSession session;
// Open the rowset and move to the 100th row
product.Open(session, "Product", &ps, 1); // ps is the property set
product.MoveToBookmark(&bookmark, 0); // Assume that bookmark is set to 100th row
// Change the values of columns "Name" and "Units in Stock" in the 100th row of the Product table
_tcscpy_s( product.m_ProductName, product.m_sizeOfProductName,
_T( "Wick" ) );
product.m_UnitsInStock = 10000;
HRESULT hr = product.SetData( ); // No changes made to row 100 yet
product.Update(); // Update row 100 now
// Change the values of columns "Name" and "Units in Stock" in the 101st row of the Product table
product.MoveNext( );
_tcscpy_s( product.m_ProductName, product.m_sizeOfProductName
_T( "Wax" ) );
product.m_UnitsInStock = 500;
HRESULT hr = product.SetData( ); // No changes made to row 101 yet
product.Update(); // Update row 101 now
保留中の変更が確実に転送されるようにするには、別の行に移動する前に Update を呼び出す必要があります。 たとえば、アプリケーションで数百行を更新する必要がある場合など、Update の呼び出しが非効率的な場合は、UpdateAll を使用してすべての行を一度に更新できます。
たとえば、最初の Update 呼び出しが上のコードから欠落していた場合、100 行目は変更されないままになり、101 行目が変更されます。 その後で、アプリケーションは UpdateAll を呼び出すか、100 行目に戻って Update を呼び出し、行を更新する必要があります。
変更を遅延させる主な理由は、変更を元に戻すことができるようにするためです。 CRowset::Undo を呼び出すと、ローカル変更キャッシュの状態が、保留中の変更が行われる前のデータ ストアの状態にロールバックされます。 Undo は、ローカル キャッシュの状態を 1 ステップだけ (最後の変更の前の状態に) ロールバックするのではなく、その行のローカル キャッシュをクリアします。 また、Undo は現在行にだけ影響します。