380 likes | 531 Views
COMP 321. Week 6. Overview. IBM DeveloperWorks Tutorials: Advanced Database Operations with JDBC Managing Database Connections with JDBC. Advanced Database Operations with JDBC. Using a DataSource Prepared Statements Stored Procedures Advanced Datatypes.
E N D
COMP 321 Week 6
Overview • IBM DeveloperWorks Tutorials: • Advanced Database Operations with JDBC • Managing Database Connections with JDBC
Advanced Database Operations with JDBC • Using a DataSource • Prepared Statements • Stored Procedures • Advanced Datatypes
Advanced Database Operations with JDBC • Simple message board application • Users post messages • Main page shows a digest of many messages • Opening a message shows message details
DeveloperWorks Tutorial Database design
Creating a DataSource • DataSource Interface • Database Independent • Database-specific implementations provided by DB vendors • Typically used with JNDI to allow implementation to be changed easily • Typically set up with an admin interface, but we’ll look at manual configuration
JNDI – a Quick Primer • A Java API that encapsulates the concept of naming and directory servers in much the same manner that JDBC encapsulates the concept of communicating with the database. • Other naming service examples: file system manager, the Web, Domain Name System (DNS)…
Typical Uses of JNDI in a J2EE Web App • Create a name and bind it to a Java object • Look up a name to retrieve a Java object • Delete a name • Rebind a name to a new Java object
Registering a Data Source Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); try { // Create the initial context Context ctx = new InitialContext(env); // Here we create the actual DataSource and then set the relevant // parameters. TdsDataSource ds = new TdsDataSource(); ds.setServerName(serverName); ds.setPortNumber(portNumber); ds.setDatabaseName(databaseName); ds.setUser(login); ds.setPassword(password); ds.setDescription("JDBC DataSource Connection"); // Now we bind the DataSource object to the name we selected earlier. ctx.bind(filePath, ds); ctx.close(); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); }
Registering a DataSource – Example w/DB2 String fsName = "jdbc/pjtutorial/db2"; // Usually not DB-specific try { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); Context ctx = new InitialContext(env); ctx.unbind(fsName); DB2DataSource ds = newDB2DataSource(); ds.setDescription("DB2 DataSource"); ds.setServerName("persistentjava.com"); ds.setPortNumber(6789); ds.setDatabaseName("jdbc"); ctx.bind(fsName, ds); ctx.close(); } catch (Exception e) { e.printStackTrace(); } * A GUI configuration tool would allow you to set the standard DataSource properties, and use reflection to discover any vendor-specific properties. In this example, they're all hard-coded.
Creating the Schema String dTableSQL = "CREATE TABLE digest (id INTEGER NOT NULL," + " title VARCHAR(64) NOT NULL," + " author VARCHAR(64) NOT NULL)"; String mTableSQL = "CREATE TABLE messages (id INTEGER NOT NULL," + " title VARCHAR(64) NOT NULL," + " author VARCHAR(64) NOT NULL, message CLOB(2048))"; String aTableSQL = "CREATE TABLE authors " + "(author VARCHAR(64) NOT NULL," + " photo BLOB(4096))";
Creating the Schema String fsName = "jdbc/pjtutorial/db2"; Connection con = null; try { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); Context ctx = new InitialContext(env); DataSource ds = (DataSource) ctx.lookup(fsName); con = ds.getConnection("java", "sun"); Statement stmt = con.createStatement(); stmt.executeUpdate(dTableSQL); stmt.executeUpdate(mTableSQL); stmt.executeUpdate(aTableSQL); System.out.println("Tables Created Successfully"); ...
Error Handling } catch (SQLException ex) { System.out.println("\nERROR:----- SQLException -----\n"); while (ex != null) { // Log ex details ex = ex.getNextException(); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (con != null) con.close(); } catch (SQLException ex) { // Log ex details } }
Dropping Tables String dDropSQL = "DROP TABLE digest"; String mDropSQL = "DROP TABLE messages"; String aDropSQL = "DROP TABLE authors"; con = ds.getConnection("java", "sun"); Statement stmt = con.createStatement(); stmt.executeUpdate(dDropSQL); stmt.executeUpdate(mDropSQL); stmt.executeUpdate(aDropSQL); System.out.println("Tables Dropped Successfully");
Populating Tables String baseInsertSQL = "Insert INTO digest VALUES("; int[] ids ={ 1, 2, 3, 4, 5 }; String[] authors ={ "java", "rjb", "java", "bill", "scott" }; String[] titles ={ "Hello", "Hello Java", "Hello Robert", "Hello from Bill", "Hello from Scott" }; // Not very efficient, but good enough for testing Connection con = ds.getConnection("java", "sun"); Statement stmt = con.createStatement(); for (int i = 0; i < ids.length; i++) { stmt.executeUpdate(baseInsertSQL + ids[i] + ", '" + titles[i] + "', '" + authors[i] + "')"); }
Viewing Table Contents String querySQL = "SELECT id, author, title FROM digest"; Connection con = ds.getConnection("java", "sun"); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(querySQL); ResultSetMetaData rsmd = rs.getMetaData(); for (int i = 1; i <= rsmd.getColumnCount(); i++) System.out.print(rsmd.getColumnName(i) + "\t"); System.out.println("\n----------------------------------------"); while (rs.next()) { System.out.print(rs.getInt(1) + "\t"); System.out.print(rs.getString(2) + "\t"); System.out.println(rs.getString(3)); }
Prepared Statements • Compiled (“prepared”) by the JDBC driver or database for faster performance • Typically accept one or more dynamic input parameters (IN parameters) • Can be executed many times without SQL parsing overhead • Eliminate SQL Injection Vulnerabilities
Prepared Insert String insertSQL = "Insert INTO digest VALUES(?, ?, ?)" ; int[] ids = {1, 2, 3, 4, 5} ; String[] authors = {"java", "rjb", "java", "bill", "scott"} ; String[] titles = { "Prepared Hello", "Prepared Hello Java", "Prepared Hello Robert", "Prepared Hello from Bill", "Prepared Hello from Scott"} ; Connection con = ds.getConnection("java", "sun") ; PreparedStatement pstmt = con.prepareStatement(insertSQL) ; for(inti = 0 ; i < ids.length ; i++){ pstmt.setInt(1, ids[i]) ; pstmt.setString(2, titles[i]) ; pstmt.setString(3, authors[i]) ; pstmt.executeUpdate() ; } ...
Prepared Query String querySQL ="SELECT id, author, title FROM digest WHERE author = ?"; Connection con = ds.getConnection("java", "sun") ; PreparedStatement pstmt = con.prepareStatement(querySQL) ; pstmt.setString(1, "rjb") ; ResultSet rs = pstmt.executeQuery() ; ResultSetMetaData rsmd = rs.getMetaData() ; for(int i = 1 ; i <= rsmd.getColumnCount() ; i++) System.out.print(rsmd.getColumnName(i) + "\t") ; System.out.println("\n----------------------------------------") ; while(rs.next()) { System.out.print(rs.getInt(1) + "\t") ; System.out.print(rs.getString(2) + "\t") ; System.out.println(rs.getString(3)) ; }
Stored Procedures • Procedures created and stored within DB • Provide improved security and performance • Can be written in SQL, and often in other languages
Stored Procedures • Three types of parameters: IN, OUT, INOUT • Syntax: • { call AuthorList} – no parameters • { call AuthorList[(?,?)]} – two IN parameters • { ? = call AuthorList[(?,?)]} – takes two IN parameters and returns one
Calling a Stored Procedure // SELECT id, author, title FROM digest WHERE author = ? Connection con = ds.getConnection("java", "sun") ; CallableStatement cstmt = con.prepareCall("{call AuthorList(?)}") ; cstmt.setString(1, "someAuthor") ; if(false== cstmt.execute()) // Handle error ResultSet rs = cstmt.getResultSet() ; ResultSetMetaData rsmd = rs.getMetaData() ; for(int i = 1 ; i <= rsmd.getColumnCount() ; i++) System.out.print(rsmd.getColumnName(i) + "\t") ; System.out.println("\n----------------------------------------") ; while(rs.next()) { System.out.print(rs.getInt(1) + "\t") ; System.out.print(rs.getString(2) + "\t") ; System.out.println(rs.getString(3)) ; } // close rs, con
Returning a Value // SELECT COUNT(*) FROM digest WHERE author = ? String callSQL = "{call CountAuthorMessage(?, ?)}" ; Connection con = ds.getConnection("java", "sun") ; CallableStatement cstmt = con.prepareCall(callSQL) ; cstmt.setString(1, "java") ; cstmt.registerOutParameter(2, java.sql.Types.INTEGER) ; cstmt.execute() ; intcount = cstmt.getInt(2) ; System.out.println(count + " messages found.") ; cstmt.close() ;
Inserting a BLOB String insertSQL = "Insert INTO authors VALUES(?, ?)" ; con = ds.getConnection("java", "sun") ; PreparedStatement pstmt = con.prepareStatement(insertSQL) ; File file = newFile("C:/images/rjb.jpg") ; FileInputStream fis = newFileInputStream(file); pstmt.setString(1, "rjb"); pstmt.setBinaryStream(2, fis, (int)file.length()); if(1 != pstmt.executeUpdate()) System.err.println("Incorrect value returned during author insert.") ; pstmt.close(); fis.close(); System.out.println("BLOB Insert Successful") ;
Selecting a BLOB String selectSQL = "SELECT photo FROM authors WHERE author = ?" ; con = ds.getConnection("java", "sun") ; PreparedStatement pstmt = con.prepareStatement(selectSQL) ; pstmt.setString(1, "rjb"); ResultSet rs = pstmt.executeQuery() ; rs.next(); Blob blob = rs.getBlob("photo") ; // Materialize BLOB onto client ImageIcon icon = newImageIcon(blob.getBytes(1, (int)blob.length())) ; // Display photo …
Inserting a CLOB String insertSQL = "Insert INTO messages VALUES(?, ?, ?, ?)" ; con = ds.getConnection("java", "sun") ; PreparedStatement pstmt = con.prepareStatement(insertSQL) ; File file = newFile("C:/data/rjb.txt") ; FileInputStream fis = newFileInputStream(file); pstmt.setInt(1, 1); pstmt.setString(2, "Hello Java"); pstmt.setString(3, "rjb"); pstmt.setAsciiStream(4, fis, (int)file.length()); if(1 != pstmt.executeUpdate()) System.err.println("Incorrect value returned during message insert.") ; pstmt.close(); fis.close(); System.out.println("CLOB Insert Successful") ;
Selecting a CLOB String selectSQL = "SELECT message FROM messages WHERE id = ?" ; con = ds.getConnection("java", "sun") ; PreparedStatement pstmt = con.prepareStatement(selectSQL) ; pstmt.setInt(1, 1); ResultSet rs = pstmt.executeQuery() ; rs.next(); Clob clob = rs.getClob("message") ; // Materialize CLOB onto client InputStreamReader in = newInputStreamReader(clob.getAsciiStream()) ; JTextArea text = newJTextArea(readString(in)) ; // Display data …
Managing Database Connections w/JDBC • Database Transactions • Connection Pools
Database Transactions • Required when a group of operations must complete as a unit, or not at all • Data is stored in a temporary area until the transaction is committed
Database Transactions try { con.setAutoCommit(false) ; Statement stmt = connection.createStatement() ; stmt.addBatch("INSERT INTO people VALUES('Joe Jackson', 0.325, 25"); stmt.addBatch("INSERT INTO people VALUES('Jim Jackson', 0.349, 18"); stmt.addBatch("INSERT INTO people VALUES('Jack Jackson', 0.295, 15"); // More code, DB operations here int[] updateCounts = stmt.executeBatch() ; con.commit() ; } catch(Exception e) // Could be caused by DB connection, or something else { con.rollback(); }
Dirty Read Transaction 1 Transaction 2 /* Query 1 */ SELECT * FROM users WHERE id = 1; /* Query 2 */ UPDATE users SET age = 21 WHERE id = 1; /* No commit here */ /* Query 1 */ SELECT * FROM users WHERE id = 1; COMMIT; /* Query 2 */ ROLLBACK; /* Query 1 data is now corrupted, or “dirty”! */
Non-repeatable Read Transaction 1 Transaction 2 /* Query 1 */ SELECT * FROM users WHERE id = 1; /* Query 2 */ UPDATE users SET age = 21 WHERE id = 1; COMMIT; /* Query 1 */ SELECT * FROM users WHERE id = 1; COMMIT;
Phantom Read Transaction 1 Transaction 2 /* Query 1 */ SELECT * FROM users WHERE age BETWEEN 10 AND 30; /* Query 2 */ INSERT INTO users VALUES ( 3, 'Bob', 27 ); COMMIT; /* Query 1 */ SELECT * FROM users WHERE age BETWEEN 10 AND 30; Receives different data the second time
Transaction Levels in JDBC API • TRANSACTION_NONE – transaction are not supported. • TRANSACTION_READ_UNCOMMITTED – one transaction can see another transaction’s changes before they are committed. Thus dirty reads, non-repeatable reads, and phantom reads are all allowed. • TRANSACTION_READ_COMMITTED – reading uncommitted data is not allowed. This level still permits both non-repeatable and phantom reads to occur. • TRANSACTION_REPEATABLE_READ – a transaction is guaranteed to be able to re-read the same data without fail, but phantom reads can still occur. • TRANSACTION_SERIALIZABLE – highest transaction level. Prevents dirty reads, non-repeatable reads, and phantom reads from occurring.
Connection Pooling • Creating/destroying database connections involves a lot of overhead • It's better to keep a handful of connections open and reuse them, rather than opening/closing connections for each operation • Performance enhancement
Creating a Connection Pool // The appropriate JNDI subcontext for PooledDataSources is jdbcpool privateStringfilePath = "jdbcPool/pjtutorial" ; privateintportNumber = 1114 ; privateintpoolSize= 10 ; // Create a pool with 10 connections. publicInitializeJNDI() { Hashtable env = newHashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); try { Context ctx = newInitialContext(env); MsqlPooledDataSource ds = newMsqlPooledDataSource() ; // Set standard parameters here – host, port, etc. ... ds.setMaxPoolSize(poolSize) ; // Bind the name and the dataSource object together. ctx.bind(filePath, ds) ; ctx.close() ; } catch(Exception ex) { System.err.println("ERROR: " + ex.getMessage()) ; } }
Using a Connection Pool try{ Hashtable env = newHashtable() ; env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory") ; Context ctx = newInitialContext(env) ; ConnectionPoolDataSource ds = (ConnectionPoolDataSource)ctx.lookup("jdbcPool/pjtutorial") ; // A PooledConnection provides a special Connection which is not // destroyed when it is closed, but is instead placed back into the // pool of connections. PooledConnection pcon = ds.getPooledConnection() ; Connection con = pcon.getConnection() ; System.out.println("Connection Established") ; con.close(); } catch(Exception e ) { e.printStackTrace(); }
Progress Check • Due this week: • Exam 1 • Due next week: • Lab 5-2 Database Application Interfaces