Programación Orientada a Objetos Fast Track to Java Unit 1: Overview and Positioning Programación Orientada a Objetos Anexo 6 JDBC Universidad de Chile Departamento de Ciencias de la Computación Profesor: Juan Claudio Navarro jnavarro@dcc.uchile.cl, juancla.navarro@gmail.com
Temario Drivers DriverManager Conexión a una base de datos Transacciones Ejecución de sentencias SQL Recuperación de resultados Invocación de procedimientos almacenados Extensiones
JDBC Java Database Connectivity: ver http://java.sun.com/products/jdbc API de acceso a bases de datos relacionales, independiente de la plataforma y del DBMS Conjunto de clases e interfaces definidas en los java.sql los javax.sql Vendedores de bases de datos proveen drivers que implementan estas interfaces
Los Paquetes de JDBC java.sql: API para el acceso y procesamiento de información de una base de datos javax.sql: API opcional de JDBC, orientada a aplicaciones J2EE javax.sql.rowset: interfaces y clases base estándar para implementaciones de RowSet javax.sql.rowset.serial: clases utilitarias para la serialización de objetos SQL y objetos Java
Lo Esencial de JDBC
Interfaz java.sql.Driver Implementado por el vendedor del Driver Se carga usando el método Class.forName() Ejemplos Class.forName("oracle.jdbc.driver.OracleDriver"); Class.forName("com.sybase.jdbc2.jdbc.SybDriver"); Class.forName("org.postgresql.Driver"); Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); Al cargarse, la clase Driver: Crea una instancia de sí misma Registra esa instancia con el DriverManager
Tipos de Drivers 4 tipos de drivers Tipo 1: utiliza la parte cliente (nativa) de la base de datos, y ODBC Tipo 2: utiliza la parte cliente (nativa) de la base de datos Tipo 1 Tipo 2
Tipos de Drivers Tipo 3: puro Java, pero accede a la base de datos a través de un middleware Tipo 4: puro Java, accede directamente a la base de datos Tipo 4 Tipo 3
Driver JDBC-ODBC Requiere que la fuente de datos ODBC esté configurada y funcionando => el driver ODBC debe estar instalado => dependiendo del driver ODBC, probablemente se requiera también del driver nativo Tiene sentido en aplicaciones en que se tiene el control de la máquina donde se utiliza el driver (en particular, no en applets)
URL en JDBC Los drivers usan un URL para localizar la base de datos Ejemplos Oracle jdbc:oracle:thin:@<host>:<port>:<db> Sybase jdbc:sybase:Tds:<host>:<port>/<db> PostgreSQL jdbc:postgresql://<host>:<port>/<db> MySQL jdbc:mysql://<host>:<port>/<db> JDBC-ODBC jdbc:odbc:<DSN ODBC>
Clase java.sql.DriverManager Clase que maneja los drivers JDBC Provee métodos static para: Cargar y descargar drivers Establecer una conexión a través de un driver Establecer log Establecer timeouts de conexión Código para establecer una conexión: Class.forName("com.sybase.jdbc2.jdbc.SybDriver"); String url = "jdbc:sybase:Tds:MyDbServer:5000"; Connection conn = DriverManager.getConnection(url, usr, pwd);
DriverManager.getConnection Connection conn = DriverManager.getConnection(URL, uid, pwd); Genera un SQLException si se produce un error DriverManager trata de encontrar un driver que corresponda al URL Recorre un vector con los drivers registrados El primer driver que retorna un objeto Connection es utilizado DriverManager Driver 1 Driver 2 Driver 3
Interfaz java.sql.Connection Representa una sesión con una base de datos específica Se obtiene invocando a DriverManager.getConnection() Utilizado para crear objetos Statement que permiten ejecutar queries El método getMetaData() permite obtener información de tablas, procedimientos almecenados, etc.
Conexión y Desconexión Al dejar de usar una conexión, ésta debe cerrarse, para efectuar la desconexión Connection conn = null; try { Class.forName("oracle.jdbc.driver.OracleDriver"); String url = "jdbc:oracle:thin:@dbserver:8443:finanzas"; conn = DriverManager.getConnection(url, usr, pwd); // uso de la conexión // ... } finally { if (conn != null) { conn.close(); }
Transacciones La interfaz Connection provee los siguientes métodos para el manejo de transacciones commit() Hace permanentes los cambios realizados rollback() Deshace los cambios de la transacción actual setAutoCommit(boolean autoCommit) Con autoCommit=true (default), cada sentencia SQL es ejecutada como una transacción individual; de lo contrario las sentencias son agrupadas en transacciones que terminan con llamadas a commit() o rollback() setTransactionIsolation(int level) Establece el “isolation level” para la transacción: Connection.TRANSACTION_READ_UNCOMITTED Connection.TRANSACTION_READ_COMITTED Connection.TRANSACTION_REPEATABLE_READ Connection.TRANSACTION_SERIALIZABLE
Interfaz java.sql.Statement Creación de un Statement Statement stmt = connection.createStatement(); Método executeQuery() retorna un ResultSet ResultSet rs = stmt.executeQuery( "SELECT id, lname, phone FROM employees"); Método executeUpdate() retorna el número de filas afectadas int rows1 = stmt1.executeUpdate( "UPDATE products SET price = price * 1.10"); int rows2 = stmt2.executeUpdate( "DELETE FROM product WHERE qty = 0");
Interfaz java.sql.ResultSet Provee acceso a una tabla de datos generados ejecutando un Statement Mantiene un cursor, posicionado inicialmente antes de la primera fila El método next() mueve el cursor a la fila siguiente; retorna true si quedan filas (si logró moverse), y false en caso contrario Los métodos getType () recuperan los valores de las columna para la fila actual El método getMetaData() retorna un objeto ResultSetMetaData, que entrega información acerca de las columnas del result set
Interfaz java.sql.ResultSet Los métodos getType () reciben como parámetro el nombre o el número de la columna (partiendo de 1) Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( "SELECT id, lname, phone FROM employees"); while (rs.next()) { System.out.println(rs.getInt(1)); System.out.println(rs.getString(2)); System.out.println(rs.getString("phone")); }
Interfaz java.sql.PreparedStatement Usado para almacenar una sentencia SQL precompilada ? permite definir parámetros dinámicos Los métodos setType() permiten proveer valores de sustitución Manera eficiente de ejecutar un Statement repetidas veces con variaciones menores
Interfaz java.sql.PreparedStatement Estableciendo parámetros PreparedStatement ps = conn.prepareStatement( "UPDATE product SET price = ? WHERE id = ?"); ps.setDouble(1, 299990); ps.setString(2, "CF6783"); ps.executeUpdate();
Interfaz java.sql.PreparedStatement Ejecutando un PreparedStatement repetidamente PreparedStatement ps = conn.prepareStatement( "SELECT autor FROM titulos WHERE seccion = ?"); for (int i=0; i < maxSeccion; i++) { ps.setInt(1, i); ResultSet rs = ps.executeQuery(); while (rs.next()) { // procesar registro } rs.close(); ps.close();
Interfaz java.sql.CallableStatement Usado para invocar un procedimiento almacenado CallableStatement cs = connection.prepareCall( "{call sp_procedimiento(?, 500, ?)}"); // ejecutamos sp_procedimiento(1000, 500, 'ZZZ') cs.setInt(1, 1000); cs.setString(2, "ZZZ"); cs.executeUpdate();
Interfaz java.sql.CallableStatement Estableciendo parámetros de salida (OUT) CallableStatement cs = connection.prepareCall( "{call sp_get_nombre_y_numero(?, ?)}"); cs.registerOutParameter(1, Types.CHAR); cs.registerOutParameter(2, Types.FLOAT); cs.executeUpdate(); String nombre = cs.getString(1); float numero = cs.getFloat(2);
Interfaz java.sql.CallableStatement Para invocar funciones, el retorno es tratado como un parámetro de salida más int codigo = ...; CallableStatement cs = connection.prepareCall( "{? = call sp_getnombre(?)}"); cs.registerOutParameter(1, Types.CHAR); cs.setInt(2, codigo); cs.executeUpdate(); String nombre = cs.getString(1);
Interfaz java.sql.CallableStatement Estableciendo parámetros de entrada y salida (INOUT) CallableStatement cs = connection.prepareCall( "{call sp_modifica(?)}"); cs.setFloat(1, 354.56f); cs.registerOutParameter(1, Types.FLOAT); cs.execute(); float numero = cs.getFloat(1);
Liberación de Recursos Los objetos Connection, Statement, ResultSet, y derivados, deben cerrarse al desocuparlos, de modo de liberar los recursos utilizados (cursores, conexiones, etc.) Statement stmt = null; ResultSet rs = null; try { stmt = conn.createStatement(); rs = stmt.executeQuery("..."); while (rs.next()) { ... } } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close();
Extensiones Incorporadas en JDBC 2.0 Statement: Permite ejecutar un conjunto de sentencias SQL como una unidad (batch updates) ResultSet: Provee navegación hacia atrás, posicionamiento absoluto, inserción, modificación y eliminación Tipos de datos SQL3: Se soportan blobs, arreglos, tipos estructurados
Extensiones Incorporadas en JDBC 2.0 Extensiones en paquete javax.sql RowSet Encapsulación de un conjunto de filas de un Result Set Uso de JNDI (Java Naming & Directory Interface) Permite identificar bases de datos mediante nombres lógicos Connection Pooling Manejo de caches de conexiones Soporte de Transacciones Distribuidas Soporte del protocolo Two-Phase Commit usado por Java Transaction API (JTA)
Connection.createStatement interface Connection { ... Statement createStatement(); Statement createStatement( int resultSetType, int resultSetConcurrency); }
Connection.createStatement resultSetType: ResultSet.TYPE_FORWARD_ONLY ResultSet.TYPE_SCROLL_INSENSITIVE ResultSet.TYPE_SCROLL_SENSITIVE resultSetConcurrency: ResultSet.CONCUR_READ_ONLY ResultSet.CONCUR_UPDATABLE
Interfaz java.sql.ResultSet interface ResultSet { boolean absolute(int row); void afterLast(); void beforeFirst(); void deleteRow(); boolean first(); Date getDate( {int colnum | String colname}); int getInt( {int colnum | String colname}); Object getObject({int colnum | String colname}); String getString({int colnum | String colname}); ... void insertRow(); boolean last(); boolean next(); boolean previous(); boolean relative(int rows); }
Posicionamiento Statement stmt = conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet srs = stmt.executeQuery( "SELECT NAME, PRICE FROM COFFEES"); srs.afterLast(); while (srs.previous()) { String name = srs.getString("NAME"); float price = srs.getFloat("PRICE"); System.out.println(name + " " + price); }
ResultSet.updateRow Statement stmt = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); ResultSet rs = stmt.executeQuery( "SELECT * FROM T1 WHERE... "); while (rs.next()) { rs.updateString(2, "xyz"); rs.updateInt(3,100); rs.updateRow(); }
ResultSet.deleteRow while(rs.next()) { int col3 = getInt(3); if (col3 >100) { rs.deleteRow(); }
ResultSet.insertRow // preparamos para insertar rs.moveToInsertRow(); // cargamos nuevos valores rs.updateString(1, "nuevos datos"); rs.updateInt(2, 42); // insertamos fila rs.insertRow();
Batch Updates conn.setAutoCommit(false); Statement stmt = conn.createStatement(); stmt.addBatch("INSERT INTO COFFEES " + "VALUES('Amaretto', 49, 9.99, 0, 0)"); "VALUES('Hazelnut', 49, 9.99, 0, 0)"); "VALUES('Amaretto_decaf', 49, 10.99, 0, 0)"); "VALUES('Hazelnut_decaf', 49, 10.99, 0, 0)"); int[] updateCounts = stmt.executeBatch();
Interfaz javax.sql.RowSet Interfaz derivada de java.sql.ResultSet Soporta el modelo de componentes JavaBeans Provee un conjunto de “setters” para establecer propiedades (dataSourceName, command, username, ...) Maneja eventos, permitiendo que otros componentes de la aplicación sean notificados cuando ocurre un evento en el RowSet, como un cambio en su valor
Interfaz javax.sql.RowSet interface RowSet extends java.sql.ResultSet { void addRowSetListener(RowSetListener l); void execute(); void setCommand(String cmd); void setConcurrency(int concurrency); void setDataSourceName(String name); void setMaxRows(int max); void setQueryTimeout(int seconds); void setTransactionIsolation(int level); void setType(int type); void setUrl(String url); void setUsername(String name); ... }
Interfaz javax.sql.DataSource DataSource es un Connection “factory” Es posible obtener un DataSource usando JNDI Los objetos Connection obtenidos pueden formar parte de Pools (PooledConnection), y pueden manejar transacciones distribuidas (XAConnection) Context context = new InitialContext(); DataSource datasource = (DataSource)context.lookup("miBD"); Connection connection = datasource.getConnection(); ...
Extensiones Incorporadas en JDBC 3.0 Nuevos features: Soporte de savepoints Recuperación de llaves auto generadas Múltiples ResultSet abiertos Paso de parámetros a CallableStatement por nombre Modificación de objetos Blob y Clob Relación entre JDBC y JCA
Recuperación de Llaves Generadas La mayoría de las bases de datos tienen mecanismos para generar llaves únicas automáticamente al insertar registros Statement stmt = conn.createStatement(); stmt.executeUpdate( "INSERT INTO DISCO(TITULO, AUTOR) " + "VALUES ('Mastropiero que nunca', 'Les Luthiers')", Statement.RETURN_GENERATED_KEYS); ResultSet rs = stmt.getGeneratedKeys(); rs.next(); int id = rs.getInt(1); ...
Tecnologías Relacionadas Java Persistence API (JPA): JPA (http://java.sun.com/javaee/technologies/persistence.jsp) es la nueva plataforma de persistencia de Java, reemplaza a los entity beans Principales implementaciones: TopLink Essentials (Oracle): http://www.oracle.com/technology/products/ias/toplink Hibernate (JBoss, libre): http://www.hibernate.org Entity Beans Los entity beans son componentes EJB que entregan una vista OO sobre una base de datos (http://java.sun.com/products/ejb) SQLJ SQLJ es un estándar que permite construir programas con “embedded SQL”, a partir del cual un preprocesador genera código JDBC estándar
Resumen Un driver JDBC retorna un objeto Connection al establecer una conexión El objeto Connection permite crear objetos Statement, PreparedStatement, y CallableStatement Un objeto Statement permite ejecutar sentencias SQL y generar objetos ResultSet Un objeto PreparedStatement permite ejecutar sentencias SQL precompiladas Un objeto CallableStatement permite invocar procedimientos almacenados
Resumen Un objeto ResultSet permite recorrer el resultado de una sentencia SELECT JDBC 2.0 incorporó mejoras en las interfaces Statement y ResultSet, y soporte de nuevos tipos de datos Las extensiones opcionales de JDBC 2.0 incluyen la implementación de la interfaz RowSet, el soporte de JNDI, pools de conexiones, y soporte de transacciones distribuidas JDBC 3.0 incorpora el soporte de savepoints, recuperación de llaves generadas automáticamente, y otras funcionalidades