Komunikacja z bazami danych ActiveX Data Objects (ADO) dr Zbigniew Lipiński Instytut Matematyki i Informatyki ul. Oleska 48 50-204 Opole [email protected] Co to jest ActiveX Data Objects? ADO jest • technologią firmy Microsoft. • komponentem Active-X (ACTIVE eXchange). • automatycznie instalowany z serwerem WWW Microsoft IIS. • interfejsem programowym dostępu do baz danych. Kontrolka Active-X - wcześniejsza nazwa kontrolką OLE, jest przykładem obiektu typu COM. Komponent realizujący określoną funkcjonalność, który może być zagnieżdżany w aplikacje, okna dialogowe, strony HTML. Kontrolka Active-X to plik z rozszerzeniem *.ocx Obiekty ADO: ADO Command ADO Connection ADO Error ADO Field ADO Parameter ADO Property ADO Record ADO Recordset ADO Stream ADO DataTypes 2 Obiekty ADO Obiekt Command – stosowany do wykonania kwerend do bazy danych w celu utworzenia, uaktualnienia, dodania, pobrania, usunięcia (create, add, retrieve, delete, update) rekordu. Jeżeli kwerenda służyła do pobrania danych, to dane są zwracane jako obiekt RecordSet (zbiór rekordów). Aby odzyskać dane z obiektu RecordSet należy posłużyć się metodami, properties, collections, obiektu Recordset. Tworzenie obiektu w asp .Net set objCommand=Server.CreateObject("ADODB.command") Obiekt Connection –obiekt służy do budowy i otwarcia połączenia z bazą danych. Tworzenie obiektu w asp .Net set objConnection=Server.CreateObject("ADODB.connection") 3 Obiekty ADO Obiekt Error - obiekt zawiera dane o błędach powstałych przy wykonaniu pojedynczych operacji na bazie danych. Przykład użycia. for each objErr in objConn.Errors response.write(objErr.Description ) response.write(objErr.HelpContext) response.write(objErr.HelpFile) response.write(objErr.NativeError) response.write(objErr.Number) response.write(objErr.Source) response.write(objErr.SQLState) Next Obiekt Field – obiekt przechowuje informacje o kolumnie danych w obiekcie Recordset. Tworzenie obiektu w asp .Net set objField=Server.CreateObject("ADODB.field") 4 Obiekty ADO Obiekt Parameter – obiekt służy do uzyskania informacji o parametrach użytych w procedurach składowanych (stored procedures) lub kwerendach. Cztery typy parametrów: input, output, input/output, return. Przykład składni. objectname.property objectname.method Obiekt Property - obiekt reprezentuje dynamiczne charakterystyki obiektów ADO zdefiniowanych dla różnych typów baz danych (db providers). Tworzenie obiektu w asp .Net set objProperty=Server.CreateObject("ADODB.property") 5 Obiekty ADO Obiekt Record (ADO ver. 2.5) – obiekt służy do przechowania wiersza danych z Recordsetu, katalogu lub pliku. Przykład użycia: objectname.property objectname.method Obiekt Recordset - obiekt służy do przechowywania danych (zbioru rekordów) z tabeli. Obiekt Recordset składa się z rekordów i kolumn (pól, ang. Fields). Recordset może uaktualnić dane na dwa sposoby: immediate updating, batch updating. Tworzenie obiektu w asp .Net set objRecordset=Server.CreateObject("ADODB.recordset") Obiekt Stream (ADO version 2.5) – obiekt stosowany do czytania, zapisywania, zarządzania strumieniem danych. 6 Kursory ADO Kursory ADO są parametrami metod obiektów ADO. Kursory definiują usługi kursora (cursor service). Typy kursorów ADO: Dynamic cursor – pozwala na przegląd operacji dodania, zmiany, usunięcia wykonane przez innego użytkownika. Keyset cursor – usługa podobna do usługi kursora ‘dynamic’, za wyjątkiem - braku możliwości przeglądania operacji dodania przez innego użytkownika, - braku dostępu do rekordów które inny użytkownik usunął. Dane zmienione (update) przez innego użytkownika są widoczne. Static cursor – pozwala dokonać ‘statycznego’ kopiowania rekordsetów w celu znalezienia danych lub wygenerowania raportów. Operacje dodania, zmiany, usunięcia wykonane przez innego użytkownika są niewidoczne. Typ kursora dostępny tylko dla obiektów typu klient rekordset. Forward-only cursor – pozwala przejrzeć rekordset. Operacje dodania, zmiany, usunięcia wykonane przez innego użytkownika są niewidoczne. Kursor Forward-only może być ustawiony poprzez property ‘CursorType’ lub poprzez parametr ‘CursorType’ w metodzie Open(). 7 Budowa połączenia z bazą danych Proces budowy połączenia z bazą danych za pomocą obiektów ADO: 1. Utworzenie połączenia ADO z bazą danych. 2. Otwarcie połączenia. 3. Utworzenie recordseta. 4. Otwarcie recordseta. 5. Pobranie danych do recordseta. 6. Zamknięcie recordseta. 7. Zamknięcie połączenia. 8 Połączenie z bazą danych za pomocą serwera WWW 1-2. Utworzenie i otwarcie połączenia z bazą danych. Baza northwind.mdb., Northwind_en 2007.accdb, northwind_pl 2007.accdb, northwind.accdb. set conn = Server.CreateObject("ADODB.Connection") conn.Provider="Microsoft.Jet.OLEDB.4.0" conn.Open "c:/webdata/northwind.mdb” Utworzenie i otwarcie połączenia z bazą danych za pomocą komponentu ODBC (Open DataBase Connectivity). set conn = Server.CreateObject("ADODB.Connection") conn.Open "northwind" 3. Utworzenie recordseta. set rs = Server.CreateObject("ADODB.recordset") 9 Połączenie z bazą danych za pomocą serwera WWW 4-5. Otwarcie i pobranie danych do recordseta. Czytanie danych z rekordseta. rs.Open "Select * from Customers", conn for each x in rs.fields response.write(x.name) response.write(" = ") response.write(x.value) Next 6. Zamknięcie recordseta. rs.close 7. Zamknięcie połączenia. conn.close 10 Metody dostępu do bazy danych Tabular Data Stream (TDS) dla SOAP Metody dostępu do bazy danych. Źródło biblioteka MSDN. 11 Połączenie z bazą danych Microsoft Access Silniki dla baz danych MS Access: • Microsoft JET engine (Joint Engine Technology, 1992), • ACE Engine (Microsoft Access Engine, 2007). Cechy ACE Engine: • nowy format danych .accdb, • dodano obsługę złożonych typów danych, • nowe mechanizmy szyfrowania, • możliwość integracji z Windows SharePoint Services 3.0, Microsoft Office Outlook 2007. Typy dostępu do danych Microsoft Access: • ADO (ActiveX Data Objects), • ATL OLE DB (Active Template Library Object Linking and Embedding DB), • Direct DAO (Data Access Objects), • ODBC (Open Database Connectivity), • MFC ODBC (Microsoft Foundation Classes Open Database Connectivity). 12 Połączenie z bazą danych Microsoft Access za pomocą obiektów ADO Funkcje biblioteki COM. Funkcja : CoInitialize(). HRESULT CoInitialize( _In_opt_ LPVOID pvReserved); Argumenty: pvReserved [in, optional] - parametr zarezerwowany, musi być NULL. Zwracana wartość: S_OK - biblioteka COM zainicjowana. S_FALSE - biblioteka COM jest już zainicjowana. RPC_E_CHANGED_MODE - inicjowany proces wielowątkowy multithread apartment (MTA). Opis: Funkcja inicjuje bibliotekę obiektów COM, zwraca wskaźnik do pamięci COM i funkcji COM. 13 Połączenie z bazą danych Microsoft Access - ADO Funkcje biblioteki COM. Funkcja: CoUninitialize(). void CoUninitialize(void); Argumenty: brak Zwracana wartość: void Opis: Funkcja zamyka bibliotekę obiektów COM. Funkcje Windows: GetCount(), GetItem(), GetName(), GetValue(). Funkcje służą do manipulacji złożonymi typami danych. Property (język CLI C++): Description – zwraca opis błędu. get_Fields – zwraca informacje o konfiguracji zasobów. Klasy i struktury: _bstr_t , _variant_t - klasy służące do zarządzania zasobami przy wywoływaniu funkcji. 14 Struktura programu - połączenie z bazą danych Microsoft Access - ADO Konfiguracja projektu. Kompilator: MS Visual Studio 2008 Projekt: Visual C++, Win32, Win32 Console application, Console application, empty project. Baza danych: Northwind_pl 2007.accdb Northwind_uk 2007.accdb #import <C:\\Program Files\\Common Files\\System\\ado\\msado15.dll> \ rename( "EOF", "AdoNSEOF" ) #include <iostream> #include <tchar.h> using namespace std; // string identyfikujący bazę danych _bstr_t bstrConnect = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Northwind 2007.accdb;"; 15 Struktura programu - połączenie z bazą danych Microsoft Access - ADO int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = CoInitialize(NULL); // inicjowanie obiektu COM if (FAILED(hr)) { cout << "Błąd wywołania funkcji CoInitialize()" << endl; return hr; } try // obsługa błędów COM w bloku try catch { ADODB::_ConnectionPtr pConn("ADODB.Connection"); hr = pConn->Open(bstrConnect, "admin", "", ADODB::adConnectUnspecified); if (SUCCEEDED(hr)) { cout<<"Połącznie z bazą: \n " << pConn->GetConnectionString() << endl; _bstr_t query = // kwerenda SQL "SELECT Customers.[Company], Customers.[First Name] FROM Customers;"; // cout << "SQL query:\n " << query << endl; // wysłanie kwerendy, utworzenie record set’a ADODB::_RecordsetPtr pRS("ADODB.Recordset"); hr = pRS->Open(query, _variant_t((IDispatch *) pConn, true), //cast Idispatch, ADO jest interf. typu ADODB::adOpenUnspecified, // Idispatch dziedziczy od interfesju IUnknown ADODB::adLockUnspecified, ADODB::adCmdText); 16 Struktura programu - połączenie z bazą danych Microsoft Access - ADO if (SUCCEEDED(hr)) { cout << "Informacja o strukturze: " << endl; ADODB::Fields* pFields = NULL; hr = pRS->get_Fields(&pFields); if (SUCCEEDED(hr) && pFields && pFields->GetCount() > 0) { for (long nIndex=0; nIndex < pFields->GetCount(); nIndex++) { cout<<" | "<<_bstr_t(pFields->GetItem(nIndex)->GetName()); } cout << endl; } else { cout << "Błąd, liczba pól ma wartość zero." << endl; } cout << "Pobieranie danych: " << endl; int rowCount = 0; while (!pRS->AdoNSEOF) { for (long nIndex=0; nIndex < pFields->GetCount(); nIndex++) { cout<<" | "<<_bstr_t(pFields->GetItem(nIndex)->GetValue()); } cout << endl; pRS->MoveNext(); rowCount++; } cout << "Liczba pobranych wierszy: " << rowCount << endl; } pRS->Close(); pConn->Close(); cout << "Połączenie zamknięte" << endl; } 17 Struktura programu - połączenie z bazą danych Microsoft Access - ADO else { cout << "Błąd połączenia z bazą: " << bstrConnect << endl; } } catch(_com_error& e) { cout << "Błąd COM: " << e.Description() << endl; } CoUninitialize(); // zwolnienie biblioteki COM return hr; } 18 Połączenie z bazą danych metodą ATL OLE DB Konfiguracja projektu. Kompilator: MS Visual Studio 2008 Projekt: Visual C++, Win32, Win32 Console application, Console application + ATL lub empty project. Baza danych: Northwind_pl 2007.accdb Northwind_uk 2007.accdb Parametry projektu: Character Set: Use Multi-Byte Character Set. Active Template Library (ATL) – biblioteka szablonów klas C++ do tworzenie obiektów COM. CDataSource - klasa, reprezentuje typ danych OLE DB. CSession - klasa, reprezentuje typ danych służący do dostępu do danych w pojedynczej sesji. CComVariant - klasa ATL, typ zawierający typy przechowywanych danych. Metody biblioteki ATL: GetInitializationString() - metoda klasy CDataSource pobiera dane inicjujące z otwartego źródła danych. OpenFromInitializationString() – metoda otwiera źródło danych zidentyfikowane przez dane inicjujące. OLE2T(), COLE2T() – makra konwertujące stringi ATL. Funkcje Windows: GetColumnCount(), GetColumnInfo(). 19 Struktura programu - połączenie z bazą danych metodą ATL OLE DB #include <atldbcli.h> #include <atldbsch.h> #include <iostream> using namespace std; // string identyfikujący bazę danych LPCOLESTR lpcOleConnect = L"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Northwind 2007.accdb; User Id=admin; Password=;"; 20 Struktura programu - połączenie z bazą danych metodą ATL OLE DB int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = CoInitialize(NULL); // inicjowanie obiektu COM if (FAILED(hr)) { cout <<"Błąd wywołania funkcji CoInitialize()" << endl; return hr; } // deklaracja obiektów klas CDataSource and CSession z biblioteki ATL CDataSource dbDataSource; CSession dbSession; // użycie makra ATL konwertującego stronę kodową USES_CONVERSION; // otwarcie połączenia hr = dbDataSource.OpenFromInitializationString(lpcOleConnect); if (FAILED(hr)) { cout<<" Błąd połączenia z bazą " << OLE2T(lpcOleConnect)<<endl; } else { hr = dbSession.Open(dbDataSource); if (FAILED(hr)) { cout<<" Błąd, budowy sesji z bazą "<< OLE2T(lpcOleConnect)<<endl; } else { CComVariant var; hr = dbDataSource.GetProperty(DBPROPSET_DATASOURCEINFO, DBPROP_DATASOURCENAME, &var); if (FAILED(hr) || (var.vt == VT_EMPTY)) { cout <<"Niewłaściwa nazwa bazy " << endl; } 21 Struktura programu - połączenie z bazą danych metodą ATL OLE DB else { cout<<"Połączenie z bazą:\n " << COLE2T(var.bstrVal)<<endl; // kwerenda SQL LPCOLESTR query = L"SELECT Customers.[Company], Customers.[First Name] FROM Customers;"; // cout<<"Kwarenda SQL:\n " << OLE2T(query)<<endl; // Wysłanie kwarendy, utworzenie record set’a CCommand<CDynamicStringAccessor> cmd; hr = cmd.Open(dbSession, query); DBORDINAL colCount = cmd.GetColumnCount(); if (SUCCEEDED(hr) && 0 < colCount) { cout<<" Pobranie informacji o strukturze danych "<<endl; DBORDINAL cColumns; DBCOLUMNINFO* rgInfo = NULL; OLECHAR* pStringsBuffer = NULL; cmd.GetColumnInfo(&cColumns, &rgInfo, &pStringsBuffer); for (int col=0; col < (int)colCount; col++) { cout<<" | "<<OLE2T(rgInfo[col].pwszName); } cout<<endl; 22 Struktura programu - połączenie z bazą danych metodą ATL OLE DB cout<<"Pobranie danych: "<<endl; int rowCount = 0; CRowset<CDynamicStringAccessor>* pRS =(CRowset<CDynamicStringAccessor>*)&cmd; // Pobranie wierszy z danymi while (pRS->MoveNext() == S_OK) { for (int col=1; col <= (int)colCount; col++) { CHAR* szValue = cmd.GetString(col); cout<<" | "<<szValue; } cout<<endl; rowCount++; } cout<<" Liczba wierszy: " << rowCount<<endl; } else { cout<<"Błąd, liczba pól zero."<<endl; } } } } dbDataSource.Close(); dbSession.Close(); CoUninitialize(); return hr; } 23 Połączenie z bazą danych metodą Direct ODBC Konfiguracja projektu. Kompilator: MS Visual Studio 2008 Projekt: Visual C++, Win32, Win32 Console application, Console application, empty project. Baza danych: Northwind_pl 2007.accdb Northwind_uk 2007.accdb Parametry projektu: Character Set: Use Multi-Byte Character Set Funkcje biblioteki ODBC: SQLAllocEnv() , SQLAllocConnect(), SQLAllocStmt() -funkcje alokują uchwyty do zmiennych środowiskowych, połączenia, wyrażeń. SQLDriverConnect() – funkcaj buduje połączenie, zwracając ‘string połączenia’. SQL_SUCCEEDED() SQLPrepare() SQLBindCol() SQLExecute() SQLNumResultCols() SQLDescribeCol) SQLFetch() SQLFreeStmt(), SQLFreeHandle() – funkcje zwalniają uchwyty. 24 Struktura programu - połączenie z bazą danych metodą Direct ODBC #include <windows.h> #include <stdio.h> #include <sqlext.h> // string identyfikujący bazę danych w ODBC char szDSN[256] = "Driver={Microsoft Access Driver (*.mdb, *.accdb)}; DSN=''; DBQ=C:\\Northwind 2007.accdb;"; 25 Struktura programu - połączenie z bazą danych metodą Direct ODBC main() { HENV hEnv; HDBC hDbc; RETCODE rc; // property, uchwyt do zmiennej środowiskowej // property, uchwyt do połączenia odbc // ODBC API return status int iConnStrLength2Ptr; char szConnStrOut[256]; unsigned char* query = "SELECT Customers.[Company], Customers.[First Name] FROM Customers;"; SQLCHAR chval1[128], chval2[128], colName[128]; int ret1; int ret2; // Number of rows and columns in result set SQLINTEGER rowCount = 0; SQLSMALLINT fieldCount = 0, currentField = 0; HSTMT hStmt; //property, uchwyt do wyrażeń odbc rc = SQLAllocEnv(&hEnv); rc = SQLAllocConnect(hEnv, &hDbc); // alokacja zmiennej środowiskowej hEnv // alokacja uchwytu do połączenia hDbc // Połączenie z bazą rc = SQLDriverConnect(hDbc, NULL, (unsigned char*)szDSN, SQL_NTS, (unsigned char*)szConnStrOut, 255, (SQLSMALLINT*)&iConnStrLength2Ptr, SQL_DRIVER_NOPROMPT); if (SQL_SUCCEEDED(rc)) { printf("Połączenie z bazą: \n %s \n", szConnStrOut); // printf("SQL query:\n %s\n", query); // Kwerenda SQL rc = SQLAllocStmt(hDbc,&hStmt); rc = SQLPrepare(hStmt, query, SQL_NTS); // Połączenie (bind) danych pobranych z bazy z buforem rc = SQLBindCol(hStmt, 1, SQL_C_CHAR, chval1, 128, (SQLINTEGER*)&ret1); rc = SQLBindCol(hStmt, 2, SQL_C_CHAR, chval2, 128, (SQLINTEGER*)&ret2); 26 Struktura programu - połączenie z bazą danych metodą Direct ODBC // wykonanie kwerendy i utworzenie recordset’a rc = SQLExecute(hStmt); if (SQL_SUCCEEDED(rc)) { printf("Pobranie informacji o strutkurze danych:\n"); SQLNumResultCols(hStmt, &fieldCount); if (fieldCount > 0) { for (currentField=1; currentField <= fieldCount; currentField++) { SQLDescribeCol(hStmt, currentField, colName, sizeof(colName), 0, 0, 0, 0, 0); printf(" | %s", colName); } printf("\n"); } else { printf("Błąd, liczba pól zero. \n"); } printf("Pobranie danych.\n"); rc = SQLFetch(hStmt); while (SQL_SUCCEEDED(rc)) { printf(" | %s | %s\n", chval1, chval2); rc = SQLFetch(hStmt); rowCount++; }; printf("Liczba wierszy: %d\n", rowCount); rc = SQLFreeStmt(hStmt, SQL_DROP); } } else { printf("Brak połaczenia z %s.\n", szDSN); // rozłączenie, zwolnienie uchwytów SQLDisconnect(hDbc); SQLFreeHandle(SQL_HANDLE_DBC, hDbc); SQLFreeHandle(SQL_HANDLE_ENV, hEnv); } } 27