Поделиться через


обновление наборов строк

Обновление или запись данных в хранилище данных — одна из основных операций баз данных. В OLE DB используется простой механизм обновления: приложение-клиент задает значения привязанных членов данных, а затем записывает эти значения в набор строк; затем клиент запрашивает у поставщика обновление хранилища данных.

Клиенты могут выполнять следующие типы обновлений в данных набора строк: задание значений столбцов в строке, вставку строки и удаление строки. Чтобы можно было использовать эти операции, класс шаблона OLE DB CRowset реализует интерфейс IRowsetChange и переопределяет следующие методы интерфейса:

  • команда SetData изменяет значения столбцов в строке или наборе строк (соответствует команде SQL UPDATE);

  • команда Insert вставляет строку в набор строк (соответствует команде SQL INSERT);

  • команда Delete удаляет строки из набора строк (соответствует команде SQL DELETE).

Поддержка операций обновления

Примечание.

Мастер объекта-получателя ATL OLE DB недоступен в Visual Studio 2019 и более поздних версиях. Эту функцию все еще можно добавить вручную. Дополнительные сведения см. в статье Creating a Consumer Without Using a Wizard (Создание объекта-получателя без помощи мастера).

При создании объекта-получателя с помощью Мастера потребителя ATL OLE DB вы можете установить поддержку операций обновления, установив флажки Изменение, Вставка и Удаление. При их установке мастер изменяет код соответствующим образом, чтобы поддерживать выбранные типы изменений. Однако если вы не используете мастер, чтобы поддерживать обновления, вам потребуется задать значение 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, чтобы записать эти значения в сотую строку набора строк.

// 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, чтобы вставить новую строку с этими значениями после сотой строки набора строк. Рекомендуется задать все значения столбцов, чтобы избежать появления неопределенных данных в новой строке.

// 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 для удаления сотой строки набора строк.

// 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).

Отложенные обновления нужны, например, в случаях выполнения серии банковских транзакций. Если одна транзакция будет отменена, вы можете отменить изменение, так как вы не отправляете эту серию изменений, пока последнее из них не будет зафиксировано. Также поставщик может объединить изменения в один вызов по сети, что более эффективно.

Для поддержки отложенных изменений необходимо задать свойство DBPROP_IRowsetChange в дополнение к свойствам, описанным в разделе Поддержка операций обновления.

pPropSet->AddProperty(DBPROP_IRowsetUpdate, true);

При вызове Update или UpdateAll эти методы перемещают изменения из локального кэша в хранилище данных, а затем очищают локальный кэш. Так как обновление перемещает изменения только для текущей строки, важно, чтобы приложение отслеживало, какие строки следует обновлять и когда. В следующем примере показано, как обновить две последовательные строки.

// 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 отсутствовал в коде выше, строка 100 осталась бы без изменений, тогда как строка 101 была бы изменена. После этого вашему приложению потребовалось бы вызвать UpdateAll или вернуться к строке 100 и вызвать Update для обновления этой строки.

Наконец, основная причина откладывания изменений, — возможность отменить их. Вызов CRowset::Undo откатывает состояние локального кэша изменений до состояния хранилища данных перед внесением ожидающих изменений. Обратите внимание, что команда Undo не откатывает состояние локального кэша на один шаг (до состояния перед последним изменением). Вместо этого она очищает локальный кэш для этой строки. Также команда Undo затрагивает только текущую строку.

См. также

Работа с шаблонами объекта-получателя OLE DB
Класс CRowset
IRowsetChange