Está en la página 1de 73

ESTRATEGIAS DE PERSISTENCIA ORIENTADA A OBJETOS EN JAVA CON JDBC, UNA COMPARATIVA DESDE LA PRCTICA

Jos Mara Arranz Santamara e-mail: jmarranz@dii.etsii.upm.es Madrid, 6 de Octubre 2003

Tecnologas de Persistencia en Java, una comparativa desde la prctica

INDICE
1. INTRODUCCIN....................................................................................................................................... 3 2. PLANTEAMIENTO DEL PROBLEMA .................................................................................................. 5 2.1 DECISIONES DE IMPLEMENTACIN........................................................................................................ 5 2.1.1 CORRESPONDENCIA ENTRE TABLA Y CLASE INCLUSO EN HERENCIA .................................................... 5 2.1.2 CORRESPONDENCIA ENTRE FILA Y OBJETO ........................................................................................... 6 2.1.3 CORRESPONDENCIA ENTRE COLUMNA DE TABLA Y ATRIBUTO DE CLASE ............................................ 6 2.1.4 IDENTIDAD GESTIONADA POR LA APLICACIN (APPLICATION IDENTITY) ............................................. 6 2.1.5 RELACIONES ENTRE TABLAS EXPRESADAS A TRAVS DE PUNTEROS Y COLECCIONES ......................... 6 2.1.6 SE TENDER A ENCAPSULAR TODO TIPO DE ACCESO A LA BASE DE DATOS EN CLASES ORIENTADAS A LA PERSISTENCIA ............................................................................................................................................... 6 2.1.7 SE TENDER HACIA EL RESPETO DE LAS FORMAS CLSICAS DE MODELAR ESQUEMAS RELACIONALES 6 2.1.8 SE USAR ANSI SQL 92 ....................................................................................................................... 6 2.1.9 NO SE USARAN CARACTERSTICAS ORIENTADAS A OBJETOS DE SQL3 (ANSI SQL1999).................... 7 2.1.10 USO DE CLAVES SEMNTICAS EN EL MODELO RELACIONAL ............................................................... 7 2.2 MODELO DE ENTIDADES O CLASES .................................................................................................... 7 2.2.1 MODELO DE UNA ENTIDAD .................................................................................................................... 7 2.2.2 MODELO DE UNA ENTIDAD RELACIONADA CON MUCHAS INSTANCIAS DE OTRA ENTIDAD................... 7 2.2.3 MODELO MUCHOS-MUCHOS ENTRE ENTIDADES .................................................................................... 8 2.2.4 MODELO DE ENTIDAD CON HERENCIA DE OTRA ENTIDAD .................................................................... 8 2.3 MODELO RELACIONAL ........................................................................................................................... 9 2.3.1 MODELO UNA TABLA............................................................................................................................. 9 2.3.2 MODELO UNO-MUCHOS (DOS TABLAS) ................................................................................................. 9 2.3.3 MODELO MUCHOS-MUCHOS (TRES TABLAS) ......................................................................................... 9 2.3.4 MODELO DE HERENCIA ........................................................................................................................ 10 3. HERRAMIENTAS .................................................................................................................................... 11 4. SOLUCIN JDBC .................................................................................................................................... 12 4.1 TIPO BMP.............................................................................................................................................. 12 4.1.1 UNA TABLA ......................................................................................................................................... 13 4.2 TIPO BMP AVANZADO O TIPO BMP 2................................................................................................ 21 4.2.1 CLASES COMUNES (FRAMEWORK) ....................................................................................................... 22 4.2.2 UNA TABLA ......................................................................................................................................... 25 4.2.3 RELACIN UNO MUCHOS ................................................................................................................. 29 4.2.4 RELACIN MUCHOS MUCHOS .......................................................................................................... 35 4.2.5 HERENCIA ............................................................................................................................................ 42 4.3 TIPO CMP ............................................................................................................................................. 52 4.3.1 CLASES COMUNES (FRAMEWORK) ....................................................................................................... 53 4.3.2 UNA TABLA ......................................................................................................................................... 56 4.3.3 RELACIN UNO-MUCHOS ................................................................................................................... 59 4.3.4 RELACIN MUCHOS-MUCHOS ............................................................................................................ 62 4.3.5 HERENCIA ............................................................................................................................................ 65 5. CONLUSIONES ........................................................................................................................................ 72 5.1 NECESIDAD DE UN FRAMEWORK.......................................................................................................... 72 5.2 NECESIDAD DE FRAMEWORKS MS SOFISTICADOS ............................................................................ 72 5.3 ORIENTACIN A OBJETOS Y PATRONES COMO HERRAMIENTAS QUE PERMITEN LA TRANSPARENCIA Y LA TOLERANCIA A CAMBIOS ........................................................................................ 72 5.4 BMP MENOS TRANSPARENTE QUE CMP............................................................................................. 73 5.5 CMP TIENE SERIOS PROBLEMAS CON LA HERENCIA ......................................................................... 73 5.6 JDBC ADECUADO PARA MODELOS SENCILLOS O BASES DE DATOS POCO ESTNDAR ...................... 73
persistencia_java.doc v.1.0 Jos Mara Arranz Santamara Pg.2

Tecnologas de Persistencia en Java, una comparativa desde la prctica

1. INTRODUCCIN
La persistencia o el almacenamiento permanente, es una de las necesidades bsicas de cualquier sistema de informacin de cualquier tipo. Desde las tarjetas perforadas hasta los modernos sistemas actuales de bases de datos, las tecnologas persistentes han realizado un largo viaje en la historia de la informtica. El problema de cmo programar la persistencia, es decir automatizar las acciones de almacenamiento y recuperacin de datos desde el sistema de almacenamiento, es uno de los ms relevantes en cualquier sistema software que manipula informacin no voltil, hasta el punto de que condiciona enormemente la programacin del mismo. El problema de la programacin de la persistencia ha estado fuertemente influido por el sistema de base de datos, de hecho el propio sistema de base de datos, sea del tipo que fuera (relacional, jerrquico, orientado a objetos etc), obviamente ofrece su propio sistema de acceder a la informacin de forma programtica. As por ejemplo en una base de datos relacional podemos hacer programas de gestin de datos en SQL, pues uniendo las operaciones DDL, DML y los procedimientos almacenados, tenemos un completo (aunque no muy estructurado y no muy estndar) lenguaje de programacin. De hecho el uso intensivo de SQL unido a herramientas de programacin visual (tal y como OracleForms, Access, PowerBuilder) tuvieron su auge como paradigma de desarrollo en una poca (esto no quita que sigan siendo productos populares). Otro ejemplo es el caso de una base de datos jerrquica como ADABAS de Software AG que acceder a sus datos a travs de su lenguaje Natural. Sin embargo SQL no fue diseado obviamente como un lenguaje de propsito general (para hacer cualquier tipo de programa), adems el problema de las herramientas visuales apoyadas en SQL es que han estado orientadas desde sus comienzos al diseo rpido de aplicaciones relativamente pequeas, con lgicas de negocio no muy complejas, por su carcter interpretado no son muy eficientes, son muy monolticas, obvian los modernos paradigmas de la programacin y adems necesitan de una infraestructura para ejecutarse y por tanto de la licencia de la herramienta visual en cada cliente (aunque ahora es habitual que la infraestructura necesaria para la ejecucin de un programa desarrollado con alguna herramienta de programacin sea de libre distribucin). Esto conlleva a que siempre ha sido una necesidad que los habituales lenguajes de programacin de propsito general, tuvieran alguna forma de acceso a los diferentes tipos de bases de datos. Los lenguajes tradicionales tal y como C y C++, han podido acceder a las bases de datos, pero casi siempre de forma propietaria, aunque en el mundo relacional estndares como el ANSI SQL/CLI para SQL dinmico (desarrollado por Microsoft de forma ms propietaria como ODBC en Windows, evolucionando a niveles ms altos de abstraccin como OleDB y ADO), y el SQL embebido, tambin un estndar ANSI para SQL esttico, han ayudado a que este terreno no fuera un absoluto reino de taifas. Otras tecnologas propietarias como el antiguo RogueWave DBTools++ para C++ tuvieron tambin su momento y popularidad. Sin embargo la popularizacin de Java1 y su fuerte apuesta por la estandarizacin promovida por su creador, Sun Microsystems, ha dado lugar a una serie de estndares para acceder a bases de datos desde Java: JDBC, J2EE Entity Beans, JDO, SQLJ y JCA. Todas ellas pretenden universalizar el acceso a diferentes fuentes de datos tal que el cdigo fuente no dependa de la base de datos concreta a la que se conecta, y unas ms que otras pretenden reducir al mnimo la incompatibilidad entre la tecnologa de la base de datos y el modelo orientado a objetos de Java (la llamada impedance mistmatch). La ms antigua de todas y la ms flexible es JDBC. JDBC2 es una API orientada a la consulta de bases de datos relacionales utilizando llamadas en donde se suministran sentencias SQL (SQL dinmico) a travs de una conexin a la base de datos, y la respuesta se convierte a objetos simples de Java correspondientes a los tipos simples SQL (aunque el uso de SQL3 permite actualmente corresponder clases Java con tipos de datos SQL definidos por el programador). Otras tecnologas de persistencia subyacen en JDBC tal y como habitualmente J2EE BMP, implementaciones de JDO y SQLJ.

http://java.sun.com JDBC Data Access API, http://java.sun.com/products/jdbc/

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.3

Tecnologas de Persistencia en Java, una comparativa desde la prctica El JDBC como tecnologa bsica de acceso a base de datos es lo suficientemente flexible como para abordar la persistencia desde diferentes estrategias. Es habitual encontrarse libros sobre como usar JDBC, pero no sobre las formas de usar JDBC por ejemplo para conseguir el santo grial de la persistencia: la persistencia transparente orientada a objetos. El presente documento pretende hacer una comparativa de estrategias de persistencia basadas en JDBC. Consideramos el problema de las bases de datos relacionales, pues estas son las que abrumadoramente estn presentes en los sistemas de informacin. Enfocaremos el problema de la persistencia claramente desde la orientacin a objetos, el gran paradigma de la programacin, nuestro problema ser cmo almacenar nuestro modelo de informacin expresado en objetos Java en una base de datos relacional, buscando a la vez el mximo nivel de transparencia de la gestin de la persistencia, es decir, que las clases que representan nuestro modelo de informacin, estn lo ms posible libres de cdigo de acceso a la base de datos. De esta manera conoceremos lo que aporta cada estrategia en la consecucin de este objetivo de persistencia transparente orientada a objetos. El objetivo es que el propio lector llegue a sus propias conclusiones desde el seguimiento del cdigo que da lugar el uso de las diversas tcnicas para resolver idnticos o muy similares problemas (pues veremos que la eleccin influye un poco en el problema). Esto no quiere decir que huyamos totalmente de hacer valoraciones, o que nuestro anlisis pretenda ser universal pues se basar en problemas concretos, aunque bastante habituales y representativos. Podremos ver numerosos ejemplos de cdigo planteados de forma sistemtica. No se mostrar todo el cdigo sino el imprescindible para poder entender los requisitos de programacin que nos exige cada tecnologa.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.4

Tecnologas de Persistencia en Java, una comparativa desde la prctica

2. PLANTEAMIENTO DEL PROBLEMA


Las bases de datos relacionales deben su nombre al concepto de relacin originalmente formulado como equivalente al trmino tabla3, trmino mucho ms popular. Luego bsicamente una base de datos relacional es un conjunto de datos organizados de forma tabular, en donde la unidad de informacin con identidad y unicidad es la fila a su vez compuesta por una serie de atributos o columnas. Cada tabla en el esquema de una base de datos suele expresar por tanto un concepto o entidad en el mundo real, entidad que a su vez tiene caractersticas las cuales se expresan a travs de columnas de la tabla. La tablas suele estar relacionadas entre s, es decir existen relaciones conceptuales entre los diferentes tipos de informacin. Estas relaciones se manifiestan estructuralmente a travs de las claves forneas (foreign keys), una clave fornea es un campo de una tabla cuyos valores presentes en filas concretas han de coincidir con valores en una correspondiente columna que ha de ser clave primaria (primary key) de una tabla relacionada. De esta manera se expresa estructuralmente el concepto de relacin y de pertenencia entre entidades presente en la realidad, pues las filas de la tabla relacionada (con la clave fornea) pertenecen a la tabla principal, puesto que no puede existir informacin en tabla fornea si a su vez no existe la informacin correspondiente en la tabla principal. Luego una base de datos relacional es un conjunto de tablas con un conjunto de relaciones entre s. Las relaciones estn sujetas, al igual que en el mundo real, a una cardinalidad, la cardinalidad expresa cuantos elementos pueden estar relacionados en una tabla relacionada para un elemento dado. En las bases de datos relacionales a travs de las relaciones clave primaria-fornea, se consiguen las cardinalidades son 1-1, 1-muchos, muchos-muchos, aunque por programacin siempre se pueden conseguir cardinalidades con valores concretos en el caso de muchos. La manipulacin del modelo relacional y su correspondencia con un modelo orientado a objetos, es uno de los problemas clsicos que afronta la programacin orientada a objetos y las tecnologas de persistencia, como ya hablamos en la introduccin, y la solucin no es nica. En nuestra aproximacin al problema a travs de ejemplos, haremos elecciones o decisiones de implementacin entre las diversas posibles.

2.1 DECISIONES DE IMPLEMENTACIN


2.1.1 Correspondencia entre tabla y clase incluso en herencia

Es la aproximacin ms obvia, pero nada impide que una clase pueda manipular varias tablas relacionadas o que una tabla se corresponda con varias clases como a veces se suele hacer en el caso de la herencia4. Esta regla se aplicar incluso en caso de herencia: cada clase del rbol de herencia se corresponder con una tabla o tambin llamada vertical. Esto exigir que las tablas tengan relaciones 1-1, partiendo de la tabla que se corresponda con la clase ms alta del rbol de derivacin como tabla primaria. En el caso de la herencia este mtodo tiene sus ventajas y sus inconvenientes (peor rendimiento que otras opciones), pero lo que si es claro es que es la expresin ms cercana al concepto de herencia en bases de datos relacionales sin caractersticas de orientacin a objetos y sin duda es la mejor desde el punto de vista de la claridad y la mantenibilidad cuando se hace un uso intensivo de la herencia en el modelo persistente.

A Relational Model of Data for Large Shared Data Banks. Codd, E.F. CACM. 1970. The Fundamentals of Mapping Objects to Relational Databases, Scott W. Ambler, 2003, http://www.agiledata.org/essays/mappingObjects.html

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.5

Tecnologas de Persistencia en Java, una comparativa desde la prctica

2.1.2

Correspondencia entre fila y objeto

Al igual que en el caso anterior no es la nica opcin. En el caso de herencia cada objeto se corresponder con las filas de las correspondientes tablas relacionadas a travs del rbol de herencia impuesto por las clases de este rbol, de acuerdo al principio de correspondencia entre tabla y clase. 2.1.3 Correspondencia entre columna de tabla y atributo de clase

Incluso en el caso de las claves primarias y forneas. 2.1.4 Identidad gestionada por la aplicacin (application identity)

Es decir los valores de las claves que dan identidad a las filas de las tablas sern impuestos por nuestro programa y se correspondern con atributos de las clases y no transparentes al programador en el caso de claves autogeneradas ya sea por la base de datos o por la infraestructura Java de persistencia, y ocultas al programador (datastore identity). 2.1.5 Relaciones entre tablas expresadas a travs de punteros y colecciones

Es decir se tratar de hacer corresponder el modelo de tablas y relaciones con un modelo de clases y relaciones. No siempre existirn atributos de clase con el correspondiente puntero o coleccin, pero existirn mtodos en la clase (gets) que nos devolvern el puntero o la coleccin de los objetos relacionados. Esto supone que las clases que representan informacin persistente tendern a encapsular los accesos a la base de datos para modelar por s mismas las relaciones, esta es una buena prctica de programacin. La finalidad aparte de la conveniente encapsulacin es conseguir la ilusin de manejar un modelo de objetos como si fueran objetos normales en memoria o POJOs (Plain Old Java Objects). 2.1.6 Se tender a encapsular todo tipo de acceso a la base de datos en clases orientadas a la persistencia

Se crearn clases de utilidad o clases Home de acuerdo con la filosofa del patrn DAO5 (que es un caso particular para persistencia del clsico patrn Faade6), correspondientes a cada clase persistente vinculada a una tabla, con la finalidad de encapsular la gestin de consultas a conjuntos de elementos, crear o destruir un elemento etc. La finalidad es conseguir que el modelo de clases persistente y de clases de utilidad persistentes tengan una imagen homognea que no dependa de la tecnologa de persistencia ni del tipo de base de datos en la misma lnea de la ilusin de manejar POJOs. 2.1.7 Se tender hacia el respeto de las formas clsicas de modelar esquemas relacionales

Es decir se tender a considerar a priori que se parte de un modelo de base de datos concebido de forma ajena al tipo de estrategia de persistencia Java que se elija. De esta manera se trata de poner a prueba la tecnologa respecto a su capacidad de modelar en clases Java, modelos de bases de datos antiguos (legacy) previos a la existencia incluso de la propia tecnologa. Se intentar que el modelo relacional elegido no vare por culpa de la estrategia seguida de la persistencia Java. 2.1.8 Se usar ANSI SQL 92

Debido a que este estndar est ampliamente soportado por la mayora de las bases de datos comerciales y gratuitas del mercado. De esta manera nuestros modelos sern muy independientes de las bases de datos elegidas, la base de datos elegida ser por tanto irrelevante desde el punto de vista de la programacin.

Data Access Objects, http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html Faade Pattern, http://home.earthlink.net/~huston2/dp/facade.html

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.6

Tecnologas de Persistencia en Java, una comparativa desde la prctica

2.1.9

No se usaran caractersticas orientadas a objetos de SQL3 (ANSI SQL1999)

El uso de estas caractersticas podra simplificar notablemente la programacin, sin embargo es un hecho que este estndar dista mucho de estar adoptado por la mayora de las bases de datos modernas. 2.1.10 Uso de claves semnticas en el modelo relacional

El uso de claves semnticas, que la columna (o columnas) clave primaria de la tabla represente en concepto y valores la identidad de la entidad en el modelo real, no es una buena prctica de diseo de base de datos, pues el modelo resultantes es extremadamente rgido ante un cambio en el concepto de identidad en el modelo real, lo cual no es en absoluto raro. Sin embargo ha sido desde siempre una prctica muy extendida y an sigue sindolo, aunque su uso tender a decaer en la medida que las herramientas de gestin de la persistencia tienden a sugerir un modelo de identidad no semntico. De esta manera pondremos a prueba las programacin de la persistecia respecto a su capacidad de gestionar la identidad impuesta por un modelo de base de datos previo a la eleccin de la tecnologa de persistencia.

2.2 MODELO DE ENTIDADES O CLASES


La realidad que queremos modelar es un simple ejemplo de las entidades bsicas y las relaciones de los clientes de un banco y sus cuentas corrientes. 2.2.1 Modelo de una entidad

Consideraremos inicialmente el modelado de una simple entidad: el cliente del banco, con atributos tales como el nif, el nombre, la edad y la fecha de alta en el banco. Obviamente la identidad ms clara de la entidad es el nif. Este es su esquema UML de la clase correspondiente ya expresados los atributos con tipos de dato Java.

Cliente #m_nif:String #m_nombre:String #m_edad:int #m_alta:java.util.Date

Implementaremos la clase Java y las operaciones necesarias para almacenar una instancia, actualizar los datos no clave, y su eliminacin en la base de datos (el ciclo de vida), as como las consultas tpicas a partir del nif, nombre, edad y alta. 2.2.2 Modelo de una entidad relacionada con muchas instancias de otra entidad

Relacionaremos al cliente con las cuentas corrientes de su propiedad a travs de la entidad CuentaCliente. Dicha entidad no representa la cuenta corriente sino ms bien la vinculacin del cliente con dicha cuenta. Un cliente podr tener varias cuentas corrientes, pero por ahora consideramos que una cuenta no puede tener varios titulares. El atributo que identifica la cuenta corriente ser el nmero de cuenta (ncc), otro atributo significativo ser el instante de la ltima operacin que ha realizado el cliente sobre su cuenta.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.7

Tecnologas de Persistencia en Java, una comparativa desde la prctica

Cliente #m_nif:String 1 #m_nombre:String #m_edad:int #m_alta:java.util.Date

CuentaCliente 0..* #m_ncc:String #m_nif:String #m_ultimaOperacion:java.sql.Timestamp

Implementaremos el ciclo de vida de ambas entidades, consultas por cada de uno de sus atributos y la relacin expresada en Java a travs de un puntero y una coleccin. 2.2.3 Modelo muchos-muchos entre entidades

A continuacin ampliaremos el modelo introduciendo la entidad CuentaCorriente, y permitiremos la posibilidad de que una cuenta corriente tenga varios titulares. Tendremos por tanto que un cliente puede tener varias cuentas en propiedad y una cuenta puede tener varios titulares, es por tanto una relacin muchos-muchos entre entidades. Como sabr el lector, en el modelo relacional la relacin directa muchos-muchos entre dos tablas no existe, para expresarla se usa una tabla intermedia. En nuestro caso esta tabla intermedia ser la entidad CuentaCliente, pero ahora al permitirse que una misma cuenta tenga varios clientes, la identidad de CuentaCliente ser la unin de ncc y nif como identidad en conjunto. El siguiente diagrama UML, expresa la relacin directa muchos-muchos entre entidades y como se consigue a travs la una entidad intermedia necesaria para el modelo relacional (en un modelo puramente Java no es necesaria).

Cliente #m_nif:String #m_nombre:String 0..* #m_edad:int #m_alta:Date CuentaCorriente 0..* #m_ncc:String #m_saldo:long

1 0..* 0..* CuentaCliente

#m_ncc:String #m_nif:String #m_ultimaOperacion:Timestamp

2.2.4

Modelo de entidad con herencia de otra entidad

Si observamos la entidad Cliente considerada anteriormente, los datos nif, nombre y edad no son datos inherentes al concepto de cliente de un banco, es decir son datos que pueden estar presentes en las entidades de otros dominios de informacin, incluso en el modelo de informacin del banco en cuanto se modele la informacin de los empleados del banco por ejemplo. Es fcil entender que estos datos son propios de cualquier Persona, si una persona dada es a su vez Cliente del banco se entiende que adems debe tener un atributo de su fecha de alta como cliente, esta fecha de alta no es un atributo de una persona (ni de un empleado del banco que puede no ser cliente del mismo) sino que se entiende que es especfico de su papel como cliente del banco. Esta relacin conceptual entre Persona y Cliente en persistencia_java.doc v.1.0 Jos Mara Arranz Santamara Pg.8

Tecnologas de Persistencia en Java, una comparativa desde la prctica

programacin orientada a objetos se ha modelado clsicamente como una herencia entre una clase o entidad Persona y Cliente, ms exactamente Cliente hereda las propiedades de Persona y aade las suyas especficas.

Persona #m_nif:String #m_nombre:String #m_edad:int

Cliente #m_alta:Date

2.3 MODELO RELACIONAL


A continuacin expresamos las correspondientes tablas para cada modelo de entidades (clases) a travs de la sentencia SQL CREATE correspondiente. 2.3.1 Modelo una tabla

CREATE TABLE cliente ( nif CHAR(9) PRIMARY KEY, nombre VARCHAR(200), edad SMALLINT, alta DATE );

2.3.2

Modelo uno-muchos (dos tablas)

La tabla cliente es idntica al caso de una tabla.


CREATE TABLE cuenta_cliente ( ncc CHAR(20), nif CHAR(9), ultima_op TIMESTAMP, PRIMARY KEY (ncc), FOREIGN KEY (nif) REFERENCES cliente (nif), UNIQUE (ncc,ultima_op) );

Notar como la clave primaria ncc y la clave fornea nif determinan la relacin uno muchos. La restriccin UNIQUE sirve para asegurar que no pueda haber dos operaciones en el mismo instante sobre la cuenta 2.3.3 Modelo muchos-muchos (tres tablas)

La tabla cliente es idntica al caso de una tabla.


CREATE TABLE cuenta

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.9

Tecnologas de Persistencia en Java, una comparativa desde la prctica


( ncc CHAR(20) PRIMARY KEY, saldo BIGINT ); CREATE TABLE cuenta_cliente ( nif CHAR(9), ncc CHAR(20), ultima_op TIMESTAMP, PRIMARY KEY (nif,ncc), FOREIGN KEY (nif) REFERENCES cliente (nif), FOREIGN KEY (ncc) REFERENCES cuenta (ncc), UNIQUE (ncc,ultima_op) );

En este caso la combinacin nif y ncc forman la clave primaria por eso la relacin es muchos-muchos, a su vez son claves forneas lo que muestra que esta tabla muestra el vnculo entre el par de filas cliente cuenta que deben existir previamente. La restriccin UNIQUE sirve para asegurar que no pueda haber dos operaciones en el mismo instante sobre la cuenta 2.3.4 Modelo de herencia

De acuerdo a las decisiones de implementacin que adoptamos anteriormente, la herencia de dos clases se manifestar como dos tablas relacionas con una relacin 1-1, siendo la tabla principal la que coincide con la clase ms alta en el rbol de derivacin. En el caso de la herencia se produce una duplicacin de columnas clave necesario en el modelo relacional, dicha duplicidad no la repetiremos en el modelo de clases, esta sera una excepcin a la regla de correspondencia atributocolumna enunciada anteriormente.
CREATE TABLE persona ( nif CHAR(9) PRIMARY KEY, nombre VARCHAR(200), edad SMALLINT ); CREATE TABLE cliente ( nif CHAR(9) PRIMARY KEY, alta DATE, FOREIGN KEY (nif) REFERENCES persona (nif) );

De acuerdo con este esquema puede existir un registro en la tabla persona que no est en la tabla cliente, pero al contrario, si existe un registro en cliente necesariamente debe existir en la tabla persona.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.10

Tecnologas de Persistencia en Java, una comparativa desde la prctica

3. HERRAMIENTAS
Todo desarrollo software exige unas herramientas de ayuda al desarrollo y de ejecucin. Compilador, libreras y mquina virtual Java: usaremos el paquete ms moderno de Sun hasta el momento, el J2SE 1.4.2 SDK7 Entorno de desarrollo: NetBeans IDE8 v3.5 Herramientas de compilacin y de gestin de configuracin: Ant9 v1.5.1, la versin integrada en NetBeans Base de datos: para nuestros fines hemos optado por una sencilla bases de datos 100% Java muy compatible con ANSI SQL 92 : PointBase10 v4.5 Entorno de gestin visual de Bases de Datos: Squirrel SQL11 v1.2 Beta 3

El hecho de usar una base de datos muy estndar ANSI SQL 92 junto con el uso de JDBC que pretende independizar el programa del sistema de base de datos concreto, unido a la fuerte presencia del estndar ANSI SQL 92 en las bases de datos comerciales y de cdigo abierto, ilustra que la eleccin de la base de datos pueda hacerse hoy por criterios de rendimiento, escalabilidad y quizs por sus herramientas de gestin, pero no por razones de acoplamiento tecnolgico a una marca concreta. Constatamos que podemos sustituir nuestra base de datos por las grandes bases de datos comerciales tal y como Oracle DB e IBM DB/2 sin necesidad de cambiar ni una sola lnea de cdigo (en la medida en que no nos salgamos del SQL estndar), pero ganando evidentemente la capacidad de gestionar enormes cantidades de informacin, para miles de usuarios concurrentes a gran velocidad.

http://java.sun.com/j2se/1.4.2/download.html http://www.netbeans.org/ http://ant.apache.org/ http://www.pointbase.com http://squirrel-sql.sourceforge.net/

10

11

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.11

Tecnologas de Persistencia en Java, una comparativa desde la prctica

4. SOLUCIN JDBC
La especificacin JDBC es la ms antigua de todas las tecnologas de persistencia Java, y en ese sentido la ms madura. Fue concebida como una API con la nica finalidad de conectar con una base de datos relacional, enviar sentencias SQL a la misma y convertir los resultados a los tipos bsicos de Java (enteros, cadenas, coma flotante ...). Estrictamente hablando, JDBC no es ms que una especificacin en papel y unas cuantas interfases que no hacen nada por s mismas, los distintos fabricantes de bases de datos son los que han de implementar, dar cuerpo, a la especificacin, pero el resultado que se consigue es que la API de acceso es la misma para cualquier base de datos que tenga un driver JDBC que cumpla la especificacin, salvo en lo que concierne a las propias sentencias SQL, que son pasadas como cadenas de argumento, en donde s puede haber una variacin significativa de una base de datos a otra (si se usan las caractersticas SQL menos estndar de cada marca). Es relativamente sencilla de entender y utilizar, y muy flexible precisamente para adaptarse a las diversas estrategias de manipulacin de datos, puesto que no impone ninguna filosofa a la hora de modelar un sistema de clases correspondientes a un modelo de tablas. La contrapartida es que en el caso de querer representar las entidades (tablas) presentes en una base de datos con un modelo de clases, dicho trabajo ha de hacerse manualmente. En resumen podemos decir que es una tecnologa muy flexible pero a su vez con una alta impedancia respecto a sincronizar clases del usuario con tablas. Como nuestra finalidad es conseguir dicha sincronizacin entre tablas y clases, filas e instancias, seguiremos las pautas que ya enunciamos en el apartado de Decisiones de Implementacin, estas decisiones nos acotan bastante nuestras posibilidades. Aun as usaremos varias filosofas de programacin, cuyos nombres vendrn dados por su similitud a las filosofas empleadas en las tecnologas J2EE EJB Entity Beans: BMP (Bean Managed Persistence) y CMP (Container Managed Persistence). Clasificaremos nuestros ejemplos en tres filosofas: Tipo BMP: se asemejar a la forma de programar un EJB BMP aunque obviamente sin las caractersticas que se obtienen por el hecho de utilizar los servicios de autentificacin, ciclo de vida, control de la persistencia, transacciones, pooling etc. Tipo BMP Avanzado o BMP 2: ser similar al tipo BMP pero modelaremos un sencillo framework que evitar repetir sistemticamente algunos algoritmos tpicos de acceso a base de datos, y por otra parte llevaremos a las clases de utilidad (Home) la mayor parte del cdigo de gestin de la persistencia. Tipo CMP: pretender ser similar en filosofa a los EJB CMP, aprovechando parte de la infraestructura definida en el tipo BMP 2.

Como podemos comprobar vienen a ser dos filosofas: BMP y CMP.

4.1 TIPO BMP


Al igual que en los EJB BMP la clase persistente es una clase de datos normal en donde adems existirn mtodos que gestionen su persistencia, es decier su vinculacin con la tabla asociada, por tanto ser encargada de hacer la insercin, actualizacin, lectura, eliminacin y tambin las consultas. Desarrollaremos una clase Home correspondiente a cada clase persistente, pero su finalidad ser poco ms que la de transferir sus llamadas a la clase persistente que acta de forma similar a un bean BMP. Aunque la idea est basada en los EJB BMP no llegaremos al extremo de definir interfaces correspondientes con cada clase, aunque esta no es mala prctica en un programa real, mostraremos que JDBC no nos obliga a esto (lo cual si ocurre en los verdaderos EJB BMP). Como mucho definiremos un par de interfases Persistente y PersistenteHome pensadas para obligar al programador a implementar los mtodos correspondientes a la gestin de la persistencia y homogeneizar la apariencia del cdigo, ms que para independizar interfase de implementacin. persistencia_java.doc v.1.0 Jos Mara Arranz Santamara Pg.12

Tecnologas de Persistencia en Java, una comparativa desde la prctica

4.1.1

Una Tabla

4.1.1.1

NoEncontradoException.java

Se usar cuando necesitemos lanzar una excepcin que indique que no se puede leer una fila en la base de datos que debera existir. Reutilizremos esta clase en todos los casos que consideremos, de ah que la situemos en un paquete ms alto (paquete jdbc) que los paquetes especficos de cada caso de uso.
package jdbc; public class NoEncontradoException extends Exception { public NoEncontradoException() { } }

4.1.1.2

Database.java

Es una clase de utilidad cuya nica finalidad es la de encapsular el establecimiento de una conexin. La reutilizaremos de nuevo en los dems ejemplos de uso de JDBC y de esta manera no repetimos un cdigo que es idntico.
package jdbc; import java.sql.*; import java.io.*; import java.util.*; import java.net.*; import util.CargarPropiedades; public class Database { private Properties conProps = new Properties(); public Database() throws ClassNotFoundException, InstantiationException, IllegalAccessException, FileNotFoundException, IOException, URISyntaxException { conProps = CargarPropiedades.cargar("jdbc/jdbc.properties"); String driver = conProps.getProperty("driver"); try { Class.forName(driver).newInstance(); } catch(ClassNotFoundException ex) { System.out.println("Driver no encontrado:" + driver); throw ex; } } public Connection conectar() throws SQLException { String url = conProps.getProperty("urldb"); String user = conProps.getProperty("user"); String password = conProps.getProperty("password"); return DriverManager.getConnection(url,user,password); } }

4.1.1.3

Persistente.java

Es la interfase con los mtodos que una clase persistente debe implementar.
package jdbc.tipobmp.comun; import java.sql.*; public interface Persistente {

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.13

Tecnologas de Persistencia en Java, una comparativa desde la prctica


public void setConnection(Connection con); public Connection getConnection(); public void setHome(PersistenteHome home); public PersistenteHome getHome(); public void eliminar() throws SQLException; public void actualizar() throws SQLException; }

Observamos la presencia de los mtodos que asocian y obtienen la conexin con la base de datos, la asociacin con el objeto de utilidad, la eliminacin y la actualizacin del registro de la base de datos. La insercin se realizar a travs del mtodo crear() especfico de cada clase persistente. 4.1.1.4 PersistenteHome.java

Es la interfase con los mtodos que una clase de utilidad persistente debe implementar.
package jdbc.tipobmp.comun; import java.sql.*; public interface PersistenteHome { public void setConnection(Connection con); public Connection getConnection(); public abstract Persistente crearObjeto(); }

4.1.1.5

Cliente.java

La clase Cliente implementa los mtodos necesarios para sincronizar su estado con el de la fila correspondiente de la base de datos. Adems implementa los mtodos de bsqueda ms habituales tal y como buscar el cliente con el nif dado, los clientes con un nombre dado, edad, fecha de alta (devolviendo una coleccin de objetos Cliente), un mtodo que cuenta los clientes que existen (contar()) y un mtodo especial, buscarAbierto() con la finalidad de poder hacer consultas menos tpicas sobre los clientes.
package jdbc.tipobmp.unatabla; import java.sql.*; import java.util.*; import jdbc.NoEncontradoException; import jdbc.tipobmp.comun.*; public class Cliente implements Persistente { protected String m_nif; protected String m_nombre; protected int m_edad; protected java.util.Date m_alta; protected Connection m_con; protected PersistenteHome m_home; public Cliente() { m_nombre = ""; m_alta = new java.util.Date(); } public Cliente(String nif,String nombre,int edad,java.util.Date alta) { m_nif = nif; m_nombre = nombre; m_edad = edad; m_alta = alta; }

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.14

Tecnologas de Persistencia en Java, una comparativa desde la prctica


public String getNif() { return m_nif; } public void setNif(String nif) { m_nif = nif; } public String getNombre() { return m_nombre; } public void setNombre(String nombre) { m_nombre = nombre; } public int getEdad() { return m_edad; } public void setEdad(int edad) { m_edad = edad; } public java.util.Date getAlta() { return m_alta; } public void setAlta(java.util.Date alta) { m_alta = alta; } public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con; } public void setHome(PersistenteHome home) { m_home = home; } public PersistenteHome getHome() { return m_home; } public void crear(String nif,String nombre,int edad,java.util.Date alta) throws SQLException { m_nif = nif; m_nombre = nombre; m_edad = edad; m_alta = alta; insertar(); }

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.15

Tecnologas de Persistencia en Java, una comparativa desde la prctica

public void actualizar() throws SQLException { PreparedStatement stmt = null; try { String sql = "UPDATE cliente SET nombre=?, edad=?, alta=? WHERE nif=?"; stmt = getConnection().prepareStatement(sql); stmt.setString(1,m_nombre); stmt.setInt(2,m_edad); stmt.setDate(3,new java.sql.Date(m_alta.getTime())); stmt.setString(4,m_nif); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } } public void eliminar() throws SQLException { PreparedStatement stmt = null; try { String sql = "DELETE FROM cliente WHERE nif=?"; stmt = getConnection().prepareStatement(sql); stmt.setString(1,m_nif); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } m_con = null; } public void insertar() throws SQLException { PreparedStatement stmt = null; try { String sql = "INSERT INTO cliente (nombre,edad,alta,nif) VALUES(?,?,?,?)"; stmt = getConnection().prepareStatement(sql); stmt.setString(1,m_nombre); stmt.setInt(2,m_edad); stmt.setDate(3,new java.sql.Date(m_alta.getTime())); stmt.setString(4,m_nif); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } } public void leer() throws SQLException,NoEncontradoException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cliente WHERE nif=?"; stmt = getConnection().prepareStatement(sql); stmt.setString(1,m_nif); ResultSet res = stmt.executeQuery(); if (!res.next()) new NoEncontradoException(); leerFila(res); } finally

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.16

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ if (stmt != null) stmt.close(); } } public void leerFila(ResultSet res) throws SQLException { m_nif = res.getString("nif"); m_nombre = res.getString("nombre"); m_edad = res.getInt("edad"); m_alta = res.getDate("alta"); } protected Collection leer(ResultSet res) throws SQLException { Collection col = new LinkedList(); while(res.next()) { Cliente obj = (Cliente)getHome().crearObjeto(); obj.leerFila(res); col.add(obj); } return col; } public Cliente buscarPorClavePrimaria(Object clave) throws SQLException,NoEncontradoException { Cliente obj = (Cliente)getHome().crearObjeto(); String nif = (String)clave; obj.setNif(nif); obj.leer(); return obj; } public Collection buscarPorEdad(int edad) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cliente WHERE edad=?"; stmt = m_con.prepareStatement(sql); stmt.setInt(1,edad); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorNombre(String nombre) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cliente WHERE nombre=?"; stmt = m_con.prepareStatement(sql); stmt.setString(1,nombre); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarAbierto(String sql) throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.17

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ Statement stmt = null; try { stmt = m_con.createStatement(); ResultSet res = stmt.executeQuery(sql); return leer(res); } finally { if (stmt != null) stmt.close(); } } public int contar() throws SQLException { Statement stmt = null; try { String sql = "SELECT COUNT(*) AS num FROM cliente"; stmt = m_con.createStatement(); ResultSet res = stmt.executeQuery(sql); res.next(); return res.getInt("num"); } finally { if (stmt != null) stmt.close(); } } public String toString() { return "nif: " + m_nif + " nombre: " + m_nombre + " edad: " + m_edad + " alta: " + m_alta; } }

A simple vista podemos comprobar la gran cantidad de sentencias similares, esto es mala seal, significa que no hemos hecho apenas ningn esfuerzo de abstraccin. La repeticin sistemtica de cdigo conlleva el problema de dar lugar a programas muy rgidos y muy difciles de evolucionar, pues cualquier pequeo cambio en la filosofa de hacer algo puede suponer una modificacin del cdigo en infinidad de lugares del cdigo, con el correspondiente riesgo de olvidar algunos sitios. Tras la inspeccin de los mtodos buscar podemos advertir varios problemas que afectan al rendimiento y a la capacidad del sistema: 1. La carga de un objeto es realizada totalmente, es decir se cargan todos los atributos de la fila en la base de datos, sin embargo es posible que la mayora de los atributos no sean accedidos en el uso del objeto cargado. Esto es un problema de rendimiento pues se hacen lecturas innecesarias de datos. La consulta de un conjunto de elementos supone la formacin de una coleccin de objetos, correspondientes a los registros resultantes de la consulta. Consideremos una consulta de 1000 de resultados cuando slo nos interesar acceder a los 10 primeros (lo cual es bastante habitual a la hora de mostrar listados de colecciones de datos muy grandes), los 1000 objetos se cargarn en memoria aunque no se usen. Esto supone un serio problema de rendimiento (carga innecesaria de elementos que no se utilizan) y capacidad (puede poner al lmite la capacidad del sistema). Los mtodos de consulta no hacen ningn tipo de comprobacin de si un objeto ya ha sido cargado anteriormente y est en memoria, por lo que vuelve a ser cargado con la prdida de rendimiento que supone esto significa que no aprovechamos las acciones previas para evitar acciones futuras innecesarias.

2.

3.

Los puntos 1 y 2 es posible minizarlos con lo que se llama la carga perezosa o lazy loading, que consiste bsicamente en cargar bajo demanda, es decir cuando se intente usar el atributo o se avance en el recorrido de la coleccin, se cargar de la base de datos la correspondiente columna o fila. Conseguir la lazy loading no es un problema trivial.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.18

Tecnologas de Persistencia en Java, una comparativa desde la prctica El punto 3 evidencia la falta de algn tipo de cach de objetos persistentes que se consultara antes de acudir a la base de datos, proceso que por muy rpida que fuera la base de datos es significativamente ms lento que una bsqueda en memoria de un pequeo fragmento de la informacin de la base de datos, por ello cada vez que hacemos una bsqueda consultamos a la base de datos informacin que podemos tener ya en memoria. Hacer un cach de objetos persistentes tiene muchas implicaciones, tal y como gestionar la identidad de los objetos en memoria de acuerdo con la identidad en la base de datos (mtodos equals, hash), controlar el estado del objeto respecto al almacenamiento persistente (si es nuevo, si ha sido modificado, si ha sido eliminado, si no es persistente etc), interceptar toda bsqueda para que se haga primero en la cach, lo cual es una gran dificultad en el caso de bsquedas abiertas pues supondra analizar la intencionalidad de la consulta SQL etc. Este problema de rendimiento no quedar resuelto en nuestros ejemplos de JDBC para ilustrar la complejidad que supone hacer un sistema persistente eficiente y a medida basado en JDBC nicamente. No resolveremos estos problemas en nuestros ejemplos de JDBC, pues as ilustramos la necesidad de una mayor infraestructura de gestin de la persistencia ms hay de las simples llamadas a JDBC que aqu exponemos, poniendo en evidencia la necesidad de tecnologas ms avanzadas. 4.1.1.6 ClienteHome.java

Como ya dijimos anteriormente, la finalidad de esta clase es la de encapsular las operaciones de creacin de un nuevo objeto persistente (a modo de clase factora, lo cual buena prctica pues asegura que el objeto persistente queda asociado a una conexin y al objeto Home que lo cre) y transferir las llamadas de bsqueda de objetos a un objeto persistente auxiliar creado al efecto.
package jdbc.tipobmp.unatabla; import import import import java.sql.*; java.util.*; jdbc.NoEncontradoException; jdbc.tipobmp.comun.*;

public class ClienteHome implements PersistenteHome { protected Connection m_con; public ClienteHome(Connection con) { m_con = con; } public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con; } public Cliente buscarPorClavePrimaria(Object clave) throws SQLException,NoEncontradoException { Cliente obj = (Cliente)crearObjeto(); return obj.buscarPorClavePrimaria(clave); } public Collection buscarPorEdad(int edad) throws SQLException { Cliente obj = (Cliente)crearObjeto(); return obj.buscarPorEdad(edad); } public Collection buscarPorNombre(String nombre) throws SQLException { Cliente obj = (Cliente)crearObjeto(); return obj.buscarPorNombre(nombre); } public Collection buscarAbierto(String sql) throws SQLException { Cliente obj = (Cliente)crearObjeto(); return obj.buscarAbierto(sql); }

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.19

Tecnologas de Persistencia en Java, una comparativa desde la prctica


public int contar() throws SQLException { Cliente obj = (Cliente)crearObjeto(); return obj.contar(); } public Cliente crear(String nif,String nombre,int edad,java.util.Date alta) throws SQLException { Cliente obj = (Cliente)crearObjeto(); obj.crear(nif,nombre,edad,alta); return obj; } public Persistente crearObjeto() { Persistente obj = new Cliente(); obj.setConnection(m_con); obj.setHome(this); return obj; } }

4.1.1.7

JDBCInicio.java

Esta clase es un programa ejemplo del tipo de operaciones tpicas de la manipulacin de datos persistentes: conectar con la base de datos, crear un objeto persistente, cambiar sus datos y actualizar en la base de datos, buscar por clave, buscar por los diferentes tipos de atributos, bsquedas ms sofisticadas y por ltimo la destruccin. Todo ello en una transaccin dirigida por la base de datos.
package jdbc.tipocmp.unatabla; import java.sql.*; import java.util.*; import java.text.*; import jdbc.Database; public class JDBCInicio { public JDBCInicio() { } public static void main(String[] args) throws Exception { Connection con = null; try { con = new Database().conectar(); ClienteHome clienteHome = new ClienteHome(con); con.setAutoCommit(false); Cliente cliente1 = clienteHome.crear("50000000P","Iaki Jabilondo",45,new GregorianCalendar(2003,8,20).getTime()); Cliente cliente2 = clienteHome.crear("40000000P","Luis del Olmo",47,new GregorianCalendar(2003,8,21).getTime()); cliente1.setNombre("Iaki Gabilondo"); cliente1.actualizar(); cliente1 = (Cliente)clienteHome.buscarPorClavePrimaria("50000000P"); System.out.println(cliente1); Collection col = clienteHome.buscarPorNombre("Luis del Olmo"); for(Iterator it = col.iterator(); it.hasNext(); ) { Cliente cliente = (Cliente)it.next(); System.out.println(cliente); }

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.20

Tecnologas de Persistencia en Java, una comparativa desde la prctica


col = clienteHome.buscarPorEdad(45); for(Iterator it = col.iterator(); it.hasNext(); ) { Cliente cliente = (Cliente)it.next(); System.out.println(cliente); } String sql = "SELECT * FROM cliente WHERE nombre LIKE 'Luis%' OR nombre LIKE '%Gabilondo'"; col = clienteHome.buscarAbierto(sql); for(Iterator it = col.iterator(); it.hasNext(); ) { Cliente cliente = (Cliente)it.next(); System.out.println(cliente); } int num = clienteHome.contar(); System.out.println(num); cliente1.eliminar(); cliente2.eliminar(); con.commit(); } catch(Exception ex) { ex.printStackTrace(); } finally { if (con != null) { con.rollback(); con.close(); } } } }

Podramos seguir con los casos de relacin uno-muchos, muchos-muchos y herencia. Sin embargo hemos visto que hay un montn de cdigo repetitivo, intentaremos generalizar las operaciones con el fin de disminuir la cantidad de cdigo elevando la calidad del mismo, para ello desarrollamos un pequeo framework persistente en el llamado tipo BMP 2 y desde dicho modelo afrontaremos el problema de las relaciones y la herencia.

4.2 TIPO BMP AVANZADO O TIPO BMP 2


Desarrollar un framework supone que el cdigo genrico, el que no est vinculado a ningn tipo de clase persistente concreto, es mayor, y establece un contrato ms complicado con la clase persistente concreta, dicha clase tendr que definir una serie de funciones que sern llamadas por el framework ms que por el programador directamente. No pretendemos realizar un framework sofisticado, sino ilustrar la necesidad de un framework para disminuir la impedancia entre el modelo relacional y de objetos, evitando hacer un montn de cdigo repetitivo. De hecho las dems tecnologas de persistencia que veremos, exceptuando el propio JDBC, son sofisticados frameworks de persistencia estandarizados por la industria. Nuestro framework no ofrecer a la necesidad del lazy loading y de una cach de objetos. Por una parte programaremos un conjunto de clases genricas que traten a los objetos persistentes de una forma genrica (el ncleo del framework), y por otra parte aquellas tareas de gestin de la persistencia que nos resultan difciles de programar las llevaremos a las clases Home especficas. Esto ltimo es en opinin del que escribe un buen

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.21

Tecnologas de Persistencia en Java, una comparativa desde la prctica patrn, pues despeja lo ms posible la clase persistente acercndonos al ideal de la persistencia transparente del modelo de informacin (digamos que la persistencia se aglutina aparte en clases especiales destinadas para ello) y es tambin en contrapartida un error en la filosofa de los EJB BMP, pues en ellos se mezcla el tratamiento de la persistencia del objeto respecto a la fila (el patrn de uso ms habitual de los BMP), con la manipulacin de conjuntos a travs de las llamadas de bsqueda (findByXXX), el hecho de que los BMP mantengan una conexin y se especificara su contrato con el container, invitara a los diseadores de la especificacin inicial a que definir una lgica similar a posibles clases Home podra suponer una reiteracin del modelo (a modo de Entity Bean Home). De todas formas siempre se puede minimizar este problema de diseo en los EJB BMP creando una especie de implementacin de la interfase Home llamada desde el bean ahora ms despejado de cdigo persistente. 4.2.1 Clases Comunes (framework)

Aunque expondremos el cdigo de las clases genricas, lo ms importante de cualquier framework es conocer cual es el contrato (expresado sobre todo por las interfases) que han de cumplir los componentes que son manipulados por el mismo y los servicios que ofrece, y no tanto como est programado por dentro. 4.2.1.1 Persistente.java

La interfaz Persistente es idntica al caso anterior, sin embargo veremos que no ser necesario implemenmtar sus mtodos en cada clase persistente, sino que lo haremos en una clase base genrica. 4.2.1.2 PersistenteHome.java

Esta interfase ser ms complicada que en el caso anterior, puesto que esta serie de mtodos han de ser implementados por una parte por la clase que implemente el interfaz de forma genrica, pero tambin por la clase Home correspondiente a cada clase persistente para aquellos aspectos que no son fcilmente generalizables.
package jdbc.tipobmp2.comun; import java.sql.*; import jdbc.NoEncontradoException; public interface PersistenteHome { public void setConnection(Connection con); public Connection getConnection(); public abstract Persistente crearObjeto(); public abstract Persistente crearObjeto(Object clave); public void setStatementParams(Persistente obj,String table,PreparedStatement stmt) throws SQLException; public void setStatementClave(Persistente obj,PreparedStatement stmt) throws SQLException; public public public public public } void void void void void eliminar(Persistente obj) throws SQLException; actualizar(Persistente obj) throws SQLException; insertar(Persistente obj) throws SQLException; leer(Persistente obj) throws SQLException,NoEncontradoException; leer(Persistente obj,ResultSet res) throws SQLException;

4.2.1.3

PersistenteImpl.java

La clase de la que han de derivar las clases persistentes concretas.


package jdbc.tipobmp2.comun; import java.sql.*; public abstract class PersistenteImpl implements Persistente { private Connection m_con; private PersistenteHome m_home; public PersistenteImpl()

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.22

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ }

public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con; } public void setHome(PersistenteHome home) { m_home = home; } public PersistenteHome getHome() { return m_home; } public void actualizar() throws SQLException { getHome().actualizar(this); } public void eliminar() throws SQLException { getHome().eliminar(this); } }

Podemos ver lo simple que es esta clase, apenas un contenedor de los atributos conexin y referencia al objeto Home que crea el objeto persistente, y los mtodos actualizar() y eliminar() que derivan la llamada al objeto Home. De hecho nada impedira poner este cdigo en la propia clase persistente concreta del modelo de datos y de esta manera no ser intrusivo en la herencia por arriba, e incluso prescindir de albergar el atributo de la conexin con la base de datos, pues sta se puede obtener a partir del objeto Home (hemos mantenido este atributo por su afinidad al verdadero J2EE BMP). 4.2.1.4 PersistenteHomeImpl.java

La clase de la que han de derivar las clases persistentes de utilidad o Home:


package jdbc.tipobmp2.comun; import java.sql.*; import java.util.*; import jdbc.NoEncontradoException; public abstract class PersistenteHomeImpl implements PersistenteHome { protected Connection m_con; public PersistenteHomeImpl(Connection con) { m_con = con; } public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con;

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.23

Tecnologas de Persistencia en Java, una comparativa desde la prctica


} public Persistente crear(Persistente obj) { obj.setConnection(m_con); obj.setHome(this); insertar(obj); return obj; } throws SQLException

public Persistente crear(Object clave) throws SQLException { Persistente obj = crearObjeto(clave); return crear(obj); } public void insertar(Persistente obj,String table,String sql) { PreparedStatement stmt = null; try { stmt = obj.getConnection().prepareStatement(sql); setStatementParams(obj,table,stmt); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } } public void actualizar(Persistente obj,String table,String sql) throws SQLException { PreparedStatement stmt = null; try { stmt = obj.getConnection().prepareStatement(sql); setStatementParams(obj,table,stmt); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } } public void eliminar(Persistente obj,String table,String sql) { PreparedStatement stmt = null; try { stmt = obj.getConnection().prepareStatement(sql); setStatementClave(obj,stmt); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } } throws SQLException throws SQLException

public void leer(Persistente obj,String sql) throws SQLException,NoEncontradoException { PreparedStatement stmt = null; try { stmt = obj.getConnection().prepareStatement(sql);

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.24

Tecnologas de Persistencia en Java, una comparativa desde la prctica


setStatementClave(obj,stmt); ResultSet res = stmt.executeQuery(); if (!res.next()) new NoEncontradoException(); leer(obj,res); } finally { if (stmt != null) stmt.close(); } } protected Collection leer(ResultSet res) throws SQLException { Collection col = new LinkedList(); while(res.next()) { Persistente obj = crearObjeto(); obj.setConnection(m_con); obj.setHome(this); leer(obj,res); col.add(obj); } return col; } public Persistente buscarPorClavePrimaria(Object clave) throws SQLException { Persistente obj = crearObjeto(clave); try { obj.setConnection(m_con); obj.setHome(this); leer(obj); return obj; } catch(NoEncontradoException ex) { return null; } } public Collection buscarAbierto(String sql) throws SQLException { Statement stmt = null; try { stmt = m_con.createStatement(); ResultSet res = stmt.executeQuery(sql); return leer(res); } finally { if (stmt != null) stmt.close(); } } }

Bsicamente el objetivo de esta clase es realizar las llamadas a la base de datos necesarias para gestionar la persistencia del objeto manipulado, llamando a los mtodos necesarios definidos en Persistente de dicho objeto que actuaran a modo de callbacks. 4.2.2 Una Tabla

Una vez definida la infraestructura abordamos el primer caso que ya consideramos de forma directa anteriormente, ahora gracias a la infraestructura tendremos una disminucin muy significativa de cdigo sin perder funcionalidad y apenas flexibilidad, lo cual redunda en la calidad del cdigo resultante.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.25

Tecnologas de Persistencia en Java, una comparativa desde la prctica 4.2.2.1 Cliente.java

La clase persistente que representa al cliente. Notar la casi completa inexistencia de cdigo relacionado con la persistencia, gracias a que la inmensa mayor parte residir en la clase Home creada al efecto. Varios de sus mtodos sern llamados por la clase Home, como por ejemplo el mtodo crear() que define todos los datos de la instancia y que se llamar en creacin del registro en la base de datos.
package jdbc.tipobmp2.unatabla; import java.sql.*; import jdbc.tipobmp2.comun.*; public class Cliente extends PersistenteImpl { protected String m_nif; protected String m_nombre; protected int m_edad; protected java.util.Date m_alta; public Cliente() { m_nombre = ""; m_alta = new java.util.Date(); } public Cliente(String nif) { m_nif = nif; } public void crear(String nif,String nombre,int edad,java.util.Date alta) { m_nif = nif; m_nombre = nombre; m_edad = edad; m_alta = alta; } public String getNif() { return m_nif; } public void setNif(String nif) { m_nif = nif; } public String getNombre() { return m_nombre; } public void setNombre(String nombre) { m_nombre = nombre; } public int getEdad() { return m_edad; } public void setEdad(int edad) { m_edad = edad; } public java.util.Date getAlta() {

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.26

Tecnologas de Persistencia en Java, una comparativa desde la prctica


return m_alta; } public void setAlta(java.util.Date alta) { m_alta = alta; } public String toString() { return "nif: " + m_nif + " nombre: " + m_nombre + " edad: " + m_edad + " alta: " + m_alta; } }

4.2.2.2

ClienteHome.java

Es la clase que ms nos interesa respecto a la persistencia, pues sobre ella recae la coordinacin del objeto persistente, teniendo a su vez un contrato con el framework a travs de su clase base.
package jdbc.tipobmp2.unatabla; import java.sql.*; import java.util.*; import jdbc.NoEncontradoException; import jdbc.tipobmp2.comun.*; public class ClienteHome extends PersistenteHomeImpl { public ClienteHome(Connection con) throws SQLException { super(con); } public void insertar(Persistente obj) { insertar(obj,"cliente","INSERT VALUES(?,?,?,?)"); } throws SQLException INTO cliente (nombre,edad,alta,nif)

public void actualizar(Persistente obj) throws SQLException { actualizar(obj,"cliente","UPDATE cliente SET nombre=?, edad=?, alta=? WHERE nif=?"); } public void eliminar(Persistente obj) throws SQLException { eliminar(obj,"cliente","DELETE FROM cliente WHERE nif=?"); } public void leer(Persistente obj) throws SQLException,NoEncontradoException { leer(obj,"SELECT * FROM cliente WHERE nif=?"); } public void leer(Persistente obj,ResultSet res) { Cliente cliente = (Cliente)obj; cliente.setNif(res.getString("nif")); cliente.setNombre(res.getString("nombre")); cliente.setEdad(res.getInt("edad")); cliente.setAlta(res.getDate("alta")); } public void setStatementParams(Persistente stmt) throws SQLException { Cliente cliente = (Cliente)obj; obj,String tabla,PreparedStatement throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.27

Tecnologas de Persistencia en Java, una comparativa desde la prctica

stmt.setString(1,cliente.getNombre()); stmt.setInt(2,cliente.getEdad()); stmt.setDate(3,new java.sql.Date(cliente.getAlta().getTime())); stmt.setString(4,cliente.getNif()); } public void setStatementClave(Persistente SQLException { Cliente cliente = (Cliente)obj; stmt.setString(1,cliente.getNif()); } public Collection buscarPorEdad(int edad) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cliente WHERE edad=?"; stmt = m_con.prepareStatement(sql); stmt.setInt(1,edad); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorNombre(String nombre) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cliente WHERE nombre=?"; stmt = m_con.prepareStatement(sql); stmt.setString(1,nombre); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public int contar() throws SQLException { Statement stmt = null; try { String sql = "SELECT COUNT(*) AS num FROM cliente"; stmt = m_con.createStatement(); ResultSet res = stmt.executeQuery(sql); res.next(); return res.getInt("num"); } finally { if (stmt != null) stmt.close(); } } public Cliente crear(String nif,String throws SQLException { Cliente obj = (Cliente)crearObjeto(); nombre,int edad,java.util.Date alta) obj,PreparedStatement stmt) throws

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.28

Tecnologas de Persistencia en Java, una comparativa desde la prctica


obj.crear(nif,nombre,edad,alta); return (Cliente)crear(obj); } public Persistente crearObjeto() { return new Cliente(); } public Persistente crearObjeto(Object clave) { String nif = (String)clave; return new Cliente(nif); } }

Los mtodos insertar, actualizar, leer, setStatementParams, setStatementeClave, crear, crearObjeto, son contractuales, realizan aquellas tareas que es difcil hacer de forma genrica y se hacen aqu de forma sencilla en la clase especfica tal y como suministrar las sentencias SQL necesarias, unas son llamadas por el programador directamente (ej. actualizar), otras son llamadas por el framework (los setStatementXXX por ejemplo) a travs de la clase base. 4.2.2.3 JDBCInicio.java

Puede ser idntico a la clase del mismo nombre en el modelo tipo BMP, pues no hemos cambiado la interfaz que es ofrecida al programador final. Esta es una de las grandezas de la programacin orientada a objetos y la aplicacin de patrones de programacin, que una parte de un programa puede cambiar profundamente pero mientras no cambie el contrato que exista con el resto de la aplicacin, lo dems no tiene que cambiar. En la prctica esto no ocurre tan idealmente, pero se consigue que las implicaciones de los cambios con el resto de la aplicacin sean mnimos respecto a una programacin con reutilizacin nula. 4.2.3 Relacin Uno Muchos

Reutilizaremos la infraestructura definida para el caso de una tabla, adaptaremos el resto de las clases para introducir la relacin y crearemos las nuevas clases correspondientes a la nueva tabla. 4.2.3.1 Cliente.java

La nica diferencia respecto al caso de una tabla es la introduccin de un mtodo para obtener las cuentas corrientes que son propiedad del cliente, esto lo conseguimos a travs de una bsqueda con un objeto CuentaClienteHome.
public Collection getClienteCuentas() throws SQLException { CuentaClienteHome cuentaCliHome = new CuentaClienteHome(getConnection()); return cuentaCliHome.buscarPorCliente(m_nif); }

4.2.3.2

ClienteHome.java

Es idntico funcionalmente al caso de una tabla, salvo la introduccin del mtodo buscarPorCuenta():
public Collection buscarPorCuenta(String ncc) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT c.nif,c.nombre,c.edad,c.alta \n"+ "FROM cliente c,cuenta_cliente cc \n"+ "WHERE cc.ncc=? AND c.nif=cc.nif"; stmt = m_con.prepareStatement(sql); stmt.setString(1,ncc); ResultSet res = stmt.executeQuery(); return leer(res);

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.29

Tecnologas de Persistencia en Java, una comparativa desde la prctica


} finally { if (stmt != null) stmt.close(); } }

Este mtodo se podra haber realizado de la siguiente forma:


public Collection buscarPorCuenta2(String ncc) throws SQLException { CuentaClienteHome cuentaCliHome = new CuentaClienteHome(getConnection()); CuentaCliente cuentaCli = (CuentaCliente)cuentaCliHome.buscarPorClavePrimaria(ncc); Collection col = new LinkedList(); Cliente cliente = cuentaCli.getCliente(); col.add(cliente); return col; }

Sin embargo presenta el problema de ser mucho menos eficiente que la forma anterior, porque realiza dos consultas en vez de una, la primera en la bsqueda de la cuenta y la segunda en la bsqueda del cliente de la cuenta. En la forma anterior hacemos un join de las dos tablas, es de sobra conocido que el join, que viene a ser una mezcla de dos consultas, es significativamente ms rpido que dos consultas separadas, pues hay que aadir que adems hay un ahorro en el transporte de informacin del programa a la base de datos. Esta es una de las pocas ventajas del JDBC frente a otros sistemas, que permite una gran flexibilidad a la hora de poder utilizar las caractersticas ms avanzadas de la base de datos concreta. 4.2.3.3 CuentaCliente.java

Modelamos la vinculacin de un cliente con su cuenta corriente con esta clase.


package jdbc.tipobmp2.unomuchos; import java.sql.*; import jdbc.tipobmp2.comun.*; public class CuentaCliente extends PersistenteImpl { protected String m_ncc; // Nmero de la cuenta corriente protected String m_nif; protected Timestamp m_ultimaOperacion; public CuentaCliente() { } public CuentaCliente(String ncc) { m_ncc = ncc; } public void crear(String nif, String ncc, Timestamp ultimaOp) { m_nif = nif; m_ncc = ncc; m_ultimaOperacion = ultimaOp; } public String getNif() { return m_nif; } public void setNif(String nif) { m_nif = nif; } public String getNcc()

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.30

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ return m_ncc; } public void setNcc(String ncc) { m_ncc = ncc; } public Timestamp getUltimaOperacion() { return m_ultimaOperacion; } public void setUltimaOperacion(Timestamp ultimaOp) { m_ultimaOperacion = ultimaOp; } public Cliente getCliente() throws SQLException { ClienteHome clienteHome = new ClienteHome(getConnection()); return (Cliente)clienteHome.buscarPorClavePrimaria(m_nif); } public String toString() { return "ncc: " + m_ncc m_ultimaOperacion; } }

"

nif:

"

m_nif

"

ltima

op.:

"

Observamos el mtodo getCliente() que obtiene a travs de una consulta usando la clase ClienteHome, el cliente asociado a la cuenta conocido el nif que es atributo de la misma. 4.2.3.4 CuentaClienteHome.java

De forma similar a ClienteHome creamos esta clase para la gestin del ciclo de vida de los objetos CuentaCliente y para realizar bsquedas.
package jdbc.tipobmp2.unomuchos; import java.sql.*; import java.util.*; import jdbc.NoEncontradoException; import jdbc.tipobmp2.comun.*; public class CuentaClienteHome extends PersistenteHomeImpl { public CuentaClienteHome(Connection con) throws SQLException { super(con); } public void insertar(Persistente obj) throws SQLException { insertar(obj,"cuenta_cliente","INSERT INTO cuenta_cliente (ultima_op,nif,ncc) VALUES(?,?,?)"); } public void actualizar(Persistente obj) throws SQLException { actualizar(obj,"cuenta_cliente","UPDATE cuenta_cliente SET ultima_op=? WHERE ncc=?"); } public void eliminar(Persistente obj) throws SQLException { eliminar(obj,"cuenta_cliente","DELETE FROM cuenta_cliente WHERE ncc=?"); }

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.31

Tecnologas de Persistencia en Java, una comparativa desde la prctica

public void leer(Persistente obj) throws SQLException,NoEncontradoException { leer(obj,"SELECT * FROM cuenta_cliente WHERE ncc=?"); } public void leer(Persistente obj,ResultSet res) { CuentaCliente cuenta = (CuentaCliente)obj; throws SQLException

cuenta.setNif(res.getString("nif")); cuenta.setNcc(res.getString("ncc")); cuenta.setUltimaOperacion(res.getTimestamp("ultima_op")); } public void setStatementParams(Persistente obj,String stmt) throws SQLException { CuentaCliente cuenta = (CuentaCliente)obj; stmt.setTimestamp(1,cuenta.getUltimaOperacion()); stmt.setString(2,cuenta.getNif()); stmt.setString(3,cuenta.getNcc()); } public void setStatementClave(Persistente obj,PreparedStatement SQLException { CuentaCliente cuenta = (CuentaCliente)obj; stmt.setString(1,cuenta.getNcc()); } public Collection buscarPorDespuesUltimaOp(Timestamp instante) SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cuenta_cliente WHERE ultima_op >= ?"; stmt = m_con.prepareStatement(sql); stmt.setTimestamp(1,instante); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } throws stmt) throws tabla,PreparedStatement

public Collection buscarPorAntesUltimaOp(Timestamp instante) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cuenta_cliente WHERE ultima_op < ?"; stmt = m_con.prepareStatement(sql); stmt.setTimestamp(1,instante); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorCliente(String nif) { throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.32

Tecnologas de Persistencia en Java, una comparativa desde la prctica


PreparedStatement stmt = null; try { String sql = "SELECT * FROM cuenta_cliente WHERE nif=?"; stmt = m_con.prepareStatement(sql); stmt.setString(1,nif); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorNcc(String ncc) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cuenta_cliente WHERE ncc=?"; stmt = m_con.prepareStatement(sql); stmt.setString(1,ncc); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public CuentaCliente crear(String nif,String ncc,Timestamp SQLException { CuentaCliente obj = (CuentaCliente)crearObjeto(); obj.crear(nif,ncc,ultimaOp); return (CuentaCliente)crear(obj); } public Persistente crearObjeto() { return new CuentaCliente(); } public Persistente crearObjeto(Object clave) { String ncc = (String)clave; return new CuentaCliente(ncc); } } ultimaOp) throws

4.2.3.5

JDBCInicio.java

Como ejemplo de uso nos interesar como novedad navegar por las relaciones, dicha navegacin supondr las necesarias consultas a la base de datos. Crearemos tres cuentas corrientes asociadas dos de ellas a un cliente y la restante al segundo cliente, obtendremos a travs del cliente las cuentas asociadas con Cliente.getClienteCuentas(), a travs de la cuenta su cliente asociado con CuentaCliente.getCliente(), buscaremos el cliente de una cuenta dada con ClienteHome.buscarPorCuenta() etc.
package jdbc.tipobmp2.unomuchos; import java.sql.*; import java.util.*; import java.text.*; import jdbc.Database; public class JDBCInicio {

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.33

Tecnologas de Persistencia en Java, una comparativa desde la prctica


public JDBCInicio() { } public static void main(String[] args) throws Exception { Connection con = null; try { con = new Database().conectar(); ClienteHome clienteHome = new ClienteHome(con); CuentaClienteHome cuentaCliHome = new CuentaClienteHome(con); con.setAutoCommit(false); Cliente cliente1 = clienteHome.crear("50000000P","Iaki Gabilondo",45,new GregorianCalendar(2003,8,20).getTime()); Cliente cliente2 = clienteHome.crear("40000000P","Luis del Olmo",47,new GregorianCalendar(2003,8,21).getTime()); CuentaCliente cuentaCli1 = cuentaCliHome.crear("50000000P","111", Timestamp(new GregorianCalendar(2003,8,20,10,0,0).getTimeInMillis())); CuentaCliente cuentaCli2 = cuentaCliHome.crear("40000000P","222", Timestamp(new GregorianCalendar(2003,8,21,11,0,0).getTimeInMillis())); CuentaCliente cuentaCli3 = cuentaCliHome.crear("50000000P","333", Timestamp(new GregorianCalendar(2003,8,22,12,0,0).getTimeInMillis())); new new new

cuentaCli1 = (CuentaCliente)cuentaCliHome.buscarPorClavePrimaria("111"); System.out.println(cuentaCli1); Collection col = cliente1.getClienteCuentas(); for(Iterator it = col.iterator(); it.hasNext(); ) { CuentaCliente cuentaCli = (CuentaCliente)it.next(); System.out.println(cuentaCli); } Cliente cliente = cuentaCli1.getCliente(); System.out.println(cliente); col = cuentaCliHome.buscarPorNcc("111"); for(Iterator it = col.iterator(); it.hasNext(); ) { CuentaCliente cuentaCli = (CuentaCliente)it.next(); System.out.println(cuentaCli); } col = cuentaCliHome.buscarPorDespuesUltimaOp(new GregorianCalendar(2003,8,20,10,0,0).getTimeInMillis())); for(Iterator it = col.iterator(); it.hasNext(); ) { CuentaCliente cuentaCli = (CuentaCliente)it.next(); System.out.println(cuentaCli); } col = clienteHome.buscarPorCuenta("111"); for(Iterator it = col.iterator(); it.hasNext(); ) { cliente = (Cliente)it.next(); System.out.println(cliente); } String sql = "SELECT * FROM cuenta_cliente"; col = cuentaCliHome.buscarAbierto(sql); for(Iterator it = col.iterator(); it.hasNext(); ) { CuentaCliente cuentaCli = (CuentaCliente)it.next(); System.out.println(cuentaCli); } Timestamp(new

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.34

Tecnologas de Persistencia en Java, una comparativa desde la prctica


cuentaCli1.eliminar(); cuentaCli2.eliminar(); cuentaCli3.eliminar(); cliente1.eliminar(); cliente2.eliminar(); con.commit(); } catch(Exception ex) { ex.printStackTrace(); } finally { if (con != null) { con.rollback(); con.close(); } } } }

4.2.4

Relacin Muchos Muchos

Reutilizamos la infraestructura persistente genrica de nuevo, e introduciremos una nueva clase CuentaCorriente que represente a la cuenta corriente, con su respectiva clase CuentaCorrienteHome, y modificaremos el modelo para permitir ahora que una cuenta pueda tener varios propietarios, gracias a que CuentaCliente, como el vnculo entre la cuenta y el cliente, ahora tendr como identidad conjunta el nif y el ncc, , en sincrona con su correspondiente tabla cuenta_cliente que ser ahora la tabla intermedia cuenta_cliente que exprese la relacin muchosmuchos en el modelo relacional. Recordar que estrictamente hablando dos clases pueden tener una relacin muchos-muchos sin necesidad de recurrir a una clase intermedia, dicha clase intermedia viene impuesta por el modelo relacional, aunque tambin es cierto (y es lo habitual) que podramos manejar la tabla cuenta_cliente de forma oculta sin manifestarse en una clase, pero por otra parte nos interesa mostrar el contenido de cuenta_cliente en su respectiva clase, pues dicha tabla intermedia puede almacenar informacin til en la relacin cliente-cuenta, aparte del vnculo entre identidades, tal y como el instante en el cliente dado hizo su ltima operacin sobre la cuenta dada. De todas formas tambin ofreceremos mtodos en Cliente y CuentaCorriente que devolvern las respectivas colecciones de cuentas y clientes de igual manera que se hara en un modelo Java normal. 4.2.4.1 Cliente.java

La gran novedad respecto al caso anterior uno-muchos es el mtodo que devuelve directamente los objetos CuentaCorriente que pertenecen al cliente:
public Collection getCuentas() throws SQLException { CuentaCorrienteHome cuentaHome = new CuentaCorrienteHome(getConnection()); return cuentaHome.buscarPorCliente(this); }

Por variar ligeramente los ejemplos, permitiremos pasar como argumento un puntero al propio objeto cuya identidad sirve como criterio de bsqueda en vez de suministrar directamente el valor clave (en este caso la cadena del nif). 4.2.4.2 ClienteHome.java

La variacin relevante respecto al caso uno-muchos es el mtodo de bsqueda (con dos formas) de los clientes asociados a una cuenta dada como argumento:
public Collection buscarPorCuenta(String ncc) throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.35

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ PreparedStatement stmt = null; try { String sql = "SELECT c.nif,c.nombre,c.edad,c.alta " + "FROM cliente c,cuenta_cliente cc,cuenta WHERE "+ "c.nif = cc.nif AND "+ "cc.ncc = cuenta.ncc AND "+ "cuenta.ncc = ?"; stmt = m_con.prepareStatement(sql); stmt.setString(1,ncc); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorCuenta(CuentaCorriente cuenta) { return buscarPorCuenta(cuenta.getNcc()); } throws SQLException

Notar que se hace un join de tres tablas, pero esto es mucho ms eficiente que hacer sucesivas consultas ms simples. 4.2.4.3 CuentaCliente.java

La novedad respecto al caso uno-muchos es un nuevo mtodo getCuenta() que devuelve la cuenta corriente de esta asociacin cliente-cuenta, al igual que el mtodo getCliente() lo haca para el cliente.
public CuentaCorriente getCuenta() throws SQLException { CuentaCorrienteHome cuentaHome = new CuentaCorrienteHome(getConnection()); return (CuentaCorriente)cuentaHome.buscarPorClavePrimaria(m_ncc); }

4.2.4.4

CuentaClienteClave.java

Esta es una clase nueva. Su finalidad es representar el valor clave o ms exactamente de las claves del vnculo clientecuenta, pues como dijimos ahora la clave es la combinacin nif ncc, nuestro framework est preparado para manejar la identidad de un objeto persistente como un simple objeto Java de ah la necesidad de crear una clase especial ahora, pues anteriormente no hubo necesidad al no existir claves compuestas, puesto que la clave del cliente era un simple objeto String que representaba el nif y el de la cuenta era tambin otro objeto cadena con el nmero de cuenta (ncc).
package jdbc.tipobmp2.muchosmuchos; public class CuentaClienteClave { private String m_nif; private String m_ncc; public CuentaClienteClave(String nif,String ncc) { m_nif = nif; m_ncc = ncc; } public String getNif() { return m_nif; } public void setNif(String nif) {

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.36

Tecnologas de Persistencia en Java, una comparativa desde la prctica


m_nif = nif; } public String getNcc() { return m_ncc; } public void setNcc(String ncc) { m_ncc = ncc; } }

4.2.4.5

CuentaClienteHome.java

No introducimos ninguna funcionalidad nueva excepto las consecuencias de manejar ahora una clave compuesta, por ejemplo el uso del nuevo objeto clave CuentaClienteClave .
public void actualizar(Persistente obj) throws SQLException { actualizar(obj,"cuenta_cliente","UPDATE cuenta_cliente SET ultima_op=? WHERE nif=? AND ncc=?"); } public void eliminar(Persistente obj) throws SQLException { eliminar(obj,"cuenta_cliente","DELETE FROM cuenta_cliente ncc=?"); }

WHERE

nif=?

AND

public void leer(Persistente obj) throws SQLException,NoEncontradoException { leer(obj,"SELECT * FROM cuenta_cliente WHERE nif=? AND ncc=?"); } . . . public void setStatementClave(Persistente obj,PreparedStatement SQLException { CuentaCliente cuenta = (CuentaCliente)obj; stmt.setString(1,cuenta.getNif()); stmt.setString(2,cuenta.getNcc()); } . . . public Persistente crearObjeto(Object clave) { CuentaClienteClave ccclave = (CuentaClienteClave)clave; return new CuentaCliente(ccclave.getNif(),ccclave.getNcc()); } stmt) throws

4.2.4.6

CuentaCorriente.java

Representar a la cuenta corriente, el nmero de cuenta tendr identidad propia independiente de los clientes, y un dato nuevo ser el saldo de la cuenta.
package jdbc.tipobmp2.muchosmuchos; import java.sql.*; import java.util.*; import jdbc.tipobmp2.comun.*;

public class CuentaCorriente extends PersistenteImpl { protected String m_ncc; // Nmero de la cuenta corriente protected long m_saldo;

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.37

Tecnologas de Persistencia en Java, una comparativa desde la prctica


/** Creates a new instance of Cliente */ public CuentaCorriente() { } public void crear(String ncc, long saldo) { m_ncc = ncc; m_saldo = saldo; } public CuentaCorriente(String ncc) { m_ncc = ncc; } public String getNcc() { return m_ncc; } public void setNcc(String ncc) { m_ncc = ncc; } public long getSaldo() { return m_saldo; } public void setSaldo(long saldo) { m_saldo = saldo; } public Collection getCuentaClientes() throws SQLException { CuentaClienteHome cuentaCliHome = new CuentaClienteHome(getConnection()); return cuentaCliHome.buscarPorNcc(m_ncc); } public Collection getClientes() throws SQLException { ClienteHome clienteHome = new ClienteHome(getConnection()); return clienteHome.buscarPorCuenta(this); } public String toString() { return "ncc: " + m_ncc + " saldo: " + m_saldo; } }

4.2.4.7

CuentaCorrienteHome.java

Como relevante de esta clase est el mtodo buscarPorCliente() que a travs de joins obtiene las cuentas corrientes de una cliente dado, de forma similar a como se obtenan los clientes propietarios de una cuenta dada en la clase ClienteHome.
package jdbc.tipobmp2.muchosmuchos; import java.sql.*; import java.util.*; import jdbc.NoEncontradoException; import jdbc.tipobmp2.comun.*; public class CuentaCorrienteHome extends PersistenteHomeImpl

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.38

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ public CuentaCorrienteHome(Connection con) throws SQLException { super(con); } public void actualizar(Persistente obj) throws SQLException { actualizar(obj,"cuenta","UPDATE cuenta SET saldo=? WHERE ncc=?"); } public void eliminar(Persistente obj) throws SQLException { eliminar(obj,"cuenta","DELETE FROM cuenta WHERE ncc=?"); } public void insertar(Persistente obj) throws SQLException { insertar(obj,"cuenta","INSERT INTO cuenta (saldo,ncc) VALUES(?,?)"); } public void leer(Persistente obj) throws SQLException,NoEncontradoException { leer(obj,"SELECT * FROM cuenta WHERE ncc=?"); } public void leer(Persistente obj,ResultSet res) throws SQLException { CuentaCorriente cuenta = (CuentaCorriente)obj; cuenta.setNcc(res.getString("ncc")); cuenta.setSaldo(res.getLong("saldo")); } public void setStatementParams(Persistente obj,String stmt) throws SQLException { CuentaCorriente cuenta = (CuentaCorriente)obj; stmt.setLong(1,cuenta.getSaldo()); stmt.setString(2,cuenta.getNcc()); } public void setStatementClave(Persistente obj,PreparedStatement SQLException { CuentaCorriente cuenta = (CuentaCorriente)obj; stmt.setString(1,cuenta.getNcc()); } public Collection buscarPorSaldoMayor(long saldo) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cuenta WHERE saldo >= ?"; stmt = m_con.prepareStatement(sql); stmt.setLong(1,saldo); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorSaldoMenor(long saldo) { throws SQLException stmt) throws tabla,PreparedStatement

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.39

Tecnologas de Persistencia en Java, una comparativa desde la prctica


PreparedStatement stmt = null; try { String sql = "SELECT * FROM cuenta WHERE saldo < ?"; stmt = m_con.prepareStatement(sql); stmt.setLong(1,saldo); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorCliente(String nif) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT ct.ncc,ct.saldo "+ "FROM cuenta ct,cuenta_cliente cc,cliente c WHERE "+ "ct.ncc = cc.ncc AND "+ "cc.nif = c.nif AND "+ "c.nif = ?"; stmt = m_con.prepareStatement(sql); stmt.setString(1,nif); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorCliente(Cliente cliente) { return buscarPorCliente(cliente.getNif()); } throws SQLException

public CuentaCorriente crear(String ncc,long saldo) throws SQLException { CuentaCorriente obj = (CuentaCorriente)crearObjeto(); obj.crear(ncc,saldo); return (CuentaCorriente)crear(obj); } public Persistente crearObjeto() { return new CuentaCorriente(); } public Persistente crearObjeto(Object clave) { String ncc = (String)clave; return new CuentaCorriente(ncc); } }

4.2.4.8

JDBCInicio.java

Este ejemplo de uso crear dos clientes y tres cuentas que asociar a los dos clientes y se navegar a travs de las relaciones entre cuentas y clientes.
package jdbc.tipobmp2.muchosmuchos; import java.sql.*; import java.util.*; import java.text.*;

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.40

Tecnologas de Persistencia en Java, una comparativa desde la prctica


import jdbc.Database; public class JDBCInicio { public JDBCInicio() { } public static void main(String[] args) throws Exception { Connection con = null; try { con = new Database().conectar(); ClienteHome clienteHome = new ClienteHome(con); CuentaClienteHome cuentaCliHome = new CuentaClienteHome(con); CuentaCorrienteHome cuentaHome = new CuentaCorrienteHome(con); con.setAutoCommit(false); Cliente cliente1 = clienteHome.crear("50000000P","Iaki Gabilondo",45,new GregorianCalendar(2003,8,20).getTime()); Cliente cliente2 = clienteHome.crear("40000000P","Luis del Olmo",47,new GregorianCalendar(2003,8,21).getTime()); CuentaCorriente cuenta1 = cuentaHome.crear("111",200000); CuentaCorriente cuenta2 = cuentaHome.crear("222",100000); CuentaCliente cuentaCli1 = cuentaCliHome.crear("50000000P","111", Timestamp(new GregorianCalendar(2003,8,20,10,0,0).getTimeInMillis())); CuentaCliente cuentaCli2 = cuentaCliHome.crear("40000000P","111", Timestamp(new GregorianCalendar(2003,8,21,11,0,0).getTimeInMillis())); CuentaCliente cuentaCli3 = cuentaCliHome.crear("50000000P","222", Timestamp(new GregorianCalendar(2003,8,22,12,0,0).getTimeInMillis())); CuentaCorriente cuenta = (CuentaCorriente)cuentaHome.buscarPorClavePrimaria("111"); System.out.println(cuenta); Collection col = cliente1.getCuentas(); for(Iterator it = col.iterator(); it.hasNext(); ) { cuenta = (CuentaCorriente)it.next(); System.out.println(cuenta); } col = cuenta1.getClientes(); for(Iterator it = col.iterator(); it.hasNext(); ) { Cliente cliente = (Cliente)it.next(); System.out.println(cliente); } col = cuentaHome.buscarPorSaldoMayor(1000); for(Iterator it = col.iterator(); it.hasNext(); ) { cuenta = (CuentaCorriente)it.next(); System.out.println(cuenta); } col = cuentaHome.buscarPorCliente(cliente1); for(Iterator it = col.iterator(); it.hasNext(); ) { cuenta = (CuentaCorriente)it.next(); System.out.println(cuenta); } col = clienteHome.buscarPorCuenta(cuenta1); for(Iterator it = col.iterator(); it.hasNext(); ) new new new

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.41

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ Cliente cliente = (Cliente)it.next(); System.out.println(cliente); } String sql = "SELECT * FROM cuenta"; col = cuentaHome.buscarAbierto(sql); for(Iterator it = col.iterator(); it.hasNext(); ) { cuenta = (CuentaCorriente)it.next(); System.out.println(cuenta); } cuentaCli1.eliminar(); cuentaCli2.eliminar(); cuentaCli3.eliminar(); cuenta1.eliminar(); cuenta2.eliminar(); cliente1.eliminar(); cliente2.eliminar(); con.commit(); } catch(Exception ex) { ex.printStackTrace(); } finally { if (con != null) { con.rollback(); con.close(); } } } }

4.2.5

Herencia

A continuacin modelaremos el ejemplo de herencia Cliente-Persona. La herencia es una paradigma bsico de la programacin orientada a objetos, pero que no concuerda bien con el modelo relacional (sin extensiones de orientacin a objetos). Como ya enunciamos en las decisiones de implementacin, haremos corresponder cada clase con una tabla aunque esto suponga que una instancia Java persistente represente a varias filas de varias tablas, pues este es el modelo ms simtrico al concepto de herencia aunque no sea el que mejor rendimiento tenga. Reutilizaremos el framework usado en los anteriores casos, de hecho est concebido para ayudar en la gestin persistente de la herencia, aunque con el apoyo del programador en las clases concretas persistentes. A la hora de hacer comparaciones, lo haremos respecto al caso de una tabla, pues el ejemplo es similar slo que ahora el cliente no est definido por s mismo sino que est basado en herencia. 4.2.5.1 Persona.java

En esta clase recae ahora buena parte del cdigo que resida en la clase Cliente en el caso de una tabla.
package jdbc.tipobmp2.herencia; import java.sql.*; import jdbc.tipobmp2.comun.*; public class Persona extends PersistenteImpl { protected String m_nif; protected String m_nombre;

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.42

Tecnologas de Persistencia en Java, una comparativa desde la prctica


protected int m_edad; public Persona() { m_nombre = ""; } public Persona(String nif) { m_nif = nif; } public void crear(String nif, String nombre, int edad) { m_nif = nif; m_nombre = nombre; m_edad = edad; } public String getNif() { return m_nif; } public void setNif(String nif) { m_nif = nif; } public String getNombre() { return m_nombre; } public void setNombre(String nombre) { m_nombre = nombre; } public int getEdad() { return m_edad; } public void setEdad(int edad) { m_edad = edad; } public String toString() { return "nif: " + m_nif + " nombre: " + m_nombre + " edad: " + m_edad; } public boolean equals(Object obj) { if (obj instanceof Persona) { Persona obj2 = (Persona)obj; return m_nif.equals(obj2.getNif()); } return false; } public int hashCode() { return m_nif.hashCode(); } }

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.43

Tecnologas de Persistencia en Java, una comparativa desde la prctica

Notar la novedad de los mtodos equals() y hashCode() que no han estado presentes hasta ahora. Justificaremos ms adelante su necesidad, adelantando que sirven para distinguir si dos objetos Persona o derivados de Persona tienen la misma identidad, es decir el mismo nif. 4.2.5.2 PersonaHome.java

Al igual que con Persona, esta clase contendr buena parte del cdigo de la clase Cliente del caso de una tabla.
package jdbc.tipobmp2.herencia; import java.sql.*; import java.util.*; import jdbc.NoEncontradoException; import jdbc.tipobmp2.comun.*; public class PersonaHome extends PersistenteHomeImpl { public PersonaHome(Connection con) throws SQLException { super(con); } public void insertar(Persistente obj) throws SQLException { insertar(obj,"persona","INSERT INTO persona (nombre,edad,nif) VALUES(?,?,?)"); } public void actualizar(Persistente obj) throws SQLException { actualizar(obj,"persona","UPDATE persona SET nombre=?, edad=? WHERE nif=?"); } public void eliminar(Persistente obj) throws SQLException { eliminar(obj,"persona","DELETE FROM persona WHERE nif=?"); } public void leer(Persistente obj) throws SQLException,NoEncontradoException { leer(obj,"SELECT * FROM persona WHERE nif=?"); } public void leer(Persistente obj,ResultSet res) { Persona persona = (Persona)obj; persona.setNif(res.getString("nif")); persona.setNombre(res.getString("nombre")); persona.setEdad(res.getInt("edad")); } public void setStatementParams(Persistente stmt) throws SQLException { Persona persona = (Persona)obj; stmt.setString(1,persona.getNombre()); stmt.setInt(2,persona.getEdad()); stmt.setString(3,persona.getNif()); } public void setStatementClave(Persistente SQLException { Persona persona = (Persona)obj; stmt.setString(1,persona.getNif()); } obj,PreparedStatement stmt) throws obj,String table,PreparedStatement throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.44

Tecnologas de Persistencia en Java, una comparativa desde la prctica


public Collection buscarPorEdad(int edad) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM persona WHERE edad=?"; stmt = m_con.prepareStatement(sql); stmt.setInt(1,edad); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorNombre(String nombre) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM persona WHERE nombre=?"; stmt = m_con.prepareStatement(sql); stmt.setString(1,nombre); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarTodos() throws SQLException { return buscarAbierto("SELECT * FROM persona"); } public Collection buscarTodosConHerencia() { Set res = new HashSet(); throws SQLException

ClienteHome clienteHome = new ClienteHome(m_con); Collection col; col = clienteHome.buscarTodos(); res.addAll(col); col = buscarTodos(); res.addAll(col); return res; } public int contar() throws SQLException { Statement stmt = null; try { String sql = "SELECT COUNT(*) AS num FROM persona"; stmt = m_con.createStatement(); ResultSet res = stmt.executeQuery(sql); res.next(); return res.getInt("num"); } finally { if (stmt != null) stmt.close(); }

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.45

Tecnologas de Persistencia en Java, una comparativa desde la prctica


} public Persona crear(String nif,String nombre,int edad) { Persona obj = (Persona)crearObjeto(); obj.crear(nif,nombre,edad); return (Persona)crear(obj); } public Persistente crearObjeto() { return new Persona(); } public Persistente crearObjeto(Object clave) { String nif = (String)clave; return new Persona(nif); } } throws SQLException

Especialmente interesante es el mtodo buscarTodosConHerencia() cuya mtodo es obtener todos los objetos Persona y aquellos que derivan de Persona. Est basado en buscarTodos() que devuelve todos los objetos Persona basados en registros de la tabla persona. El mtodo ClienteHome.buscarTodos() es idntico salvo que los objetos retornados son los objetos Cliente correspondientes a los objetos Persona.
public Collection buscarTodosConHerencia() { Set res = new HashSet(); throws SQLException

Una coleccin del tipo java.util.HashSet, es por definicin al implementar la interfaz Set, una coleccin que no admite duplicados, la duplicidad es comprobada a travs de los mtodos Object.equals() y Object.hashCode(), el primero ha de devolver true si el objeto argumento es s mismo (tiene la misma identidad), y el segundo ha de devolver un entero que sea nico respecto a la identidad del objeto.
ClienteHome clienteHome = new ClienteHome(m_con); Collection col; col = clienteHome.buscarTodos(); res.addAll(col);

Introduce en la coleccin Set la coleccin de objetos Cliente retornados en la consulta.


col = buscarTodos(); res.addAll(col);

Introduce tambin en la coleccin HashSet la coleccin de objetos Persona retornados en la consulta. De acuerdo a nuestro modelo de tabular para cada registro en la tabla cliente existe un registro en la tabla persona, esto significa que una parte de los objetos Persona son los mismos (tienen la misma identidad, el mismo nif, aunque la instancia sea diferente) que los objetos Cliente obtenidos anteriormente. Como en Persona definimos la identidad respecto al nif en equals() y hashCode() y no respecto a la posicin en memoria (el comportamiento por defecto), y como los objetos Cliente heredan este concepto de identidad, la coleccin HashSet (res) no insertar estos objetos Persona coincidentes en identidad con los objetos Cliente ya insertados. De esta forma para cada identidad diferente se devuelve el objeto ms derivado, pues es el que originariamente introdujo la informacin en la base de datos.
return res; }

Si existieran ms clases persistentes derivadas de Persona, tendramos que aadir el cdigo especfico correspondiente para que este mtodo retornara dichos objetos. Hay que constatar que existen varios problemas de rendimiento: 1. Se obtiene informacin repetida, pues al hacer la consulta de los objetos Cliente es necesario leer los datos de la tabla persona para cargar el objeto completo, dichos datos ya han sido leidos en la anterior consulta de objetos Persona. Como ya sabemos este problema quedara minimizado con un cach de objetos.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.46

Tecnologas de Persistencia en Java, una comparativa desde la prctica

2.

Al leer objetos Cliente es posible que solo accedamos a atributos de la clase Cliente o a atributos de la clase Persona pero no a ambos conjuntos a la vez, y sin embargo hemos ledo ambos registros. La optimizacin posible como sabemos es la lazy loading.

Este mismo tratamiento podra aplicarse a los diversos mtodos de bsqueda si se quisiera que se devolviera el objeto ms derivado. Implementamos slo un mtodo para ilustrar que la extraccin de datos con la herencia dista mucho de ser automtica. Existen otras tcnicas que simplificaran el cdigo y lo hara ms eficiente en el caso de la herencia, tal y como aadir en la tabla persona algn tipo de cdigo que nos informara sobre el tipo de objeto (el nombre de la clase Java por ejemplo) que escribi el registro, sin embargo esto viola el principio de partir de un modelo relacional clsico sin dependencias con la tecnologa de persistencia. 4.2.5.3 Cliente.java

Implementa lo que es especfico de ser cliente de un banco tal y como el alta, basndose cuando sea necesario en la clase base Persona.
package jdbc.tipobmp2.herencia; import java.sql.*; import jdbc.tipobmp2.comun.*; public class Cliente extends Persona { protected java.util.Date m_alta; public Cliente() { } public Cliente(String nif) { super(nif); } public void crear(String nif,String nombre,int edad,java.util.Date alta) { super.crear(nif,nombre,edad); m_alta = alta; } public java.util.Date getAlta() { return m_alta; } public void setAlta(java.util.Date alta) { m_alta = alta; } public String toString() { String res = super.toString(); return res + " alta: " + m_alta; } }

4.2.5.4

ClienteHome.java

La clase Home se nos presenta significativamente ms complicada que en los ejemplos sin herencia, pues es necesario coordinar la carga de las filas de las diferentes tablas (persona y cliente) que forman un objeto Cliente.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.47

Tecnologas de Persistencia en Java, una comparativa desde la prctica En las operaciones de insercin, actualizacin y eliminacin no tenemos ms remedio que usar dos sentencias SQL independientes, una para la fila en persona y otra para la fila en cliente, pues las sentencias INSERT, UPDATE y DELETE no fueron concebidas para hacer joins entre tablas y operar en varias filas de diferentes tablas a la vez. En las operaciones de lectura s conseguiremos hacer la lectura de a la vez de las filas correspondientes en cliente y persona, a travs de joins. Si inspeccionamos el cdigo vemos cmo hay mtodos que sern llamados directamente por el programador o bien por el framework que llaman a su vez a los correspondientes mtodos de la clase base coordinando las operaciones a lo largo del rbol de derivacin.

package jdbc.tipobmp2.herencia; import java.sql.*; import java.util.*; import jdbc.NoEncontradoException; import jdbc.tipobmp2.comun.*; public class ClienteHome extends PersonaHome { /** Creates a new instance of ClienteHome */ public ClienteHome(Connection con) throws SQLException { super(con); } public void insertar(Persistente obj) throws SQLException { super.insertar(obj); insertar(obj,"cliente","INSERT INTO cliente (alta,nif) VALUES(?,?)"); } public void actualizar(Persistente obj) throws SQLException { super.actualizar(obj); actualizar(obj,"cliente","UPDATE cliente SET alta=? WHERE nif=?"); } public void eliminar(Persistente obj) throws SQLException { eliminar(obj,"cliente","DELETE FROM cliente WHERE nif=?"); super.eliminar(obj); } public void leer(Persistente obj) { leer(obj,"SELECT * FROM persona.nif=cliente.nif"); } throws SQLException,NoEncontradoException cliente,persona WHERE cliente.nif=? AND

public void leer(Persistente obj,ResultSet res) { super.leer(obj,res); Cliente cliente = (Cliente)obj; cliente.setAlta(res.getDate("alta")); }

throws SQLException

public void setStatementParams(Persistente obj,String tabla,PreparedStatement stmt) throws SQLException { if (!tabla.equals("cliente")) super.setStatementParams(obj,tabla,stmt); else { Cliente cliente = (Cliente)obj; stmt.setDate(1,new java.sql.Date(cliente.getAlta().getTime())); stmt.setString(2,cliente.getNif()); } }

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.48

Tecnologas de Persistencia en Java, una comparativa desde la prctica


public Collection buscarPorEdad(int edad) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cliente,persona WHERE edad=? AND persona.nif = cliente.nif"; stmt = m_con.prepareStatement(sql); stmt.setInt(1,edad); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorNombre(String nombre) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cliente,persona WHERE nombre=? AND persona.nif = cliente.nif"; stmt = m_con.prepareStatement(sql); stmt.setString(1,nombre); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarPorAlta(java.util.Date alta) throws SQLException { PreparedStatement stmt = null; try { String sql = "SELECT * FROM cliente,persona WHERE cliente.alta=? persona.nif = cliente.nif"; stmt = m_con.prepareStatement(sql); stmt.setDate(1,new java.sql.Date(alta.getTime())); ResultSet res = stmt.executeQuery(); return leer(res); } finally { if (stmt != null) stmt.close(); } } public Collection buscarTodos() throws SQLException { return buscarAbierto("SELECT * FROM cliente.nif=persona.nif"); }

AND

cliente,persona

WHERE

public int contar() throws SQLException { Statement stmt = null; try { String sql = "SELECT COUNT(*) AS num FROM cliente"; stmt = m_con.createStatement(); ResultSet res = stmt.executeQuery(sql); res.next(); return res.getInt("num");

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.49

Tecnologas de Persistencia en Java, una comparativa desde la prctica


} finally { if (stmt != null) stmt.close(); } } public Cliente crear(String nif,String throws SQLException { Cliente obj = (Cliente)crearObjeto(); obj.crear(nif,nombre,edad,alta); return (Cliente)crear(obj); } public Persistente crearObjeto() { return new Cliente(); } public Persistente crearObjeto(Object clave) { String nif = (String)clave; return new Cliente(nif); } } nombre,int edad,java.util.Date alta)

Hemos tenido que repetir la mayor parte de los mtodos de bsqueda definidos en Persona, pues si son llamados desde un objeto ClienteHome se entiende que se pretende obtener objetos Cliente, necesitando consultas ms complicadas que sean capaces de leer a la vez los registros de las dos tablas. Esto es un problema importante, pues significa que en todas las clases derivadas se debe de repetir esta funcionalidad con joins cada vez ms complicados a medida que bajamos en el rbol de derivacin. De todas formas la complejidad del join es preferible a realizar consultas sucesivas en cada tabla del rbol de derivacin, pues la prdida de rendimiento sera muy signifativa: consideremos una consulta sobre la tabla cliente con 1000 resultados y que tuviramos que hacer otras 1000 correspondientes consultas para obtener la parte de la tabla persona que forma el objeto Cliente, como bien sabe cualquier administrador de bases de datos, una consulta de 1000 resultados es enormemente ms eficiente que 1000 consultas de un solo resultado (quizs en muy muy altos volmenes de concurrencia de usuarios pudiera igualar o superar el rendimiento las consultas mltiples, pero esto no ocurre en niveles normales de escala). Un framework ms avanzado podra componer estos joins de una forma genrica que evitara la repeticin sistemtica en cada clase especfica, sin embargo, como veremos ms adelante, en la industria no es habitual ver resuelto de forma satisfactoria y con un buen rendimiento este problema, en diversos frameworks de hecho se elude (Libelis Lido JDO 1.4.4) con un modelo de tablas ms simple (una sola) o bien directamente no se plantea el problema de la herencia (EJB Entity Beans) o bien se aborda slo para sistemas de bases de datos orientadas a objetos (Poet FastObjects12, Versant13) que no gozan del amplio favor de la industria como es el caso de las relacionales. Esto es sin duda bastante frustante para un desarrollador que usa intensivamente el concepto de herencia y lo quiere ver expresado en el modelo relacional de la forma ms simple. 4.2.5.5 JDBCInicio.java

Ponemos en accin la herencia con un ejemplo de uso, creando un objeto Persona y dos objetos Cliente en la base de datos.
package jdbc.tipobmp2.herencia; import java.sql.*; import java.util.*; import java.text.*; import jdbc.Database;

12

http://www.fastobjects.com/ http://www.versant.com/

13

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.50

Tecnologas de Persistencia en Java, una comparativa desde la prctica


public class JDBCInicio { public JDBCInicio() { } public static void main(String[] args) throws Exception { Connection con = null; try { con = new Database().conectar(); ClienteHome clienteHome = new ClienteHome(con); PersonaHome personaHome = new PersonaHome(con); con.setAutoCommit(false); Persona persona = personaHome.crear("60000000P","Jos Mara Garca",45); persona = (Persona)personaHome.buscarPorClavePrimaria("60000000P"); System.out.println(persona); Cliente cliente1 = clienteHome.crear("50000000P","Iaki Jabilondo",45,new GregorianCalendar(2003,8,20).getTime()); Cliente cliente2 = clienteHome.crear("40000000P","Luis del Olmo",47,new GregorianCalendar(2003,8,21).getTime()); cliente1.setNombre("Iaki Gabilondo"); cliente1.actualizar(); cliente1 = (Cliente)clienteHome.buscarPorClavePrimaria("50000000P"); System.out.println(cliente1); Collection col = clienteHome.buscarPorNombre("Luis del Olmo"); for(Iterator it = col.iterator(); it.hasNext(); ) { Cliente cliente = (Cliente)it.next(); System.out.println(cliente); } col = clienteHome.buscarPorEdad(45); for(Iterator it = col.iterator(); it.hasNext(); ) { Cliente cliente = (Cliente)it.next(); System.out.println(cliente); } col = clienteHome.buscarPorAlta(new GregorianCalendar(2003,8,21).getTime()); for(Iterator it = col.iterator(); it.hasNext(); ) { Cliente cliente = (Cliente)it.next(); System.out.println(cliente); } String sql = "SELECT * FROM cliente,persona WHERE (nombre LIKE 'Luis%' OR nombre LIKE '%Gabilondo') AND cliente.nif=persona.nif"; col = clienteHome.buscarAbierto(sql); for(Iterator it = col.iterator(); it.hasNext(); ) { Cliente cliente = (Cliente)it.next(); System.out.println(cliente); } int num = personaHome.contar(); System.out.println(num); num = clienteHome.contar(); System.out.println(num);

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.51

Tecnologas de Persistencia en Java, una comparativa desde la prctica

col = personaHome.buscarTodosConHerencia(); for(Iterator it = col.iterator(); it.hasNext(); ) { Persona per = (Persona)it.next(); System.out.println(per); // Muestra dos clientes y una persona } cliente1.eliminar(); cliente2.eliminar(); persona.eliminar(); con.commit(); } catch(Exception ex) { ex.printStackTrace(); } finally { if (con != null) { con.rollback(); con.close(); } } } }

Es de destacar las siguientes sentencias:


int num = personaHome.contar(); System.out.println(num); num = clienteHome.contar(); System.out.println(num); col = personaHome.buscarTodosConHerencia(); for(Iterator it = col.iterator(); it.hasNext(); ) { Persona per = (Persona)it.next(); System.out.println(per); // Muestra dos clientes y una persona }

Antes de su ejecucin en la tabla persona existen tres registros (tres personas) y en la tabla cliente dos registros (dos clientes), puesto que hemos guardado en la base de datos un objeto Persona y dos Cliente, ambos clientes son tambin objetos Persona, de ah que personaHome.contar() devuelva el valor 3 y clienteHome.contar() devuelta el valor 2. La coleccin devuelta por personaHome.buscarTodosConHerencia() contendr dos tres objetos: un objeto Persona y dos objetos Cliente, fcil de comprobar al mostrarse la informacin por pantalla el resultado (notar que Jose Mara Garca es un objeto Persona pues no tiene el dato del alta en el banco):
nif: 40000000P nombre: Luis del Olmo edad: 47 alta: 2003-09-21 nif: 60000000P nombre: Jos Mara Garca edad: 45 nif: 50000000P nombre: Iaki Gabilondo edad: 45 alta: 2003-09-20

4.3 TIPO CMP


Podemos dar una vuelta de tuerca ms a nuestro esfuerzo por minimizar el cdigo persistente de nuestras clases persistentes y conseguir la mxima transparencia.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.52

Tecnologas de Persistencia en Java, una comparativa desde la prctica Utilizaremos ahora la estrategia usada en los EJB Entity Beans CMP: se trata de hacer que la clase persistente sea abstracta, no implemente atributos que vayan a ser persistentes siendo sustituidos por los mtodos get y set tambin abstractos, y los mtodos que representan las relaciones entre clases sean tambin abstractos. En el caso de J2EE tambin son abstractos los mtodos de bsqueda que era necesario implementar en el caso de los BMP. La finalidad que persigue la filosofa CMP es que la infraestructura (el servidor de aplicaciones en el caso verdadero J2EE) se encargue de hacer la clase que implemente todo aquello que es abstracto gestionando la persistencia al implementar los mtodos abstractos. En el caso de J2EE esta clase es generada dinmicamente en el despliegue (deployment) a partir de las declaraciones del bean CMP. En sntesis es un BMP automtico en donde el programador declara de forma abstracta (el bean CMP) lo que ha de implementarse por generacin de cdigo, en dicha generacin de cdigo es donde el servidor de aplicaciones demuestra su saber hacer. Nosotros seguiremos esta filosofa pero no de forma exacta al CMP J2EE obviamente, sino como estrategia de conseguir la transparencia buscada. De acuerdo con el enfoque CMP que busca la ampliacin de las clases persistentes por abajo, eliminaremos la clase PersistenteImpl en todos los casos y el cdigo necesario lo aadiremos en las clases derivadas de las clases persistentes. Esto no afecta a la clase PersistenteHomeImpl pues supondra la reescritura de mucho cdigo idntico en las clases Home especficas, es preciso recordar que el foco de la transparencia est en las clases persistentes no en las clases de utilidad (en el verdadero J2EE CMP la implementacin de las clases Home es asunto del servidor de aplicaciones, y son generadas automticamente en el despliegue), como ahora dispondremos de una clase que deriva de la clase persistente orientada fuertemente a la gestin de la persistencia de su clase base, algunas funciones presentes en las clases Home estar ahora en estas clases de forma un poco ms adecuada. Implementaremos los mismos casos de uso que con el mtodo BMP Avanzado y pondremos un nfasis en las diferencias, de cada caso (una tabla, uno-muchos, muchos-muchos, herencia) respecto al caso correspondiente BMP. 4.3.1 Clases Comunes (framework)

4.3.1.1

Persistente.java

package jdbc.tipocmp.comun; import java.sql.*; public interface Persistente { public void setConnection(Connection con); public Connection getConnection(); public void setHome(PersistenteHome home); public PersistenteHome getHome(); public void setStatementParams(String table,PreparedStatement stmt) throws SQLException; public void setStatementClave(PreparedStatement stmt) throws SQLException; public void actualizar() throws SQLException; public void eliminar() throws SQLException; public void leer(ResultSet res) throws SQLException; }

Notar la presencia de los mtodos:


public void setStatementParams(Persistente obj,String table,PreparedStatement stmt) throws SQLException; public void setStatementClave(Persistente obj,PreparedStatement stmt) throws SQLException; public void leer(Persistente obj,ResultSet res) throws SQLException;

Estos mtodos estaban presentes en la interfase PersistenteHome en el framework tipo BMP Avanzado, su presencia aqu es debido al movimiento de la implementacin de esta funcionalidad a la clase de gestin de la persistencia de la clase persistente. 4.3.1.2 PersistenteHome.java Pg.53

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Tecnologas de Persistencia en Java, una comparativa desde la prctica Esta interfase ser casi idntica al anterior framework excepto en los mtodos que ahora estn en la interfase Persistente.
package jdbc.tipocmp.comun; import java.sql.*; import jdbc.NoEncontradoException; public interface PersistenteHome { public void setConnection(Connection con); public Connection getConnection(); public abstract Persistente crearObjeto(); public abstract Persistente crearObjeto(Object clave); public public public public } void void void void actualizar(Persistente obj) throws SQLException; eliminar(Persistente obj) throws SQLException; insertar(Persistente obj) throws SQLException; leer(Persistente obj) throws SQLException,NoEncontradoException;

4.3.1.3

PersistenteHomeImpl.java

La clase es prcticamente idntica a la correspondiente del anterior framework, las diferencias estn en las llamadas a los tres mtodos ahora presentes en el objeto Persistente en vez de en el objeto Home derivado.
package jdbc.tipocmp.comun; import java.sql.*; import java.util.*; import jdbc.NoEncontradoException; public abstract class PersistenteHomeImpl implements PersistenteHome { protected Connection m_con; public PersistenteHomeImpl(Connection con) { m_con = con; } public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con; } public Persistente crear(Persistente obj) { obj.setConnection(m_con); obj.setHome(this); insertar(obj); return obj; } throws SQLException

public Persistente crear(Object clave) throws SQLException { Persistente obj = crearObjeto(clave); return crear(obj); } public void insertar(Persistente obj,String table,String sql) { PreparedStatement stmt = null; try { throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.54

Tecnologas de Persistencia en Java, una comparativa desde la prctica


stmt = obj.getConnection().prepareStatement(sql); obj.setStatementParams(table,stmt); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } } public void actualizar(Persistente obj,String table,String sql) throws SQLException { Connection con = obj.getConnection(); if (con == null) return; PreparedStatement stmt = null; try { stmt = obj.getConnection().prepareStatement(sql); obj.setStatementParams(table,stmt); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } } public void eliminar(Persistente obj,String table,String sql) { PreparedStatement stmt = null; try { stmt = obj.getConnection().prepareStatement(sql); obj.setStatementClave(stmt); stmt.executeUpdate(); } finally { if (stmt != null) stmt.close(); } } public void leer(Persistente obj,String sql) throws SQLException,NoEncontradoException { PreparedStatement stmt = null; try { stmt = obj.getConnection().prepareStatement(sql); obj.setStatementClave(stmt); ResultSet res = stmt.executeQuery(); if (!res.next()) new NoEncontradoException(); obj.leer(res); } finally { if (stmt != null) stmt.close(); } } protected Collection leer(ResultSet res) throws SQLException { Collection col = new LinkedList(); while(res.next()) { Persistente obj = crearObjeto(); obj.setConnection(m_con); obj.setHome(this); throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.55

Tecnologas de Persistencia en Java, una comparativa desde la prctica


obj.leer(res); col.add(obj); } return col; } public Persistente buscarPorClavePrimaria(Object clave) throws SQLException { Persistente obj = crearObjeto(clave); try { obj.setConnection(m_con); obj.setHome(this); leer(obj); return obj; } catch(NoEncontradoException ex) { return null; } } public Collection buscarAbierto(String sql) throws SQLException { Statement stmt = null; try { stmt = m_con.createStatement(); ResultSet res = stmt.executeQuery(sql); return leer(res); } finally { if (stmt != null) stmt.close(); } } }

4.3.2

Una Tabla

Abordamos ahora el caso de una tabla con este nuevo estilo CMP. Como novedad dispondremos de una clase ClienteImpl, derivada de Cliente, que implementar la gestin de la persistencia de Cliente, completando todo aquello que es abstracto. 4.3.2.1 Cliente.java

package jdbc.tipocmp.unatabla; import java.sql.*; import java.util.*; import jdbc.tipocmp.comun.*; public abstract class Cliente implements Persistente { public Cliente() { } public public public public public public public public abstract abstract abstract abstract abstract abstract abstract abstract String getNif(); void setNif(String nif) throws SQLException; String getNombre(); void setNombre(String nombre) throws SQLException; int getEdad(); void setEdad(int edad) throws SQLException; java.util.Date getAlta(); void setAlta(java.util.Date alta) throws SQLException;

public void crear(String nif,String nombre,int edad,java.util.Date alta) throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.56

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ setNif(nif); setNombre(nombre); setEdad(edad); setAlta(alta); } public String toString() { return "nif: " + getNif() + " nombre: " + getNombre() + " edad: " + getEdad() + " alta: " + getAlta(); } }

4.3.2.2

ClienteImpl.java

La clase ClienteImpl implementar todo aquello que es abstracto en la clase Cliente. De esta manera podemos controlar la persistencia de los atributos, por ejemplo en los mtodos set, esto es una ventaja respecto al enfoque BMP Avanzado en donde tenamos dos opciones: o hacer una llamada a actualizar() en cada mtodo set, lo cual es una intromisin de la gestin persistente en la clase, o bien llamar desde fuera explcitamente al mtodo actualizar() del objeto persistente tras modificar alguno de sus atributos (la segunda opcin es la usada en la clase JDBCInicio). Por otra parte los mtodos nuevos indicados en Persistente respecto al caso BMP se implementan ahora:
package jdbc.tipocmp.unatabla; import java.sql.*; import java.util.*; import jdbc.tipocmp.comun.*; public class ClienteImpl extends Cliente { private Connection m_con; private PersistenteHome m_home; protected protected protected protected String m_nif; String m_nombre; int m_edad; java.util.Date m_alta;

public ClienteImpl() { m_nombre = ""; m_alta = new java.util.Date(); } public ClienteImpl(String nif) { m_nif = nif; } public String getNif() { return m_nif; } public void setNif(String nif) throws SQLException { m_nif = nif; actualizar(); } public String getNombre() { return m_nombre; } public void setNombre(String nombre) throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.57

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ m_nombre = nombre; actualizar(); } public int getEdad() { return m_edad; } public void setEdad(int edad) throws SQLException { m_edad = edad; actualizar(); } public java.util.Date getAlta() { return m_alta; } public void setAlta(java.util.Date alta) throws SQLException { m_alta = alta; actualizar(); } public void leer(ResultSet res) throws SQLException { m_nif = res.getString("nif"); m_nombre = res.getString("nombre"); m_edad = res.getInt("edad"); m_alta = res.getDate("alta"); } public void setStatementParams(String tabla,PreparedStatement stmt) throws SQLException { stmt.setString(1,m_nombre); stmt.setInt(2,m_edad); stmt.setDate(3,new java.sql.Date(m_alta.getTime())); stmt.setString(4,m_nif); } public void setStatementClave(PreparedStatement stmt) throws SQLException { stmt.setString(1,m_nif); } public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con; } public void setHome(PersistenteHome home) { m_home = home; } public PersistenteHome getHome() { return m_home; } public void actualizar() throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.58

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ if (getHome() != null) getHome().actualizar(this); } public void eliminar() throws SQLException { getHome().eliminar(this); } }

El inconveniente de introducir la llamada a actualizar() en los mtodos set de los atributos, es que el registro se actualiza cada vez que se modifica un atributo, esto puede suponer un problema de rendimiento ante un cambio externo de varios atributos, cuando dicha actualizacin podra realizarse una sola vez, sin embargo por otra parte conseguimos un avance en la transparencia de la gestin de la persistencia. Un framework ms sofisticado (caso de los servidores de aplicaciones que implementan CMP) podra marcar el objeto como modificado (dirty) ante una modificacin de un atributo, y en el commit realizar la actualizacin de una sola vez (lgicamente esto supone que la conexin a la base de datos est gestionada por el framework, as como la apertura y cierre de la transaccin, lo cual ciertamente ocurre en los servidores de aplicaciones). Observemos la presencia de los mtodos getConnection, setConnection, getHome, setHome, actualizar y eliminar, en el caso BMP Avanzado estos mtodos estaban en la clase genrica PersistenteImpl, clase base de las clases persistentes que constituyen el modelo de datos. Esto es una desventaja en la filosofa CMP respecto a la BMP, puesto que al ampliar exclusivamente por abajo es preciso repetir sistemticamente en todas las clases de implementacin de la persistencia, cierto cdigo que es siempre el mismo. En los servidores de aplicaciones esto no supone ningn problema pues el cdigo es generado, en nuestro caso podemos eliminarlo introduciendo de nuevo la clase PersistenteImpl (es preciso recordar que estamos hablando de filosofas o estrategias no de una imitacin exacta por otra parte imposible salvo que diseramos lgicamente un servidor de aplicaciones como framework). 4.3.2.3 ClienteHome.java

La clase ClienteHome es idntica funcionalmente al caso BMP Avanzado de una tabla excepto por la ausencia de los 3 mtodos anteriormente indicados en PersistenteHome. 4.3.2.4 JDBCInicio.java

La clase es idntica funcionalmente a la correspondiente en BMP, pues hemos cambiado la forma de gestionar la persistencia pero prcticamente nada la interfaz que se ofrece a la aplicacin. La nica diferencia es que ahora no necesitamos hacer una llamada explcita a actualizar() ante un cambio de un atributo (o varios) de un objeto persistente para que se haga efectivo en la base de datos, puesto que ahora lo har el propio mtodo set definido en ClienteImpl, consiguiendo un grado ms de transparencia respecto a la aplicacin:
cliente1.setNombre("Iaki Gabilondo"); // No es necesario: cliente1.actualizar();

4.3.3

Relacin Uno-Muchos

Consideraremos dos tipos de comparacin: respecto al caso de una tabla anterior analizando los nuevos elementos fruto de la necesidad de la gestin de una relacin, y respecto al caso BMP Avanzado. 4.3.3.1 Cliente.java

Esta clase aade respecto al caso de una tabla, el mtodo necesario para gestionar la relacin con los objetos ClienteCuenta:
public abstract Collection getClienteCuentas() throws SQLException;

Hacemos que sea abstracto de acuerdo con la filosofa CMP y de esa manera despejamos todo cdigo de persistencia, presente aunque reducido en la filosofa BPM (la bsqueda correspondiente a travs de la clase CuentaClienteHome), por tanto conseguimos a travs de la abstraccin un pequeo hito ms hacia la transparencia. persistencia_java.doc v.1.0 Jos Mara Arranz Santamara Pg.59

Tecnologas de Persistencia en Java, una comparativa desde la prctica 4.3.3.2 ClienteImpl.java

Es en esta clase en donde implementamos el mtodo getClienteCuentas() al igual que el resto de mtodos abstractos de Cliente, de la misma forma que en el caso de una tabla.
public Collection getClienteCuentas() throws SQLException { CuentaClienteHome cuentaCliHome = new CuentaClienteHome(getConnection()); return cuentaCliHome.buscarPorCliente(getNif()); }

Mtodo idntico al correspondiente en el caso BMP Cliente.getClienteCuentas() solo que ahora hemos conseguido sacarlo de la clase que representa al objeto a persistir. 4.3.3.3 ClienteHome.java

La clase es idntica funcionalmente al caso uno-muchos BMP Avanzado excepto por la ausencia de los tres mtodos que indicamos en PersistenteHome. 4.3.3.4 CuentaCliente.java

De forma similar a Cliente implementamos la clase abstracta CuentaCliente en donde el mtodo abstracto getCliente() es el que establece la relacin con el cliente propietario de la cuenta.
package jdbc.tipocmp.unomuchos; import java.sql.*; import jdbc.tipocmp.comun.*; public abstract class CuentaCliente implements Persistente { public CuentaCliente() { } public void crear(String nif, String ncc, Timestamp ultimaOp) throws SQLException { setNif(nif); setNcc(ncc); setUltimaOperacion(ultimaOp); } public abstract String getNif(); public abstract void setNif(String nif) throws SQLException; public abstract String getNcc(); public abstract void setNcc(String ncc) throws SQLException; public abstract Timestamp getUltimaOperacion(); public abstract void setUltimaOperacion(Timestamp ultimaOp) throws SQLException; public abstract Cliente getCliente() throws SQLException; public String toString() { return "ncc: " + getNcc() getUltimaOperacion(); } }

"

nif:

"

getNif()

"

ltima

op.:

"

4.3.3.5

CuentaClienteImpl.java

De forma similar a ClienteImpl implementamos los mtodos abstractos definidos en CuentaCliente y los atributos.
package jdbc.tipocmp.unomuchos; import java.sql.*; import jdbc.tipocmp.comun.*; public class CuentaClienteImpl extends CuentaCliente { private Connection m_con;

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.60

Tecnologas de Persistencia en Java, una comparativa desde la prctica


private PersistenteHome m_home; protected String m_ncc; // Nmero de la cuenta corriente protected String m_nif; protected Timestamp m_ultimaOperacion; public CuentaClienteImpl() { } public CuentaClienteImpl(String ncc) { m_ncc = ncc; } public CuentaClienteImpl(String nif,String ncc,Timestamp ultimaOp) { m_nif = nif; m_ncc = ncc; m_ultimaOperacion = ultimaOp; } public String getNcc() { return m_ncc; } public void setNcc(String ncc) throws SQLException { m_ncc = ncc; actualizar(); } public String getNif() { return m_nif; } public void setNif(String nif) throws SQLException { m_nif = nif; actualizar(); } public Timestamp getUltimaOperacion() { return m_ultimaOperacion; } public void setUltimaOperacion(Timestamp ultimaOp) throws SQLException { m_ultimaOperacion = ultimaOp; actualizar(); } public Cliente getCliente() throws SQLException { ClienteHome clienteHome = new ClienteHome(getConnection()); return (Cliente)clienteHome.buscarPorClavePrimaria(m_nif); } public void leer(ResultSet res) throws SQLException { m_nif = res.getString("nif"); m_ncc = res.getString("ncc"); m_ultimaOperacion = res.getTimestamp("ultima_op"); } public void setStatementParams(String tabla,PreparedStatement stmt) throws SQLException

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.61

Tecnologas de Persistencia en Java, una comparativa desde la prctica


{ stmt.setTimestamp(1,m_ultimaOperacion); stmt.setString(2,m_nif); stmt.setString(3,m_ncc); } public void setStatementClave(PreparedStatement stmt) throws SQLException { stmt.setString(1,m_ncc); } public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con; } public void setHome(PersistenteHome home) { m_home = home; } public PersistenteHome getHome() { return m_home; } public void actualizar() throws SQLException { if (getHome() != null) getHome().actualizar(this); } public void eliminar() throws SQLException { getHome().eliminar(this); } }

4.3.3.6

CuentaClienteHome.java

Al igual que ClienteHome, la implementacin es idntica funcionalmente al caso correspondiente en BMP Avanzado excepto la ausencia de los tres mtodos que ya conocemos y citamos en PersistenteHome. 4.3.3.7 JDBCInicio.java

De nuevo constatamos que no hay ningn cambio respecto a la funcionalidad presente en BMP Avanzado al igual que ocurra en caso de una tabla, un nuevo xito de nuestra estrategia de encapsulacin de la persistencia. 4.3.4 Relacin Muchos-Muchos

Al igual que hicimos en caso previo, compararemos nuestro nuevo caso respecto al caso uno-muchos anterior y el caso correspondiente muchos-muchos con la filosofa BMP Avanzado. 4.3.4.1 Cliente.java

Como novedad respecto al caso de uno-muchos introducimos el mtodo getCuentas() que nos devuelve directamente los objetos CuentaCorriente que pertenecen al cliente:
public abstract Collection getCuentas() throws SQLException;

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.62

Tecnologas de Persistencia en Java, una comparativa desde la prctica 4.3.4.2 ClienteImpl.java

Como novedad respecto al caso uno-muchos definimos el mtodo getCuentas() para establecer la relacin muchosmuchos por este lado.
public Collection getCuentas() throws SQLException { CuentaCorrienteHome cuentaHome = new CuentaCorrienteHome(getConnection()); return cuentaHome.buscarPorCliente(this); }

4.3.4.3

ClienteHome.java

La implementacin es idntica funcionalmente al caso correspondiente en BMP Avanzado excepto la ausencia de los tres mtodos citados en PersistenteHome. 4.3.4.4 CuentaCliente.java

Introducimos respecto al caso uno-muchos el nuevo mtodo getCuenta() .


public abstract CuentaCorriente getCuenta() throws SQLException;

4.3.4.5

CuentaClienteImpl.java

Respecto al caso uno-muchos, introducimos lgicamente el nuevo mtodo getCuenta().


public CuentaCorriente getCuenta() throws SQLException { CuentaCorrienteHome cuentaHome = new CuentaCorrienteHome(getConnection()); return (CuentaCorriente)cuentaHome.buscarPorClavePrimaria(m_ncc); }

4.3.4.6

CuentaClienteClave.java

Es idntica funcionalmente al caso correspondiente en BMP Avanzado. 4.3.4.7 CuentaClienteHome.java

Idntica funcionalmente al caso correspondiente en BMP Avanzado excepto la ausencia de los tres mtodos citados en PersistenteHome. 4.3.4.8 CuentaCorriente.java

Mostramos la nueva expresin abstracta de esta clase:


package jdbc.tipocmp.muchosmuchos; import java.sql.*; import java.util.*; import jdbc.tipocmp.comun.*; public abstract class CuentaCorriente implements Persistente { public CuentaCorriente() { } public void crear(String ncc, long saldo) throws SQLException { setNcc(ncc); setSaldo(saldo); } public abstract String getNcc(); public abstract void setNcc(String ncc) throws SQLException;

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.63

Tecnologas de Persistencia en Java, una comparativa desde la prctica


public abstract long getSaldo(); public abstract void setSaldo(long saldo) throws SQLException; public abstract Collection getCuentaClientes() throws SQLException; public abstract Collection getClientes() throws SQLException; public String toString() { return "ncc: " + getNcc() + " saldo: " + getSaldo(); } }

4.3.4.9

CuentaCorrienteImpl.java

Implementamos los mtodos abstractos de CuentaCorriente :


package jdbc.tipocmp.muchosmuchos; import java.sql.*; import java.util.*; import jdbc.tipocmp.comun.*; public class CuentaCorrienteImpl extends CuentaCorriente { private Connection m_con; private PersistenteHome m_home; protected String m_ncc; // Nmero de la cuenta corriente protected long m_saldo; public CuentaCorrienteImpl() { } public CuentaCorrienteImpl(String ncc) { m_ncc = ncc; } public String getNcc() { return m_ncc; } public void setNcc(String ncc) throws SQLException { m_ncc = ncc; actualizar(); } public long getSaldo() { return m_saldo; } public void setSaldo(long saldo) throws SQLException { m_saldo = saldo; actualizar(); } public Collection getCuentaClientes() throws SQLException { CuentaClienteHome cuentaCliHome = new CuentaClienteHome(getConnection()); return cuentaCliHome.buscarPorNcc(m_ncc); } public Collection getClientes() throws SQLException { ClienteHome clienteHome = new ClienteHome(getConnection());

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.64

Tecnologas de Persistencia en Java, una comparativa desde la prctica


return clienteHome.buscarPorCuenta(this); } public void leer(ResultSet res) throws SQLException { m_ncc = res.getString("ncc"); m_saldo = res.getLong("saldo"); } public void setStatementParams(String SQLException { stmt.setLong(1,m_saldo); stmt.setString(2,m_ncc); } tabla,PreparedStatement stmt) throws

public void setStatementClave(PreparedStatement stmt) throws SQLException { stmt.setString(1,m_ncc); } public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con; } public void setHome(PersistenteHome home) { m_home = home; } public PersistenteHome getHome() { return m_home; } public void actualizar() throws SQLException { if (getHome() != null) getHome().actualizar(this); } public void eliminar() throws SQLException { getHome().eliminar(this); } }

4.3.4.10

CuentaCorrienteHome.java

Idntica funcionalmente al caso correspondiente en BMP Avanzado excepto la ausencia de los tres mtodos citados en PersistenteHome. 4.3.4.11 JDBCInicio.java

Al igual que en el caso uno-muchos y con una tabla no se produce ningn cambio respecto a la funcionalidad presente en el corrspondiente ejemplo de BMP Avanzado. 4.3.5 Herencia

Nuestro ltimo caso que cierra la estrategia de diseo CMP. persistencia_java.doc v.1.0 Jos Mara Arranz Santamara Pg.65

Tecnologas de Persistencia en Java, una comparativa desde la prctica Es aqu donde veremos que el modelo CMP ofrece un problema conceptual muy serio a la hora de modelar la herencia: la extensin por abajo implica herencia de la clase implementacin de la clase abstracta, esto supone una muy fuerte intromisin sobre el modelo de datos. En el caso BMP vimos que la intromisin era por arriba a travs de la clase PersistenteImpl que fcilmente podramos eliminar sin introducir mucho cdigo sobre las clases persistentes, ahora al ser por abajo supondr un verdadero problema en la herencia de una clase persistente de otra, pues hay que tener en cuenta que las clases implementacin tambin derivan. Existen dos formas de modelar la herencia: 1. Primera Forma:

Persona

Cliente

PersonaImpl

ClienteImpl

Esta es la manera menos intrusiva pues permite que Cliente derive de Persona, pero tiene el problema de que ClienteImpl debe implementar la persistencia de ambas clases: Persona y Cliente, pues no puede aprovecharse de la gestin ya realizada en PersonaImpl. A medida que se introdujeran ms clases en el rbol de derivacin por debajo de Cliente el problema se agravara (este agravamiento no sera tan grave si el modelo de correspondencia clases-tablas fuera el de una sola tabla para toda una lnea ascendente de herencia, llamado tambin horizontal, pero es ms grave cuando la correspondencia es de tipo vertical que es nuestro caso). 2. Segunda Forma:

Persona

PersonaImpl

Cliente

ClienteImpl

Esta segunda opcin es intrusiva respecto a la derivacin normal pero por otra parte se aprovecha desde la herencia la gestin de PersonaImpl por parte de ClienteImpl.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.66

Tecnologas de Persistencia en Java, una comparativa desde la prctica

La eleccin no es fcil, hemos visto que cada opcin tiene su parte a favor y su parte en contra. Nosotros elegiremos la opcin segunda o intrusiva porque nos ahorra bastante cdigo y encaja mejor en nuestra correspondencia de 1 clase 1 tabla, pagando el precio con la fuerte intrusin de la gestin de la persistencia en el modelo de herencia. Hay que hacer notar que este problema no est resuelto todava en la verdadera especificacin J2EE CMP (actualmente la 2.0), pues de hecho CMP no soporta la herencia todava, pero no parece probable que lo haga en un futuro cercano, en cuanto que la herencia es uno de los aspectos que suele estar sacrificado en los modelos de datos persistentes en Java por culpa de la presin de la tecnologa relacional clsica y la comn separacin en diferentes personas entre el diseador de la base de datos y el diseador de la aplicacin. JDO en el tema de la herencia es una eleccin mucho ms adecuada que J2EE CMP. 4.3.5.1 Persona.java

La clase Persona ahora ser fundamentalmente abstracta:


package jdbc.tipocmp.herencia; import java.sql.*; import jdbc.tipocmp.comun.*; public abstract class Persona implements Persistente { public Persona() { } public void crear(String nif, String nombre, int edad) throws SQLException { setNif(nif); setNombre(nombre); setEdad(edad); } public public public public public public abstract abstract abstract abstract abstract abstract String getNif(); void setNif(String nif) throws SQLException; String getNombre(); void setNombre(String nombre) throws SQLException; int getEdad(); void setEdad(int edad) throws SQLException;

public String toString() { return "nif: " + getNif() + " nombre: " + getNombre() + " edad: " + getEdad(); } }

Adems de la abstraccin de los mtodos hay que notar una importante diferencia respecto al modelo BMP Avanzado, y es la ausencia de los mtodos equals() y hashCode(), esto es debido a que los implementaremos en la clase Impl y de esta manera evitamos incluir la gestin de la identidad en este nivel, cuando es un aspecto obligado por la gestin de la persistencia (ms exactamente por la herencia). 4.3.5.2 PersonaImpl

Implementamos la persistencia de Persona,es aqu donde codificamos la gestin de la identidad:


package jdbc.tipocmp.herencia; import java.sql.*; import jdbc.tipocmp.comun.*; public class PersonaImpl extends Persona {

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.67

Tecnologas de Persistencia en Java, una comparativa desde la prctica


private Connection m_con; private PersistenteHome m_home; protected String m_nif; protected String m_nombre; protected int m_edad; public PersonaImpl() { m_nombre = ""; } public PersonaImpl(String nif) { m_nif = nif; } public String getNif() { return m_nif; } public void setNif(String nif) throws SQLException { m_nif = nif; actualizar(); } public String getNombre() { return m_nombre; } public void setNombre(String nombre) throws SQLException { m_nombre = nombre; actualizar(); } public int getEdad() { return m_edad; } public void setEdad(int edad) throws SQLException { m_edad = edad; actualizar(); } public boolean equals(Object obj) { if (obj instanceof Persona) { Persona obj2 = (Persona)obj; return m_nif.equals(obj2.getNif()); } return false; } public int hashCode() { return m_nif.hashCode(); } public void leer(ResultSet res) throws SQLException { m_nif = res.getString("nif"); m_nombre = res.getString("nombre"); m_edad = res.getInt("edad");

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.68

Tecnologas de Persistencia en Java, una comparativa desde la prctica


} public void setStatementParams(String table,PreparedStatement stmt) throws SQLException { stmt.setString(1,m_nombre); stmt.setInt(2,m_edad); stmt.setString(3,m_nif); } public void setStatementClave(PreparedStatement stmt) throws SQLException { stmt.setString(1,m_nif); }

public void setConnection(Connection con) { m_con = con; } public Connection getConnection() { return m_con; } public void setHome(PersistenteHome home) { m_home = home; } public PersistenteHome getHome() { return m_home; } public void actualizar() throws SQLException { if (getHome() != null) getHome().actualizar(this); } public void eliminar() throws SQLException { getHome().eliminar(this); } }

4.3.5.3

PersonaHome.java

La clase es idntica a la correspondiente en BMP Avanzado con la excepcin de la ausencia de los tres mtodos considerados en PersistenteHome. 4.3.5.4 Cliente.java

Derivaremos de PersonaImpl de acuerdo la eleccin que hicimos en el comienzo de este caso, aunque Cliente sea fundamentalmente abstracta y las llamadas a la clase base las reciba el nivel de la clase Persona.
package jdbc.tipocmp.herencia; import java.sql.*; import jdbc.tipocmp.comun.*; public abstract class Cliente extends PersonaImpl { public Cliente() { } public void crear(String nif,String nombre,int edad,java.util.Date alta)

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.69

Tecnologas de Persistencia en Java, una comparativa desde la prctica


throws SQLException { super.crear(nif,nombre,edad); setAlta(alta); } public abstract java.util.Date getAlta(); public abstract void setAlta(java.util.Date alta) throws SQLException; public String toString() { String res = super.toString(); return res + " alta: " + getAlta(); } }

4.3.5.5

ClienteImpl

Ahora ClienteImpl deriva de Cliente e indirectamente de PersistenteImpl, es a esta clase a donde se dirigen las llamadas a la clase base para que se realicen las operaciones de persistencia.
package jdbc.tipocmp.herencia; import java.sql.*; public class ClienteImpl extends Cliente { protected java.util.Date m_alta; public ClienteImpl() { } public ClienteImpl(String nif) { m_nif = nif; } public java.util.Date getAlta() { return m_alta; } public void setAlta(java.util.Date alta) { m_alta = alta; actualizar(); } throws SQLException

public void leer(ResultSet res) throws SQLException { super.leer(res); m_alta = res.getDate("alta"); } public void setStatementParams(String tabla,PreparedStatement stmt) throws SQLException { if (!tabla.equals("cliente")) super.setStatementParams(tabla,stmt); else { stmt.setDate(1,new java.sql.Date(m_alta.getTime())); stmt.setString(2,m_nif); } } }

4.3.5.6

ClienteHome.java

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.70

Tecnologas de Persistencia en Java, una comparativa desde la prctica La clase es idntica a la correspondiente en BMP Avanzado con la excepcin de la ausencia de los tres mtodos considerados en PersistenteHome. 4.3.5.7 JDBCInicio.java

Al igual que en los casos anteriores no se produce ningn cambio respecto a la funcionalidad presente en el corrspondiente ejemplo de BMP Avanzado.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.71

Tecnologas de Persistencia en Java, una comparativa desde la prctica

5. CONLUSIONES
Una vez llegados aqu podemos ver en perspectiva nuestros modelos y llegar a varias conclusiones:

5.1 NECESIDAD DE UN FRAMEWORK


Cuando programamos el ejemplo inicial tipo BMP de una tabla, vimos que en las tareas bsicas de leer, insertar, actualizar y eliminar contenan mucho cdigo que muy poco tena que ver con cada tipo de datos, invitndonos a generalizar estas funcionalidades con el fin de disminuir la cantidad de cdigo repetitivo y mejorar la calidad del programa (aparte de programar menos). Este es un problema o ventaja de JDBC, su sencillez, supone un problema en cuanto que nos exige programar un montn de cdigo para gestionar la persistencia de nuestro modelo de datos, pero supone una ventaja respecto a que podemos elegir con flexibilidad nuestra manera de gestionar la persistencia y construir nuestro propio framework persistente ya sea simple o sofisticado segn el nivel de transparencia y reduccin de cdigo que queramos alcanzar.

5.2 NECESIDAD DE FRAMEWORKS MS SOFISTICADOS


Aunque nuestros modelos pueden considerarse suficientemente satisfactorios en cuestin de rendimiento, por ejemplo a travs del uso de joins cuando era necesario, hemos detectado desde el comienzo que nuestra infraestructura no es suficiente, comprobamos como es fcil repetir la carga de registros desde la base de datos que ya se han cargado anteriormente, como cargbamos la coleccin completa de objetos resultante de una consulta independientemente de si se accedan a todos o no, realizbamos la carga de atributos que no eran utilizados y por ltimo la carga en herencia de partes del objeto que pueden no accederse nunca. Vimos que estos problemas quedaran resueltos con frameworks que tuvieran caractersticas de carga de datos bajo demanda (lazy-loading) para cargar aquello que se necesita, y que incorporaran cachs de objetos para no cargar aquellos objetos ya cargados. Hicimos un esfuerzo de generalizacin y por tanto de transparencia a travs de las clases PersistenteXXX, pero es posible realizar un esfuerzo mayor a travs de un framework ms sofisticado, existen varias estrategias tal y como el uso de Java reflection, proxys dinmicos, generacin de cdigo, enhancement de bytecode, crear nuevos lenguajes de consulta orientados a objetos etc.

5.3

ORIENTACIN A OBJETOS Y PATRONES COMO HERRAMIENTAS QUE PERMITEN LA TRANSPARENCIA Y LA TOLERANCIA A CAMBIOS

Gracias a usar la orientacin a objetos tal y como la herencia de nuestro modelo de datos de las clases genricas, la encapsulacin para ocultar la gestin interna de la persistencia respecto al exterior, las funciones virtuales (polimorfismo) para poder hacer la gestin de la persistencia en la herencia por niveles o para poder manejar las clases concretas de una forma genrica utilizando funciones a modo de callbacks, y la divisin del trabajo a travs del patrn DAO (sobre todo a travs de las clases Home), hemos conseguido aparte de una notable transparencia en el modelo de datos, una gran tolerancia frente a cambios, esta tolerancia se ha manifestado claramente en la clase JDBCInicio que tena la finalidad de realizar un conjunto de operaciones sobre el modelo de datos de forma persistente como ejemplo de

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.72

Tecnologas de Persistencia en Java, una comparativa desde la prctica uso, dicha clase a lo largo de los diferentes ejemplos ha quedado prcticamente inalterada aunque la gestin de la persistencia siguiera estrategias notablemente diferentes.

5.4 BMP MENOS TRANSPARENTE QUE CMP


Hemos comprobado que con la estrategia CMP conseguimos una mayor transparencia en las clases persistentes que con la estrategia BMP, pero a costa de definir ms clases y hacer abstracto el modelo de datos, resultndonos algo artificial respecto a un modelo normal de objetos de datos. De todas formas va generalizacin a travs de las clases PersistenteXXX hemos conseguido que en el caso BMP el cdigo persistente de las clases de datos sea mnimo. En el caso CMP se consigue que sea nulo. CMP tiene la importante ventaja de que al hacer abstracta la clase de datos, permite trabajar con libertad en la interceptacin de las llamadas a los diferentes atributos y colecciones de relaciones, y en la programacin de estrategias de persistencia bajo demanda o cachs de objetos. La eleccin de la filosofa CMP en el verdadero J2EE CMP tiene mucho que ver con esto. A travs de la via BMP, donde los atributos estn visibles y se deja implementar los mtodos de la obtencin de objetos relacionados al programador de la clase, la consecucin de un mayor nivel de transparencia realizada por el servidor de aplicaciones sera mucho ms complicado, salvo que se use la tcnica de bytecode enhancement de JDO que no deja de ser una transformacin del cdigo original (en este sentido podemos decir que JDO permite un modo BMP de programar las clases de datos).

5.5 CMP TIENE SERIOS PROBLEMAS CON LA HERENCIA


Si la herencia es importante, CMP puede no ser apropiado, salvo que se admita una fuerte intrusin de la persistencia en el modelo de herencia o se admita tener que el cdigo de gestin de la persistencia de toda la lnea vertical de herencia para cada clase persistente, que ante un esquema de muchas clases supone una fuerte redundancia de cdigo. BMP por otra parte encajaba muy bien la herencia, al no hacer ninguna intrusin en la herencia salvo por arriba y que como dijimos esta era evitable (supresin de la clase PersistenteImpl).

5.6 JDBC ADECUADO PARA MODELOS SENCILLOS O BASES DE DATOS POCO ESTNDAR
Esta es nuestra conclusin final, lo aconsejable es usar tecnologas de persistencia ms avanzadas cuando se trata de modelos de datos con muchas clases (muchas tablas) y complejos (herencias), salvo que el sistema de base de datos sea muy peculiar o tenga caractersticas avanzadas que slo se aprovecharan con un uso directo y a medida de JDBC, un buen ejemplo de esto es el uso de SQL 3 (ANSI SQL 1999), con reflejo en la API de JDBC, cuyo uso simplificara notablemente nuestros ejemplos, esto no quita que tecnologas ms avanzadas no puedan hacer uso de SQL 3 cuando saben que la base de datos que acceden lo soporta. Actualmente disponemos de toda una artillera de tecnologas de persistencia ms avanzadas, que de hecho casi siempre estn construidas sobre JDBC aunque lo oculten, tal y como JDO, J2EE CMP, JCA y SQLJ por citar las estndar, e Hibernate, Castor, TopLink y CocoBase por citar productos no estndar libres y comerciales.

persistencia_java.doc v.1.0 Jos Mara Arranz Santamara

Pg.73

También podría gustarte