Обновление наборов строк
Обновлен: Ноябрь 2007
Одной из основных операций по работе с базами данных является обновление или запись данных в источник. В OLE DB реализован простой механизм обновления. Значения связанных членов данных устанавливаются в приложении-получателе, после чего записываются в набор строк. Затем в приложении-получателе выполняется запрос поставщика на обновление хранилища данных.
Для объектов-получателей доступны следующие способы обновления данных в наборе строк: установка значений столбцов в строке, а также вставка или удаление строки. Для выполнения этих операций в классе шаблона OLE DB CRowset реализуется интерфейс IRowsetChange, в котором переопределяются следующие методы интерфейса:
SetData — изменение значений столбцов в строке набора. Эквивалентен команде UPDATE языка SQL.
Insert — вставка строки в набор. Эквивалентен команде INSERT языка SQL.
Delete — удаление строки из набора. Эквивалентен команде DELETE языка SQL.
Поддержка операций обновления
При создании объекта-получателя с помощью мастера объектов получателей OLE DB библиотеки ATL можно реализовать поддержку операций обновления, установив любые из флажков Изменение, Вставка и Удаление. В соответствии с установленными флажками код автоматически изменяется мастером, обеспечивая поддержку выбранных типов изменения. Если мастер не используется, для поддержки обновлений следует установить значения 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)
Если один или несколько столбцов недоступны для записи, при выполнении операций изменения, вставки и удаления могут возникать ошибки. Чтобы устранить эти ошибки, измените таблицу курсоров.
Установка данных в строках
Метод CRowset::SetData предназначен для установки значений данных в одном или нескольких столбцах текущей строки. В следующем примере устанавливаются значения членов данных, связанных со столбцами "Name" и "Units in Stock" в таблице Products, после чего вызывается метод 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 для них.
Обратите внимание, что при обновлении с использованием этих методов выполняются определенные изменения команд, и их не следует путать с командой UPDATE языка SQL (метод SetData эквивалентен команде UPDATE языка SQL).
Например, отложенное обновление может использоваться при выполнении серии банковских транзакций. В этом случае при отмене одной транзакции можно отменить все изменения, поскольку они не передаются в хранилище данных до того момента, пока не будет зафиксирована последняя транзакция серии. Также в поставщике может реализовать пакетную передачу изменений в одном сетевом вызове, что является более эффективным.
Чтобы обеспечить поддержку отложенных обновлений, следует установить свойство DBPROP_IRowsetChange в дополнение к свойствам, описанным в разделе "Поддержка операций обновления":
pPropSet->AddProperty(DBPROP_IRowsetUpdate, true);
При вызове метода Update или UpdateAll выполняется передача изменений из локального кэша в хранилище данных, после чего кэш очищается. Поскольку метод Update передает обновления только для текущей строки, в приложении необходимо отслеживать, какая строка и когда обновляется. В следующем примере описывается порядок обновления двух следующих друг за другом строк:
// 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 следует вызывать до перемещения на другую строку. Если этот метод неэффективен (например при обновлении сотен строк) можно использовать метод UpdateAll для одновременного обновления всех строк.
Например, если в приведенном выше примере удалить первый вызов метода Update, будет изменена только строка 101 (строка 100 останется без изменений). После этого в приложении следует вызвать метод UpdateAll или перейти к строке 100 и вызвать метод Update для ее обновления.
Основной причиной использования ожидающих изменений является возможность их отмены. При вызове метода CRowset::Undo выполняется откат состояния локального кэша изменений до состояния хранилища данных, которое существовало до выполнения каких-либо ожидающих изменений. Обратите внимание, что при вызове метода Undo не выполняется пошаговый откат состояния локального кэша. В этом случае полностью очищается локальный кэш для этой строки. Метод Undo применяется только к одной строке.