Relacyjne Bazy Danych wykład XII 1 opr. Lech Banachowski, Jan Wierzbicki ADO - programistyczny interfejs do bazy danych Co to są obiekty ADO (ActiveX Data Objects)? Przy układaniu aplikacji bazodanowej często zachodzi potrzeba wykonania ciągu operacji na bazie danych bez interwencji użytkownika. Jedna możliwość to zastosować ciąg wywołań metody RunSQL na obiekcie DoCmd. Ma to swoje wady. Nie daje możliwości przetwarzania danych w trakcie ich odczytywania wiersz po wierszu. Po drugie, używany jest obiekt DoCmd niedostępny w innych środowiskach programistycznych. 2 opr. Lech Banachowski, Jan Wierzbicki Istnieje ogólniejsza i bardziej dopasowana do przetwarzania danych metoda oparta na tzw. programistycznym interfejsie wywołań, którego zasady są zawarte w standardzie języka SQL. Na tym wykładzie przedstawimy jeden taki interfejs o nazwie ADO (ActiveX Data Objects) - przygotowany przez firmę Microsoft, a na następnym wykładzie drugi taki interfejs o nazwie JDBC - przygotowany przez firmę Sun. 3 opr. Lech Banachowski, Jan Wierzbicki Model programowania ADO określa grupę obiektów i ich metod, które umożliwiają uzyskanie dostępu i aktualizację różnego rodzaju źródeł danych w tym baz danych. Oto jego zasady: •Połączenie ze źródłem danych odbywa się przy użyciu obiektu klasy Connection. •Określa się instrukcję SQL na źródle danych. •Wykonuje się instrukcję SQL. Jeśli jest nią SELECT, zapisuje się wynikowe wiersze w obiekcie klasy RecordSet w celu ich przejrzenia i aktualizacji. •Jeśli trzeba, aktualizuje się źródło danych przy pomocy metod obiektu RecordSet. •Ewentualnie, następuje wykrycie błędów podczas połączenia i wykonywania polecenia. 4 opr. Lech Banachowski, Jan Wierzbicki Podstawowe obiekty modelu ADO •Connection - korzeń w hierarchii obiektów ADO, używany przy dokonywaniu połączenia aplikacji ze źródłem danych. •Recordset - reprezentuje zbiór rekordów przekazywanych ze źródła danych. Jest używany do przetwarzania rekordów w bazie danych. Używając tego obiektu można nawigować po zbiorze rekordów, modyfikować istniejące rekordy, dodawać nowe rekordy i usuwać wskazane rekordy. W danej chwili dostęp jest tylko do jednego rekordu nazywanego bieżącym rekordem. •Command - reprezentuje instrukcję SQL. •Error - reprezentuje błąd ADO. 5 opr. Lech Banachowski, Jan Wierzbicki Obiekty Connection i RecordSet Uzyskanie połączenia z bieżącą bazą danych MS Access Dim cnCurrent As ADODB.Connection Set cnCurrent = CurrentProject.Connection Poprzez obiekt cnCurrent i instrukcje języka SQL uzyskujemy dostęp do wszystkich danych zapisanych w bieżącej bazie danych MS Access. Będziemy używać bazy danych MS Access z przykładowymi tabelami: Klienci o kolumnach IDKlienta i Nazwisko oraz Towary o kolumnie IDTowaru. 6 opr. Lech Banachowski, Jan Wierzbicki 7 Uzyskanie połączenia z odległą bazą danych przy pomocy ODBC Dim cnCurrent As ADODB.Connection Set cnCurrent = New ADODB.Connection cnCurrent.ConnectionString = "DSN=scott;UID=scott;PWD=tiger;" cnCurrent.Open Będziemy używać bazy danych Oracle z jej przykładowym użytkownikiem o identyfikatorze scott i haśle tiger oraz przykładowej tabeli zakładanej na koncie użytkownika scott o nazwie Emp. Skorzystamy z kolumn Ename oraz Sal tej tabeli. Zakładamy, że mamy określone połączenie ODBC z tą bazą danych DSN=scott. Oczywiście fakt, że jest to baza danych firmy Oracle nie jest istotny. W taki sam sposób używalibyśmy bazy danych każdej innej firmy. opr. Lech Banachowski, Jan Wierzbicki Deklaracja i utworzenie obiektu Recordset Dim rsKlienci As ADODB.Recordset Set rsKlienci = New ADODB.Recordset 8 opr. Lech Banachowski, Jan Wierzbicki Otwarcie zestawu rekordów dla tabeli Klienci rsKlienci.Open "Klienci", cnCurrent Open jest metodą obiektu klasy RecordSet. Zamknięcie i usunięcie z pamięci obiektów Connection i Recordset rsKlienci.Close cnCurrent.Close Set rsKlienci = Nothing Set cnCurrent = Nothing 9 opr. Lech Banachowski, Jan Wierzbicki Odwołanie do pola rekordu w zestawie rekordów MsgBox rsKlienci!Nazwisko Symbol '!' sygnalizuje wybór z kolekcji pól (Fields). Nawigacja po zbiorze rekordów w zestawie rekordów - metody obiektu klasy RecordSet: •MoveFirst •MoveLast •MoveNext •MovePrevious i właściwości: •BOF - pozycja bieżącego rekordu jest przed pierwszym rekordem, •EOF - pozycja bieżącego rekordu jest po ostatnim rekordzie. 10 opr. Lech Banachowski, Jan Wierzbicki 11 Przeglądanie wszystkich rekordów w pętli: Do Until rsKlienci.EOF MsgBox rsKlienci!Nazwisko rsKlienci.MoveNext Loop Otwarcie zestawu rekordów na obiekcie klasy RecordSet przy pomocy instrukcji SELECT rsKlienci.Open "SELECT * FROM Klienci", cnCurrent lub strSQL = "SELECT * FROM Klienci " & _ "WHERE Nazwisko = '" & Forms!Klienci!txtNazwisko.Value & "'" rsKlienci.Open strSQL, cnCurrent (Zakładamy, że txtNazwisko i txtIDKlienta są polami tekstowymi na otwartym formularzu MS Access Klienci.) opr. Lech Banachowski, Jan Wierzbicki Wykonywanie instrukcji SQL Instrukcję SQL można wykonać w kodzie VBA w różny sposób. Oto możliwości: •opisana na poprzednich wykładach metoda RunSQL obiektu DoCmd; jej stosowanie jest ograniczone do tabel MS Access (w tym załączonych z innych baz danych), •metoda Execute obiektu Connection, •metoda Execute obiektu Command, •metoda Open obiektu Recordset. 12 opr. Lech Banachowski, Jan Wierzbicki Uwaga: •W przypadku instrukcji SELECT metoda Execute w obiektach Connection i Command zwraca zestaw rekordów tylko-do-odczytu. •Metodę Open obiektu RecordSet można stosować także do wykonywania instrukcji INSERT, UPDATE i DELETE. 13 opr. Lech Banachowski, Jan Wierzbicki Oto przykłady stosowania instrukcji UPDATE, INSERT i DELETE alternatywnie przy pomocy metody Execute obiektu klasy Connection i przy pomocy metod obiektu klasy RecordSet: Dla instrukcji UPDATE: strSQL = "UPDATE Klienci " SET Nazwisko = '" & _ txtNazwisko.Value & "'" & _ "WHERE IDKlienta = '" & txtIDKlienta.Value & "'" cnCurrent.Execute strSQL lub po ustawieniu się na odpowiednim rekordzie w obiekcie rsKlienci: rsKlienci!Nazwisko = InputBox("Podaj nazwisko:") rsKlienci.Update 14 opr. Lech Banachowski, Jan Wierzbicki Podobnie dla instrukcji INSERT: strSQL = "INSERT INTO Klienci(IDKlienta, Nazwisko)_ VALUES (' _ & txtIDKlienta.Value & "','" _ & txtNazwisko.Value & "')" cnCurrent.Execute strSQL lub rsKlienci.AddNew rsKlienci!IDKlienta = txtIDKlienta.Value rsKlienci!Nazwisko = InputBox("Podaj nazwisko:") rsKlienci.Update 15 opr. Lech Banachowski, Jan Wierzbicki Dla DELETE: strSQL = "DELETE * FROM Produkty " & _ "WHERE IDProduktu = " & txtIDProduktu.Value cnCurrent.Execute strSQL lub po ustawieniu się na odpowiednim rekordzie w obiekcie rsProdukty: rsProdukty.Delete rsProdukty.MoveNext If rsProdukty.EOF Then rsProdukty.MoveLast End If Zwróćmy uwagę, że w ostatnim przykładzie konieczne jest przesunięcie wskaźnika bieżącego rekordu (rsProdukty.MoveNext) za usunięty rekord. Ponadto w przypadku wyjścia poza ostatni rekord, ustawiamy wskaźnik bieżącego rekordu na ostatnim rekordzie (rsProdukty.MoveLast). 16 opr. Lech Banachowski, Jan Wierzbicki Przykład Załóżmy, że chcemy w tabeli Pracownicy zmienić zawód Sprzedawca na Księgowy. Najprostsze rozwiązanie to użyć instrukcji SQL (w metodzie RunSQL lub Execute): UPDATE Pracownicy SET Tytuł = "Księgowy" WHERE Tytuł = "Sprzedawca"; Natomiast używając języka programowania, moglibyśmy napisać instrukcję iteracji sprowadzającą kolejne rekordy z tabeli Pracownicy. W przypadku napotkania wartości Sprzedawca zmienilibyśmy ją na Księgowy. Dostęp do kolejnych rekordów tabeli (a także rekordów kwerend i formularzy) umożliwia konstrukcja zestawu rekordów. Stosując ją należy na samym początku utworzyć zestaw rekordów dla źródła rekordów, a następnie używając metod MoveFirst i MoveNext obiektu 17 RecordSet przejść po wszystkich rekordach ze źródła rekordów. opr. Lech Banachowski, Jan Wierzbicki Dim cnCurrent As ADODB.Connection Set cnCurrent = CurrentProject.Connection Dim rsPracownicy As ADODB.Recordset Set rsPracownicy = New ADODB.Recordset rsPracownicy.Open "Pracownicy", cnCurrent rsPracownicy.MoveFirst Do Until rsPracownicy.EOF If rsPracownicy!Tytuł = "Sprzedawca" Then rsPracownicy!Tytuł = "Księgowy" rsPracownicy.Update 'Zapisanie zmian End If rsPracownicy.MoveNext Loop rsPracownicy.Close Set rsPracownicy = Nothing 18 opr. Lech Banachowski, Jan Wierzbicki 19 Uwaga: Nie każde źródło danych umożliwia ten typ modyfikacji bazy danych! Jest możliwość przeglądania rekordów zgodnie z porządkiem określonym przez pewien, uprzednio założony na tabeli indeks. Gdyby na kolumnie Nazwisko był założony indeks, moglibyśmy przeglądać rekordy zgodnie z porządkiem określonym przez ten indeks, kładąc przed instrukcją rsPracownicy.MoveFirst instrukcję: rsPracownicy.Index = "Nazwisko" Podstawą użyteczności modelu ADO jest niezależność modelu programistycznego od źródła danych. Wystarczy tylko odpowiednio określić napis połączenia ConnectionString a następnie korzystać z jednolitego kodu. opr. Lech Banachowski, Jan Wierzbicki Biblioteki ADO można użyć wszędzie tam gdzie można użyć kodu Visual Basic np. w niezależnym programie klienckim napisanym w języku Visual Basic lub skrypcie ASP – Active Server Pages – w aplikacji internetowej. Dim cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.ConnectionString = "DSN=Pubs;UID=sa;PWD=sss;" cnn.Open (Pubs jest przykładową bazą danych SQL Server z użytkownikiem sa.) 20 opr. Lech Banachowski, Jan Wierzbicki Jeśli w napisie połączenia załączymy parametr Provider możemy w napisie ConnectionString użyć dodatkowych parametrów zdefiniowanych przez tego szczególnego "dostawcę danych". •Provider = SQLOLEDB oznacza "Microsoft OLE DB Provider for SQL Server", •Provider = MSDAORA oznacza "Microsoft OLE DB Provider for Oracle", •Domyślne ustawienie to Provider = MSDASQL oznaczające "Microsoft OLE DB Provider for ODBC". 21 opr. Lech Banachowski, Jan Wierzbicki Przykład Dim cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.Provider = "sqloledb" cnn.Open "Data Source=srv;Initial Catalog=pubs;","sa","" 22 opr. Lech Banachowski, Jan Wierzbicki Podnieś zarobki wszystkich pracowników, którzy zarabiają mniej niż 2000. Public Sub SalRise() Dim cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.ConnectionString = "DSN=scott;UID=scott;PWD=tiger;" cnn.Open Dim strSQL As String strSQL = "UPDATE Emp SET Sal=Sal*1.1 WHERE Sal < 2000" cnn.Execute strSQL cnn.Close Set cnn = Nothing End Sub 23 opr. Lech Banachowski, Jan Wierzbicki Wypisz nazwiska wszystkich pracowników. Public Sub Show_Emps() Dim cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.ConnectionString = "DSN=scott;UID=scott;PWD=tiger;" cnn.Open 24 Dim rsEmps As ADODB.Recordset Set rsEmps = New ADODB.Recordset rsEmps.Open "Emp", cnn rsEmps.MoveFirst Do Until rsEmps.EOF MsgBox rsEmps!Ename rsEmps.MoveNext Loop rsEmps.Close cnn.Close Set rsEmps = Nothing Set cnn = Nothing End Sub opr. Lech Banachowski, Jan Wierzbicki Wyznacz pracownika, który zarabia najwięcej. Załącz do wyniku jego zarobki. Public Sub EmpHighSal() Dim cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.ConnectionString = "DSN=scott;UID=scott;PWD=tiger;" cnn.Open Dim rsEmps As ADODB.Recordset Set rsEmps = New ADODB.Recordset Dim strSQL As String strSQL = "SELECT Ename, Sal FROM Emp WHERE Sal =(SELECT Max(Sal) FROM Emp)" rsEmps.Open strSQL, cnn rsEmps.MoveFirst Do Until rsEmps.EOF MsgBox rsEmps!Ename & " Sal = " & rsEmps!Sal rsEmps.MoveNext Loop rsEmps.Close cnn.Close Set rsEmps = Nothing Set cnn = Nothing 25 End Sub opr. Lech Banachowski, Jan Wierzbicki Transakcje na bazie danych Transakcja bazy danych to ciąg instrukcji INSERT, DELETE i UPDATE traktowany jako niepodzielna całość według zasady albo wszystkie instrukcje są wykonywane albo żadna. Wymagane jest explicite rozpoczęcie transakcji, ponieważ domyślnie w ADO każda instrukcja SQL stanowi jednoelementową transakcję kończącą się automatycznym zatwierdzeniem (auto-commit). Za pomocą metod obiektu Connection: BeginTrans – rozpoczyna nową transakcję. CommitTrans – zapisuje zmiany i kończy aktualną transakcję. RollbackTrans – kasuje zmiany dokonane w trakcie transakcji i kończy aktualną transakcję. 26 opr. Lech Banachowski, Jan Wierzbicki Transakcje mogą być zagnieżdżone. Składnia (gdzie obiekt oznacza obiekt klasy Connection) poziom = obiekt.BeginTrans() – zwraca poziom zagnieżdżenia transakcji obiekt.BeginTrans obiekt.CommitTrans obiekt.RollbackTrans Ten sam efekt można uzyskać przy pomocy instrukcji SQL wykonywanych przez metodę Execute: •BEGIN TRANSACTION •COMMIT •ROLLBACK Uwaga: Nie każdy "dostawca danych" realizuje transakcje! 27 opr. Lech Banachowski, Jan Wierzbicki Obiekt Command Obiekt Command jest reprezentacją instrukcji SQL w celu jej wykonania na źródle danych. Obiekt Command jest istotny w przypadkach gdy trzeba powtórnie wykonać tę samą instrukcję bądź, gdy wygodnie jest w aplikacji używać parametrów (których tutaj nie będziemy omawiać). Pokazaliśmy uprzednio jak wykonywać instrukcje SQL przy pomocy metod obiektów Connection i RecordSet. 28 opr. Lech Banachowski, Jan Wierzbicki Dim strCnn As String strCnn = "Provider=sqloledb;Data Source=srv;" & _ "Initial Catalog=pubs;User Id=sa;Password=;" Dim cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.Open strCnn Dim cmdChange As ADODB.Command 'Tworzenie obiektu typu Command: Set cmdChange = New ADODB.Command Set cmdChange.ActiveConnection = cnn Dim strSQL As String strSQL = "UPDATE Titles SET Type = 'Informatyka' " & _ "WHERE Type = 'Computer Science'" cmdChange.CommandText = strSQL Tabela Titles z kolumną Type jest cmdChange.Execute tabelą w przykładowej bazie danych 29 SQL Server o nazwie Pubs. opr. Lech Banachowski, Jan Wierzbicki 30 Użycie kolekcji Errors i obiektu Error Każda operacja dotycząca obiektów ADO może spowodować wystąpienie jednego lub więcej błędów, zgłaszanych przez dostawcę danych w wyniku wykonywania instrukcji SQL. Każdy błąd jest reprezentowany przez osobny obiekt klasy Error w kolekcji Errors. Gdy kolejna instrukcja generuje błędy - poprzednie obiekty kolekcji Errors są kasowane. Oprócz błędów zgłaszanych przez dostawcę danych mogą wystąpić błędy ADO w ramach realizacji kodu w VBA – są one standardowo zapisywane w obiekcie Err omawianym na poprzednich wykładach. Obiekt typu Error zawiera: 1. właściwość Description – tekst opisujący błąd, 2. właściwość Number – numer błędu, 3. właściwość Source – identyfikacja obiektu, który podniósł błąd, 4. właściwości SQLState i NativeError – dostarczające informacji opr. Lech Banachowski, Jan Wierzbicki ze źródeł danych SQL. Zaprogramujemy obsługę błędów dla instrukcji cmdChange.Execute z ostatniego przykładu. Dim strCnn As String strCnn = "Provider=sqloledb;Data Source=srv;" & _ "Initial Catalog=pubs;User Id=sa;Password=;" Dim cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.Open strCnn Dim cmdChange As ADODB.Command 'Tworzenie obiektu typu Command: Set cmdChange = New ADODB.Command Set cmdChange.ActiveConnection = cnn Dim strSQL As String strSQL = "UPDATE Titles SET Type = 'Informatyka' " & _ "WHERE Type = 'Computer Science'" cmdChange.CommandText = strSQL 31 opr. Lech Banachowski, Jan Wierzbicki On Error GoTo Err_Execute cmdChange.Execute ...... Err_Execute: 'Powiadom użytkownika o błędach pochodzących z wykonania instrukcji SQL. Dim errLoop As ADODB.Error If cnn.Errors.Count > 0 Then For Each errLoop In cnn.Errors MsgBox "Numer błędu: " & errLoop.Number & vbCr & errLoop.Description Next errLoop End If Resume Next ..... 32 opr. Lech Banachowski, Jan Wierzbicki Podsumowanie Języka programowania przy tworzeniu aplikacji bazy danych używamy do: •przetwarzania danych wymagającego iteracji i podejmowania wyborów; •sprawdzania poprawności danych, ich poprawiania i diagnostyki błędów; •odpowiedniej reakcji na zaistniałe błędy, do ich diagnostyki i dynamicznego poprawiania; •współpracy z innymi programami systemu Windows jak i z odległymi bazami danych; •wielokrotnego użycia tego samego kodu. 33 W wykładzie 12 przedstawiliśmy metodę programowania dostępu do baz danych przy użyciu biblioteki ADO opartej na obiektach Connection, RecordSet, Command i Error oraz na ich właściwościach i metodach. opr. Lech Banachowski, Jan Wierzbicki 34 Recordset - obiekt reprezentujący cały zbiór rekordów z tabeli w bazie danych lub z wyniku zapytania na tabelach bazy danych. W danej chwili dostęp jest tylko do jednego rekordu nazywanego bieżącym rekordem. Connection - obiekt - korzeń w hierarchii obiektów ADO, używany przy dokonywaniu połączenia między aplikacją i źródłem danych. Command - obiekt reprezentujący instrukcję SQL do wykonania na bazie danych. transakcja - ciąg instrukcji INSERT, DELETE i UPDATE traktowany jako niepodzielna całość według zasady albo wszystkie instrukcje są wykonywane albo żadna. Instrukcja COMMIT (metoda CommitTrans obiektu klasy Connection) kończy transakcję zatwierdzeniem dokonanych zmian; instrukcja ROLLBACK (metoda RollbackTrans obiektu klasy Connection) kończy transakcję wycofaniem dokonanych zmian. Error - obiekt reprezentujący błąd przy wykonywaniu instrukcji SQL na bazie danych. opr. Lech Banachowski, Jan Wierzbicki Koniec Wykładu XII 35 opr. Lech Banachowski, Jan Wierzbicki