MOTORES DE PERSISTENCIA DE OBJETOS EN BASE DE DATOS RELACIONALES. Fernando Boiero, Laura Castelli, Leandro Coschiza y Paulo Schiavi. Alumnos de la cátedra de Proyecto Final, carrera de Ingeniería en Sistemas de Información, Facultad Regional Villa María, Universidad Tecnológica Nacional. Av. Universidad 450, Villa María, X5900GLB, Cba. – e-mail: fboiero@gmail.com Tutores: Ing. Zohil Julio Cesar - Mg. Juan Vanzetti Introducción El común de las aplicaciones que permiten almacenar y recuperar información necesitan de alguna interacción con una base de datos relacional. Esto ha representado un problema para los desarrolladores porque el diseño de datos relacionales y los ejemplares orientados a objetos comparten estructuras de relaciones muy diferentes dentro de sus respectivos entornos. Las bases de datos relacionales poseen una configuración tabular y los ambientes orientados a objetos normalmente están relacionados en forma de árbol. Esta 'diferencia de impedancia' ha llevado a los desarrolladores a intentar construir un puente entre el mundo relacional y el mundo orientado a objetos. De acuerdo a lo estudiado en diferentes asignaturas que componen la carrera de Ingeniería en Sistemas de Información, se observa que las opciones basadas en imponer un único formato de datos a toda la aplicación presentan algunos inconvenientes. En el caso de que toda la aplicación siga el modelo relacional, se pierden las ventajas del paradigma objeto. En cambio si la aplicación sigue únicamente el modelo orientado a objetos, se tiene que emplear repositorios de objetos. En la actualidad dichos repositorios son tecnologías inmaduras y tienen un bajo nivel de estandarización. Si la aplicación está programada en un lenguaje orientado a objetos y la persistencia se realiza con una base de datos relacional, se tiene que especificar un puente que permita la comunicación conjunta, simple, transparente y eficiente entre estos dos modelos [4]. Este puente o traductor entre los dos formatos no es más que un componente de software (específicamente, una capa de programación), a la que se le dan los nombres de “capa de persistencia”, “capa de datos”, “correspondencia O/R” (“OR mapping”) o “motor de persistencia” [2]. En este trabajo, se comparan dos de las tecnologías de persistencia de objetos Java Persistent Objects (JPOX) e Hibernate) que intentan simplificar la tarea de conectar el lenguaje de programación orientado a objetos Java con bases de datos relacionales. Metodología La investigación se elaboró en tres etapas. En la primera, se describe teóricamente las dos tecnologías. En la segunda etapa, se implementan aplicaciones simples con el propósito de testear el funcionamiento de las dos tecnologías de mapeo expuestas. Por último, se exponen los resultados obtenidos. Descripción teórica Hibernate Es una implementación del API de Persistencia de Java (JPA). Una de las características que distingue al Hibernate es que los desarrolladores no tienen que implementar interfaces propietarias o extender clases bases propietarias para poder persistir las clases. Hibernate trata con el API reflection de Java y el aumento de clases en tiempo de ejecución utilizando una librería de generación de código Java de alto rendimiento, llamada CGLIB. La misma se utiliza para extender clases e implementar interfaces en tiempo de ejecución [1]. A continuación se detallan los pasos necesarios para la implementación de un programa que utiliza Hibernate [3] [4]: • En primer lugar, se deben agregar las librerías del framework al proyecto (antlr.jar, cglib.jar,asm.jar, asm-attrs.jar, commons-collections.jar, commons-logging.jar, hibernate3.jar, jta.jar, dom4j.jar, ehcache.jar, c3po.jar). • Posteriormente, se debe definir la configuración global del Session Factory de Hibernate mediante un archivo XML(hibernate.cfg.xml), o se puede pasar esta parametrización desde la aplicación. En la misma se definen los siguientes datos: a. Definición de la base de datos y su forma de conexión: i. Dialecto para interactuar con el motor de BD. ii. Driver JDBC que utilizaremos para conectarnos. iii. Usuario, clave, URL de conexión y base de datos a la cual se va a conectar. b. Forma de administrar el pool de conexiones y sesiones simultáneas por la aplicación a la base de datos. c. Forma de mapeo. i. En el caso de utilizar XML, usando patrones de persistencia, se define cómo cada atributo y relación de las clases se convierten en registros de una base de datos relacional. ii. En el caso de utilizar anotaciones, se tiene que agregar comentarios especiales dentro del código fuente especificando, al igual que el caso anterior, cómo persistir los objetos. • Luego, se debe instanciar con la configuración anteriormente detallada Session Factory de Hibernate. • Por último, a la instancia anterior se le solicita una sesión para poder persistir o consultar los objetos cuando sea necesario. Existen dos formas de realizar dichas consultas: a. Hibernate Query Languaje(HQL): es un lenguaje de consultas similar a SQL pero no se pregunta por registros y tablas, sino que se consulta por objetos. b. Criteria: esta interfaz permite especificar consultas a lo largo del desarrollo del programa (en base a clases y métodos de las mismas) sobre las entidades definiendo un conjunto de restricciones. El uso de Criteria puede ser muy conveniente cuando se tienen que componer consultas de forma dinámica. Java Persistent Objects (JPOX) JPOX fue lanzado al mercado en el año 2003, siendo éste una implementación de JDO1 para RDBMS. En la versión más reciente de JPOX (1.2.2) soporta JDO, JPA, RDBMS y db4o. Debido a los cambios en la dirección de JPOX desde 24/04/2008, se lo denomina DataNucleus. El uso de JPOX se divide en los siguientes pasos [7]: • En primer lugar se deben agregar las librerías del framework al proyecto (gdo.jar, JPOX.jar, log4j.jar) • En segundo lugar, se debe setear la configuración global de la persistencia en un objeto de tipo Properties. • Posteriormente, mediante anotaciones o XML se crean los mapeos de las distintas clases • En cuarto lugar se crea un objeto PersistenceManagerFactory. El mismo sirve para administrar la persistencia de los objetos sobre la base de datos. • Por último, usando la instancia PersistenceManagerFactory se crean transacciones, guardando o recuperando objetos sobre la base de datos. Para recuperar los objetos se puede usar el lenguaje de consultas JDOQL. Implementación de Aplicaciones Simples Para testear las tres opciones se utilizará el diagrama de clases de la figura 1: Provincia -nombre -pais -cuidades +getNombre() +setNombre() +getPais() +setPais() +getCiudades() +setCiudades() 1 * 1 * País -nombre -provincias +getNombre() +setNombre() +getProvincias() +setProvincias() Ciudad -nombre -provincia +getNombre() +setNombre() +getProvincia() +setProvincia() Figura 1: Diagrama de clases del test. Para realizar el ejemplo se utilizará la clase Provincia, agregándole un atributo id y sus respectivas anotaciones para que pueda persistir en la base de datos. @Entity @Table(name="provincia") public class Provincia { public Provincia (){ } @Id @GeneratedValue(strategy=javax.persistence.GenerationType.IDENTITY) private Long id; private String nombre; @ManyToOne private Pais pais; @OneToMany(mappedBy="ciudad") private Set<Ciudad> ciudades=new HashSet(0); public String getNombre() { return nombre; } public void setNombre(String nuevoNombre) { this.nombre = nuevoNombre; } public Pais getPais() { return pais; } public void setPais(Pais nuevoPais) { this.pais = nuevoPais; } public String toString() { return nombre ; } public int compareTo(Object x) { Ciudad p = ( Ciudad) x; return nombre.compareTo(p.getNombre()); } public boolean equals(Object obj) { if(obj == null) return false; Ciudad p = ( Ciudad)obj; if(nombre.compareTo(p.getNombre())==0) return true; else return false; } public int hashCode() { return nombre.hashCode(); } } Para implementar este ejemplo se emplea como motor de base de datos MySQL. La base de datos se llamará “ejemplo”, el usuario para accederla se denominará “root” y la contraseña “ninguna” [5] [6]. Implementación de Hibernate AnnotationConfiguration conf = new AnnotationConfiguration(); conf.setProperty("hibernate.connection.driver_class","com.mysql.jdbc.Driver"); conf.setProperty("hibernate.dialect","org.hibernate.dialect.MySQLDialect"); conf.setProperty("hibernate.connection.url","jdbc:mysql://localhost/ejemplo"); conf.setProperty("hibernate.connection.username","root"); conf.setProperty("hibernate.connection.password","ninguna");; conf.addAnnotatedClass(Pais.class); conf.addAnnotatedClass(Provincia.class); conf.addAnnotatedClass(Ciudad.class); SessionFactory sessionFactory = conf.buildSessionFactory(); Session session= sessionFactory.openSession(); Pais p = new Pais(); p.setNombre("Argentina"); Transaction tx = session.beginTransaction(); session.save(p); tx.commit(); Criteria criterio=session.createCriteria(Provincia.class) .add(Restrictions.like("nombre","Córdoba")); List listado= criterio.list(); Implementación de JPOX Primero hay que crear JPOX.properties donde se definen los contextos de persistencia de la aplicación. javax.jdo.PersistenceManagerFactoryClass=org.JPOX.jdo.JDOPersistenceManagerFactory javax.jdo.option.ConnectionDriverName=com.mysql.jdbc.Driver javax.jdo.option.ConnectionURL=jdbc:mysql://localhost/ejemplo javax.jdo.option.ConnectionUserName=root javax.jdo.option.ConnectionPassword=ninguna org.JPOX.autoCreateSchema=true org.JPOX.validateTables=false org.JPOX.validateConstraints=false Desde la aplicación se implementa como se detalla el siguiente ejemplo: PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("JPOX.properties"); PersistenceManager pm = pmf.getPersistenceManager(); Transaction tx=pm.currentTransaction(); tx.begin(); Pais p = new Pais(); p.setNombre("Argentina"); pm.makePersistent(p); tx.commit(); Query q = pm.newQuery(Provincia.class, "nombre == \" Córdoba\"); List listado=(List)q.execute(); Conclusiones Luego de desarrollar las dos implementaciones, se derivan las siguientes conclusiones, las cuales fueron plasmadas en la tabla que se muestra a continuación. Tabla 1: Resumen comparativo. Simplicidad Flexibilidad Portabilidad Performance Estándares Curva de aprendizaje y soporte Recuperación de objetos Hibernate Muy simple Media Media Muy buena Implementa JPA Baja JPOX Muy Simple Alta Media Muy buena Implementa JDO y JPA Media Por objetos simples, consulta con HQL y el API Criteria Por objetos simples y consulta con JDOQL Cabe destacar que el aspecto analizado Simplicidad se ha medido en líneas de código. En los casos de estudio se obtuvo que para Hibernate se emplearon 17 líneas de código y en cambio para JPOX sólo 18. La portabilidad para ambas opciones es media, debido a que se necesitan agregar algunas librerías al kit de desarrollo estándar para java de la Sun. Al analizar el parámetro Performance se advierte que existen demasiados factores que inciden en forma determinante al momento de ponderar los casos de pruebas. Como por ejemplo: driver de base de datos, indexación de la misma y tipos de objetos. Tomando como base el benchmarks publicado en http://www.devx.com/Java/Article/33768/0/page/4 Hibernate demoró 360 ms para crear y registrar 100 objetos simples contra 560 ms de JPOX y para leer 100 objetos simples Hibernate demoró 295 ms contra 325 ms de JPOX [9]. Se considera que es difícil cuantificar el análisis de la curva de aprendizaje y el soporte, dado que encierran numerosos aspectos subjetivos. En ambos casos se poseen páginas oficiales no sólo con la documentación sino también con ejemplos didácticos que ayudan a su rápida implementación. Hibernate en términos de soporte es más estable debido a que lleva muchos años en el mercado siendo una de las comunidades Open Source con miembros muy activos. Desde el punto de vista de su utilización, se percibe a través de los ejemplos dados anteriormente que los pasos y formas de uso son muy similares, por lo que se concluye que el tiempo de aprendizaje entre ambas es muy similar [9]. En vista de estos resultados, para un desarrollador junior que desea persistir sus objetos, se recomienda utilizar Hibernate, debido a su simplicidad, disponibilidad de información y su alta aplicación en la industria. Agradecimientos Se agradece la colaboración del Ing. Zohil Julio Cesar y Mg. Juan Vanzetti quienes contribuyeron al desarrollo del mismo. Un agradecimiento especial a Mariano Turcutto. Referencias [1] Introducción a Hibernate - Francesc Roses Albial - Octubre de 2004 [2] Apunte de Motores de Persistencia - Dr. Vincent Ramon Palasillana - Director Académico Universidad Francisco Gaviria [3] Persistencia de Objetos utilizando Hibernate - Jeff Hanson - (www.deux.com/java ) [4] Persistencia de Objetos Java: El camino hacia Hibernate - Michael Glogls (www.goegl.de ) [5] www.javahispano.org - (tutoriales, foros y ejemplos) [6] http://www.adictosaltrabajo.com - (tutoriales y ejemplos) [7] http://JPOX.org - (Glosario y definiciones) [8] http://wikiipedia.com - (Glosario y definiciones) [9] http://www.devx.com/Java/ (artículos y ejemplos) Glosario Bean: Clase simple programada de manera encapsulada en el lenguaje JAVA. Debe implementar Serializable y tener un constructor por defecto. [8] Criteria: API de Hibernate para recuperar objetos de la base de datos que permite ir agregando filtros de manera simple y escalonada. [8] JPA: Java Persistence API, API de persistencia desarrollada para la plataforma Java EE e incluida en el estándar EJB3. [8]