Universität Augsburg, Institut für Informatik Prof. Dr. W. Kießling Dr. A. Huhn, F. Wenzel, M. Endres WS 2009/2010 18. Dez. 2009 Lösungsblatt 8 Datenbanksysteme I Aufgabe 1: JDBC 2.0 und Scrollable Result Sets Lösungen unter Berücksichtigung bereits implementierter Methoden aus Übungsblatt 7: a) Methodenaufruf zur Erstellung der new product Relation: executeStatement("CREATE TABLE new_product AS SELECT * FROM s_product"); b) Methode, die ein Scrollable Result Set zurückliefert, das wenn möglich updatebar ist: private ResultSet executeJDBC2Statement(String statement){ try { Statement stmt = this.openConnection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); return stmt.executeQuery(statement); } catch (SQLException e) { e.printStackTrace(); return null; } } Methodenaufruf und Bearbeitung des Result Sets um die unverbindliche Preisempfehlung zu erhöhen: ResultSet rs = executeJDBC2Statement("SELECT id, name, suggested_whlsl_price FROM new_product"); try { rs.last(); float price = rs.getFloat("suggested_whlsl_price"); rs.updateFloat("suggested_whlsl_price", price+10); rs.updateRow(); rs.close(); } catch (SQLException e) { e.printStackTrace(); } System.out.println("Update Result: "); executeQuery("SELECT * FROM new_product"); 1 c) Einfügen eines neuen Produkteintrags mittels Cursor: rs = executeJDBC2Statement("SELECT id, name, short_Desc, suggested_whlsl_price, whlsl_units FROM new_product"); try { rs.last(); int id = rs.getInt("id"); rs.moveToInsertRow(); rs.updateString("id", new Integer(id+1).toString()); rs.updateString("name", "SpiderPig Protection Gear"); rs.updateString("short_Desc", "keeps your pig save"); rs.updateFloat("suggested_whlsl_price", (float) 11.99); rs.updateString("whlsl_units", "100"); rs.insertRow(); rs.close(); } catch (SQLException e) { e.printStackTrace(); } System.out.println("Insert Result: "); executeQuery("SELECT * FROM new_product"); d) Löschen des vierten Tupels aus der Relation: rs = executeJDBC2Statement("SELECT id, name, short_Desc, suggested_whlsl_price, whlsl_units FROM new_product"); try { rs.absolute(4); rs.deleteRow(); rs.close(); } catch (SQLException e) { e.printStackTrace(); } System.out.println("Delete Result: "); executeQuery("SELECT * FROM new_product"); e) Result Set, das alle Produktnamen und Ids enthält, die bereits in einer Bestellung vorkamen: rs = executeJDBC2Statement("SELECT p.id, p.name FROM s_product p, s_item i WHERE p.id = i.product_id"); try { System.out.println("Scrollable Result Set with Join updatable? " + (rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE)); rs.close(); } catch (SQLException e) { e.printStackTrace(); } Das Result Set ist nicht updatebar, da es sich nicht nur auf eine Relation bezieht, sondern einen Join enthält. Ein Update könnte somit nicht eindeutig den Basisrelationen zugeordnet werden. Folgende Erklärung bestätigt die Beobachtung: In some situations, a driver may need to choose an alternate result set type or concurrency type at statement execution time. For example, a SELECT statement that contains a join over multiple tables might produce a result set that is not updatable.[. . .] The driver will then choose an appropriate result set type and/or concurrency type [. . .]. Aus: http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/resultset.html 2 f) Intuitive Definition eines Result Sets, das alle Tupel der Relation enthält: rs = executeJDBC2Statement("SELECT * FROM new_product"); try { System.out.println("Result Set with SELECT * updatable? "+ (rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE)); rs.close(); } catch (SQLException e) { e.printStackTrace(); } Dieses Result Set ist nicht updatebar, wie dem Test auf rs.getConcurrency() und der angegebenen Oracle JDBC Dokumentation zu entnehmen ist. Als Workaround gibt die Dokumentation folgende Lösung an: rs = executeJDBC2Statement("SELECT p.* FROM new_product p"); try { System.out.println("Workaround SELECT p.* updatable? " + (rs.getConcurrency()==ResultSet.CONCUR_UPDATABLE)); rs.close(); } catch (SQLException e) { e.printStackTrace(); } Aufgabe 2: Erstellen einer Multimedia-Datenbank Grundlage sind wiederum bereits implementierte Methoden aus Übunsblatt 7 (siehe Lösung 7): a) siehe Methode private boolean establishConnection() b) Methodenaufruf der bereits implementierten Methode executeStatement mit entsprechender DDL: executeStatement("create table Papers(" + "id integer primary key," + "title varchar(100)," + "pdf blob)"); c) Neue Methode, die Batch Updates ausführt: private void insertValues() throws SQLException { try{ //set auto-commit to false for batch updates this.openConnection.setAutoCommit(false); Statement stmt = this.openConnection.createStatement(); // add inserts to batch // initialize blobs as EMPTY_BLOB() stmt.addBatch("insert into Papers values(1,’ECTS-GRUNDSÄTZE’ , EMPTY_BLOB())"); stmt.addBatch("insert into Papers values(2,’PRINCIPALES CARACTERISTIQUS DE L´ECTS’ , EMPTY_BLOB())"); stmt.addBatch("insert into Papers values(3,’CARACTERÍSTICAS PRINCIPALES DE LOS ECTS’ , EMPTY_BLOB())"); // execute batch and commit stmt.executeBatch(); this.openConnection.commit(); stmt.close(); } 3 catch (BatchUpdateException e) { // in case of batch update error -> rollback this.openConnection.rollback(); throw e; } catch (SQLException e) { // in case of SQL error -> rollback this.openConnection.rollback(); throw e; } finally { // set auto-commit to true and close statement this.openConnection.setAutoCommit(true); } } Zur Ausführung der Batch-Updates wird Auto-Commit deaktiviert. Anschließend werden die Werte eingefügt und eventuell auftretende Fehler mit einem Rollback rückgängig gemacht. d) Erstellen der Stored Procedure wie in Aufgabenblatt 7 via Methodenaufruf von executeStatement(): String procedure = new String("create procedure INSERT_PDF(idi integer, pdfi blob) as begin " + "update Papers set pdf = pdfi " + "where id = idi; end INSERT_PDF;"); executeStatement(procedure); e) Methode zum Einfügen der Pdfs: private void insertData(Integer id, String pdf) throws Exception { try { File pdfFile= new File(pdf); FileInputStream pdfFis = new FileInputStream(pdfFile); // prepare call for the stored procedure CallableStatement cstmt = this.openConnection.prepareCall("{CALL INSERT_PDF(?,?)}"); cstmt.setInt(1,id); // setting the id cstmt.setBinaryStream(2, pdfFis, (int)pdfFile.length()); //setting the PDF // execute cstmt.executeUpdate(); // closing file input stream pdfFis.close(); cstmt.close(); } catch(Exception e){ throw e; } } Die Methode ist mit den entsprechenden Pfadangaben als Parameter pdf aufzurufen. Sind die Dateien direkt im Eclipse Projekt eingebunden, so lautet der entsprechende Methodenaufruf: insertData(1, "key_de.pdf"); insertData(2, "key_fr.pdf"); insertData(3, "key_es.pdf"); 4 Ob das Einfügen der Blobs erfolgreich war, kann mittels SQL-Developer mit folgendem Statement überprüft werden, welches die Dateigrößen der Blobs anzeigt: SELECT ID, DBMS_LOB.GETLENGTH(PDF) FROM Papers; 5