Datenbankprogrammierung 2 JDBC Java Database Connectivity Call Level Interface Schnittstelle zwischen DBMS und Applikation JDBC API ist seit JDK1.1 inkludiert Ermöglicht Zugriff aus Java-Applikationen auf Datenbanken Unterstützt dynamisches SQL two-tier architecture three-tier architecture Wichtigste Klassen Im Package java.sql zusammengefasst: java.sql.DriverManager: java.sql.Connection: java.sql.Statement: java.sql.ResultSet: Laden der Treiber, Verbindung zur DB Repräsentiert DB-Verbindung Ermöglicht Ausführung von SQLAnweisungen über gegebene Verbindung Verwaltet Ergebnisse einer Anfrage in Form einer Relation und unterstützt Zugriff auf einzelne Spalten JDBC: Prinzipielle Schritte 1.Aufbau einer Verbindung zur Datenbank 2.Senden einer SQL-Anweisung 3.Verarbeiten der Anfrageergebnisse Aufbau einer Verbindung zur Datenbank 1. Laden des JDBC Treibers: Entweder explizit im Programm: Class.forName(“oracle.jdbc.OracleDriver”); oder in System-Property sql.drivers 2. Herstellen einer Verbindung: Connection con = DriverManager.getConnection (“jdbc:oracle:[email protected] :1521:xe”,”gruppeX”,”passwort”); JDBC URL Bsp.: jdbc:oracle:thin:@gutemine.ict.tuwien.ac.at:1521:xe oracle:thin ... Treibername gutemine.ict.tuwien.ac.at:1521 ... Host xe ... Name der Datenbank Senden einer SQL-Anweisung Datenbankabfrage: String query = “SELECT Titel FROM Buch”; Statement stmt=con.createStatement(); ResultSet result=stmt.executeQuery(query); Datenbankänderung: String update = “INSERT INTO Buch VALUES(...)”; Statement stmt=con.createStatement(); ResultSet result=stmt.executeUpdate(update); ResultSet ResultSet Interface bietet Methoden zum Abfragen und Ändern von Resultaten Ergebnismenge ist eine Tabelle auf die zeilenweise und spaltenweise zugegriffen werden kann Cursorbewegungen: next(), previous(), first(), absolute(int row), ... Achtung: Interner Cursor ist vor dem ersten Tupel positioniert, d.h. bevor ein Tupel gelesen wird, muss next() aufgerufen werden Abfragen: getString(“Titel”), getBoolean(1), ... Änderungen: updateString(...), updateBoolean(...), ... DB-Änderung mit Java-Befehlen statt SQL-Befehlen String query = “SELECT Titel, Preis FROM Buch”; Statement stmt=con.createStatement(); ResultSet result=stmt.executeQuery(query); result.last(); result.updateFloat(“Preis”, 19.90); result.updateRow(); Update Operationen betreffen Zeile, in der sich der Cursor gerade befindet updateFloat(), updateString(), ... wirken sich nur auf ResultSet aus updateRow(), insertRow(), deleteRow() bewirken Änderung der Datenbank Verarbeiten der Anfrageergebnisse while(result.next()){ System.out.println(result.getString(“Ort”); System.out.println(result.getString(1);//1. Spalte } Fehlerbehandlung try { //Aufruf von JDBC-Anweisungen, die möglicherweise //Exceptions erzeugen } catch(SQLException exc) { System.out.println(“SQLException:” + exc.getMessage()); } Transaktionen Werden über Methoden der Klasse java.sql.connection realisiert Nach Aufbau einer Verbindung zur DB: Auto-Commit-Modus Änderung durch con.setAutoCommit(false); con.commit() ... explizites bestätigen der Transaktion con.rollback() ... zurückrollen der Transaktion try{ con.setAutoCommit(false); //INSERT Anweisungen con.commit(); } catch(SQLException exc{ con.rollback(); } Prepared and Callable Statements Prepared Statement Vorkompiliert Reduziert Execution Time Vorteilhaft, wenn Statement öfters verwendet wird PreparedStatement stmt = con.preparedStatement (“SELECT plz FROM Orte”); ResultSet result= stmt.executeQuery(); Callable Statement Enthält Aufruf zu Stored Procedure CallableStatement stmt = con.prepareCall (“{call SP_KUNDEN}”); ResultSet result= stmt.executeQuery( ); SQLJ Embedded SQL für Java Wurde von verschiedenen DB-Herstellern standardisiert Kein API wie JDBC, sondern Spracherweiterung SQLJ Implementierungen basieren allerdings auf JDBC Übersichtlicher als JDBC, aber auch weniger flexibel da statische Einbettung SQLJ vs. JDBC: INSERT Statement // SQLJ // JDBC int n; int n; #sql {INSERT INTO Gehalt VALUES(:n)}; Statement stmt = conn.prepareStatement(“INS ERT INTO Gehalt VALUES(?)”); stmt.setInt(1,n); stmt.execute(); stmt.close(); Abbildung einer Vererbungshierarchie auf das relationale Modell Abbildung einer Vererbungshierarchie auf das relationale Modell (Forts.) 1. Eigene Tabelle für jede Subklasse und für die Basisklasse • • Allerdings nur Spalten für die Felder, die in der Subklasse hinzukommen Wieso ist diese Variante ineffizient? Viele Joins nötig, um eine Instanz zu erstellen (z.B. 4 Vererbungen 4 Joins) 2. Eigene Tabelle für jede Subklasse • • Tabellen der Subklasse enthalten auch sämtliche Spalten der Basisklasse Wann kann man sich die erste Tabelle sparen? Wenn die Basisklasse abstrakt ist. Abbildung einer Vererbungshierarchie auf das relationale Modell (Forts.) 3.Alle Klassen (Basisklasse + Subklassen) in eine Tabelle • • • aufgeblähte Tabelle mit vielen Null-Werten Diskriminator-Attribut, damit man die Klasse unterscheiden kann Unterscheidung nur aufgrund der NullWerte ist problematisch O/R Mapping mit Hibernate Quellen Hibernate Docu: www.hibernate.org Hibernate und die Java Persistence API (Robert Hien und Markus Kehle, entwickler.press, 2007) Hibernate in Action (Christian Bauer und Gavin King, Manning 2004) Studienbrief DBPR2 Was ist Hibernate? weit-verbreiteter O/R Mapper (www.hibernate.org) unterstützt eine Vielzahl von DB, inklusive Oracle, MS SQL Server, IBM DB2, PostgreSQL, MySQL open source Versionen für Java und .NET (NHibernate) verfügbar Lebenszyklus einer Hibernate-Entity Zustände einer Hibernate-Entity Transient Jedes Entity-Objekt, das mit new erzeugt wird, befindet sich im Zustand transient. Zustand der Entity wird nicht persistent gespeichert Entity verhält sich wie normales Objekt Keine Repräsentation in der DB Daten gehen verloren, sobald es der Garbage Collector vernichtet Entities sind im Zustand transient nicht Teil einer Transaktion, d.h. es gibt auch kein Rollback Zustände einer Hibernate-Entity Persistent Sobald eine Entity einer Session zugeordnet ist Noch nicht notwendigerweise in der DB repräsentiert, da Hibernate nicht sofort nach jedem save() runterspeichert. Alle Entities in diesem Zustand sind Teil einer Transaktion Änderung von Attributen einer persistenten Entity wird von Hibernate erkannt und UPDATE in der DB durchgeführt. Durch session.delete() werden die Daten der Entity aus der DB gelöscht. Zustände einer Hibernate-Entity Detached Sobald eine Session mit session.close() geschlossen wird, werden Änderungen mit der DB nicht mehr synchronisiert. Die wichtigsten Methoden session.save(): session.load(): session.refresh(): session.flush(): session.update(): session.delete(): Persistierung transienter Objekte Erneutes Laden von Objekten, Klasse und Primärschlüssel nötig Erneutes Laden aller in der Session befindlichen Objekte, z.B. nötig wenn sich Attribute durch Trigger geändert haben Persistiert alle Änderungen von Objekten, die sich in der Session befinden Erneutes Anbinden eines „detached“ Objektes an eine neue Session Löschen eines persistenten Objektes. Wird aus DB und Session entfernt, kann aber natürlich in der Anwendung weiter existieren. Mapping in Hibernate Metadata Annotation des Quellcodes 1. • Z.B. mit Java Annotations oder XDoclet Markup Explizit mit einer Mapping-Datei (xml) 2. • • • Enthält Infos über Verknüpfung von Objekten mit Tabellen Tools helfen bei der Erstellung des Grundgerüsts Erlaubt Hibernate SQL Schemata und Quellcode zu generieren Nur Spezifikation des Quellcodes 3. • • Hibernate versucht mit Java Reflection und Heuristiken ein passendes Mapping zu erstellen Für einfache Mappings und zur Erstellung des MappingGrundgerüsts Beispiel Annotation @Entity(access=AccessType.FIELD) public class Customer implements Serializable { @Id; //kundennummer ist der Primärschlüssel von Customer Long kundennummer; String firstName; ... @Transient Integer age; ... } //age soll nicht persistiert werden Bsp. Mapping in XML File Teil der Mapping-Datei Klasse Cat wird auf Tabelle cats gemappt. id ist Primärschlüssel birthdate wird persistiert Datentype date not null darf nicht upgedated werden Mapping-Datei Fortsetzung Unidirektionale Assoziationsabbildung Unidirektionale Assoziationsabbildung Unterschied zu vorher: unique Parameter Unidirektionale Assoziationsabbildung 33 Unidirektionale Assoziationsabbildung Bidirektionale Beziehungen Wie kann man ein Objekt aus der DB instanzieren? Session.load() Dazu muss man den Primary Key kennen Was macht man, wenn man ihn nicht kennt? Query Language Query Languages von Hibernate 1. Hibernate Query Language (HQL) Syntaktisch ähnlich zu SQL Aber voll objekt-orientiert, d.h. Java-Klassen können in der Abfrage verwendet werden Objekte werden als Ergebnis geliefert Bsp.: select c from eg.Cat as c where c.sex=„F‟ Was liefert „from java.lang.object o“ Liefert alle persistenten Objekte aus der DB (da alles von Object abgeleitet wird) 2. Native SQL Query Languages von Hibernate 3. Criteria Queries • Zusammenbauen einer Abfrage • Einzelne Bedingungen, Joins, Sortierungen werden durch Aufruf von Methoden zum Criteria Object hinzugefügt. Was macht dieses Statement? Selektiert alle Katzen, deren Name mit F beginnt und sortiert sie aufsteigend nach Name und absteigend nach dem Alter. Die maximale Anzahl an Datensätzen wird auf 50 beschränkt.