Java Database Connectivity-API (JDBC) Motivation Design Grundlagen Typen Metadaten Transaktionen JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 1 Motivation Problem: Zugriff auf ein DBMS ist Herstellerabhängig Anwendung Anwendung Anwendung MySQL API DB2 API Oracle API MySQL DB2 Oracle JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 2 Motivation Lösung: Zwischenschicht Anwendung JDBC API JDBC MySQL API DB2 API Oracle API MySQL DB2 Oracle JDBC Treiber JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 3 Design Entwicklung seit: 1995 Erster Ansatz: Java erweitern Zweiter Anzatz: Treiber der Datenbankhersteller Vorbild: ODBC Unterschiede: • • • • ODBC wenige Befehle, viele Optionen JDBC viele einfache Methoden ODBC nutzt void-Zeiger Java kennt keine Zeiger Flexibilität: JDBC erlaubt beliebige Zeichenfolgen • Anpassung an Datenbank möglich. - Optimierung Bindung JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 4 Design Java Anwendung JDBC-Treibermanager JDBC/ODBC -Brücke JDBCTreiber ODBCTreiber Datenbank Datenbank JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 5 Treiber Typ 1: JDBC/ODBC-Brücke • • • • • ODBC ist sehr weit verbreitet Leistung Wartung Testen, Experimentieren kein JDBC Treiber verfügbar Windows Plattformen JDBCAnwendung ODBC Brücke • DB Client Server Typ 2: Partial Java Driver • • • • konvertiert JDBC Aufruf in DB abhängigen API Aufruf schnell, weil API Aufruf kompiliert ist A DB + OS abhängig JDBCP Nutzer braucht plattformabhängige API Anw. I Client DB Server JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 6 Treiber Typ 3: Reiner Java Treiber zu Middleware • • • • Keine plattformabhängigen Treiber am Client DB unabhängig JDBCFlexibel, mehrere DB möglich Anw. DB abhängiger Code in Middleware Client M W DB Server Server Typ 4: Reiner Java Treiber direkt zur DB • • • • JDBC in DB spezifische Netzwerkaufrufe verpackt Schnell Keine plattformabhängigen Treiber am Client Client braucht für verschiedene DB verschiedene Treiber JDBCAnw. DB Client Server JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 7 Treiberinstallation Download • • http://servlet.java.sun.com/products/jdbc/drivers Datenbankhersteller Installation • Eintragen in den Klassenpfad Registrieren • Bei dem Programmstart durch Parameter: - • Setzen der Systemeigenschaft "jdbc.drivers": - • java –Djdbc.drivers=com.mysql.jdbc.Driver <Programm> System.setProperty("jdbc.drivers", "com.mysql.jdbc.Driver"); Händisches Instanzieren der Treiber-Klasse: - Class.forName("com.mysql.jdbc.Driver"); JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 8 Verbindungsaufbau Verbindungsaufbau erfolgt mit • • Url zur Datenbank Benutername, Passwort Datenbank Url • • jdbc:<Datenbanktreiber>:<treiberspezifische Angaben> MySql: - • jdbc:mysql://<host>:<port>/< Datenbankname> HSQLDB: - jdbc:hsqldb:<Dateipfad> JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 9 Grundlagen: DriverManager DriverManager • • Verwaltet registrierte Treiber Aufbau von Verbindungen getConnection(String url, String user, String password) - Liefert eine Connection zu der gegebenen url wenn ein passender Treiber registriert ist. /* Registrieren des Treibers */ Class.forName("com.mysql.jdbc.Driver").newInstance(); /* Anfordern einer Datenbankverbindung */ Connection con = DriverManager.getConnection(url, user, pass); JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 10 Grundlagen: Connection Connection: Verwaltet die Verbindung (Session) zu einer Datenbank. • close() - • commit() - • Schließen der Verbindung Bestätigen alle bisher vorgenommenen Änderungen, standard ist auto-commit Statement createStatement() - Erzeugt ein Statement mit dem SQL-Statements an die Datenbank abgegeben werden können. Statement stat = con.createStatement(); stat.executeUpdate("INSERT INTO test VALUES ('Hallo')"); • PreparedStatement prepareStatement(String sql) - Erzeugt Statements welche von der Datenbank vorkompiliert werden können. PreparedStatement stat; stat = con.prepareStatement("INSERT INTO test VALUES ('Welt')"); ... stat.executeUpdate(); JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 11 Grundlagen: Statement Statement: • ResultSet executeQuery(String sql) - • int executeUpdate(String sql) - • - Ausführen einer beliebigen SQL Anweisung. Rückgabewert Zeigt an ob eine Ergebnismenge geliefert wurde. int getUpdateCount() - • Ausführen eines Updates (UPDATE, INSERT, DELETE, CREATE). Rückgabewert zeigt die Anzahl der betroffenen Zeilen. boolean execute(String sql) - • Ausführen einer SQL Abfrage (SELECT) Anzahl der von der letzten Anweisung betroffenen Zeilen oder -1 wenn die Anweisung keinen Zähler hatte. ResultSet getResultSet() - Ergebnismenge der letzten Abfrage oder null wenn wenn die Anweisung keine Ergebnismenge hatte. JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 12 Grundlagen: PreparedStatement PreparedStatement: Absetzen von vorkompilierten Statements. • void set<Typ>(int n, <Typ> x) - • void clearParameters() - • Löschen aller Parameterwerte. ResultSet executeQuery() - • Setzen des Parameters an der Stelle n (1 .. m). Ausführen der vorkompilierten SQL Abfrage (SELECT) int executeUpdate() - Ausführen des vorkompilierten Updates (UPDATE, INSERT, DELETE, CREATE). Rückgabewert zeigt die Anzahl der betroffenen Zeilen. PreparedStatement stat; stat = con.prepareStatement("INSERT INTO test VALUES (?)"); stat.setString(1, "Hallo"); stat.executeUpdate(); stat.setString(1, "Welt!"); stat.executeUpdate(); JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 13 Grundlagen: Callable Statement CallableStatement: Ausfüren von Datenbankprozeduren (SQL stored procedures) über spezielle SQL strings: • • • • Parameterlose Prozedur: {call procedure_name} Prozedur: {call procedure_name[(?, ?, ...)]} Funktion: {? = call procedure_name[(?, ?, ...)]} Das setzen der Parameter erfolgt analog zu den PreparedStatements JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 14 Grundlagen: ResultSet ResultSet: Ermöglicht das zeilenweise Abarbeiten der Ergebnistabelle. • boolean next() - • • • Anspringen der nächsten Zeile, begonnen wird vor der ersten Zeile. true solange noch eine gültige Zeile erreicht wurde. <Typ> get<Typ>(int spalte) <Typ> get<Typ>(String spaltenName) int findColumn(String spaltenName) getString(3) => 25 getString("Name") => Max findColumn("Nr") => 1 next() Nr 1 2 ... Name Max Kurt ... Age 25 27 ... JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 15 Grundlagen: ResultSet Fortsetzung • boolean first() - • beforeFirst() - • - Letzte Zeile im ResultSet. true wenn eine gültige Zeile erreicht wurde. afterLast() - • Vor die erste Zeile im ResultSet. boolean last() - • Erste Zeile im ResultSet. true wenn eine gültige Zeile erreicht wurde. Nach letzter Zeile im ResultSet. boolean absolute(int row) - Eine Zeile anspringen - • row > 0 ... von oben gezählt (1 erste Zeile, 2 zweite Zeile, ...) row < 0 ... von unten gezählt (-1 letzte Zeile, -2 vorletzte Zeile, ...) true wenn eine gültige Zeile erreicht wurde. int getRow() - Nummer der aktuellen Zeile JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 16 Grundlagen: Beispiel ResultSet Wie viele Zeilen hat ein ResultSet? Connection con; ... Statement stat = con.createStatement(); ResultSet result = stat.executeQuery("SELECT ..."); int rowAmount; result.last(); rowAmount = result.getRow(); result.beforeFirst(); // Mit ResultSet arbeiten while (result.next()) { ... } JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 17 Typen Standard-Typemapping zwischen SQL und JAVA SQL Type Java Type CHAR, VARCHAR, LONGVARCHAR String NUMERIC, DECIMAL java.math.BigDecimal BIT boolean TINYINT byte SMALLINT short INTEGER int BIGINT long REAL float FLOAT, DOUBLE double BINARY, VARBINARY, LONGVARBINARY byte[] DATE java.sql.Date TIME java.sql.Time TIMESTAMP java.sql.Timestamp JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 18 Metadaten Beschreibung der Struktur der Datenbank und deren Tabellen DatabaseMetaData: • • • <Connection>.getMetaData() ResultSet getTables(String catalog, String schema, String table, String[] types) ResultSet getColumns(String catalog, String schema, String table, String column) - catalog: Name des Katalogs - scheme: Schemaname - null nicht berücksichtigen column: Spaltenname - "" Tabellen ohne Schema, null nicht berücksichtigen table: Tabellenname - "" Tabellen ohne Katalog, null Katalognamen nicht berücksichtigen null nicht berücksichtigen types: Typische namen: "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM" JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 19 Metadaten ResultSetMetaData: • • - • • • • • Nr 1 2 ... <ResultSet>.getMetaData() int getColumnCount() Anzahl der Spalten im ResultSet. Name Title ... Max Mag. ... Kurt DI ... ... ... Age 25 27 ... String getColumnName(int column) int getColumnType(int column) String getColumnTypeName(int column) SELECT Nr, Name, Age String getTableName(int column) FORM users; ... getColumnCount() => 3 getColumnName(1) => "Nr" getColumnType(3) => 4 getColumnTypeName(3) => "int" getTableName(2) => "users" Nr 1 2 ... Name Age Max 25 Kurt 27 ... ... JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 20 Transaktionen Auto Commit: Jede Anweisung ist eine abgeschlossene Transaktion. • Abfragen: - • <Connection>.getAutoCommit() Setzen: - <Connection>.setAutoCommit(bool) Abschliessen einer Transaktion: • <Connection>.commit() Rücksetzen im Fehlerfall (z.B.: SQLException): • <Connection>.rollback() Connection con; ... try { con.setAutoCommit(false); Statement stat = con.createStatement(); stat.executeUpdate("INSERT ..."); stat.executeUpdate("INSERT ..."); stat.executeUpdate("UPDATE ..."); con.commit(); } catch (SQLException e) { con.rollback(); } JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 21 Zusammenfassung Datenbankunabhängigkeit • • Zwischenschicht Treiberschnittstelle (mind. SQL 92) - 4 Treiberarten: • JDBC -> ODBC Teilweise Java Nur Java zu einer Middleware Nur Java zur Datenbank Einfachere Programmentwicklung Beliebige SQL-Kommandos absetzbar • Optimierung / Datenbankabhängigkeit Arten von Statements • java.sql.Statement - • java.sql.PreparedStatement - • Statisch oder vom Benutzer frei wählbar (Achtung: SQL injection) Vorbereitete Statements (Sicher gegen SQL injection, schnell) java.sql.CallableStatement - Ausführen von SQL stored procedures JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer 22