Mise à jour des jeux de lignes
Mettre à jour le magasin de données, ou y écrire des données, est une opération de base de données très élémentaire. Dans OLE DB, le mécanisme de mise à jour est simple : l'application du consommateur définit les valeurs des données membres liées, puis écrit ces valeurs dans l'ensemble de lignes ; le consommateur demande ensuite que le fournisseur mette à jour le magasin de données.
Les consommateurs peuvent effectuer les types de mises à jour suivants sur les données de l'ensemble de lignes : définition des valeurs de colonnes dans une ligne, insertion d'une ligne et suppression d'une ligne. Pour effectuer ces opérations, la classe des modèles OLE DB CRowset implémente l'interface IRowsetChange, puis substitue les méthodes d'interface suivantes :
SetData modifie les valeurs des colonnes dans une ligne d'un ensemble de lignes ; c'est l'équivalent de la commande SQL UPDATE.
Insert insère une ligne dans un ensemble de lignes ; c'est l'équivalent de la commande SQL INSERT.
Delete supprime une ligne d'un ensemble de lignes ; c'est l'équivalent de la commande SQL DELETE.
Prise en charge des opérations de mise à jour
Quand vous créez un consommateur par le biais de l'Assistant Consommateur OLE DB ATL, vous pouvez prendre en charge les opérations de mise à jour en activant au moins l'une des trois cases à cocher Modifier, Insérer et Supprimer. Si vous activez ces cases à cocher, l'Assistant modifie le code de façon appropriée afin de prendre en charge le type de modifications que vous souhaitez. Cependant, si vous ne faites pas appel à l'Assistant, vous devez attribuer la valeur VARIANT_TRUE aux propriétés d'ensemble de lignes suivantes pour prendre en charge les mises à jour :
DBPROPVAL_UP_CHANGE permet de modifier les valeurs des données dans une ligne.
DBPROPVAL_UP_INSERT permet d'insérer une ligne.
DBPROPVAL_UP_DELETE permet de supprimer une ligne.
Vous définissez les propriétés de la façon suivante :
CDBPropSet ps(DBPROPSET_ROWSET);
ps.AddProperty(DBPROP_IRowsetChange, true)
ps.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE)
Les opérations de modification, d'insertion ou de suppression risquent d'échouer si une ou plusieurs colonnes ne peuvent pas être écrites. Modifiez le mappage du curseur pour remédier à cette situation.
Définition des données dans les lignes
CRowset::SetData définit les valeurs des données dans une ou plusieurs colonnes de la ligne en cours. Le code suivant définit les valeurs des données membres liées aux colonnes "Name" et "Units in Stock" de la table Products, puis appelle SetData pour écrire ces valeurs dans la 100 ème ligne de l'ensemble de lignes :
// 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( );
Insertion de lignes dans des ensembles de lignes
CRowset::Insert crée et initialise une nouvelle ligne en utilisant les données provenant de l'accesseur. Insert crée une ligne entièrement nouvelle après la ligne en cours ; vous devez spécifier si vous voulez incrémenter la ligne en cours de manière à en faire la ligne suivante ou s'il faut la laisser inchangée. Pour cela, vous devez définir le paramètre bGetRow :
HRESULT Insert(int nAccessor = 0, bool bGetRow = false)
false (valeur par défaut) spécifie l'incrémentation de la ligne en cours de manière à en faire la ligne suivante (auquel cas, elle désigne la ligne insérée).
true spécifie que la ligne en cours demeure là où elle est.
Le code suivant définit les valeurs des données membres liées aux colonnes de la table Products, puis appelle Insert pour insérer une nouvelle ligne à l'aide des valeurs situées après la 100 ème ligne de l'ensemble de lignes. Il est recommandé de définir toutes les valeurs des colonnes afin d'éviter d'avoir des données non définies dans la nouvelle ligne :
// 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( );
Pour obtenir un exemple plus détaillé, consultez CRowset::Insert.
Pour plus d'informations sur la définition des données membres d'état et de longueur, consultez Données membres de l'état des champs dans les accesseurs générés par l'Assistant.
Suppression de lignes dans des ensembles de lignes
CRowset::Delete supprime la ligne en cours dans l'ensemble de lignes. Le code suivant appelle Delete pour supprimer la 100 ème ligne dans l'ensemble de lignes :
// 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( );
Mises à jour immédiates et différées
Sauf indication contraire, les appels aux méthodes SetData, Insert et Delete mettent à jour immédiatement le magasin de données. Vous pouvez, toutefois, différer ces mises à jour de façon que le consommateur stocke toutes les modifications dans un cache local, puis les transfère au magasin de données lors d'un appel à l'une des méthodes de mise à jour suivantes :
CRowset::Update transfère les modifications en attente apportées à la ligne en cours depuis la dernière extraction ou le dernier appel Update la concernant.
CRowset::UpdateAll transfère les modifications en attente apportées à toutes les lignes depuis la dernière extraction ou le dernier appel Update les concernant.
Notez que la notion de mise à jour utilisée par les méthodes de mise à jour a la signification spécifique suivante : effectuer des modifications sur commande ; elle ne doit pas être confondue avec la commande SQL UPDATE (SetData est équivalente à la commande SQL UPDATE).
Les mises à jour différées sont utiles, par exemple, dans des situations telles qu'une série de transactions bancaires ; si une transaction est annulée, vous pouvez annuler la modification, car vous n'envoyez pas la série de modifications tant que la dernière modification n'a pas été validée. Le fournisseur peut également regrouper les modifications en un seul appel réseau, ce qui est plus efficace.
Pour prendre en charge des mises à jour différées, vous devez définir la propriété DBPROP_IRowsetChange en plus des propriétés décrites dans « Prise en charge des opérations de mise à jour » :
pPropSet->AddProperty(DBPROP_IRowsetUpdate, true);
Quand vous appelez Update ou UpdateAll, les méthodes transfèrent les modifications du cache local vers le magasin de données, puis effacent entièrement le cache local. L'opération de mise à jour transfère les modifications uniquement pour la ligne en cours ; il est donc important que votre application assure le suivi des lignes à mettre à jour et détermine quand les mettre à jour. L'exemple suivant montre comment mettre à jour deux lignes consécutives :
// 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
Pour garantir le transfert des modifications en attente, vous devez appeler Update avant de passer à une autre ligne. Cependant, quand cette opération est fastidieuse ou inefficace, par exemple lorsque votre application doit mettre à jour des centaines de lignes, vous pouvez utiliser UpdateAll pour mettre à jour toutes les lignes simultanément.
Par exemple, si le premier appel Update est manquant dans le code ci-dessus, la ligne 100 resterait inchangée, alors que la ligne 101 serait modifiée. À ce stade, votre application doit appeler UpdateAll ou retourner à la ligne 100 et appeler Update afin de procéder à la mise à jour de cette ligne.
Enfin, l'une des principales raisons qui justifient l'ajournement des modifications est la possibilité de les annuler. L'appel de CRowset::Undo restaure le cache local des modifications à l'état où se trouvait le magasin de données avant l'exécution des modifications en attente. Il est important de noter que Undo ne restaure pas l'état du cache local en une seule étape (l'état qui précède uniquement la dernière modification) ; il efface plutôt le cache local relatif à cette ligne. Undo agit aussi uniquement sur la ligne en cours.