Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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 = new DB2DataSource(); 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(int i = 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() ; int count = 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 = new File("C:/images/rjb.jpg") ; FileInputStream fis = new FileInputStream(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 = new ImageIcon(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 = new File("C:/data/rjb.txt") ; FileInputStream fis = new FileInputStream(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 = new InputStreamReader(clob.getAsciiStream()) ; JTextArea text = new JTextArea(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 private String filePath = "jdbcPool/pjtutorial" ; private int portNumber = 1114 ; private int poolSize= 10 ; // Create a pool with 10 connections. public InitializeJNDI() { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); try { Context ctx = new InitialContext(env); MsqlPooledDataSource ds = new MsqlPooledDataSource() ; // 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 = new Hashtable() ; env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory") ; Context ctx = new InitialContext(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