Sei sulla pagina 1di 16

Manejar Conexiones a Bases de Datos con JDBC 3.0 Introducin al Tutorial Debera leer este Tutorial?

Este tutorial presenta los diferentes conceptos implicados en el establecimiento y control de una conexin con bases de datos dentro de una aplicacin Java usando J ava Database Connection (JDBC). Est dirigido principalmente a desarrolladores que quieren entender que "qu hay detrs de la escena" cuando usan una base de datos de sde dentro de una aplicacin Java Este tutorial asume que ests familiarizado con el lenguaje de programacin Java. Sobre qu va este Tutorial? Este tutorial demuestra cmo conectar con una base de datos usando JDBC. Aunque pa rece inocuo, este punto es realmente un obstculo tanto para novatos como para vet eranos en Java. Este tutorial explicar cmo una aplicacin Java dentro de una JVM des cubre y se comunica con una base de datos, empezando con el tradicional driver J DBC y los objetos DriverManager. Despus de varios ejemplos que demuestran los cua tro tipos diferentes de drivers JDBC, el tutorial se mueve sobre una explicacin d e objetos DataSource que usan JNDI. Tambin se incluye una discusin sobre JNDI, y cm o unir, usar, reunir y borrar el objeto DataSource. Finalmente, se presentan y d emuestran los conceptos de almacen de conexiones, y especficamente los objetos Po oledConnection. El tutorial concluye con una discusin de problemas de ajuste que normalmente se nos pasan cuando desarrollamos aplicaciones de conectividad de ba se de datos. Herramientas Aunque el tutorial proporciona numerosos fragmentos de cdigo para reflejar concep tos y mtodos descritos en el texto, la mayora de la gente aprender mejor trabajando realmente a travs de los ejemplos. Para trabajar con los ejemplos, necesitars ten er instaladas y funcionando las siguientes herramientas: Un editor de texto: los ficheros fuente Java son simplemente texto, por eso para crearlos y leerlos, necesitamos un editor de texto. Si tienen acceso a un IDE J ava, puedes usarlo, pero algunas veces ocultan muchos detalles. Un entorno de desarrollo Java, como el Java2 SDK, que est disponible en http://ja va.sun.com/j2se/1.4/. El Java2 SDK, Standard Edition versin 1.4, incluye las exte nsiones estndard JDBC as como JNDI, mbos son necesarios para algunos de los ejemplo s posteriores de este tutorial Una base de datos compatible SQL: los ejemplos de este tutorial usan una amplia variedad de bases de datos para ayudarnos a demostrar cmo se puede hacer programa cin JDBC independiente de la base de datos. Un driver JDBC: como el API JDBC est predominantemente compuesto por interfaces, necesitamos obtener una implementacin de un driver JDBC real para poder conectar realmente con una base de datos usando JDBC. Si nuestra base de datos (o nuestra maleta) no permite el uso de JDBC, siempre podemos usar el dirver puente JDBC-O DBC para conectar con cualquier base de datos (o fuente de datos) que soporta el protocolo ODBC. Arquitectura de la Aplicacin Arquitecturar Nuestro Sistema Uno de los problemas de diseo ms importantes cuando desarrollamos una aplicacin de bases de datos en Java es la arquitectura general del sistema; en particular, cun tos componentes diferentes deberamos desplegar. Tradicinonalmente, esto est caract erzado por el nmero de capas que requiere la aplicacin. Hay dos modelos arquitectur ales bsicos que pueden describir un sistema: el modelo de dos capas y el modelo d e n-capas.

Antes de entrar en los detalles del manejo de conexiones a bases de datos desde una aplicacin Java, necesitamos explicar estos dos modelos. Cada modelo tiene sus propias ventajas e inconvenientes; cada uno tambin requiere que ciertos componen tes se configuren apropiadamente, y , como resultado, cada uno funciona mejor en entornos diferentes. Las dos siguientes secciones explican en ms detalle cada un o de los dos modelos arquitecturales. El Modelo de Dos Capas El modelo de dos capas es el marco de trabajo tradicional cliente-servidor; tien e una capa cliente y una capa servidor. Este modelo simple requiere que el clien te tenga cuidado especfico de la base de datos. As, por ejemplo, el cliente necesi ta cdigo especfico de la base de datos resultando en un acoplamiento fuerte entre las dos capas. Este acoplamiento fuerte tienes varias ventajas. Primero, puede r educir el tiempo de desarrollo debido al hecho de que todo el sistema es conside rablemente ms simple y ms pequeo. Segundo, el acoplamiento fuerte puede mejorar pot encialmente el rendimiento del sistema ya que el cliente puede fcilmente aprovech arse de las funcionalidades especficas del servidor que podran no estar disponible s para sistemas con un acoplamiento ms ligero. Por otro lado, este acoplamiento fuerte puede provocar varios problemas. El mas notable, el mantenimiento del sistema se puede volver ms dficil porque los cambios en el servidor pueden romper al cliente y viceversa. Adems, si la base de datos cambia, todo el cdigo del cliente deber ser modificado. Si el cliente est altamente distribuido, la propagacin de los cambios en el sistema puede ser dficil, y en al gunos escenarios imposible. Como resultado, las aplicaciones de dos capas pueden ser tiles en un entorno de LAN corporativa donde el completo control de todos lo s clientes se consigue, o al inicio, o en el estado prototipal de un proyecto do nde diferentes opciones estn siendo evaluadas. El Modelo de n-Capas El modelo de n-capas tiene una capa cliente, al menos una capa servidor, y al me nos una cada intermedia. Debido a la capa extra, muchos de los problemas que afe ctan a los modelos de dos capas no afectarn. Por ejemplo, la capa media ahora men tiene informacin de la conexin a la base de datos. Esto significa que los clientes slo tienen que conocer la capa media. Como la capa media generalmente est operand o en la misma localizacin fsica que el servidor (por ejemplo, ambos componentes pu eden estar detrs del mismo firewall), mantener la capa media es considerablemente ms sencillo que mantener cientos de instalaciones clientes. Otra ventaja de la aproximacin de n-capas es que todo el sistema se puede escalar fcilmente para manejar ms usuarios.Todo lo que necesitamos hacer es aadir ms capas medias o ms capas servidores, dependiendo de los resultados de las operaciones de perfilado. Como las capas intermedias normalmente se implementan usado servidor es Web -- usando tecnologas JavaServer Pages y Servlets -- es muy sencillo aadir b alance de carga o incluso nuevos componentes hardware. Sin embargo, no todo es de color rosa, ya que la capa extra intoduce complejidad adicional en todo el sistema. Esto significa ms cdigo, ms duro de probar y potenci almente ms dficil de encontrar sus errores. Afortunadamente, el lenguaje Java prop orciona muchos de los componentes necesarios, pre-construidos. para construir ap licaciones de n-capas viables. Adems, este modelo se presta a si mismo el fcil sop orte de autentificacin e internacionalizacin, ya que la capa media controla el flu jo de informacin y proporciona localizacin natural para manjear todo lo que concie rne al manejo de seguridad y la localizacin. Fundamentos de los Drivers JDBC Introduccin a los Drivers JDBC Una inspeccin casual del API JDBC muestra rpidamente la dominacin de los interfaces dentro del API, lo que podra llevar al usuario a preguntarse dnde se realiza el t rabajo. Realmente esta es slo una aproximacin que tocan los desarrolladores JDBC p orque la implementacin real es la proporcionada por los vendedores de Drivers JDB C, que a su vez proporcionan las clases que implementan los interfaces necesario

s. Esta aproximacin presenta la competicin que proporciona al consumidor ms opcione s, y para la mayor parte, produce mejor software. Con todos los drivers disponib les. elegir uno puede ser dficil. Aforturnadamente, Sun Microsystems mantiene una base de datos con ms de 150 drivers JDBC de una amplia variedad de vendedores. E sta debera ser la primera parada despus de seleccionar una base de datos. Desde una perspectiva de programacin, hay dos clases principales respnsables para el establecimiento de una conexin con una base de datos. La primera clase es Dri verManager, que es una de las clases que realmente proprociona el API JDBC. Driv erManager es responsable de manejar un almacen de drivers registrados, esencialm ente abstrayendo los detalles del uso de un driver para que el programador no te nga que tratar con ellos directamente. La segunda clase es la clase real del Dri ver JDBC. Estas son proporcionadas por vendedores independientes. La clase Drive r JDBC es la responsable de establecer la conexin con la base de datos y de manej ar todas las comunicaciones con la base de datos. Los drivers JDBC vienen en cua tro tipos diferentes Registrar un Driver JDBC El primer paso en el proceso de crear una conexin entre una aplicacin Java y una b ase de datos es el registro de un driver JDBC con la Mquina Virtual Java (JVM) en la que se est ejecutando la aplicacin Java. En el mecanimso de conexin tradicional (en oposicin al mecanismo de conexin del DataSource) la conexin y todas las comuni cacin con la base de datos son controladas por un objeto DriverManager. Para esta blecer una conexin, se debe registrar un driver JDBC adecuado para la base de dat os objetivo con el objeto DriverManager. La especificacin JDBC, dice que se supon e que los dirvers JDBC se registran automticamente a s mismos con el objeto Driver Manager cuando se cargan en la JVM. Por ejemplo, el siguiente fragmento de cdigo usa un inicializador esttico para primero crear un ejemplar del driver JDBC persi stentjava y luego resgistrarlo con el DriverManager: static { java.sql.DriverManager.registerDriver(new com.persistentjava.JdbcDriver()) ; } Registrar un driver es tan simple como cargar la clase del driver en la JVM, lo que puede hacerse de muchas maneras. Una forma es con el ClassLoader Class.forNa me(com.persistentjava.JdbcDriver). Otro mtodo, que no es tan bien conocido, usa l a propidad del sistema jdbc.drivers. Este mtodo se puede usar de tres formas dist intas: Desde la lnea de comandos: java -Djdbc.drivers=com.persistentjava.JdbcDriver Connect Dentro de la palicacin Java: System.setProperty("jdbc.drivers","com.persistentjava.JdbcDriver") ; Seleccionando la propiedad jdbc.drivers en el fichero de propiedades del sistema , que generalmente depende del sistema. Separando los drivers con una coma, se pueden registrar varios drivers usando la tcnica de la propiedad del sistema mostrada arriba. Uno de los beneficios de usa r la tcnica de la propiedad del sistema es que se los drivers se pueden intercamb iar fcilmente sin modificar ningn cdigo (o al menos con unos mnimos cambios). Si se registran varios drivers, su orden de precedencia es: Drivers JDBC registrados por la propiedad jdbc.drivers en la inicializacin de la JVM, y Drivers JDBC cargados dinmicamente. Como la propiedad jdbc.drivers slo se chequea una vez durante la primera invocacin del mtodo DriverManager(), es importante asegurarse de que todos los drivers estn registrados correctamente antes de establecer la conexin con la base de datos. S in embargo, no todas las JVM estn creadas igual, y algunas de ellas no siguen la especificacin JVM. Como resultado, los inicializadores estticos no siempre funcion an como hemos dibujado. Esto resulta en mltiples formas de registrar un driver JD BC, incluyendo: Class.forName("com.persistentjava.JdbcDriver").newInstance(); DriverManager.registerDriver(new com.persistentjava.JdbcDriver()) ; Estas alternativas deberan funcionar bien en todas las JVMs, por eso deberamos sen

tirnos agusto usndolas a lo largo del amplio conjunto de JVM disponibles. Un prob lema final es que Class.forname() puede lanzar una ClassNotFoundException, por e so debemos envolver el cdigo de registro en un manejador de excepcin apropiado. URLs de Drivers JDBC Una vez que un Driver JDBC se ha registrado con el DriverManager, puede usarse p ara establecer una conexin a una base de datos. Pero cmo selecciona DriverManager e l driver correcto, dado que puede haber realmente rigstrados cualquier nmero de d rivers? (Recuerda, una sla JVM podra soportar mltiples aplicaciones concurrentes, q ue podran conectarse con diferentes bases de datos con diferentes drivers). La tcn ica es bastante simple: cada driver JDBC usa una URL JDBC especfica (que tiene el mismo formato que una direccin Web) como un significado de auto-identificacin. El formato de la URL es correcto y probablemente parece familiar: jdbc:sub-protoco l:database locator. El sub-protocol es especfico del driver JDBC y puede ser odbc , oracle, db2, etc., dependiendo del vendedor del driver real. El localizador de la base de datos es un indicador especfico del driver para especficar de forma nic a la base de datos con la que una aplicacin quiere interactar. Dependiendo del tip o de driver, este localizador incluye un nombre de host, un puerto, y un nombre de sistema de base de datos. Cuando se presenta con una URL especfica, el DriverManager itera sobre la coleccin de drivers registrados hasta que uno de ellos reconoce la URL especficada. Si no se encuentra ningn driver adecuado, se lanza una SQLException. La siguiente list a presenta varios ejemplos especficos de URLs JDBC reales: jdbc:odbc:jdbc jdbc:oracle:thin:@persistentjava.com:1521:jdbc"; jdbc:db2:jdbc Muchos drivers, incluyendo el driver puente JDBC-ODBC, acepta parmetros adicional es al final de la URL como un nombre de usuario y una password. El mtodo para obtener una conexin a una base de datos, dando una URL JDBC especfica , es llamar a getConnection() sobre el objeto DriverManager. Este mtodo tiene var ias formas: DriverManager.getConnection(url) ; DriverManager.getConnection(url, username, password) ; DriverManager.getConnection(url, dbproperties) ; Aqu url es un objeto String que es la URL JDBC, username y password son objetos S tring que representan el nombre de usuario y la password que la aplicacin JDBC de bera usar para conectar con la fuente de datos; y dbproperties es un objeto Prope rties de Java que encapsula todos los parmetros (posiblemente incluyendo nombre d e usuario y la password) que requiere un driver JDBC para hacer una conexin con xi to. Ahora que tenemos el driver bsico en la mano, podemos examinar los tipos de d rivers individuales con ms detalle: Drivers del Tipo 1

Los drivers del tipo uno tienen algo en comn: todos usan el puente JDBC-ODBC, que est incluido como parte estndard del JDK. Los drivers del tipo uno son diferentes al driver ODBC (Open DataBase Connectivity) adjunto al puente JDBC-ODBC. Para c onectar con una fuente de datos diferente, simplemente tenemos que registrar (o unir efectivamente) una fuente de datos ODBC diferente, usando el Administrador ODBC, al nombre de la fuente de datos apropiada. Como ODBC se ha estado utilizando desde hace bastante tiempo (ms que el lenguaje Java), los drivers ODBC son ubicuos. Esto hace de este tipo de drivers una buena eleccin para aprender cmo conectar programas Java a bases de datos. De hecho, inc luso hay drivers ODBC que nos permiten asignar fuentes de datos ODBC a una aplic

acin Microsoft Excel o ficheros de texto plano. Sin embargo, el nivel extra de in direccin, puede resultar en una prdida de rendimiento ya que el JDBC es transferid o dentro de ODBC, que luego es transferido en el protocolo especfico de la base d e datos. Otro problema potencial de los drivers del tipo uno es su utilizacin en aplicacio nes distribuidas. Como el propio puente no soporta comunicacin distribuida, la nic a forma de que los drivers del tipo uno puedan trabajar a travs de la red es si e l propio driver ODBC soporta interaccin remota. Para drivers ODBC sencillos, esta no es una opcin, y mientras que las grandes bases de datos tienen drivers ODBC q ue pueden trabajar de forma remota, no pueden competir con el mejor rendimiento de los drivers JDBC puro Java. Codificacin para Drivers del Tipo 1 El nombre de clase para el driver puente JDBC-ODBC es sun.jdbc.odbc.JdbcOdbcDriv er y al URL JDBC toma la forma jdbc:odbc:dsn, donde dsn es el Data Source Name ( nombre de la fuente de datos) usado para registrar la base de datos con el Admin istrador ADBC. Por ejemplo, si una base de datos se registra con una fuente de d atos ODBC llamada jdbc; un nombre de usuario de java y una password de sun, se p uede usar el siguiente fragmento de cdigo para establecer una conexin. Nota: En inters de la claridad y la brevedad, se ha eliminado del listado el chequeo y manejo de errores. Posteriores ejemplos, demostrarn la importancia de estos mecan ismos (especficamente, la captura de errores encadenando sentencias SQLException) . String url = "jdbc:odbc:jdbc" ; Connection con ; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver") ; } catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: ") ; System.err.println(e.getMessage()) ; return ; } try { con = DriverManager.getConnection(url, "java", "sun"); } catch(SQLException ex) { System.err.println("SQLException: " + ex.getMessage()); } finally { try{ con.close ; } catch(SQLException ex) { System.err.println(SQLException: " + ex.getMessage()) ; } } Drivers del Tipo 2

Los drivers del tipo dos tambin son conocidos como drivers Java parciales, porque traducen directamente el API JDBC en un API especfico de la base de datos. La ap licacin cliente de base de datos (para el propstio de este tutorial, el host que e st ejecutando la JVM) debe tener las libreras cliente apropiadas para la base de d atos, que podran incluir cdigo binario instalado y posiblemente ejecutndose. Para u na aplicacin distribuida, este requerimietno puede introducir problemas extra con las licencias, as como posibles pesadillas con los problemas de distribucin de cdi

go. Por ejemplo, usar un modelo del tipo dos restringe a los desarrolladores a u tilizar plataformas y sistemas operativos soportados por la librera cliente de la base de datos. Sin embargo, este modelo puede funcionar eficientemente, cuando la base cliente est fuertemente controlada.. Esto ocurre tpicamente en LANs corporativas. Un ejemp lo de driver del tipo dos es el driver de aplicacin JDBC para DB2. El siguiente e jemplo demuestra cmo establecer una conexin usando un driver DB2: String url = "jdbc:db2:jdbc" ; try { Class.forName("COM.ibm.db2.jdbc.app.DB2Driver") ; } catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: ") ; System.err.println(e.getMessage()) ; return ; } ... Observa la similitud que tiene este fragmento de cdigo con el del ejemplo 1. Esta es la principal caracterstica del modelo del tipo 2: la curva de aprendizaje par a un programador que se mueve de un modelo a otro es prcticamente inexistente. Los dos ltimos tipos de drivers son drivers puro Java. El beneficio de los driver s puro Java es su fcil despliegue en entonos altamente distribuidos. Drivers del Tipo 3

Los drivers del tipo tres son drivers puro Java que transforman el API JDBC en u n protocolo independiente de la base de datos. El driver JDBC no comunica direct amente con la base de datos; comunica con un servidor de capa media, que a su ve z comunica con la base de datos. Este nivel extra de indireccin proporciona flexi bilidad en que se puede acceder a diferentes bases de datos desde el mismo cdigo porque el servidor de la capa media oculta las especificidades a la aplicacin Jav a. Para cambiar a una base de datos diferente, slo necesitamos cambiar los parmetr os en el servidor de la capa media. (Un punto a observar: el formato de la base de datos a la que estamos accediendo debe ser soportado por el servidor de la ca pa media). El lado negativo de los drivers del tipo tres es que el nivel extra de indireccin puede perjudicar el rendimiento general del sistema. Por otro lado, si una apli cacin necesita interactar con una variedad de formatos de bases de datos, un drive r del tipo tres en una aproximacin adecuada debido al hecho de que se usa el mism o driver JDBC sin importar la base de datos subyacente. Adems, como el servidor d e la capa media se puede instalar sobre una plataforma hardware especfica, se pue den realizar ciertas optimizaciones para capitalizar los resultados perfilados. Drivers del Tipo 4

Los drivers del tipo cuatro son drivers puro Java que se comunican directamente con la base de datos. Muchos programadores consideran ste el mejor tipo de driver , ya que normalmente proporciona un rendimiento ptimo y permite al desarrollador utilizar las funcionalidades especficas de la base de datos. Por supuesto este ac oplamiento puede reducir la flexibilidad, especialmente si necesitamos cambiar l a base de datos subyacente en una aplicacin. Este tipo de driver se usa frecuente mente en applets y otras aplicaciones altamente distribuidas. El siguiente fragm ento de cdigo muestra cmo usar un driver DB2 del tipo cuatro: String url = "jdbc:db2://persistentjava.com:50000/jdbc" ; try { Class.forName("COM.ibm.db2.jdbc.net.DB2Driver") ; } catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: ") ;

System.err.println(e.getMessage()) ; return ; } ... Un Ejemplo Completo de Driver del Tipo 4 El siguiente ejemplo muestra cmo usar un driver JDBC de un vendedor de terceras p artes, en este caso Merant, para conectar con una base de datos DB2. DB2 UDB req uiere informacin adicional, no estndard para establecer la conexin a la base de dat os, que en este ejemplo estn aadidos a la URL JDBC como parmetros opcionales. package com.persistentjava; import java.sql.*; public class ConnectMerantDB2 { static { try { Class.forName("com.merant.datadirect.jdbc.db2.DB2Driver").newInstanc e(); } catch (Exception e) { System.out.println(e); } } public static void main(String args[]) { String url = "jdbc:merant:db2://persistentjava.com:50000;" ; url += "DatabaseName=jdbc;CollectionId=DEFAULT;" ; url += "PackageName=JDBCPKG;CreateDefaultPackage=TRUE"; Connection con; System.out.println("Connecting"); try { con = DriverManager.getConnection(url, "java", "sun"); System.out.println("Connection Established"); con.close(); // In this example, the proper handling of SQLExceptions is // demonstrated as they can be potentially chained. } catch (SQLException e) { System.out.println("\nERROR:----- SQLException -----\n"); while (e != null) { System.out.println("Message: " + e.getMessage()); System.out.println("SQLState: " + e.getSQLState()); System.out.println("ErrorCode: " + e.getErrorCode()); e.printStackTrace(); e = e.getNextException(); } } } } Transaciones con Bases de Datos Transaciones Bsicas Uno concepto que causa problemas a los principiantes en el mundo del desarrollo de aplicaciones de base de datos es la idea de transaciones. Fundamentalmente, u na transacin representa una unidad de trabajo lgica. Como la principal responsabil idad de una base de datos es preservar la informacin, necesita tener algo que le indique a un usuario que se debe salvar el estado actual del programa. De igual forma, cuando las cosas han ido mal, necesita una forma para indicar que una bas

e de datos deberia ignorar el estado actual y volver a atrs, al estado del progra ma salvado anteriormente. En el idioma de las bases de datos, estas funciones se llaman entregar una trans acin y deshacer una transacin, respectivamente. Para realizar estas tareas, el API JDBC incluye dos mtodos como parte del interface Connection. Dando un objeto Con nection llamado con, el estado del programa se graba llamando a con.commit(), pa ra volver al estado salvado anteriormente, con.rollback(). Estos dos mtodos puede n lanzar SQLExceptions si algo va mal cuando la base de datos realice realmente la operacin, por eso necesitamos envolverlos en bloques try ... catch. Ms sobre Transaciones En un entorno mono-usuairo, las transaciones son bastantes sencillas de entender -- simplemente implican salvar o deshacer el estado de una aplicacin. Sin embarg o, en modo multi-usuario, las transaciones son ms complejas. La demostracin clsica de una transacin multi-usuario es una cuenta bancaria donde una aplicacin est inten tando hacer un cargo mientras otra aplicacin est intentando hacer un depsito en la misma cuenta. Si estas familiarizado con la programacin concurrente (tambin conoci da como programacin multi-thread), probablemente hayas visto este problema antes. El problema fundamental es que a menos que las dos transaciones estn aisladas la una de la otra, una aplicacin podra interrumpir a la otra resultando en un estado del programa incorrecto. En nuestra sencilla demostracin, esto podra significar u na cuenta con un saldo errneo, algo que no es precisamente bueno para retener cli entes. Pueden aparecer tres problemas comunes cuando tratamos con varios usuarios que a cceden a los mismos datos: Lecturas sucias. Un lectura sucia ocurre cuando una aplicacin usa datos que han s ido modificados por otra aplicacin, y esos datos estn en un estado sin entregar. L a segunda aplicacin entonces solicita que los datos que fueron modificados sean d esechos. Entonces los datos de la primera transacin estn corruptos o "sucios". Lecturas no-repetibles. Una lectura no-repetible ocurre cuando una transacin obti ene datos, que posteriormente son alterados por una transacin separada, y la prim era transacin re-lee los datos ahora alterados. As, la primera transacin hizo una l ectura no-repetible. Lecturas fantasmas. Una lectura fantasma ocurre cuando una transacin adquiere dat os mediante alguna consulta, otra transacin modifica algunos de los datos, y la t ransacin original recupera los datos una segunda vez. La primera transacin ahora t endr un conjunto de resultados diferentes, que podran contener datos fantasmas. Niveles de Transacin Para resolver los problemas asociados a mltiples threads solicitando los mismos d atos, las transaciones estn aisladas unas de otras por bloqueos. La mayora de las bases de datos soportan diferentes tipos de bloqueo; por lo tanto, el API JDBC s oporta diferentes tipos de transaciones, que son asignadas o determinadas por el objeto Connection. En el API JDBC tiene disponibles los siguientes niveles de t ransaciones: TRANSACTION_NONE indica que las transaciones no estn soportadas. TRANSACTION_READ_UNCOMMITTED indica que una transacin puede ver los cambios de ot ra transacin antes de ser entregada. As estn permitidas las lecturas sucias, las le cturas no-repetibles, y las lecturas fantasmas. TRANSACTION_READ_COMMITTED indica que la lectura de datos no entregados, no est p ermitida. Este nivel todava permite que ocurran las lecturas no-repetibles y las lecturas fantasmas. TRANSACTION_REPEATABLE_READ indica que una transacin est garantizada que pueda releer el mismo dato sin fallar, pero las lecturas fantasmas todava pueden ocurrir. TRANSACTION_SERIALIZABLE es la transacin de ms alto nivel y evita que ocurran las lecturas sucias, las lecturas no-repetibles y las lecturas fantasmas. Podramos pr eguntarnos porqu todas las transaciones no operan en modo TRANSACTION_SERIALIZABL E para garantizar el grado ms alto de integridad de datos. El problema es, de for ma similar a los problemas implicados con el manejo de la programacin de mltiples threads, que cuanto ms sea el nivel de proteccin de transacin, ms alta ser la prdida d

e rendimiento. Dando un objeto Connection podemos seleccionar explcitamente el nivel de transacin , asumiendo que nuestra base de datos y nuestro driver JDBC soporten esta caract erstica: con.setTransactionLevel(TRANSACTION_SERIALIZABLE) ; Tambin podemos determinar el nivel de transacin actual: if(con.getTransactionLevel() == TRANSACTION_SERIALIZABLE) System.out.println("Highest Transaction Level in operation.") ; Lotes y Transaciones Por defecto, los drivers JDBC operan en lo que se llama modo autoentrega. En est e modo, todos los comandos enviados a la base de datos operan en su propia trans acin. Aunque esto puede ser til para los principiantes, implica una prdida de rendi miento porque las transaciones requieren una cierta cantidad de sobrecarga para configurar todo apropiadamente. Si queremos poder controlar explcitamente las ent regas y los retrocesos (deshacer la transacin), necesitamos desactivar el modo au tocommit: con.setAutoCommit(false) ; Tambin podemos determinar rpidamente el modo autocommit de un objeto Connection da do: if(con.getAutoCommit() == true) System.out.println("Auto Commit mode"); Muchas bases de datos soportan lotes, en los que se minimiza la sobrecarga de tr ansaciones realizando mltiples operaciones update de la base de datos en una sla o peracin, o lote. Las operaciones por lotes fueron introducidas en JDBC 2.0 y requ ieren que una transacin no est en modo autocommit. En el siguiente ejemplo tenemos una operacin por lotes, que asume que existe una Connection a una base de datos que tiene una sola tabla: con.setAutoCommit(false) ; Statement stmt = connection.createStatement() ; stmt.addBatch("INSERT INTO people VALUES('Joe Jackson', 0.325, 25, 105) ; stmt.addBatch("INSERT INTO people VALUES('Jim Jackson', 0.349, 18, 99) ; stmt.addBatch("INSERT INTO people VALUES('Jack Jackson', 0.295, 15, 84) ; int[] updateCounts = stmt.executeBatch() ; con.commit() ; Observa que el mtodo executeBatch() devuelve un array de cuentas actualizadas, un a por cada operacin del lote. Un ltimo problema con las operaciones por lotes es q ue pueden lanzar una nueva excepcin del tipo BatchUpdateException, lo que indica que fall al menos uno de los comandos del lote. Por eso, necesitamos aadir un mane jador de excepciones correspondiente a nuestras operaciones por lotes. Control Fino de las Transaciones Empezando con el API JDBC 3.0, se aadi un nuevo elemento interface relacionado con las transaciones. Este interface presenta el concepto de savepoints. Los Savepo ints proporcionan un marca dentro de una aplicacin de base de datos que puede usa rse como un argumento cuando se llama al mtodo rollback. Como resultado, usando e l API JDBC 3.0, ahora es posible seleccionar un savepoint antes de empezar una i nteraccin complicada con la base de datos y, dependiendo del resultado, entregar la transacin completa o deshacer hasta el savepoint y devolver la aplicacin a un p unto conocido. Para seleccionar un punto de salvado, creamos un objeto Savepoint desde el objet o Connection, como se ve aqu: Savepoint svpt = con.setSavepoint("Savepoint") ; Para deshacer hasta un Savepoint dado, simplemente le pasamos el objeto Savepoin t deseado al mtodo rollback: con.rollback(svpt) ; Cuando no se necesitan ms, liberamos todos los objetos Savepoint para liberar los caros recursos de la base de datos para otros usuarios: con.releaseSavepoint(svpt) ;

Observa que cuando entregamos o deshacemos una transacin, cualquier Savepoints cr eado podra convertirse en invlido dependiendo del orden exacto y del tipo de opera cin. Puedes ver la especificacin del API JDBC 3.0 o el manual de tu driver para ms informacin. Fuentes de Datos Fuentes de Datos Bsicas Uno de los principales benficios de usar el API JDBC es facilitar una programacin independiente de la base de datos, as la mayora de las aplicaciones JDBC pueden t ransferirse fcilmente a otra base de datos diferente. Sin embargo, todava hay dos t ems que permacenen unidos a una base de datos particular, la clase JDBC Driver y la URL JDBC. Con la introducin de las fuentes de datos en el API JDBC 2.0, se el iminaron incluso estas dependencias. Esencialmente un objeto DataSource representa una fuente de datos particular en una aplicacin Java. Adems de encapsular la informacin especfica de la base de datos y del driver JDBC en un slo objeto estndarizado, las fuentes de datos pueden actua r como una factora de Connection y proporcionar mtodos para seleccionar y obtener propiedades particulares que requiere el objeto DataSource para una operacin sati sfactoria. Algunas propiedades estndar que podra requerir un objeto DataSource inc luyen: databaseName serverName portNumber userName password Un beneficio adicional de usar una DataSource, que podras haber adivinado de la l ista anterior, es que la informacin sensible relacionada con la seguridad como el nombre de usuario, la password, e incluso el servidor de la base de datos estn cd ificados slo en un lugar, lo que puede hacer un administrador de sistemas. Mientr as que la interaccin con un objeto DataSource se puede hacer con una aplicacin grfi ca, es instructivo ver como trabajan realmente los ejemplos. Aunque los concepto s de un objeto DataSource son bastantes simples, para usarlo dentro de una aplic acin Java, un objeto DataSource es referenciado usando Java Naming and Directory Interface, o JNDI. Antes de saltar dentro del cdigo de ejemplo de DataSource, el siguiente punto presenta conceptos relevantes de JNDI que son necesarios para us ar apropiadamente un objeto DataSource. Repaso Rpido de JNDI JNDI es un API Java que encapsula el concepto de servidores de nombres y directo rios de la misma forma que JDBC encapsula los conceptos que hay detrs de la comun icacin con una base de datos. Aunque podra parecer confuso, es bastante sencillo - todos lo usuarios de ordenadores usan servicios de nombres y directorios todos los das. Por ejempo, los discos duros trabajan con pistas y sectores, aunque un usuario slo se preocupa de nombres de ficheros y directorios. El sistema de fiche ros maneja el servicio de nombrado que asocia un nombre de fichero dado con una localizacin especfica en el disco duro. Otro ejemplo es la Web, donde la mayora de los usuarios slo se preocupan del nombre de la Web site, como www.persistentjava. com, y no de la direccin IP subyacente. Sin embargo, la comunicacin TCP/IP se hace usando la direccin IP y no usando el nombre que leemos los humanos. La transform acin entre las dos representaciones la realiza el DNS, o Domain Name System. Aunque JDNI proporciona un API mplio y til por s mismo, nuestras necesidades son co nsiderablemente simples. En breve, necesitamos conocer como hacer cuatro cosas: Crear un nombre y unirlo a un objeto Java. Buscar un nombre para recuperar un objeto Java. Borrar un nombre. Re-unir un nombre a nuevo objeto Java. En vez de proporcionar ejemplos JNDI imaginarios para las tareas anteriores, las siguientes secciones muestran ejemplos de estas tareas usando fuentes de datos JDBC. Todos estos ejemplos usan el proveedor de sistema de ficheros, que es una

descarga separada. Registrar una Fuente de Datos Este ejemplo es una implementacin de un DataSource de tereceras partes de i-net s oftware para conectar con una base de datos MS SQL Server. Los comentarios en el cdigo marcan los puntos importantes de registro (o inicialziacin) de una fuente d e datos JDBC: // We need to import the actual DataSource implementation import com.inet.tds.TdsDataSource; import java.util.Hashtable; import javax.naming.*; import javax.naming.directory.*; import java.sql.* ; import javax.sql.* ; public class InitializeJNDI { // First we define the relevant parameters for this datasource private String serverName = "persistentjava.com"; private int portNumber = 1433; private String login = "java"; private String password = "sun"; private String databaseName = "jdbc"; // This is the name we will assign to our datasource. Because we are // using the file system provider, our name follows the file system // naming rules. The JNDI reserved subcontext for JDBC applications is // jdbc, thus our name starts appropriately. private String filePath = "jdbc/pjtutorial"; public InitializeJNDI() { // To pass in the necessary parameters, we need to create and then // populate a Hashtable. 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(); // Generic Exception handler, in practice, this would be replaced by an // appropriate Exception handling hierarchy. } catch (Exception ex) { System.err.println("ERROR: " + ex.getMessage()); } } public static void main(String args[]) { new InitializeJNDI(); } }

Usar una Fuente de Datos El ejemplo anterior establece la relacin de unin entre el objeto DataSource y un n ombre particular. La mgia de JNDI realmente la realiza el proveedor de servicio a propiado. En nuestro caso, usamos el proveedor del sistema de ficheros. Existen otras opciones, incluyendo LDAP (Lightweight Directory Access Protocol) o inclus o un DNS. Para hacer realmente una conexin, necesitamos buscar el objeto DataSour ce usando el nombre al que fue unido. Observa en el siguiente ejemplo que no hay ningn cdigo especfico de base de datos en ningn lugar: import java.util.Hashtable ; import javax.naming.* ; import java.sql.* ; import javax.sql.* ; public class UtilizeJNDI { public UtilizeJNDI(){ try { // We need to set up the JNDI context so that we can properly interf ace // to the correct service provider, in this case the file system. Hashtable env = new Hashtable() ; env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory") ; Context ctx = new InitialContext(env) ; // Given the JNDI Context, we lookup the object and are returned // our DataSource DataSource ds = (DataSource)ctx.lookup("jdbc/pjtutorial") ; // Now we get a database connection and proceed to do our job. Connection con = ds.getConnection() ; System.out.println("Connection Established.") ; con.close(); // Note that proper error handling is not included here in order to keep // the example short. }catch(Exception e ) { e.printStackTrace(); } } public static void main (String args[]){ new UtilizeJNDI() ; } } Re-Unir una Fuente de Datos Cuando queremos cambiar la base de datos particular o incluso el vendedor del Da taSource JDBC con el que queremos que comunique nuestro cdigo, todo lo que necesi tamos hacer es re-unir un nuevo DataSource al nombre original. En este ejemplo, usamos el mismo driver, pero cambiamos varios parmetros relevantes: // We need to import the actual DataSource import com.inet.tds.TdsDataSource; import java.util.Hashtable; import javax.naming.*; import javax.naming.directory.*; import java.sql.* ; import javax.sql.* ; public class InitializeJNDI { // First we define the relevant parameters for this datasource private String serverName = "persistentjava.com"; private int portNumber = 1434; // Note the new port number private String login = "sun"; // New username/password combination private String password = "java";

private String databaseName = "ds"; // And even a new database name. // We keep the same name for our datasource, just bind a new DataSource // to it. private String filePath = "jdbc/pjtutorial"; public InitializeJNDI() { // Establish the proper JNDI Context Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); try { // Create the context Context ctx = new InitialContext(env); TdsDataSource ds = new TdsDataSource(); ds.setServerName(serverName); ds.setPortNumber(portNumber); ds.setDatabaseName(databaseName); ds.setUser(login); ds.setPassword(password); ds.setDescription("JDBC DataSource Connection, take two"); // Now we just call the rebind method with the new DataSource. ctx.rebind(filePath, ds); ctx.close(); // Replace this with real Exception handlers in production code. } catch (Exception ex) { System.err.println("ERROR: " + ex.getMessage()); } } public static void main(String args[]) { new InitializeJNDI(); } } Borrar una Fuente de Datos Algunas veces, querremos borrar un nombre de DataSource para que no pueda volver a ser usado: import java.util.Hashtable ; import javax.naming.* ; import java.sql.* ; import javax.sql.* ; public class DeleteJNDI { public DeleteJNDI() { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); try { Context ctx = new InitialContext(env); // unbinding the name object association effectively deletes the obj ect. ctx.unbind("jdbc/pjtutorial") ; ctx.close() ; }catch (Exception ex) { System.err.println("ERROR: " + ex.getMessage()) ; } } public static void main (String args[]){ new DeleteJNDI() ; } } Almacenes de Conexiones

Por qu necesitamos Almacenes de Conexiones Cuando usamos el DriverManager o el mtodo DataSource para obtener conexiones a ba ses de datos, cada solicitud para una nueva conexin implica una sobrecarga signif icante. Esto puede impactar en el rendimiento si la obtencin de nuevas conexiones ocurre con cierta frecuencia, como podra ser el caso en un entorno de servidor W eb. Para enfatizar por qu esto es cierto, sigamos un camino potencial de una soli citud de conexin tpica con una base de datos: La aplicacin Java llama a getConnection(). El cdigo del vendedor JDBC (la implementacin del driver o del DataSource) solicita una conexin socket desde la JVM. La JVM necesita chequear los aspectos de seguridad de la llamada potencial. Por ejemplo, los applets slo se pueden comunicar con el servidor del que son originar ios. Si lo aprueba, la llamada necesita pasar a travs del interface de red del host ha sta la LAN corporativa. La llamada podra necesitar pasar a travs de un cortafuegos para alcanzar Internet o una WAN. La llamada alcanza eventualmente su subred de destino, donde podra necesitar pasa r a travs de otro cortafuegos. La llamada alcanza el host de la base de datos. El servidor de base de datos procesa la solicitud de la nueva conexin. Se podra necesitar requerir la licencia del servidor para determinar si hay una l icencia apropiada disponible. La base de datos inicializa una nueva conexin cliente, incluyendo toda las sobrec argas de memoria y sistema operativo. La llamada de retorno es enviada de vuelta al cliente JDBC (donde tiene que pasa r por todos los cortafuegos y routers). La JVM recibe la llamada de retorno y crea un objeto Connection apropiado. La aplicacin Java recibe el objeto Connection. Claramente, solicitar un nuevo objeto Connection presenta una gran sobrecarga y muchos puntos de fallo potenciales. Para minimizar esta sobrecarga, por qu no reut ilizar conexiones a bases de datos en vez de borrarlas cuando hayamos terminado con ellas? Los diseadores de JDBC usaron este patrn de diseo popular cuando crearon el ConnectionPoolDataSource, que nos permite crear un almacen de conexiones a b ases de datos que son reutilizadas en vez de eliminarlas cuando se cierran. Qu es una PooledConnection? Una PooledConnection es un tipo especial de conexin a base de datos que no se bor ra cuando se cierra, al contrario que los objetos Connection normales (el recole ctor de basura puede borrar las conexiones normales una vez que ya no son refere nciadas). En en vez de esto, la PooledConnection es almacenada para una reutiliz acin posterior, produciendo potencialmente una gran mejora del rendimiento. Traba jar con un almacen de conexiones a bases de datos es casi idntico a trabajar con objetos DataSource. Primero, en vez de crear un ejemplar de una clase que implem ente el interface DataSource, creamos un ejemplar de una clase que implemente Co nnectionPoolDataSource. Podemos usar JDNI para unir esta nueva fuente de datos a un nombe como antes. Para usar realmente un objeto de fuente de datos almacenado, llamamos a getPoole dConnection() sobre el ConnectionPooledDataSource, que a su vez establece conexi ones con la base de datos. Para crear un objeto Connection que ser usado, llamamo s a getConnection() sobre el objeto PooledConnection en vez sobre los objetos Dr iverManager o DataSource como antes. Un beneficio adicional de esta aproximacin e s que es mucho ms fcil manejar varios conjuntos de conexiones a bases de datos con un ConnectionPool porque l tiene cuidado automticamente. Esta automatizacin puede ser muy importante si nuestra licencias de cliente limitan el nmero de clientes q ue pueden conectarse simultneamente a la base de datos. El proceso completo es mu cho ms sencillo de lo que suena, como muestran los siguientes ejemplos. Inicialiar un Almacen de Conexiones En este ejemplo, usamos una base de datos mSQL y el driver JDBC de cdigo abierto

mSQL para crear un PooledDataSource. Todo el cdigo especfico de la base de datos e st contenido en los procesos de inicializacin o de unin. // First we import the relevant package import com.imaginary.sql.msql.* ; import java.util.Hashtable ; import javax.naming.* ; public class InitializeJNDI { private String serverName = "localhost" ; private String databaseName = "jdbc" ; private String userName = "java" ; private String password = "sun" ; // The appropriate JNDI subcontext for PooledDataSources is jdbcpool private String filePath = "jdbcPool/pjtutorial" ; private int portNumber = 1114 ; private int poolSize= 10 ; // We want to 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); // Create the PooledDataSource and set the relevant parameters. MsqlPooledDataSource ds = new MsqlPooledDataSource() ; ds.setServerName(serverName) ; ds.setPort(portNumber) ; ds.setDatabaseName(databaseName) ; ds.setUser(userName) ; ds.setPassword(password) ; 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()) ; } } public static void main (String args[]){ new InitializeJNDI() ; } } Usar un Almacen de Conexiones Una vez que hemos inicializado el PooledDataSource, ahora podemos usarlo en apli caciones Java para crear almacenes de conexiones a bases de datos. El siguiente ejemplo, es imaginario, pero efectivamente demuestra los puntos importantes. Un ejemplo ms realista podra ser un servlet, que configurara el almacen de conexiones en el mtodo init() del servlet y (re-)utilizara la conexin por cada nueva solicitud del servicio: import java.util.Hashtable ; import javax.naming.* ; import java.sql.* ; import javax.sql.* ; public class UtilizeJNDI { public UtilizeJNDI(){ try { Hashtable env = new Hashtable() ; env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory") ; Context ctx = new InitialContext(env) ;

// Look up the DataSource given the appropriate name. 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(); } } public static void main (String args[]){ new UtilizeJNDI() ; } } Optimizar las Comunicaciones con Bases de Datos Mtodos JDBC DataSource y Driver Esta seccin ecplia algunos de los mtodos menos conocidos del API JDBC que pueden s er utilizados para mejorar el rendimiento del sistema. Primero, todas las clases de fuentes de datos JDBC, el Driver , el DataSource, o el ConnectionPooledDataS ource, proporcionan la habilidad de designar explcitamente un stream de salida de l tipo caracter, setLogWriter(). Este stream acepta todos los logs y mensajes de seguimiento. Tambin hay un mtodo disponible para obtener el stream actual, getLog Writer(). Este puede ser extremadamente valioso cuando intentamos diagnosticar e xtraos bugs o para seguir el flujo de la aplicacin. Adems, todas las clases de fuen tes de datos JDBc proporcionan un medio para asignar (setLoginTimeout()) u obten er (getLoginTimeout()) la cantidad de tiempo mxima que la clase de fuente de dato s debera esperar a que se estalezca una conexin. Una clase interesante y rara vez utilizada es la clase DriverPropertyInfo. Esta clase encapsula toda la informacin de propiedades que necesita un driver para est ablecer una conexin a una base de datos. DriverPropertyInfo puede usarse en una h erramienta grfica para encontrar interactivamente los parmetros que un driver nece sita y pedirle al usuario de la base de datos los valores correctos. Mtodos JDBC Connection Un objeto Connection o PooledConnection tambin tienen varios mtodos que se pueden utilizar para mejorar el rendimiento del sistema, tanto directa como indirectame nte. Primero, asumiendo que el driver JDBC y la base de datos subyacente lo sopo rtan, un objeto connection se puede configurar para ser de slo lectura, setReadOn ly(). Esto puede mejorar el rendimiento del sistema para aplicaciones que no nec esitan hacer cambios en la base de datos, ya que la base de datos no necesita pr eocuparse de almacenar nuevas pginas, mantener entradas de diario, o adquirir blo queos de escritura sobre cualquiera de los datos. Otros mtodos que estn relacioandos de alguna forma son setAutoCommit() y setTransa ctionIsolation(). Como el descontrol de las transaciones puede causar grandes prd idas de rendimiento en la base de datos, estos mtodos mtodos y sus efecto deberan s er claramente entenedidos y cuidadosamente aplicados. Finalmente, un ltimo y frecuentemente olvidado mtodo es nativeSQL(), que taduce un a consulta SQL dada en el lenguaje de consulta nativo de la base de datos subyac ente. Examinando la versin nativa SQL de nuestra consulta, podriamos entender mej or cmo la base de datos est intepretando nuestra consulta, y entonces hacer las mo dificaciones adecuadas a nuestra aplicacin para mejorar el rendimiento general.

Potrebbero piacerti anche