Kapitel 8: Datenbankanbindung SoPra 2008 Kap. 8: Datenbankanbindung (1/40) Übersicht 1. Objekte und relationale Datenbanken 2. SQL 3. ADO.NET 4. Persistenzschicht SoPra 2008 Kap. 8: Datenbankanbindung (2/40) Kapitel 8.1: Objekte vs. relationale Datenbanken Persistente Objekte • auch ein objektorientiertes Programm muss Daten speichern. . . • Möglichkeiten ⋆ einfache Dateien: Serialized Objects, XML,. . . • für große Datenmengen ungeeignet • keine Sicherheitsmechanismen, . . . ⋆ Persistenz durch Speicherung in DB • optimiert für große Datenmengen • bieten Transaktionskonzept • Backups • und vieles mehr SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (3/40) Speichermöglichkeiten Objektdatenbanken • unterstützten das OO-Modell direkt Relationale Datenbanken • passen nicht direkt zum OO-Modell • Anpassungen (Zwischenschicht) erforderlich Sonstige • hierarchische Datenbanken • einfache Dateien, XML • auch hier: Anpassungen erforderlich SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (4/40) Relationale Datenbanken Grundlagen • basiert auf mathematischen Relationen (Tupeln) • beschreibt Information in Prädikatenlogik und mit Fakten Entity-Relationship (ER) Modelle • nur Objekte/Attribute/Relationen • nur einfache Attribute, keine Listen oder Records • keine Vererbung/Aggregation etc. SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (5/40) Abbildung OO nach relational Impedance Mismatch • OO und Relationale DB sind stark unterschiedliche Ansätze OO Relational Modellierung Zustand & Verhalten nur Daten Identität ja nein Navigation über Assoziationen Daten duplizieren, joins. . . Anfragen über Attributwerte Selektion und Projektion Granularität einzelne Objekte Ergebnismengen • moderne SW wird meist nach OO-Konzepten entwickelt • verwendete DBMS sind überwiegend relational ⇒ wichtig: gute Abbildung von OO nach relational SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (6/40) Grundlagen der Abbildung Welche Konstrukte müssen wir abbilden? • Klassen • Attribute • Assoziationen • Vererbungshierarchien SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (7/40) Beispiel: WebBank Client 1 Account Account 1 firstName lastName PIN accountNumber balance 1 1 IssuingAccount ReceivingAccount * * Transfer date amount reasonForTransfer SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (8/40) Object Identifiers Problem • Effizienter Tabellenzugriff erfordert eindeutigen Primärschlüssel • Objekte können nicht anhand ihres Wertes unterschieden werden – sie besitzen Identität • Tupel haben keine Identität Lösung • Object Identifier (OID) • Tupeln wird eindeutige OID zugeordnet • Objekte werden in der DB anhand ihres OIDs identifiziert SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (9/40) OIDs No Business Meaning: • keine Attribute aus dem modellierten Problembereich als OIDs (d. h. als (Fremd-) Schlüssel) • bei Attributen oft keine Eindeutigkeit gegeben • im Prinzip kann sich jedes Attribut ändern, die Objektidentität bleibt aber unverändert • Abhängig vom Typ eventuell ineffizient Wie eindeutig sollen die OIDs sein? • für die konkrete Klasse? • innerhalb der Klassenhierarchie? • über alle Klassen hinweg? SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (10/40) OIDs - Generierung Globally/Universally Unique IDs: Berechne quasi eindeutige ID (128 Bit) aus • MAC-Adresse, Datum, Zeit, oder • (kryptographischem) Zufallszahlengenerator • in ASP.NET: Klasse/Struct System.Guid, Methode Guid.NewGuid() Bewertung • funktioniert • Datentabellen nicht gelocked • proprietär • zu groß (kein Int/Long mehr) Es gibt diverse andere Möglichkeiten . . . SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (11/40) Abbildung von Klassen Prinzip Klassen werden auf Tabellen abgebildet. • die erste Spalte ist für die OID • dann folgen die Attribute Client firstName : String lastName : String PIN : int <<Table>> Client OID : uniqueidentifier firstName : varchar(20) lastName : varchar(20) PIN : int SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (12/40) Abbildung von Attributen Prinzip Attribute werden in (≥ 0) Spalten abgebildet. • einfache Attribute: eine Spalte • objektwertige Attribute ohne eigene Identität: eine Spalte pro Attribut Beispiel Client firstName : String lastName : String PIN : int address : Address <<Table>> Person Address street : String city : String zip : String OID : uniqueidentifier firstName : varchar(20) lastName : varchar(20) PIN : int street : varchar(50) city : varchar(50) zip : varchar(5) SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (13/40) Abbildung von Assoziationen Prinzip Jede Assoziation wird auf eine eigene Tabelle abgebildet. Die OIDs der in Relation stehenden Objekte bilden die Tupel. Vereinfachung • 1:1, *:1 Assoziation: keine eigene Tabelle, sondern OID als zusätzliches Attribut in der anderen Tabelle • unabhängig von Implementierung der Assoziation • Portierbarkeit: Keine Mengen von Fremdschlüsseln verwenden SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (14/40) Abbildung von Klassenhierarchien • Möglichkeit 1: Eine einzige Tabelle für die gesamte Hierarchie. ⋆ Eine Spalte für jedes Attribut ⋆ NULL für nicht benutzte Attribute ⋆ Eine Spalte für den Typ • Möglichkeit 2: Eine eigene Tabelle für jede konkrete Klasse mit allen Attributen (inkl. Attribute aus Superklassen). • Möglichkeit 3: Eine eigene Tabelle für jede Klasse mit genau den Attributen der Klasse (ohne Attribute der Superklassen). SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (15/40) Datenbanktabellen (Zusammenfassung) Vorgehensweise • Klassen werden zu Tabellen • Objekte mit Attributen als Zeilen der Tabellen • schwache Klassen als zusätzliche Attribute • OID für jede Klasse/Tabelle • 1:*-Assoziationen durch Referenz (zusätzliches Attribut) mit Fremdschlüssel • *:*-Assoziationen durch eigene Tabelle • Vererbung: drei Möglichkeiten SoPra 2008 Kap. 8.1: Datenbankanbindung – Objekte vs. relationale Datenbanken (16/40) Kapitel 8.2: SQL SQL = Structured Query Language Anfragen in SQL • SELECT Selektion von Spalten • FROM Auswahl beteiligter Tabellen • WHERE Bedingung für Zeilen • ORDER BY Sortierung der Zeilen SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (17/40) Anfragen in SQL (Beispiel WebBank) Welche Kunden haben die PIN 1234? SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (18/40) Anfragen in SQL (Beispiel WebBank) Welche Kunden haben die PIN 1234? SELECT FirstName, LastName FROM Client WHERE PIN = 1234; Wie heißt der Besitzer des Kontos mit der Nummer 10001? SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (18/40) Anfragen in SQL (Beispiel WebBank) Welche Kunden haben die PIN 1234? SELECT FirstName, LastName FROM Client WHERE PIN = 1234; Wie heißt der Besitzer des Kontos mit der Nummer 10001? SELECT FirstName, LastName FROM Client, Account WHERE AccountNumber = 10001 and Account = Account.OID SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (18/40) Anfragen in SQL (JOIN Operator) Spezialoperator JOIN: SELECT FROM LastName, AccountNumber Client INNER JOIN Account a ON (Account = a.OID) • Liefert alle Paare aus Kunden und Konten • RIGHT JOIN: liefert zusätzlich alle Paare (NULL, Konto) für Konten, die keinen Besitzer haben • Analog LEFT JOIN: Auch Kunden, die kein Konto besitzen SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (19/40) Anfragen in SQL (Besonderheiten) Ergebnis hat immer die Form einer Tabelle Möglichkeiten • Auswahl von Spalten aus mehreren Tabellen • Filterung von Zeilen nach relativ komplexen Bedingungen • Sortierung des Ergebnisses Einschränkung • algorithmische Auswertung der Daten nicht möglich • nur Abfrage positiver Informationen • eventuell Effizienzprobleme bei komplexen Anfragen SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (20/40) SQL: Weiteres Weitere wichtige Befehle: • create table (neue Tabelle anlegen) • insert (neue Zeile in eine Tabelle einfügen) • update (Tabellenzeile ändern) • delete (Tabellenzeile löschen) Datentypen: • int, decimal, datetime • varchar(max), varbinary(max), xml • char(n), binary(20) SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (21/40) SQL: Create table CREATE TABLE [dbo].[Client]( [OID] [uniqueidentifier] NOT NULL, [FirstName] [varchar](20) NOT NULL, [LastName] [varchar](20) NOT NULL, [PIN] [varchar](50) NOT NULL, [Account] [uniqueidentifier] NOT NULL ) ON [PRIMARY] SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (22/40) Mehr Infos Weitere Informationen z. B.: • http://msdn.microsoft.com/ • SQL Server 2005 Books online • Google: ’SQL Tutorial’ • Bibliothek • Server Explorer in Visual Studio • Microsoft SQL Server Management Studio Express Sicherheit: • SQL-Injection SoPra 2008 Kap. 8.2: Datenbankanbindung – SQL (23/40) Kapitel 8.3: ADO.NET Application Interface (API) • ADO = ActiveX Data Objects • Microsoft definiertes generisches Interface, um aus .NET auf Daten zuzugreifen. Implementierung (Treiber) • SQL Server von Microsoft oder Drittanbieter implementiert Interface • ähnliches Konzept in Java: JDBC SoPra 2008 Kap. 8.3: Datenbankanbindung – ADO.NET (24/40) ADO.NET: Datenbankanbindung 1 SqlConnection erzeugen using System.Data.SqlClient; ... // Verbindungsstring besteht aus Benutzername, // Passwort, Name des Servers, Name der DB, // aus Web.config laden Connection con = new SqlConnection( ConfigurationManager .ConnectionStrings["WebBankConnectionString"] .ConnectionString); Data Source=”Server”;Initial Catalog=”DB Name”; User ID=”Login Name”;Password=”XXX”; ”Optionen” SoPra 2008 Kap. 8.3: Datenbankanbindung – ADO.NET (25/40) ADO.NET: Datenbankanbindung 2 Verbindung auf- und abbauen Connection con = new SqlConnection(...); // Öffnen der Verbindung con.Open(); // Hier können Anfragen gestellt werden ... // Schließen der Verbindung con.Close(); SoPra 2008 Kap. 8.3: Datenbankanbindung – ADO.NET (26/40) ADO.NET (Anfragen 1) Anfrage stellen { string queryString = "SELECT OID " + "FROM Account " + "WHERE accountNumber = 10001;"; SqlCommand cmd = CreateCommand(queryString); SqlDataReader reader = cmd.ExecuteReader(); cmd.Dispose(); } catch (SqlException ex) { ... } try SoPra 2008 Kap. 8.3: Datenbankanbindung – ADO.NET (27/40) ADO.NET (Anfragen 2) Ergebnis auswerten try { (reader.Read()) { // Variante 1: Spaltennummer oid = reader.getString(0); // Variante 2: Wie Array-Zugriff, mit Cast oid = (string)reader["OID"]; ... } } catch (SqlException e) { ... } while SoPra 2008 Kap. 8.3: Datenbankanbindung – ADO.NET (28/40) ADO.NET (Anfragen 3) Ergebnis auswerten try { (reader.Read()) { // Variante 3: Mit Zusatztest, ob // oid = NULL (falls das erlaubt ist) if (reader.IsDBNull(0)) oid = "unbekannt"; while else oid = (string)(reader.getValue(0)); ... } } catch (SqlException e) { ... } SoPra 2008 Kap. 8.3: Datenbankanbindung – ADO.NET (29/40) ADO.NET (Update) Ändern eines Datensatzes { string updateString = "UPDATE [Account] " + "SET [Balance] = ’" + newBalance + "’, " + "WHERE OID = ’" + oid + "’;"; SqlCommand cmd = CreateCommand(updateString); cmd.ExecuteNonQuery(); try } catch (SqlException e) { ... } SoPra 2008 Kap. 8.3: Datenbankanbindung – ADO.NET (30/40) ADO.NET (Update) Variante ohne Probleme mit Quotes Problem: spezielle Zeichen (Quotes) in Argumenten – SQL injection! { string updateString = "UPDATE Client " + "SET FirstName = @fn, LastName = @ln " + "WHERE OID = @oid"; SqlCommand cmd = CreateCommand(updateString); cmd.Parameters.Add(new SqlParameter("@fn", newFirstName)); cmd.Parameters.Add(new SqlParameter("@ln", newLastName)); cmd.Parameters.Add(new SqlParameter("@oid", oid)); cmd.ExecuteNonQuery(); } catch (SqlException e) { ... } } try SoPra 2008 Kap. 8.3: Datenbankanbindung – ADO.NET (31/40) Kapitel 8.4: Persistenzschicht Was wissen wir jetzt? • Struktur von DB-Tabellen klar • Manipulation mit SQL-Befehlen • Aufruf von SQL-Befehlen in C# • Abbildung von Objektmenge auf DB-Tabellen klar Persistenzproblem: 1. Daten sollen dauerhaft in DB gespeichert werden. 2. Systemoperationen manipulieren Objekte, keine DB-Tabellen. Wie also Operationen implementieren? SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (32/40) Persistenzproblem: Einfache (Nicht-)Lösung 1 Lösung 1: Wir arbeiten nur mit Tabellen, nicht mit Objekten. SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (33/40) Persistenzproblem: Einfache (Nicht-)Lösung 1 Lösung 1: Wir arbeiten nur mit Tabellen, nicht mit Objekten. Vorteile: • Direkte Verwendung der ADO.NET API • einfach zu programmieren (???) • am effizientesten (???) SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (33/40) Persistenzproblem: Einfache (Nicht-)Lösung 1 Lösung 1: Wir arbeiten nur mit Tabellen, nicht mit Objekten. Vorteile: • Direkte Verwendung der ADO.NET API • einfach zu programmieren (???) • am effizientesten (???) Nachteile: • keine Objektorientierung (keine Konzepte aus der Realität, kein Zusammenhang zum Design) • damit: Code wird low-level, DB-orientiert • Ergebnis: Code schlecht wartbar, schlecht zu debuggen SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (33/40) Persistenzproblem: Einfache Lösung 2 Lösung 2: Zum Programmstart wird die ganze DB hereingeladen und in Objekte konvertiert. Zum Programmende wird alles hinaus geschrieben. SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (34/40) Persistenzproblem: Einfache Lösung 2 Lösung 2: Zum Programmstart wird die ganze DB hereingeladen und in Objekte konvertiert. Zum Programmende wird alles hinaus geschrieben. Vorteil: Operationen können ganz normal Objekte manipulieren. SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (34/40) Persistenzproblem: Einfache Lösung 2 Lösung 2: Zum Programmstart wird die ganze DB hereingeladen und in Objekte konvertiert. Zum Programmende wird alles hinaus geschrieben. Vorteil: Operationen können ganz normal Objekte manipulieren. Nachteile: • Platzbedarf zu groß bei großen Datenmengen • lange Startup-Dauer • Datenverlust bei Crash • keine aktuellen Daten bei mehreren Clients • Konflikte bei gleichzeitigem Schreiben von mehreren Clients SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (34/40) Persistenzproblem: Einfache Lösung 3 Lösung 3: Jede Operation liest Objekte aus der DB und schreibt sofort in die Datenbank, Objekte nur ganz kurzfristig im Speicher. SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (35/40) Persistenzproblem: Einfache Lösung 3 Lösung 3: Jede Operation liest Objekte aus der DB und schreibt sofort in die Datenbank, Objekte nur ganz kurzfristig im Speicher. Vorteil: immer aktuelle Daten, minimaler Hauptspeicherbedarf SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (35/40) Persistenzproblem: Einfache Lösung 3 Lösung 3: Jede Operation liest Objekte aus der DB und schreibt sofort in die Datenbank, Objekte nur ganz kurzfristig im Speicher. Vorteil: immer aktuelle Daten, minimaler Hauptspeicherbedarf Nachteile: • alles passiert über ineffiziente DB-Zugriffe • Datenbank-Code (ADO.NET) überall im Programm • Was ist mit den Assoziationen eines Objekts? (sofort mit laden? Deren Assoziationen laden? Eventuell alles laden?) SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (35/40) Persistenzschicht Grundidee • DB-Anschluss (fast) komplett verstecken ⋆ Klassen aus Analyse und Design werden persistent ⋆ Kapseln der SQL-Anfragen in einer Klasse ⋆ Zugriff auf DB (möglichst) nur über Laden und Speichern von Objekten • Lesen von Assoziationen bei Bedarf • (fast) so zu benutzen wie ’normale’ Objekte • zusätzliche Aufgaben: ⋆ Caching (nur dann auf DB zugreifen, wenn notwendig) ⋆ Synchronisation (bei parallelem Zugriff auf DB) ⋆ Transaktionen (mehrere Änderungen passieren nur zusammen) SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (36/40) Persistenzschicht Realisierungen professionelle/generische Lösungen: • Enterprise Java Beans (EJB) • Java Persistency API (JPA) • Hibernate (für Java) • NHibernate (für C#) • andere Middleware, z. B. CORBA • Objektorientierte Datenbanken SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (37/40) Persistenzschicht Realisierung Im SoPra: selbstgemachte Lösung • zeigt wesentliche Konzepte (⇒ Lerneffekt) • relativ klein (kein Caching) • bietet Raum für eigene Verbesserungen • mehr in Vorlesung Softwaretechnik • keine Lizenzprobleme . . . SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (38/40) Persistenzschicht im Sopra • Kapselung der SQL-Statements in einer Klasse DBConnection ⋆ Generische Methoden zum Lesen/Schreiben/Ändern von Zeilen in einer Tabelle ⋆ Suchen nach einzelnen Werten (Select) ⋆ Transaktionen • Eine zentrale Klasse PersistenceManager mit Methoden zum Lesen/Schreiben/Ändern von persistenten Objekten: public Object load(String table, Guid oid) { ...} public void persist(Object o) { ...} • initiales Anlegen der Tabellen SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (39/40) Anforderungen an eine persistente Klasse • Muss ein OID Feld haben (Typ Guid). • Lade/Speicher/Update-Operation für die Klasse muss in PersistenceManager programmiert werden. • Umgang mit Assoziationen: Entscheidung des Programmierers • Suchen mit Select in einer Tabelle oder gesamte Tabelle als Liste von Objekten laden und darüber iterieren: Entscheidung des Programmierers Allgemeine Anforderung: Anwendung kann mit leerer Datenbank starten. Das heißt: Anwendung hat Code zum Anlegen der Tabellen. SoPra 2008 Kap. 8.4: Datenbankanbindung – Persistenzschicht (40/40)