Java Database Connectivity API / JDBC Motivation Design Treiber Grundlagen Metadaten Transaktionen Exceptions Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 1 Motivation • Problem: Zugriff auf DBMS nicht einheitlich Anwendung Anwendung Anwendung MySQL API DB2 API Oracle API Praktikum aus Softwareentwicklung 2 © Markus Löberbauer MySQL DB2 Oracle 2 Motivation • Lösung: Zwischenschicht MySQL API Anwendung JDBC API J D B C DB2 API Oracle API Praktikum aus Softwareentwicklung 2 © Markus Löberbauer MySQL DB2 Oracle 3 Design • • • • • Entwicklung seit: 1995 Erster Ansatz: Java erweitern Zweiter Ansatz: 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 ✖ Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 4 Design Java Anwendung JDBC-Treibermanger JDBC/ODBC-Brücke JDBC-Treiber ODBC-Treiber Datenbank Datenbank Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 5 Treiber, Typ 1 und 2 • Typ 1: Brücke – zB: JDBC/ODBC • ODBC ist verbreitet ✔ • Testen, Experimentieren • Windows Plattformen – Leistung ✖ – Wartung ✖ – Wenn kein JDBC-Treiber verfügbar ist • B r ü c k e JDBCAnwendung ODBC ODBCTreiber DB Client Server Typ 2: Partial Java Driver – Gibt Aufruf an native API weiter – DB und OS abhängig ✖ – Nutzer braucht plattformabhängige API ✖ JDBCAnwendung A P I DB Client Praktikum aus Softwareentwicklung 2 © Markus Löberbauer Server 6 Treiber, Typ 3 und 4 • Typ 3: Reiner Java Treiber zu Middleware – – – – • Portabel ✔ DB unabhängig ✔ Flexibel, mehrere DB möglich ✔ DB abhängiger Teil in Middleware Typ 4: Reiner Java Treiber zur DB JDBCAnwendung M W DB Client Server Server – Portabel ✔ – Schnell ✔ JDBC– Verbindung Anwendung • Netz • File I/O • Embedded Client – Client braucht DB abhängige Treiber ✖ Praktikum aus Softwareentwicklung 2 © Markus Löberbauer DB Server 7 Treiber, Installation • Download – http://developers.sun.com/product/jdbc/drivers – Datenbankhersteller • Installation – Eintragen in den Klassenpfad • Laden eines JDBC Treibers – System Property: jdbc.drivers • java-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriverXyz • System.setProperty("jdbc.drivers", "org.apache.derby.jdbc.EmbeddedDriver"); – Manuelles laden der Treiberklasse • Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); – Automatisch durch DriverManager • Java Service: java.sql.Driver Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 8 Verbindungsaufbau • DriverManager – Verwaltet registrierte Treiber, baut Verbindungen auf • ConnectiongetConnection(Stringurl, String user, String password) • Datenbank URL – Aufbau: jdbc:<subprotrokoll>:<subname> • jdbc:<Datenbanktreiber>:<treiberspezifische Angaben> – Derby • jdbc:derby:/path/to/Database • jdbc:derby:Database – MySQL • jdbc:mysql://<host>:<port>/<Database> Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 9 Verbindung • Connection: Verwaltet Verbindung (Session) zur DB – close() • Schließen der Verbindung – commit() • Bestätigen vorangegangener Änderungen, Standard ist auto-commit – StatementcreateStatement() Statement stat = con.createStatement(); stat.executeUpdate("INSERT INTO test VALUES ('Hallo')"); – PreparedStatementprepareStatement(Stringsql) PreparedStatementstat = con .prepareStatement("INSERT INTO test VALUES ('Hallo')"); ... stat.executeUpdate(); Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 10 Statement • ResultSetexecuteQuery(Stringsql) Keine Parameter! SQL-Injection – Ausführen einer SQL-Abfrage (SELECT) • intexecuteUpdate(Stringsql) – Ausführen eines Updates (UPDATE, INSERT, DELETE) – Liefert die Anzahl der betroffenen Zeilen • booleanexecute(Stringsql) – Ausführen beliebiger SQL-Anweisungen – Liefert truewenn eine Ergebnismenge geliefert wurde • intgetUpdateCount() – Anzahl der betroffenen Zeilen, oder -1 wenn Anweisung keinen Zähler hatte • ResultSetgetResultSet() – Ergebnismenge der letzten Abfrage, oder null wenn keine Ergebnismenge geliefert wurde • voidclose() – Gibt alle JDBC Ressourcen frei Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 11 PreparedStatement • voidset<Typ>(int n, <Typ> x) – Setzen des Parameters an der Stelle n (1..m) • voidclearParameters() – Löschen aller Parameterwerte • ResultSetexecuteQuery() – Ausführen der vorübersetzten SQL-Anfrage (SELECT) • intexecuteUpdate() – Ausführen des vorübersetzten Updates (UPDATE, INSERT, DELETE) – Gibt die Anzahl betroffener Zeilen zurück PreparedStatementstat; stat = con.prepareStatement("INSERT INTO test VALUES (?,?)"); stat.setString(1, "Hallo"); stat.setString(2, "Welt"); stat.executeUpdate(); stat.setString(2, "Jane"); stat.executeUpdate(); Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 12 CallableStatement • • Ausführen von DB-Prozeduren (SQL storedprocedures) Parameterlose Prozedur – {callprocedure_name } • Prozedur – {callprocedure_name(?,?,...)} • Funktion – { ? = callprocedure_name(?,?,...) } • • Eingangsparameter analog zu PreparedStatement Ausgabeparameter müssen registriert werden – voidregisterOutParameter(intindex, intsqlType) CallableStatementcs = con .prepareCall("{ CALL GET_NUMBER_FOR_NAME(?, ?) }"); cs.registerOutParameter(2, java.sql.Types.INTEGER); cs.setString(1, "Duke"); cs.execute(); intnumber = cs.getInt(2); Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 13 ResultSet • Zeilenweise abarbeiten der Ergebnistabelle – booleannext() • Anspringen der nächsten Zeile • true solange gültige Zeile erreicht wird • ResultSet beginnt vor der ersten Zeile – <Typ>get<Typ>(int spalte) – <Typ>get<Typ>(StringspaltenName) – booleanwasNull() • true wenn letzter Wert SQL-NULL war – intfindColumn(StringspaltenName) Nr Name Age getInt(3) => 25 wasNull() =>false getString("Name") => Max findColumn("Nr") => 1 1 Max 25 2 Kurt 27 ... ... ... next() Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 14 ResultSet Fortsetzung • booleanfirst() – Erste Zeile im ResultSet – true wenn gültige Zeile erreicht • voidbeforeFirst() – Vor die erste Zeile im ResultSet • boolean last() – Letzte Zeile im ResultSet – true wenn gültige Zeile erreicht • voidafterLast() – Nach letzter Zeile im ResultSet • booleanabsolute(introw) – Eine Zeile anspringen • row> 0 ... von oben (1 erste Zeile, 2 zweite Zeile, ...) • row< 0 ... von unten (-1 letzte Zeile, -2 vorletzte Zeile, ...) – true wenn gültige Zeile erreicht • intgetRow() – Nummer der aktuellen Zeile Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 15 Typen SQL-Typ Java-Typ 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 ... siehe JSR-221, Appendix B, Date Type ConversionTables Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 16 Metadaten, Datenbank • Erstellen über Verbindung – DatabaseMetaData<Connection>.getMetaData() • Allgemeines – – – – • ConnectiongetConnection() String getURL() String getUserName() booleanisReadOnly() Eigenschaften – boolean supportsANSI92EntryLevelSQL() – booleansupportsTransactions() – booleansupportsGroupBy() • Beschränkungen – intgetMaxStatementLength() – intgetMaxStatements() – intgetMaxConnections() 0 .. kein Limit oder unbekannt Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 17 Metadaten, Tabellen • • ResultSetgetTables(Stringcatalog, String schema, String table, String[] types) ResultSetgetColumns(Stringcatalog, String schema, String table, String column) – catalog: Name des Katalogs • "" Tabellen ohne Katalog, null Katalognamen nicht berücksichtigen – scheme: Schemaname • "" Tabellen ohne Schema, null nicht berücksichtigen – table: Tabellenname • null nicht berücksichtigen – column: Spaltenname • null nicht berücksichtigen – types: • zB: "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM" • null nicht berücksichtigen Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 18 Metadaten, Ergebnismenge • Erstellen – ResultSetMetaData<ResultSet>.getMetaData() • Eigenschaften – – – – – – Nr Name Title ... Age intgetColumnCount() 1 String getColumnName(int column) 2 intgetColumnType(int column) ... String getColumnTypeName(int column) String getTableName(int column) booleanisCurrency(intcolumn) getColumnCount() => 3 getColumnName(1) => "Nr" getColumnType(3) => 4 // INTEGER getColumnTypeName(3) => "int" getTableName(2) => "users" Praktikum aus Softwareentwicklung 2 © Markus Löberbauer Max DI ... 30 Susi Mag ... 28 ... ... ... ... SELECT Nr, Name, Age FROM users; Nr Name Age 1 Max 30 2 Susi 28 ... ... ... 19 Transaktionen • Standard: auto-commit, jedes Statement eine Transaktion – boolean<Connection>.getAutoCommit() – void<Connection>.setAutoCommit(booleanac) • • Transaktion startet automatisch Transaktion abschießen Auto-Commit – void<Connection>.commit() • Transaktion rücksetzen (zB: SQLException) – void<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(); } Praktikum aus Softwareentwicklung 2 © Markus Löberbauer Savepoints TransactionIsolation NONE READ_UNCOMMITTED READ_COMMITTED REPEATABLE_READ SERIALIZABLE 20 Exceptions SQLException SQLNonTransient Exception Erneuter Versuch wird fehlschlagen SQLTransient Exception Erneuter Versuch kann durchgehen SQLRecoverable Exception Erneuter Versuch nach Eingriffen kann durchgehen Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 21 JavaDB, Derby • Seit Java 6.0 mitgeliefert • Web-Seite: http://db.apache.org/derby • Umgebungsvariablen – JAVA_HOME=Pfad zur Java JDK Installation – DERBY_HOME=Pfad zur Derby Installation – PATH um DERBY_HOME/bin erweitern • JARs in DERBY_HOME/lib – – – – – derby.jar, Engine, genügt für embedded DB derbynet.jar, Netzzugriff, Serverseitig derbyclient.jar, Netzzugriff, Clientseitig derbytools.jar, Verwaltungs-Werkzeuge derbyrun.jar verweist auf: • derby.jarderbyclient.jarderbytools.jarderbynet.jar Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 22 JavaDB, Derby, Kommandozeilen-Werkzeuge • Systeminformation: sysinfo Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 23 JavaDB, Derby, Kommandozeilen-Werkzeuge • Kommandozeilenwerkzeug: ij Verbinden zu (und erzeugen einer) Datenbank Tabelle erzeugen Beschreibung einer Tabelle Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 24 JavaDB, Derby, Kommandozeilen-Werkzeuge • Kommandozeilenwerkzeug: ij Abfragen von Datensätzen Aktualisieren eines Datensatzes Löschen einer Tabelle Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 25 Zusammenfassung • Datenbankunabhängigkeit – Zwischenschicht -> einfachere Programmentwicklung – Treiberschnittstelle (min. SQL 92 entrylevel) • 4 Treiberarten – Brücke (zB: JDBC -> ODBC) – Teilweise Java – Java zu Middleware – Java zu Datenbank • Beliebige SQL-Kommandos absetzbar – Optimierung vs. Datenbankunabhängigkeit • Statements – PreparedStatement • Vorkompiliert, mit Parametern (Sicher gegen SQL-Injection) – Statement • Statisch, Abfragen vom Benutzer, keine Parameter – CallableStatement • StoredProcedures, mit IN, OUT und INOUT Parametern Praktikum aus Softwareentwicklung 2 © Markus Löberbauer 26