Suportul inregistrarilor in ODBC - clasa CRecordset
Un obiect CRecordset reprezinta un set de inregistrari selectate dintr-o sursa de date. Cunoscute ca recordsets , obiectele CRecordset sunt folosite de obicei in doua forme : dynasets si snapshots. Un dynaset este sincronizat cu modificarile pe care le face un utilizator in interactiune cu alti utilizatori. Un snapshot este o vedere statica a datelor. Fiecare forma reprezinta un set de inregistrari fixe in momentul cand recordset-ul este deschis, dar cand se navigheaza prin inregistrari cu dynaset, sunt evidentiate si modificarile facute de alti utilizatori sau de aplicatie.
Pentru a folosi o clasa proprie, de deschide o baza de date si se construieste un obiect CRecordset, pasandu-se constructorului un pointer la obiectul CDatabase. Apoi, se apeleaza functia CRecordset::Open, unde se specifica daca obiectul este dynaset sau snapshot. Aceasta functie selecteaza date de la sursa de date a aplicatiei. Dupa creare, se pot folosi pentru navigare si operare pe inregistrari functiile membru ale CRecordset. Operatiile disponibile depind de tipul obiectului (dynaset sau snapshot), daca este updatabil, daca este sau nu doar pentru citire. Pentru a reimprospata inregistrarile modificate sau nou adaugate de la ultimul apel Open, se apeleaza functia Requery. Pentru terminare si distrugere a obiectului CRecordset se apeleaza functia Close.
Un "dynaset" reprezinta un recordset cu proprietati dinamice. In timpul sau de viata, un obiect recordset in modul dynaset (numit simplu dynaset) este sincronizat cu sursa de date si actiunile tuturor utilizatorilor. Inregistrarile adaugate de alti utilizatori nu sunt reflectate de dynaset pana la apelarea functiei membru Requery. Cand alti utilizatori sterg inregistrari, codul MFC ignora stergerile din recordset. Schimbarile facute prin editare sunt evidentiate in momentul navigarii prin inregistrari.
Daca exista mai multe conexiuni la aceeasi baza de date (obiecte CDatabase multiple) inregistrarile asociate conexiunilor au aceeasi stare cu a oricarui alt utilizator.
Dynaset sunt de mare ajutor cand datele trebuie sa fie dinamice, ca de exemplu, intr-o linie de rezervari de bilete de avion.
Pentru a specifica ca un recordset este dynaset, se trece CRecordset::dynaset ca prim parametru al functei Open al obiectului CRecordset.
Clasele MFC ale bazelor de date suporta dynaset daca urmatoarele cerinte sunt indeplinite :
-Biblioteca de cursoare a ODBC nu trebuie sa fie in uz.
Daca ea este folosita, se mascheaza unele functionalitati ale driverului ODBC pentru suportul dynaset. In terminologia ODBC, dynaset si snapshot sunt referite drept cursoare.
-Driver-ul ODBC al sursei de date trebuie sa suporte cursoare keyset-driven.
Cursorul keyset-driven administreaza datele dintr-o tabela prin preluare si incarcare a unui set de chei. Cheile sunt folosite pentru a obtine datele curente dintr-o tabela cand utilizatorul navigheaza spre o inregistrare particulara.
Daca se incearca a fi deschis un dynaset fara suportul key-driven, va fi primita o exceptie cu valoarea AFX_SQL_ERROR_DYNASET_NOT_ SUPPORTED.
-Driver-ul ODBC trebuie sa suporte "extended fetching"
"Extended fetching" este abilitatea de navigare atat inainte cat si inapoi prin recordset.
Spre deosebire de snapshot, dynaset "impacheteaza" direct de la sursa de date inregistrarile imediat ce se navigheaza prin recordset. Aceasta da posibilitatea de sincronizare intre vederea datelor si sursa de date.
Snapshot
Un "snapshot" este un obiect care reflecta o vedere statica a datelor existente in momentul crearii sale. Odata ce a fost deschis snapshot-ul si s-a navigat prin toate inregistrarile, setul de inregistrari care il contin si valorile sale nu se schimba pana la apelarea functiei Requery.
Se poate crea un snapshot modificabil sau doar pentru citire cu ajutorul claselor bazelor de date. Spre deosebire de dynaset, un snapshot modificabil nu reflecta schimbarile facute de alti utilizatori, dar reflecta schimbarile facute de aplicatia proprie.
Un snapshot este un cursor static ODBC. Cursoarele statice nu primesc date pana cand nu se navigheaza prin inregistrari. Pentru a fi sigur ca datele sunt returnate imediat, se navigheaza spre ultima inregistrare si apoi spre inregistrarea dorita.
Snapshot-urile sunt importante cand este nevoie ca datele sa ramana fixe in timpul operatiilor, cum ar fi generare de rapoarte sau efectuare de calcule. Oricum, si asa este posibila dorinta de refacere a datelor din cand in cand.
Snapshot-ul se bazeaza pe biblioteca de cursoare ODBC, care trebuie sa fie incarcata in memorie. Cand se construieste un obiect CDatabase si se apeleaza functia membru OpenEx, trebuie specificata optiunea CDatabase::useCursorLib pentru parametrul dwOptions.
Daca se doreste folosirea atat de snapshot cat si de dynaset, ele trebuie sa se bazeze pe doua obiecte CDatabase diferite (doua conexiuni diferite).
Arhitectura CRecordset - RFX
RFX (Record Field Exchange) - vedere generala
Cand se foloseste ClassWizard pentru a declara o clasa tip recordset derivata din CRecordset, clasa rezultata are structura generala ca cea de mai jos :
class CXSalSet : public CRecordset
}AFX_FIELD
// Overrides
// ClassWizard generated virtual function overrides
//
Cand se pregateste a se modifica un recordset prin apelul Update, trebuie avut grija ca toate coloanele care alcatuiesc cheia primara a tabelei (sau toate coloanele care alcatuiesc un index unic al tabelei) sa fie incluse. In unele cazuri, framework-ul poate folosi doar coloanele selectate in recordset pentru a identifica care inregistrare este updatata. Fara coloanele necesare, pot fi modificate inregistrari multiple, cauzand alterarea referentiala a tabelei. Framework-ul va genera exceptii cand se va apela Update.
Adaugarea unei inregistrari la un recordset
Se pot adauga noi inregistrari la un recordset daca functia sa membru CanAppend returneaza o valoare nenula. Pentru adaugarea unei inregistrari :
-se asigura asupra posibilitatii de modificare a recordset-ului.
-se apeleaza functia membru AddNew.
AddNew pregateste recordset-ul sa activeze ca un buffer de editare. Toate datele membru camp sunt setate la o valoare speciala Null si marcate ca neschimbate astfel incat doar valorile schimbate ("dirty") vor fi scrise in sursa de date la apelul functiei Update.
-se seteaza valorile datelor membru ale noii inregistrari.
-se apeleaza functia membru Update.
Update-ul completeaza adaugarea prin scrierea inregistrarii la sursa de date. Urmatorul exemplu ilustreaza modalitatea de adaugare a unei inregis-trari :
if( !rsClient.Open( ) )
return FALSE;
if( !rsClient.CanAppend( ) )
return FALSE;//nu au fost setate valorile
rsClient.AddNew( );
rsClient.m_strName = strNume;
rsClient.m_strOras = strOras;
rsClient.m_strStrada = strStrada;
if( !rsClient.Update( ) )
Pentru anularea unui apel AddNew sau apel Edit, se face un alt apel la AddNew sau Edit sau se apeleaza Move cu parametrul AFX_MOVE_ REFRESH. Datele membru vor fi resetate la valorile anterioare si se va pastra modul de editare sau adaugare.
Editarea unei inregistrari intr-un recordset
Se pot edita inregistrari intr-un recordset daca functia sa membru CanAppend returneaza o valoare nenula. Pentru editarea unei inregistrari :
-se asigura asupra posibilitatii de modificare a recordset-ului.
-se apeleaza functia membru Edit.
Edit pregateste recordset-ul sa activeze ca un buffer de editare. Toate datele membru camp sunt marcate pentru ca recordset-ul sa stie mai tarziu ca au fost schimbate. Noile valori vor fi scrise la sursa de date cand va fi apelata functia Update.
-se seteaza valorile datelor membru ale noii inregistrari.
-se apeleaza functia membru Update.
Update-ul completeaza adaugarea prin scrierea inregistrarii la sursa de date. Dupa editarea unei inregistrari, inregistrarea editata ramane cea curenta. Urmatorul exemplu ilustreaza modalitatea de adaugare a unei inregistrari - se presupune ca utilizatorul a ajuns la inregistrarea dorita a fi editata :
rsClient.Edit( );
rsClient.m_strStrada = strStradaNoua;
rsClient.m_strOras = strOrasNou;
rsClient.m_strStat = strStatNou;
rsClient.m_strCodPostal = strCodPostalNou;
if( !rsClient.Update( ) )
Stergerea dintr-un recordset
Se pot sterge inregistrari dintr-un recordset daca functia sa membru CanAppend returneaza o valoare nenula. Pentru editarea unei inregistrari :
-se asigura asupra posibilitatii de modificare a recordset-ului.
-se navigheaza spre inregistrarea dorita.
-se apeleaza functia membru Delete.
Delete marcheaza imediat inregistrarea ca fiind stearsa, atat in recordset cat si in sursa de date. Spre deosebire de AddNew si Edit, Delete nu are un apel Update corespunzator.
-se navigheaza spre o alta inregistrare.
Cand se navigheaza prin inregistrari, inregistrarile sterse s-ar putea sa nu fie sarite. Pentru acesta se testeaza cu ajutorul functiei IsDeleted.
Urmatorul exemplu arata modul de operare al functiei Delete. Se presupune ca utilizatorul a navigat la inregistrarea dorita. Dupa apelul Delete, este importanta deplasarea la o alta inregistrare.
rsClient.Delete( );
rsClient.MoveNext( );
Operatia de join in obiectul tip CRecordset
Operatia de join - un task obisnuit de acces - permite lucrul cu date din mai multe tabele folosind un singur recordset. Alaturarea a doua tabele sau mai multe poate fi generata intr-un singur recordset care va contine tabelele din fiecare tabela, dar vor aparea ca o singura tabela in timpul operatiilor din aplicatia proprie. Uneori join-ul foloseste coloanele din toate tabelele, dar uneori clauza SQL SELECT intr-un join foloseste doar o parte a coloanelor din fiecare tabela. Clasele bazelor de date MFC suporta join-uri read-only, dar nemodificabile.
Cheia unui operatii de join o reprezinta una sau mai multe coloane pe care le au tabelele in comun. De exemplu, sa presupunem ca exista o coloana "ClientID" atat in tabela "Client" cat si in tabela "Contract" a unei aplicatii de evidenta a clientilor unei societati. In tabela "Client", coloana "ClientID" contine o valoare de identificare unica pentru fiecare posibil client. In tabela "Contract", coloana "ClientID" probabil nu contine valoari unice, de vreme ce un client poate fi beneficiarul mai multor contracte.
Pentru a selecta inregistrari care contin coloane ale unor tabele participante intr-un join, ar fi nevoie de :
-o tabela-lista care sa contina numele tuturor tabelelor participante in join.
-o coloana-lista care sa contina numele tuturor coloanelor afisate de join. Coloane cu acelasi nume, dar din tabele diferite sunt calificate suplimentar dupa numele tabelei din care fac parte.
-un filtru (o clauza SQL WHERE) care specifica coloanele pe baza carora sunt unite tabelele. Filtrul are forma "Tabela1.KeyCol=Tabela2.KeyCol" si de fapt realizeaza join-ul propriu-zis. Pentru aplicatia enuntata mai sus, filtrul ar avea forma :
Client.ClientID = Contract.ClientID;
Urmatoarea procedura prezinta join-ul a
doua tabele, dar se poate aplica pentru orice numar de tabele (toate legate
prin aceeasi sursa de date). Pentru
legarea
coloanelor din ambele tabele intr-un singur recordset :
-se foloseste ClassWizard pentru a crea o clasa recordset pentru join. In ClassWizard se alege Data Sources pentru a deschide caseta de dialog Data Sources si se leaga coloanele primei tabele la datele membru ale recordset-ului (vezi figura).
-se apasa butonul Update Columns din ClassWizard pentru a se deschide caseta de dialog Data Sources pentru a doua oara (prima data a fost deschisa la generarea scheletului aplicatiei cu AppWizard) - (vezi figura).
- se selecteaza o sursa de date (in cazul de fata ODBC).
-in caseta de dialog Tables (care contine toate tabelele disponibile cu sursa de date respectiva), se selecteaza numele celei de a doua tabele (vezi figura).
-se leaga coloanele din cea de a doua tabela la membrii camp nou adaugati in recordset.
Daca oricare din numele unei coloane din a doua tabela este duplicat al numelui unei coloane din a doua tabela, trebuie schimbat numele corespunzator membrului camp din recordset. De exemplu, daca se leaga "Client" si "Stoc" , fiecare tabela poate contine o coloana numita "Depozit"; o coloana poate fi legata ca m_clientDepozit si cealalta ca m_stocDepozit.
-se inchide ClassWizard.
Cand se creaza o derivata CRecordset cu ClassWizard, trebuie avut grija la selectarea tabelelor multiple sau a interogarilor. Selectarea tabelelor multiple sau a interogarilor va genera construirea unei interogari join fara restrictie asupra modului de actiune al join-ului (join in cross sau cartezian). Ar trebui specificat un filtru folosind CRecordset::m_strFilter (rezultand in MFC constructia unei clauze SQL WHERE) inainte de deschiderea recordset-ului. Filtrul ar restrange numarul de inregistrari din setul rezultat. Acesta este necesara in special cand se foloseste ODBC Cursor Library, deoarece ODBC Cursor Library poate crea fisiere temporare mari pentru seturile rezultate cu multe inregistrari.
Odata creat recordset-ul cu ClassWizard, trebuie personificate doua parti ale codului sursa. Mai intai, se editeaza lista tabelei din clasa, apoi se redenumesc coloanele cu acelasi nume dar din tabele diferite. Vor trebui editate apelurile din functia suprascrisa DoFieldExchange pentru a insera numele din tabele. De exemplu, aplicatia contine tabela "Client" si tabela "Contract". Tabela "Contract" contine coloanele ClientID, Nume, Ncontract, iar tabela "Contract" contine ClientID, Depozit, Ncontract etc.
In continuare trebuie rescris codul functiei membru GetDefaultSQL a recordset-ului pentru a se returna o lista de tabele limitata. De exemplu, daca recordset-ul leaga prin join tabela "Client" de tabela "Contract" codul functiei GetDefaultSQL ar trebuie sa arate asa
CString CClientSet::GetDefaultSQL()
O alta modalitate ar fi pasarea unui sir continand o instructiune SQL in parametrul lpszSQL la apelul fuctiei membru Open a recordset-ului. Sirul are aceeasi forma ca in exemplu de mai sus.
Pentru a se redenumi coloanele cu acelasi nume, dar din tabele diferite se editeaza apelurile RFX din functia DoFieldExchange a recordset-ului. Pentru fiecare nume duplicat, se editeaza al doilea parametru din RFX pentru a prefixa numele coloanei aflat deja aici, prin adaugarea numelui tabelei din care coloana face parte. De exemplu, deoarece CClientSet leaga o coloana ClientID din fiecare tabela, trebuie modificat cele doua apeluri RFX pentru aceste coloane :
void CClientSet::DoFieldExchange(CFieldExchange* pFX)
}AFX_FIELD_MAP
Cand se construieste un obiect CClientSet prin operatia de join, se obisnuieste a se seta un filtru care va specifica ce coloane constituie join-ul. Apoi se apeleaza functia membru Open a recordset-ului ca in exemplul urmator, care leaga cele doua tabele de mai sus dupa coloana comuna ClientID :
CClientSet ssJoin( NULL );
ssJoin.m_strFilter = "Client.ClientID = Contract.ClientID";
if( !ssJoin.Open( ) )
return FALSE;//recordsetul nu a putut fi deschis
Filtrul sustine conexiunea intre doua tabele pentru a face posibila vederea tabelelor ca fiind una singura.
Se pot lega prin join mai mult de doua tabele prin aceeasi modalitate de egalare a perechilor de coloane din tabelele corespunzatoare, fiecare pereche legata prin cuvantul cheie SQL AND.
Politica de confidentialitate |
.com | Copyright ©
2024 - Toate drepturile rezervate. Toate documentele au caracter informativ cu scop educational. |
Personaje din literatura |
Baltagul – caracterizarea personajelor |
Caracterizare Alexandru Lapusneanul |
Caracterizarea lui Gavilescu |
Caracterizarea personajelor negative din basmul |
Tehnica si mecanica |
Cuplaje - definitii. notatii. exemple. repere istorice. |
Actionare macara |
Reprezentarea si cotarea filetelor |
Geografie |
Turismul pe terra |
Vulcanii Și mediul |
Padurile pe terra si industrializarea lemnului |
Termeni si conditii |
Contact |
Creeaza si tu |