Actualizar conjuntos de filas
Actualización: noviembre 2007
Una operación básica con bases de datos es la de actualizar o escribir datos en el almacén de datos. En OLE DB, el mecanismo de actualización es sencillo: la aplicación de consumidor establece los valores de los miembros de datos enlazados y a continuación los escribe en el conjunto de filas; el consumidor puede solicitar en ese momento que el proveedor actualice el almacén de datos.
Los consumidores pueden realizar las mismas actualizaciones en los datos de un conjunto de filas: establecer valores de columna en una fila, insertar una fila o eliminar una fila. Para poder realizar estas operaciones, la clase de plantilla OLE DB CRowset implementa la interfaz IRowsetChange y reemplaza los métodos de interfaz siguientes:
SetData cambia los valores de columna de una fila de un conjunto de filas; equivale al comando SQL UPDATE.
Insert inserta una fila en un conjunto de filas; equivale al comando SQL INSERT.
Delete elimina filas de un conjunto de filas; equivale al comando SQL DELETE.
Admitir operaciones de actualización
Al crear un consumidor con el Asistente para consumidores OLE DB ATL, se pueden admitir las operaciones de actualización activando una o varias de las casillas Cambiar, Insertar, y Eliminar. Si las activa, el asistente modifica el código en consecuencia para admitir el tipo de cambios elegido. Sin embargo, si no se utiliza el asistente, debe establecer las siguientes propiedades del conjunto de filas en VARIANT_TRUE para admitir actualizaciones:
DBPROPVAL_UP_CHANGE permite modificar los valores de los datos de una fila.
DBPROPVAL_UP_INSERT permite insertar una fila.
DBPROPVAL_UP_DELETE permite eliminar una fila.
Las propiedades se establecen de esta forma:
CDBPropSet ps(DBPROPSET_ROWSET);
ps.AddProperty(DBPROP_IRowsetChange, true)
ps.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE)
Las operaciones de cambio, inserción o eliminación pueden no funcionar debido a un error de escritura. Modifique el mapa de cursores para corregirlo.
Establecer datos en filas
CRowset::SetData establece valores de datos en una o varias columnas de la fila actual. En el código siguiente se establecen los valores de miembros de datos enlazados con las columnas "Name" y "Units in Stock" de la tabla Products y, a continuación, se llama a SetData para escribir dichos valores en la fila número 100 del conjunto de filas:
// 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( );
Insertar filas en conjuntos de filas
CRowset::Insert crea e inicializa una nueva fila con datos del descriptor de acceso. Insert crea una fila totalmente nueva a continuación de la fila actual; necesitará especificar si se debe incrementar la fila actual a la siguiente fila o dejarla sin modificar. Para hacer eso, establezca el parámetro bGetRow:
HRESULT Insert(int nAccessor = 0, bool bGetRow = false)
false (el valor predeterminado) especifica que la fila actual se incremente a la siguiente fila (en cuyo caso apunta a la fila insertada).
true especifica que la fila actual debe permanecer donde está.
En el código siguiente se establecen los valores de los miembros de datos enlazados con las columnas de la tabla Products y, después, se llama a Insert para insertar una fila nueva con esos valores a continuación de la fila número 100 del conjunto de filas. Se recomienda establecer todos los valores de columna para evitar los datos no definidos en la nueva fila:
// 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( );
Vea un ejemplo más detallado en CRowset::Insert.
Para obtener más información sobre cómo establecer los miembros de datos de estado y longitud, vea Miembros de datos sobre el estado de un campo en los descriptores de acceso generados por el asistente.
Eliminar filas de conjuntos de filas
CRowset::Delete elimina la fila actual del conjunto de filas. En el código siguiente se llama a Delete para que quite la fila número 100 del conjunto de filas:
// 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( );
Actualizaciones inmediatas y diferidas
A menos que se especifique lo contrario, las llamadas a los métodos SetData, Insert y Delete actualizan el almacén de datos inmediatamente. Sin embargo, puede diferir las actualizaciones hasta que el consumidor guarde todos sus cambios en una memoria caché local, y después transferirlos al almacén de datos al llamar a uno de los siguientes métodos de actualización:
CRowset::Update transfiere los cambios pendientes realizados en la fila actual desde la última recuperación o llamada a Update.
CRowset::UpdateAll transfiere los cambios pendientes realizados en todas las filas desde la última recuperación o llamada a Update.
Observe que Update (Actualizar), cuando lo usan los métodos de actualización, tiene el significado específico de hacer cambios como resultado de un comando, y no se debe confundir con el comando SQL UPDATE (el equivalente de este comando es SetData).
Las actualizaciones diferidas son útiles, por ejemplo, en situaciones como una serie de transacciones bancarias; si se cancela una transacción, se puede deshacer el cambio, ya que no se envía la serie de cambios hasta que se confirma el último de ellos. Asimismo, el proveedor también agrupa los cambios en una llamada de red, lo que es más eficiente.
Para admitir actualizaciones diferidas, se debe establecer la propiedad DBPROP_IRowsetChange además de las propiedades descritas en "Admitir operaciones de actualización":
pPropSet->AddProperty(DBPROP_IRowsetUpdate, true);
Al llamar a Update o UpdateAll, los métodos transfieren los cambios de la memoria caché local al almacén de datos y, después, liberan la memoria caché local. Puesto que la actualización transfiere los cambios sólo en la fila actual, es importante que la aplicación controle qué fila se debe actualizar y cuándo se debe llevar a cabo. El ejemplo siguiente muestra cómo actualizar dos filas consecutivas:
// 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
Para asegurar que se transfieren los cambios pendientes, se debe llamar a Update antes de desplazarse a otra fila. Sin embargo, cuando esto resulte tedioso o poco eficaz, por ejemplo, cuando la aplicación necesite actualizar cientos de filas, se puede usar UpdateAll para actualizar todas las filas de una vez.
Por ejemplo, si faltara la primera llamada a Update en el código anterior, la fila 100 no se modificaría, pero la fila 101 sí quedaría modificada. En ese punto, la aplicación debe llamar a UpdateAll o regresar a la fila 100 y llamar a Update para que se pueda actualizar dicha fila.
Por último, una razón de principal para diferir los cambios es poder deshacerlos. Al llamar a CRowset::Undo se revierte el estado de la memoria caché de cambios local y se recupera el estado del almacén de datos antes de que se realizara ningún cambio pendiente. Es importante observar que Undo no revierte el estado de la memoria caché local en un paso (del estado anterior al último cambio), sino que borra la memoria caché local de esa fila. Asimismo, Undo afecta sólo a la fila actual.