Resumen del Proyecto Fin de Carrera Alejandro Nogueiro Mariscal, (1) Juan Boubeta Puig, Antonio Garcı́a Dominguez. Calle Jacinto Benavente nº1 3ºD, CP 11100, San Fernando, Cádiz. 696 999 226, [email protected] (1) ESI I - E. Superior de Ingenierı́a C/ Chile, 1 11002 Cádiz Extracto Este proyecto consiste en un generador de código para Hibernate desde modelos UML. El código generado es el conjunto de clases del modelo implementadas en Java con una serie de anotaciones JPA 2.0. Estas clases, mediante Hibernate, sirven para añadir persistencia a nuestro código. Los modelos UML tendrán una serie de anotaciones para poder transformarlos correctamente. Palabras Clave: hibernate, papyrus, uml. 1. Motivación y contexto 1.1. Introducción y motivación Las bases de datos relacionales y los programas orientados a objetos organizan la información de formas muy distintas. Las bases de datos relacionales tienden a guardar datos escalares en las filas de una o más tablas, mientras que los programas 1 orientados a objetos operan con grafos de objetos en memoria. Esto genera una serie de dificultades a la hora de cargar y guardar objetos en la base de datos, que se conocen en conjunto como la “impedancia objeto/relacional”. En los últimos años se han creado diversas bibliotecas para resolver este problema. Una de las más conocidas es Hibernate [1, 2, 3], una biblioteca de código abierto bajo la licencia GNU Lesser General Public License(LGPL) disponible para Java y .NET. Esta biblioteca puede integrarse con bases de datos de una amplia variedad de tipos, incluyendo Oracle, Microsoft SQL Server, MySQL o PostgreSQL, entre otras. Hibernate es una biblioteca avanzada, con muchas funcionalidades. Por ello se han creado herramientas de todo tipo para asistir a sus usuarios, ya sea para depurar, desarrollar los programas, etc. Un problema común es la dificultad en elaborar una primera versión de la correspondencia entre la base de datos y los objetos Java. Esta tarea requiere escribir un fichero de configuración de cierta complejidad, o escribir código Java con una mezcla de anotaciones propietarias de Hibernate y otras de la especificación Java Persistence API 2.0 (JPA 2.0, JSR-317). Por desgracia, las herramientas existentes para realizar esta tarea son en su mayorı́a privativas y sus licencias pueden ser prohibitivas para desarrolladores individuales o empresas pequeñas. Las alternativas libres o no realizan dicha función o son poco accesibles para nuevos usuarios, debido a su falta de usabilidad o la baja calidad de su documentación. En este Proyecto Fin de Carrera(PFC) se propone desarrollar un enfoque dirigido por modelos que ayude a escribir la versión inicial de los ficheros necesarios para que Hibernate utilice una base de datos y genere el código Data Definition Language(DDL) que define su estructura. Estos ficheros serán una series de clases Java con anotaciones JPA 2.0. Para ello, se partirá de diagramas de clases UML anotados mediante un perfil apropiado para bases de datos. Estos diagramas de clases UML se validarán y transformarán a los ficheros oportunos mediante algunas de las tecnologı́as disponibles actualmente. 2 1.2. Objetivos Este PFC pretende satisfacer los siguientes objetivos Implementar transformaciones automatizadas de modelos UML a los artefactos necesarios para que Hibernate pueda acceder a las bases de datos modeladas. Validar los modelos UML comprobando que cumplen ciertas propiedades deseables en una base de datos. Proporcionar ayuda en diversos formatos al usuario, asistiéndole en su uso cotidiano. El PFC contará con una correcta documentación en español, con una guı́a que detallará lo mejor posible la instalación y el uso del programa. La documentación, además, estará disponible en lı́nea. Por otro lado, el PFC será publicado bajo licencia libre. Además, pretende ser distribuido mediante una serie de extensiones para uno de los entornos de desarrollo más populares. 2. Planificación En esta sección veremos cómo se ha organizado temporalmente el trabajo de este proyecto ası́ como las tareas que han tenido lugar y la planificación de las mismas. En este proyecto se ha seguido el enfoque que promueve una metodologı́a de desarrollo ágil de software, más concretamente, la programación extrema [4] o “eXtreme Programming”(XP) [5, 6], debido a la simplicidad de sus reglas y prácticas, su orientación a equipos de desarrollo de pequeño tamaño y su flexibilidad ante los cambios. La diferencia inmediata con otras metodologı́as es que se fija más en mantener una comunicación con el usuario dı́a a dı́a, por lo que es necesaria menos documentación que blinde a los desarrolladores. De muchas maneras son 3 más bien orientados al código: siguiendo un camino que dice la parte importante de la documentación es el código fuente. El desarrollo seguido se puede dividir en distintos apartados: Aprendizaje de las tecnologı́as necesarias(27 de febrero al 9 de abril). Pruebas y familiarización con el entorno de trabajo(16 de abril al 18 de mayo). Prueba de transformación a código Java de Hibernate(21 de mayo al 18 de junio). Primera Iteración(9 de julio al 16 de julio). Segunda Iteración(del 16 al 23 de julio). Tercera Iteración(del 23 al 30 de julio). Cuarta Iteración(31 de julio al 7 de agosto). Quinta Iteración(del 8 al 15 de agosto) Sexta Iteración(1del 6 al 23 de agosto). Séptima Iteración(del 24 al 31 de agosto). Octava Iteración(del 3 al 10 de septiembre). 3. Análisis En esta sección hablaremos de las necesidades que debe cubrir el proyecto, es decir, los requisitos que debe cumplir. 4 3.1. Requisitos En la realización de UML2Hibernate se pensaron en varios requisitos que deberı́a cumplir este proyecto. Para empezar, se estableció que el proyecto iba a constar de una serie de extensiones para uno de los entornos de programación más conocidos. En segundo lugar, se estableció que se iba a usar UML para los diagramas de clases a transformar debido a que es el estándar mas conocido y usado para el diseño de modelos. En tercer lugar, establecimos las caracterı́sticas que se modeları́an, como claves primarias, atributos no nulos, relaciones de asociación, herencia, agregación y composición. 3.2. Análisis de las Herramientas existentes Antes de la realización de UML2Hibernate, tuvimos que realizar un exhaustivo análisis de las herramientas que existı́an en el mercado que pudiesen hacer la tarea que nosotros deseábamos realizar. Se analizaron principalmente AndroMDA y Hibernate Tools. La primera se descartó debido a su dificultad de aprendizaje y a su escasa documentación. La segunda se descarto debido a que no realizaba las funciones deseadas. 3.3. Cosas a modelar por UML2Hibernate Los modelos UML a transformar deben poder contener los siguientes elementos, necesarios para la correcta transformación a código para Hibernate. Tablas. Si se desea crear una base de datos Objeto/Relacional, como es el caso, es necesario disponer de tablas. Por ello UML2Hibernate debe poder modelar Tablas para la base de datos. Atributos. Aunque no hiciese falta aclararlo, una Tabla debe poder tener distintos atributos. Estos atributos deberán disponer de un tipo para su correcta transformación en código. Debido a que las clases generadas para Hibernate 5 se generan en código Java con una serie de anotaciones, los tipos serán los tipos de Java más el tipo Date(también de Java), tipo muy común en las Bases de Datos. Claves Primarias simples. Si se dispone de Tablas, es fundamental disponer de Claves Primarias para las mismas, ya que toda tabla debe tener una clave primaria. Atributos no nulos y de valor único. Estos tipos de atributos también son frecuentes en el diseño de una base de datos, por lo que serı́a deseable que UML2Hibernate pudiese tratarlos. Atributos de tipo vector. En muchas bases de datos, se usan atributos que son una lista de distintos atributos, como por ejemplo si se quisiera tener una lista de teléfonos para cada empleado. Es por ello por lo que UML2Hibernate debe modelar este tipo especial de atributos. Herencia. En las Bases de Datos Objeto/Relacionales la herencia es un recurso muy usado, por ello UML2Hibernate debe poder tratarlo. Asociaciones binarias. Es fundamental en una Base de Datos que las tablas estén relacionadas entre sı́, por ello UML2Hibernate debe poder tratar las asociaciones entre dos tablas, ası́ como la composición y la agregación. 4. Perfil UML En este capı́tulo hablaremos de la creación del perfil UML usado en este pro- yecto. 4.1. Definición del Perfil UML Para poder modelar diagramas de clases que se puedan transformar en código para Hibernate sin ninguna información adicional, es necesario un perfil que defina 6 las anotaciones necesarias para la definición de una base de datos.Para ello se encontró un perfil de una tesis [7] para el diseño de bases de datos en UML. La autora de esta tesis es Elisa Martı́nez Real y su tı́tulo es “Extensión de una herramienta CASE para la traducción de esquemas conceptuales en UML a esquemas lógicos en UML Data Modeling Profile”. El proyecto consistı́a en desarrollar una nueva funcionalidad CASE que permita desarrollar un esquema conceptual y que genere automáticamente el esquema lógico y su posterior transformación a código SQL estándar y a código especı́fico del Sistema de gestión de bases de datos(SGBD). Debido a que este perfil era más amplio de lo que necesitabamos, se decidió crear un perfil UML con un subconjunto de los componentes de ese perfil, de tal manera que tuviésemos sólo las anotaciones necesarias. A continuación se expone el perfil UML(figura 1) y se explica brevemente cada uno de sus elementos. Figura 1: Perfil UML de UML2Hibernate construido con Papyrus Table. Para poder modelar las tablas, se ha creado un Estereotipo de la Metaclase 7 “Class” llamado “Table”. Ası́ se distinguirán las clases que son tabla de las que no. PK. Para poder modelar las claves primarias, se ha creado un Estereotipo de la Metaclase “Property” llamado “PK”. Ası́ se distinguirán los atributos que son clave primaria de los que no. Unique. Para poder modelar los atributos de valor único, se ha creado un Estereotipo de la Metaclase “Property” llamado “Unique”. Ası́ se distinguirán los atributos de valor único. NotNull. Para poder modelar los atributos de valor no nulo, se ha creado un Estereotipo de la Metaclase “Property” llamado “NotNull”. Date. Debido a que en los tipos primitivos de Java para los modelos UML de Papyrus no está definido el tipo “Date”, se ha tenido que definir un tipo primitivo para poder modelar los atributos de este tipo. 4.2. Implementación del Perfil UML En la creación del perfil, se crean dos tipos distintos de perfil: uno dinámico y uno estático. Un perfil dinámico de Papyrus define su metamodelo cada vez que se usa, es decir, define su metamodelo en el instante en el que es usado y de forma temporal. Un perfil estático, en cambio, tiene asociado un metamodelo de Ecore de forma permanente, generando las clases Java necesarias para ello. Pasemos ahora al proceso de creación del perfil UML. Para la creación del perfil dinámico, simplemente se crea un perfil de UML mediante Papyrus y se le añaden los tipos básicos de Java. Para la creación del perfil estático de UML hemos usado la herramienta de Ecore para generar metamodelos, un archivo de extensión “.genmodel”. A partir de él se ha generado la estructura necesaria para poder crear el perfil estático. 8 5. Generación de Código para Hibernate y Pruebas En este punto hablaremos sobre la estructura general de la validación y trans- formación a código del modelo, y de las pruebas realizadas. 5.1. Generación de código para Hibernate Para la generación del código para Hibernate se ha implementado un programa EGL. A continuación vamos a exponer su estructura pincipal. Creación de la Clase. Es la estructura principal del programa EGL. Aquı́ se escribira la estructura principal de la clase, incluyendo los “include”. Se compone de dos módulos: Atributos. En este módulo se escribirán los distintos atributos de la clase. Este módulo se divide a su vez en distintos submódulos. Estos módulos son los siguientes: Claves Primarias. En este submódulo se tratarán los atributos que son clave primaria. Asociaciones. En este submódulo se tratarán los atributos procedentes de una asociacion, composición o agregación. Resto de Atributos. En este submódulo se tratan el resto de atributos, es decir, atributos simples, no nulos o de valor único. Métodos “get”‘y “set”. En este módulo se crearán los métodos “get” y “set” para todos los atributos de la clase. 5.2. Validación del modelo Para comprobar que el modelo a transformar es válido antes de transformarlo, se ha implementado un programa EVL para validar el modelo. Este programa EVL 9 se compone de los módulos expuestos a continuación. Módulo de comprobación de las Clases. En este módulo se comprueba que las clases sean correctas. Para ello se definirán las siguientes pruebas: Toda clase posee nombre. Toda clase es Tabla. Todos los atributos de la clase son correctos. Toda clase tiene clave primaria. Módulo de comprobación de los Atributos. En este módulo se comprueba que todos los atributos son correctos. Para ello se definirán las siguientes pruebas: Todo atributo posee nombre. Todo atributo tiene tipo. El tipo de todo atributo es válido, es decir, es un tipo de Java. 5.3. Pruebas Para probar el correcto funcionamiento de las clases generadas, se han creado una serie de pruebas unitarias integradas en un proyecto de Maven [8]. Para ello, hemos generado el código para un diagrama de clases UML con las anotaciones del perfil anteriormente expuesto. Además, para demostrar que las clases generadas por UML2Hibernate son las esperadas, se dispone de una prueba unitaria que comprueba que las clases con las que se hacen las pruebas unitarias y las que generarı́a el programa son las mismas. Se ha tenido que hacer de esta manera, ya que es bastante complicado integrar en un proyecto Maven(en el cual realizamos las pruebas) un conjunto de clases directamente generado por el programa. Para la realización de las pruebas se ha usado EUnit [9] y JUnit [10]. 10 Además de este proyecto Maven, se dispone de otro proyecto de pruebas para comprobar que se valida correctamente los modelos antes de transformarlos. Por ello, se ha creado un proyecto con una serie de pruebas que testean el correcto funcionamiento de la validación. Para este proyecto, unicamente se usan pruebas unitarias del lenguaje EUnit. 6. Conclusiones 6.1. Resultados obtenidos En este proyecto se han desarrollado una serie de plugins para Eclipse para poder transformar modelos de UML, que representan una base de datos, en código para Hibernate. Para esto, se ha tenido que cumplir una serie de requisitos, que exponemos a continuación. Implementa transformaciones automatizadas de modelos UML a los artefactos necesarios para que Hibernate pueda acceder a las bases de datos modeladas. UML2Hibernate posee un perfil UML con varias anotaciones para poder cumplir este requisito. Existen las anotaciones para distinguir tablas, claves primarias, atributos no nulos y atributos con valor único. Con estas anotaciones se puede transformar correctamente el modelo en código para Hibernate. Valida los modelos UML comprobando que cumplen ciertas propiedades deseables en una base de datos. La validación se encarga de comprobar que los modelos son válidos para la transformación a código para Hibernate, pro ello hace comprobaciones tales como que toda tabla posee clave primaria. Proporciona ayuda en diversos formatos al usuario, asistiéndole en su uso cotidiano. En este proyecto era muy importante la documentación para el uso de la herramienta. Es por ello que se ha realizado un completo manual de 11 usuario en español y una wiki con la documentación en inglés, varios vı́deos ilustrativos de cómo se usa UML2Hibernate y varios ejemplos de modelos ya realizados con UML2- Hibernate. Con estos requisitos, se han cumplido todos los objetivos propuestos inicialmente para este proyecto. 6.2. Valoración personal La realización de este proyecto fin de carrera ha sido muy interesante, pues me ha permitido conocer una cantidad considerable de nuevas técnicas, lenguajes de programación y aplicaciones; además me ha servido para conocer cómo se trabaja en un proyecto de mayor envergadura que los que se han hecho en la Ingenierı́a Técnica. La realización de UML2Hibernate ha sido muy compleja, porque además de la programación se ha tenido que trabajar con un programa concreto(Eclipse) e introducir mi herramienta en el, he tenido que desarrollar un documento técnico pro primera vez, y además he tenido que crear manuales para el usuario en varios formatos poniéndome en el lugar del que no conoce mi herramienta. Antes de este proyecto sólo conocı́a el lenguaje de programación C++, por lo que hubo una fase de aprendizaje. Los lenguajes EGL y EVL de Epsilon no poseen una estructura similar a C++, pero no son lenguajes para nada complicados y no me costo mucho aprenderlos. En cuanto a Java, su sintaxis es similar a C++, pero los problemas surgidos a Java no tienen nada que ver con el lenguaje, sino con la inclusión del proyecto en el entorno de trabajo de Eclipse. En esta fase de aprendizaje, además, tuve que aprender conceptos sobre Ingenierı́a Dirigida por Modelos, puesto que en la Ingenierı́a Técnica no se habla nada sobre ello. Para la inclusión de UML2Hibernate en el entorno de Eclipse y Papyrus se ha tenido que indagar bastante en código fuente de estas herramientas. Para añadir una pequeña entrada en un menú, por ejemplo, habı́a que ir mirando que plugin implementaba el menú, acceder al código fuente, ver la clase que finalmente 12 implementaba el menú y finalmente quedarse con ella para implementar el punto de extensión. Es por ello que, en este proyecto, he aprendido bastante a entender código fuente, y sobre todo, a encontrar y solucionar los numerosos problemas que salen al crear plugins para Eclipse. Por primera vez, se ha tenido la oportunidad de aplicar un diseño iterativo, y comprobar su gran utilidad en proyectos en los que no se tienen claro desde el principio todos los requisitos que deben cumplir. Se ha aplicado este proceso hasta conseguir que la aplicación cumpliera con los requisitos esperados. Para el control de versiones se ha usado Git. Gracias a este proyecto he conocido esta herramienta, puesto que en el pasado sólo habı́a usado Subversion. EL código fuente del proyecto, y por tanto cada una de sus modificaciones, se ha subido a la forja del grupo UCASE. En cuanto a la colaboración con el grupo UCASE, decir que ha sido la primera vez que he participado en un grupo de investigación. Prácticamente todas las semanas habı́a una reunión en la que se podı́a exponer las dudas y problemas que iban surgiendo a lo largo del desarrollo. Estas reuniones me han servido para aprender conceptos ajenos a mi proyecto unas veces, y otras veces relativos a mi proyecto. En ambos casos ha sido de utilidad, puesto que nunca viene de más conocer más tecnologı́as. Uno de los seminarios más útiles en la realización de mi proyecto fue uno en el que se explicó como desarrollar un plugin de Eclipse y un Update Site. A pesar de que al realizarlo muchas cosas eran para nada parecidas, me sirvió como conocimiento previo para no empezar de cero. En cuanto a las pruebas unitarias, eran un requisito adicional que se habı́a incluido con el fin de mejorar la calidad del producto resultante y darle mayor dimensión al proyecto. Además, como se usa la metodologı́a “eXtreme Programing” estas pruebas forman parte del desarrollo de software. Era la primera vez que usaba este tipo de pruebas de software, ya que en la Ingenierı́a Técnica no desarrollamos pruebas de este tipo. Como habı́a que probar distintas cosas, se realizaron dos 13 proyectos de pruebas, uno para probar la validación y otro para probar que el código generado funcionase correctamente. En las pruebas de validación se usa EUnit, puesto que sólo se probaba un programa en EVL con diversos modelos. En las pruebas del código generado se usa tanto EUnit como JUnit, puesto que hay que demostrar que las clases del proyecto son iguales a las que se generarı́an, y luego demostrar que estas clases Java con anotaciones de Hibernate funcionan correctamente. Para concluir, queda destacar que este proyecto me ha enseñado, aparte de las diversas tecnologı́as aprendidas, a “buscarme la vida” solucionando errores en los que tenı́a que leer bastante código fuente no escrito por mi, por lo que he aprendido bastante a entender código fuente de otros programadores. 6.3. Trabajo futuro Como trabajo futuro se pueden añadir elementos de las bases de datos más complejos y difı́ciles de implementar su transformación, como es el caso de asociaciones n-arias, claves primarias compuestas o lanzadores(triggers). En cuanto a la transformación en sı́, se podrı́a dar la opción de transformar el modelo, en vez de a código Java, a código HEDL [11, 12](Hibernate Entity Definition Language). Es un lenguaje de fecha de creación muy cercana(febrero de 2012) que sirve para generar código para Hibernate. En futuras versiones, se implementará la corrección automática, para que el usuario no tenga que corregir los errores cometidos en el modelo. Referencias [1] JBoss Inc. Hibernate. [2] D. Minter y J. Linwood. Beginning Hibernate: From Novice to Professional. Apress, primera edición, agosto 2006. ISBN 1590596935. 14 [3] G. Mak y S. Guruzu. Hibernate Recipes: A Problem-Solution Approach. Apress, primera edición, junio 2010. ISBN 1430227966. [4] K. Beck. Programacion extrema. URL http://www.programacionextrema.org/ [5] K. Beck. Extreme programming: A gentle introduction. URL http://www.extremeprogramming.org/ [6] K. Beck. Extreme Programming Explained: Embrace Change. Addison-Wesley Professional, US ed edición, octubre 1999. ISBN 0201616416. [7] E. Martı́nez Real. Extensión de una herramienta case para la traducción de esquemas conceptuales en uml a esquemas lógicos en uml data modeling profile. septiembre 2008. URL http://upcommons.upc.edu/pfc/handle/2099.1/5385 [8] J. van Zyl. Maven. URL http://maven.apache.org/ [9] Eclipse Foundation. Epsilon unit testing framework. URL http://www.eclipse.org/epsilon/doc/eunit/ [10] E. Gamma y K. Beck. JUnit.org. URL http://www.junit.org/ [11] DevBoost. HEDL. URL http://www.hibernate-dsl.org/index.php/HEDL [12] DevBoost. HEDL en eclipse. URL http://marketplace.eclipse.org/content/ hedl-hibernate-entity-definition-language#.UFFr1mbI9Bl 15