El Paradigma de la Orientación a Objeto en SQL:1999 y Oracle 8i Capacidades y Limitaciones Universidad Del Bío-Bío Facultad de Ciencias Empresariales Depto. de Auditoría e Informática Ingeniería de Ejecución en Computación e Informática El Paradigma de la Orientación a Objeto en SQL:1999 y Oracle 8i Capacidades y Limitaciones Informe para optar al título profesional de Ingeniero de Ejecución en Computación e Informática Alumno Miguel Esteban Romero Vásquez. Profesor Guía Gilberto Gutiérrez Retamal. Chillán, diciembre del 2001 “Los buenos viven eternamente; El Altísimo cuida de ellos. Por lo tanto recibirán de manos del Señor un reino glorioso y una hermosa corona; ...” ( Sabiduría 5, 15-17) A la Memoria de Rodrigo Muñoz (1977-1997) Y dedicado a Mis Padres. Resumen del Contenido General Resumen del Contenido General ..............................................................iv Contenido General ...................................................................................vi Resumen .............................................................................................xiv Agradecimientos .....................................................................................xv 1 Introducción.......................................................................................... 1 2 El Paradigma de la Orientación a Objeto................................................ 3 2.1 Introducción ............................................................................................................. 4 2.2 Conceptos Básicos.................................................................................................... 6 3 Bases de datos Orientadas a Objeto .................................................... 22 3.1 Introducción ........................................................................................................... 23 3.2 Estándares de SABDOO ........................................................................................... 27 4 La Orientación a Objeto en SQL:1999 .................................................. 34 4.1 Introducción ........................................................................................................... 35 4.2 Soporte a la Orientación a objeto ............................................................................ 40 4.3 Evaluación .............................................................................................................. 83 5 Oracle 8i y la Orientación a Objeto. ..................................................... 87 5.1 Introducción ........................................................................................................... 88 5.2 Soporte a la Orientación a Objeto ........................................................................... 89 5.3 Evaluación ............................................................................................................ 134 6 Una Aplicación de ejemplo ................................................................ 138 6.1 Introducción ......................................................................................................... 139 6.2 Descripción del Prototipo...................................................................................... 146 6.3 Diseño del prototipo............................................................................................. 154 7 Conclusiones .................................................................................... 169 Apéndice A: Sintaxis de SQL:1999 Relacionada con la OO. ................... 172 A.1 Leyenda ............................................................................................................... 173 A.2 Sentencias............................................................................................................ 174 Apéndice B: UML .................................................................................. 190 B.1 ¿Qué es UML?........................................................................................................ 191 Apéndice C: Código Fuente del Prototipo y Pantallas ........................... 201 C.1 Creación de tipos ................................................................................................. 202 C.2 Definición de Tablas de objetos e inserción de instancias ..................................... 239 C.3 Pantallas. ............................................................................................................. 245 C.4 Código Java. ......................................................................................................... 254 Bibliografía........................................................................................... 375 Contenido General Resumen del Contenido General ..............................................................iv Contenido General ...................................................................................vi Resumen .............................................................................................xiv Agradecimientos .....................................................................................xv 1 Introducción.......................................................................................... 1 2 El Paradigma de la Orientación a Objeto................................................ 3 2.1 Introducción ............................................................................................................. 4 2.1.1 ¿Qué es el paradigma de la orientación a objeto?............................................. 4 2.2 Conceptos Básicos.................................................................................................... 6 2.2.1 Objetos ........................................................................................................... 6 2.2.1.1 Estado....................................................................................................... 7 2.2.1.2 Comportamiento y Mensajes ..................................................................... 8 2.2.1.3 Identidad .................................................................................................. 9 2.2.2 Abstracción y Clase. ........................................................................................ 9 2.2.3 Encapsulamiento ........................................................................................... 11 2.2.4 Modularidad.................................................................................................. 11 2.2.5 Jerarquía de Clases........................................................................................ 12 2.2.5.1 Herencia ................................................................................................. 13 2.2.5.1.1 Polimorfismo .................................................................................... 15 2.2.5.2 Asociación .............................................................................................. 16 2.2.5.3 Agregación y Composición ...................................................................... 17 2.2.6 Tipos (tipificación)......................................................................................... 17 2.2.7 Concurrencia................................................................................................. 18 2.2.8 Persistencia................................................................................................... 19 2.2.8.1 Formas de solucionar el problema de la persistencia ............................... 19 2.2.8.1.1 Archivos convencionales ................................................................... 19 2.2.8.1.2 Bases de datos relacionales ............................................................... 20 2.2.8.1.3 Bases de datos orientadas a objetos.................................................. 20 3 Bases de datos Orientadas a Objeto .................................................... 22 3.1 Introducción ........................................................................................................... 23 3.1.1 Bases de datos relacionales ........................................................................... 23 3.1.2 Bases de datos orientadas a objeto................................................................ 24 3.1.2.1 Nuevos requerimientos ........................................................................... 24 3.2 Estándares de SABDOO ........................................................................................... 27 vi Contenido General 3.2.1 Las tendencias: SABD relacionales “Extendidos” vs. SABDOO “Puros” .............. 27 3.2.2 Estándar SQL:1999 ........................................................................................ 30 3.2.2.1 Origen y evolución .................................................................................. 30 3.2.2.2 Partes que lo componen .......................................................................... 31 3.2.3 Estándar ODMG 2.0 ....................................................................................... 32 3.2.3.1 Origen y evolución .................................................................................. 32 3.2.3.2 Partes que lo componen .......................................................................... 32 4 La Orientación a Objeto en SQL:1999 .................................................. 34 4.1 Introducción ........................................................................................................... 35 4.1.1 Organizaciones preocupadas de la estandarización de SQL........................... 35 4.1.2 Nuevas características. .................................................................................. 37 4.1.2.1 Nuevos Tipos de Datos............................................................................ 37 4.1.2.2 nuevos predicados .................................................................................. 38 4.1.2.3 semántica reforzada................................................................................ 38 4.1.2.4 seguridad adicional ................................................................................. 39 4.1.2.5 bases de datos activa .............................................................................. 39 4.2 Soporte a la Orientación a objeto ............................................................................ 40 4.2.1 Tipos, abstracción y clases ............................................................................ 40 4.2.1.1 Creación de tipos (CREATE TYPE) ............................................................. 40 4.2.1.2 Atributos................................................................................................. 42 4.2.1.3 Métodos.................................................................................................. 43 4.2.1.3.1 Métodos de Instancia ........................................................................ 44 4.2.1.3.2 Métodos estático............................................................................... 47 4.2.1.3.3 Constructores ................................................................................... 48 4.2.1.3.4 Creación de métodos (CREATE METHOD) ........................................... 48 4.2.1.4 Modificación de tipos (ALTER TYPE) ......................................................... 52 4.2.1.4.1 Agregar un atributo .......................................................................... 52 4.2.1.4.2 Eliminar un atributo .......................................................................... 53 4.2.1.4.3 Agregar un método........................................................................... 53 4.2.1.4.4 Eliminar un método........................................................................... 54 4.2.1.5 Eliminación de tipos (DROP TYPE) ............................................................ 54 4.2.1.6 Comparación de instancias...................................................................... 55 4.2.1.6.1 Eliminación de funciones de comparación ......................................... 57 4.2.1.7 Conversión de UDT (CAST definidos por el usuario) ................................. 58 4.2.1.7.1 Eliminación de CAST definidos por el usuario .................................... 59 4.2.2 Colecciones................................................................................................... 59 4.2.2.1 Arreglos.................................................................................................. 59 4.2.3 Encapsulamiento ........................................................................................... 60 4.2.4 Modularidad.................................................................................................. 62 4.2.5 Persistencia................................................................................................... 62 vii Contenido General 4.2.5.1 Manejo de Tablas .................................................................................... 62 4.2.5.1.1 Creación (CREATE TABLE) .................................................................. 62 4.2.5.1.2 Modificación (ALTER TABLE) .............................................................. 65 4.2.5.1.3 Eliminación (DROP TABLE) ................................................................. 66 4.2.5.2 Manejo de Instancias............................................................................... 67 4.2.5.2.1 Inserción de objetos.......................................................................... 67 4.2.5.2.2 Selección de objetos (SELECT) ........................................................... 69 4.2.5.2.3 Modificación de objetos .................................................................... 71 4.2.5.2.4 Eliminación de objetos ...................................................................... 72 4.2.6 Jerarquía de Clases........................................................................................ 73 4.2.6.1 Asociación .............................................................................................. 73 4.2.6.2 Agregación y Composición ...................................................................... 75 4.2.6.3 Herencia y Polimorfismo.......................................................................... 76 4.2.7 Concurrencia................................................................................................. 78 4.2.8 Manejo de Versiones de objetos y configuraciones ........................................ 79 4.2.9 Evolución de esquemas y de Instancias.......................................................... 79 4.2.10 Transacciones y recuperación ante fallos ..................................................... 80 4.2.11 Mecanismos de Autorización ....................................................................... 80 4.2.12 Compatibilidad con el modelo relacional ..................................................... 80 4.2.13 Limitaciones encontradas en SQL:1999........................................................ 82 4.3 Evaluación .............................................................................................................. 83 4.3.1 Criterios de evaluación .................................................................................. 83 4.3.2 Matriz de evaluación ..................................................................................... 85 5 Oracle 8i y la Orientación a Objeto. ..................................................... 87 5.1 Introducción ........................................................................................................... 88 5.2 Soporte a la Orientación a Objeto ........................................................................... 89 5.2.1 Tipos, abstracción y clases ............................................................................ 89 5.2.1.1 Estructura de un tipo objeto .................................................................... 90 5.2.1.2 Sintaxis para la creación de tipos ............................................................ 90 5.2.1.2.1 CREATE TYPE .................................................................................... 91 5.2.1.2.2 CREATE TYPE BODY ........................................................................... 94 5.2.1.3 Atributos................................................................................................. 95 5.2.1.4 Métodos.................................................................................................. 99 5.2.1.4.1 Constructor ...................................................................................... 99 5.2.1.4.2 Miembro (member) ......................................................................... 100 5.2.1.4.3 Estático (Static) ............................................................................... 102 5.2.1.4.4 Comparación (Map u Order) ............................................................ 102 5.2.1.4.5 Sobrecarga de métodos................................................................... 104 5.2.1.5 Alternativas para la implementación de los métodos ............................. 105 5.2.1.5.1 Implementación de los métodos con PL/SQL ................................... 106 viii Contenido General 5.2.1.5.2 Implementación de métodos con JAVA ............................................ 106 5.2.1.5.3 Implementación de métodos con C ................................................. 109 5.2.1.6 Modificación de tipos (ALTER TYPE) ....................................................... 110 5.2.1.7 Eliminación de tipos (DROP TYPE y DROP TYPE BODY) ............................ 112 5.2.2 Tipo Colección ............................................................................................ 112 5.2.2.1 Métodos de un tipo colección................................................................ 113 5.2.2.2 VARRAY ................................................................................................ 114 5.2.2.3 Tablas anidadas .................................................................................... 116 5.2.3 Encapsulamiento ......................................................................................... 117 5.2.4 Modularidad................................................................................................ 118 5.2.5 Persistencia................................................................................................. 118 5.2.5.1 Manejo de tablas (relacionales y de objetos) .......................................... 118 5.2.5.1.1 Creación (Create Table) ................................................................... 118 5.2.5.1.2 Modificación (Alter Table)................................................................ 121 5.2.5.1.3 Eliminación (Drop Table) ................................................................. 121 5.2.5.2 Manejo de objetos................................................................................. 122 5.2.5.2.1 Inserción de objetos (INSERT INTO) ................................................. 122 5.2.5.2.2 Selección de objetos (SELECT ... FROM)............................................ 124 5.2.5.2.3 Modificación de objetos (UPDATE) ................................................... 125 5.2.5.2.4 Eliminación de objetos (DELETE) ...................................................... 126 5.2.6 Jerarquía de Clases...................................................................................... 126 5.2.6.1 Asociación ............................................................................................ 126 5.2.6.2 Agregación y Composición .................................................................... 127 5.2.6.3 Herencia y Polimorfismo........................................................................ 128 5.2.7 Concurrencia............................................................................................... 129 5.2.8 Manejo de Versiones de objetos y Configuraciones...................................... 129 5.2.9 Evolución de esquemas y de Instancias........................................................ 130 5.2.10 Transacciones y recuperación ante fallos. .................................................. 130 5.2.11 Mecanismos de autorización ..................................................................... 131 5.2.12 Compatibilidad con el modelo relacional ................................................... 131 5.2.13 Limitaciones encontradas en Oracle 8i....................................................... 132 5.3 Evaluación ............................................................................................................ 134 5.3.1 Criterios de evaluación ................................................................................ 134 5.3.2 Matriz de evaluación ................................................................................... 136 6 Una Aplicación de ejemplo ................................................................ 138 6.1 Introducción ......................................................................................................... 139 6.1.1 Importancia del recurso hídrico [29] ............................................................ 139 6.1.2 Gestión del recurso hídrico en Chile ............................................................ 140 6.1.3 Modelación de cuencas y proceso de Simulación hidrológico operacional. ... 141 6.1.3.1 Simulación ............................................................................................ 145 ix Contenido General 6.2 Descripción del Prototipo...................................................................................... 146 6.2.1 Descripción de los objetos soportados ........................................................ 147 6.2.1.1 Nodos. .................................................................................................. 147 6.2.1.2 Ríos y tramos de río. ............................................................................. 148 6.2.1.3 Embalses. ............................................................................................. 149 6.2.1.4 Central Hidroeléctrica de pasada ........................................................... 150 6.2.1.5 Canal Principal ...................................................................................... 151 6.2.1.6 Hoya Intermedia.................................................................................... 152 6.2.1.7 Régimen Natural ................................................................................... 152 6.2.1.8 Captación de Agua Potable.................................................................... 153 6.3 Diseño del prototipo............................................................................................. 154 6.3.1 Diagramas de clases.................................................................................... 154 6.3.1.1 Clases implementadas en Oracle 8i ....................................................... 154 6.3.1.2 Clases implementadas en Java............................................................... 161 6.3.2 Diagramas de colaboración ......................................................................... 166 6.3.2.1 Simulación ............................................................................................ 166 6.3.3 Limitaciones Encontradas ............................................................................ 168 6.3.3.1 Limitaciones de PL/SQL ......................................................................... 168 6.3.3.2 Limitaciones de Oracle y la exportación de tipos ................................... 168 7 Conclusiones .................................................................................... 169 Apéndice A: Sintaxis de SQL:1999 Relacionada con la OO. ................... 172 A.1 Leyenda ............................................................................................................... 173 A.2 Sentencias............................................................................................................ 174 A.2.1 Creación de tipos (CREATE TYPE)................................................................. 174 A.2.2 Definición de atributos................................................................................ 176 A.2.3 Modificación de tipos (ALTER TYPE) ............................................................. 176 A.2.4 Creación de métodos, funciones y procedimientos ...................................... 177 A.2.5 Modificación de métodos, funciones y procedimientos ................................ 179 A.2.6 CAST definido por el usuario ....................................................................... 180 A.2.7 Eliminación de un CAST............................................................................... 181 A.2.8 Definición del ORDERING de un tipo. ........................................................... 181 A.2.9 Eliminación de un user definer ordering ...................................................... 182 A.2.10 Funciones de transformación de tipos ....................................................... 182 A.2.11 Eliminación de funciones de transformación.............................................. 183 A.2.12 Definición de Tablas (CREATE TABLE) ........................................................ 183 A.2.13 Modificación de tablas (ALTER TABLE)........................................................ 186 A.2.14 Eliminación de tablas (DROP TABLE) .......................................................... 187 A.2.15 Inserción de filas (INSERT INTO) ................................................................ 187 A.2.16 Actualización de filas (UPDATE) ................................................................. 188 A.2.17 Eliminación de filas (DELETE FROM) ........................................................... 188 x Contenido General Apéndice B: UML .................................................................................. 190 B.1 ¿Qué es UML?........................................................................................................ 191 B.1.1 Diagramas de clases .................................................................................... 193 B.1.1.1 Compartimentos adicionales ................................................................. 194 B.1.1.2 Estereotipos .......................................................................................... 195 B.1.1.3 Asociaciones ......................................................................................... 196 B.1.1.4 Agregación y composición..................................................................... 196 B.1.1.5 Generalización ...................................................................................... 197 B.1.1.6 Restricciones y comentarios .................................................................. 198 B.1.2 Diagrama de colaboración ........................................................................... 199 B.1.3 Para Más información. ................................................................................. 200 Apéndice C: Código Fuente del Prototipo y Pantallas ........................... 201 C.1 Creación de tipos ................................................................................................. 202 C.1.1 array_caudal. .............................................................................................. 202 C.1.2 arr_caudales................................................................................................ 202 C.1.3 Punto .......................................................................................................... 204 C.1.4 arr_puntos .................................................................................................. 204 C.1.5 Polilinea ...................................................................................................... 204 C.1.6 Cuadrado .................................................................................................... 207 C.1.7 Elipse.......................................................................................................... 207 C.1.8 Triangulo .................................................................................................... 208 C.1.9 Graf_flujo.................................................................................................... 209 C.1.10 graf_cent_hidro ......................................................................................... 209 C.1.11 graf_nodo ................................................................................................. 210 C.1.12 graf_emb .................................................................................................. 210 C.1.13 graf_Cuenca.............................................................................................. 210 C.1.14 Cuenca ..................................................................................................... 211 C.1.15 Nodo......................................................................................................... 213 C.1.16 Rio............................................................................................................ 217 C.1.17 Hoya_inter ................................................................................................ 217 C.1.18 Regimen_Natural....................................................................................... 219 C.1.19 Salida_Emb................................................................................................ 220 C.1.20 Aportante.................................................................................................. 221 C.1.21 Canal ........................................................................................................ 222 C.1.22 Tramo....................................................................................................... 224 C.1.23 Central_hidro ............................................................................................ 226 C.1.24 Apor_extrac .............................................................................................. 228 C.1.25 Cap_agua_Pot ........................................................................................... 230 C.1.26 Extracción................................................................................................. 231 C.1.27 Flujo ......................................................................................................... 233 xi Contenido General C.1.28 Embalse .................................................................................................... 235 C.2 Definición de Tablas de objetos e inserción de instancias ..................................... 239 C.2.1 Tabla: tbl_cuenca ..................................................................................... 239 C.2.2 Tabla: tbl_nodo ........................................................................................ 239 C.2.3 Tabla: tbl_rio .......................................................................................... 239 C.2.4 Tabla: tbl_Flujo ...................................................................................... 239 C.2.5 Tabla: tbl_Emb .......................................................................................... 239 C.2.6 Inserción de una Cuenca ............................................................................. 240 C.2.7 Inserción de un Rio ..................................................................................... 240 C.2.8 Inserción de un Nodo .................................................................................. 240 C.2.9 Inserción de un Embalse y sus salidas ......................................................... 240 C.2.10 Inserción de un Regimen Natural............................................................... 242 C.2.11 Inserción de una Hoya Intermedia ............................................................. 242 C.2.12 Inserción de una Captación de Agua Potable ............................................. 243 C.2.13 Inserción de una Central Hidroeléctrica ..................................................... 243 C.2.14 Inserción de un Tramo de Rio.................................................................... 243 C.2.15 Inserción de un Canal................................................................................ 244 C.3 Pantallas. ............................................................................................................. 245 C.3.1 Datos de Conexión...................................................................................... 245 C.3.2 Abrir cuenca existente ................................................................................ 245 C.3.3 Ventana Principal, después de abrir una cuenca. (Frame1.java) .................... 246 C.3.4 Agregar Cuenca .......................................................................................... 246 C.3.5 Ventana simulación al finalizar.................................................................... 247 C.3.6 Nodos y sus caudales .................................................................................. 247 C.3.7 Modificando un Rio (FrmModificarRio.java y FrmRio.java)............................. 248 C.3.8 Acerca de (Frame1_AboutBox.java) .............................................................. 248 C.3.9 Propiedadas de un Canal (FrmCanal.java) .................................................... 249 C.3.10 Propiedades de una Captación de Agua Potable (FrmCapAguaPot.java) ...... 249 C.3.11 Propiedades de una central hidroeléctrica (FrmCentralHidro.java) .............. 249 C.3.12 Propiedades de una hoya intermedia (frmHoyaInter.java) ........................... 250 C.3.13 Propiedades de un régimen natural (frmRegNat.java)................................. 250 C.3.14 Propiedades de una salida de embalse (frmSalidaEmb.java) ....................... 250 C.3.15 Propiedades de un tramo de río (FrmTramo.java)....................................... 251 C.3.16 Propiedades de un nodo (frmNodo.java) .................................................... 251 C.3.17 Ventana para la modificación del Zoom (FrmZoom.java) ............................ 251 C.3.18 Menu Cuenca ............................................................................................ 252 C.3.19 Menu Río .................................................................................................. 252 C.3.20 Menu Zoom............................................................................................... 252 C.3.21 Menu Insertar............................................................................................ 252 C.3.22 Menu Consultas ........................................................................................ 253 xii Contenido General C.3.23 Menu Ayuda.............................................................................................. 253 C.4 Código Java. ......................................................................................................... 254 C.4.1 Paquete consultorSql................................................................................... 254 C.4.1.1 Clase: ConectorJDBC ............................................................................... 254 C.4.1.2 Clase: FrmConexion ................................................................................ 257 C.4.1.3 Clase: FrmConsultor .............................................................................. 265 C.4.1.4 Clase: JDBCAdapter ................................................................................ 268 C.4.1.5 Clase: OldJTable..................................................................................... 273 C.4.1.6 Clase sqlQuery....................................................................................... 277 C.4.1.7 Clase: TableMap ...................................................................................... 281 C.4.1.8 Clase: TableSorter................................................................................. 282 C.4.2 Paquete cuencamaule.................................................................................. 289 C.4.2.1 Clase: Frame1 ........................................................................................ 289 C.4.2.20 Clase: MainGui..................................................................................... 313 C.4.3 Paquete Figuras .......................................................................................... 314 C.4.3.1 Clase: Cuadrilatero ............................................................................. 314 C.4.3.2 Clase: Dibujante.................................................................................... 316 C.4.3.3 Clase: Elipse ........................................................................................ 321 C.4.3.4 Clase: FigGeo ........................................................................................ 323 C.4.3.5 Interfaz: Graficable ............................................................................. 323 C.4.3.6 Clase: LienzoCuenca ............................................................................. 324 C.4.3.7 Clase: Poligono..................................................................................... 327 C.4.3.8 Clase: PoliLinea................................................................................... 329 C.4.3.9 Clase: Punto .......................................................................................... 333 C.4.3.10 Clase: Triangulo................................................................................. 334 C.4.4 Paquete objcuenca ...................................................................................... 337 C.4.4.1 Clase: AporExtracOra ........................................................................... 338 C.4.4.2 Clase: AporExtracOraRef...................................................................... 346 C.4.4.3 Clase: BDObjCuenca ............................................................................... 348 C.4.4.4 Clase: GuiEmbalse ................................................................................. 353 C.4.4.5 Clase: GuiFlujo..................................................................................... 358 C.4.4.6 Clase: GuiNodo....................................................................................... 371 Bibliografía........................................................................................... 375 xiii Resumen Cuando desarrollamos aplicaciones bajo el paradigma de la orientación a objeto, nos encontramos con el problema de la persistencia. Para solucionar esto, existen diversas estrategias pasando por simples archivos, hasta bases de datos orientadas a objetos. Las bases de datos relacionales hasta el estándar SQL-92 no daban soporte al almacenamiento de objetos, pero, la actual versión aprobada, del estándar llamada SQL:1999, da soporte a dichas características. El presente trabajo tuvo como objetivo general analizar las capacidades y limitaciones del estándar SQL:1999 y del SABD Oracle 8i, para dar soporte al paradigma de la orientación a objeto. En una primera etapa, describimos los conceptos básicos del paradigma de la orientación a objeto y las características y los tipos de bases de datos orientadas a objeto junto con sus estándares respectivos. Después, analizamos y describimos detalladamente cómo el estándar SQL:1999 implementa la orientación a objeto, y evaluamos dicha implementación. Lo mismo hicimos con el SABD Oracle 8i. Finalmente, implementamos un prototipo, utilizando como lenguaje a Java y como SABD a Oracle 8i, con el objetivo de evaluar empíricamente la implementación del paradigma de la orientación a objeto en Oracle8i. Como resultado de este trabajo, podemos decir que el estándar SQL:1999 implementa en un 78,25% el paradigma de la orientación a objeto, mientras que el SABD Oracle 8i lo implementa en un 70,58%. La limitación más importante que tiene el SABD Oracle 8i frente a SQL:1999 es la falta de herencia y el deficiente soporte a la evolución de esquemas. El estándar SQL:1999, es lo suficientemente robusto para dar soporte a la persistencia de los objetos, a diferencia del SABD Oracle 8i debido a su falta de herencia. xiv Agradecimientos Si hay algo que me ha costado hacer, es el trabajo que hoy estás leyendo. Dos años de sacrificios, que hubieran sido eternos y quizás un rotundo fracaso, sin la ayuda de muchos. En primer lugar quiero dar gracias a Dios, por llenarme de bendiciones todos los días, quien me dio todos mis talentos y es la luz en mi camino. A mis padres, Miguel y Patricia, que me han sabido enseñar, acompañar y amar, dándome todo su apoyo y comprensión, “tirándome las orejas” cuando me lo merecía y buscando siempre lo mejor para mi. Junto con ellos a mis hermanos, Fabián y Pablo, por soportar mi mal humor durante el desarrollo del proyecto. A mis amigos y compañeros de universidad, JUFRA, Hogar de Cristo, AUC, RCC, Peñihuén Juvenil y a mis amigos que han partido: Gonzalo Seguel, Rodrigo Fuentes y Rodrigo Muñoz por toda la amistad y los buenos momentos que pasamos juntos. A mis profesores, en especial a los que me acompañaron en la universidad por su apoyo, consejo y educación. A la profesora María Antonieta, por su tiempo y apoyo en lo referente a UML. A mi profesor guía Gilberto Gutiérrez, por su apoyo, guía y comprensión a lo largo de todo el proyecto. Al Ministerio de Obras Públicas (MOP), pero en particular a Mauricio Zambrano, por su tiempo y apoyo en la explicación de los modelos de simulación hidrológica, y por las revisiones hechas al prototipo. A Marlene Sáez por su ayuda en la traducción del estándar SQL-1999. Y a todos los que de una u otra manera han estado conmigo... ...Muchas Gracias. Miguel Esteban Romero Vásquez. Paz y Bien a todos xv 1 Introducción La orientación a objeto, desde sus orígenes ha sido un paradigma muy prometedor para el desarrollo de software. En estos últimos años, hemos visto aparecer varios productos, estándares y herramientas que han marcado un hito en la historia de la Orientación a Objeto. Uno de ellos es el lenguaje Java que da un soporte muy completo al paradigma de la orientación a objeto, además, de contar con características que facilitan el desarrollo de programas (fácil de aprender, multiplataforma, multihilo, cliente/servidor, robusto, seguro, etc). Otro hito importante lo ha marcado la notación UML, que rápidamente se ha convertido en el estándar para el desarrollo orientado a objeto (ver apéndice B). A partir de UML, se han desarrollado varias herramientas CASE, por ejemplo: Rational Rose de Rational Software y Object Database Designer (ODD) de Oracle. Rational Rose permite diseñar sistemas utilizando la notación UML y a partir de estos diseños, generar automáticamente los programas en distintos lenguajes orientados a objeto, como por ejemplo en Java y en C++. Mediante ODD podemos diseñar las bases de datos y los tipos objetos que utilizaremos en Oracle, luego serán generadas automáticamente las estructuras en el SABD. Otra área del desarrollo del paradigma, son las bases de datos orientadas a objeto. Las cuales nacen para solucionar el problema de la persistencia de los objetos, en forma eficiente. En esta área existen dos líneas de investigación y desarrollo, cada una con su respectivo estándar. El tema central de este trabajo corresponde a una de las líneas de investigación, definida por el estándar SQL:1999. Los objetivos de este trabajo son: Objetivo general: Analizar las capacidades y limitaciones del estándar SQL:1999 y del SABD Oracle 8i, para dar soporte al paradigma de la orientación a objeto. Objetivos específicos: 1. Analizar las capacidades y limitaciones del modelo objeto considerado por SQL:1999. Introducción 2. Analizar las capacidades y limitaciones del modelo objeto en el SABD Oracle 8i. 3. Verificar las capacidades mediante la implementación de un prototipo Al iniciar este proyecto, el estándar SQL:1999 no había sido aprobado, y era conocido como SQL-3. A esas alturas, ya existían productos que daban soporte a la orientación a objeto basándose en el borrador de trabajo, entre ellos Oracle 8i. Ahora que el estándar está aprobado, es de esperar que aparezcan productos que implementen dicha especificación, pero por el momento tendremos que conformarnos con implementaciones incompletas basadas en el borrador de trabajo de SQL. En el capítulo uno de este trabajo, explicaremos las características fundamentales del paradigma de la orientación a objeto, y plantearemos el problema de la persistencia, que es lo que solucionan las bases de datos orientadas a objeto. En el dos, haremos una breve reseña histórica de las bases de datos. Explicaremos las dos líneas de desarrollo en este campo y sus estándares. En el tres, describiremos detalladamente todas las características de la orientación a objeto manejadas por SQL:1999. El orden en que describimos los conceptos es el mismo en el que explicamos las características del paradigma de la orientación a objeto en el capítulo 1. Al final del capítulo haremos una evaluación del soporte de la orientación a objeto en SQL:1999. En el capítulo 4, tendrá la misma estructura del capítulo 3, para facilitar su comparación y estudio. Aquí describiremos detalladamente el manejo de objetos que hace Oracle 8i. Al final del capítulo haremos una evaluación del soporte del paradigma de la orientación a objeto que hace Oracle 8i. En el capítulo cinco encontrará el desarrollo de una aplicación orientada a objeto, utilizando el lenguaje Java y a Oracle 8i para hacer persistir los objetos. La aplicación consiste en la implementación de un modelo de simulación hidrológico operacional para una cuenca, realizando una simulación de temporada. Es necesario indicar que, por motivos de tiempo, no existe una documentación bien detallada del diseño. 2 2 El Paradigma de la Orientación a Objeto 2.1 Introducción Las ideas de la orientación a objeto nacen a partir de los lenguajes de programación orientados a objeto. El primero de ellos fue Simula, y desde entonces se han desarrollado muchos otros (Smalltalk, C++, Java, etc.). Aprender estos lenguajes no es suficiente para desarrollar un software con estas ideas, puesto que ellas forman un paradigma. 2.1.1 ¿Qué es el paradigma de la orientación a objeto? Para poder responder a esta pregunta, analizaremos primero lo que significa el vocablo paradigma. Si buscamos el significado de esta palabra en un diccionario, nos dirá que es un Ejemplo o Modelo, pero en el contexto utilizado, es mucho más amplio. El Historiador Tomas Kuhn define paradigma como un conjunto de teorías, estándares y métodos que juntos representan una forma de organizar el conocimiento, esto es, una forma de ver el mundo [5]. Todos los seres humanos tenemos un conjunto de paradigmas que adquirimos a lo largo de nuestra vida a través del aprendizaje y actuamos de acuerdo con ellos. Nuestros paradigmas son de gran ayuda, porque nos permiten analizar rápidamente la información que recibimos, además, nos dan una pauta de cómo debemos actuar y reaccionar. Pero, cuando los hechos se escapan a la regla general, nuestros paradigmas nos inducen a cometer errores o a tener que reaccionar mucho más lento, para no cometerlos. Por ejemplo si vamos conduciendo un automóvil, y el semáforo está en verde, simplemente cruzamos la calle, puesto que el conocimiento que tenemos sobre el semáforo, nos dice que podemos cruzar cuando la luz está de ese color, y que por supuesto el otro lado tiene rojo. Pero ¿qué pasa si en realidad los dos lados del semáforo están en verde?, lo más probable es que ocurra un accidente. Que pasó aquí, bueno nuestros paradigmas nos jugaron una mala pasada, tomamos una decisión rápida basada en ellos, sin percatarnos de esta situación que escapa a la regla general. 4 El Paradigma de la orientación a objeto - Introducción Al Programar un software tenemos varios paradigmas, que nos permiten plantear soluciones. Algunos ejemplos de estos paradigmas son [4]: • Orientados a procedimientos (enfoque clásico) • Orientados a objetos • Orientados a Lógica • Orientados a Reglas • Orientados a Restricciones Si ponemos atención a la definición de paradigma que plantea Tomas Kuhn, nos daremos cuenta, que cada uno de estos ejemplos tiene su propia forma de organizar el conocimiento y de mirar al mundo. No existe el mejor paradigma, cada uno es más adecuado que el otro para resolver cierto tipo de problemas. Por esta razón es necesario que sean conocidos por los desarrolladores, sólo de este modo podrán escoger el mejor, para su problema. Los paradigmas que utilizamos para desarrollar un software abarcan más que lo relacionado con la tarea de programar. Tomas Kuhn en su definición nos plantea que son un conjunto de teorías, estándares y métodos, a partir de ello, podríamos decir que un paradigma de desarrollo de software esta compuesto por: modelos, métodos, y diagramas para realizar análisis, diseño, programación, gestión del desarrollo y control de la calidad. Cada uno de estos elementos está basado en un marco de referencia conceptual propio del paradigma. En apoyo al desarrollo de software bajo un paradigma en particular, tenemos herramientas, lenguajes de programación, y bases de datos que, por supuesto, están acordes al marco conceptual del paradigma. Entonces, el paradigma de la orientación a objeto, estaría compuesto por todos los elementos antes descritos, donde el marco de referencia conceptual, o toda la teoría en la que se basa, está orientada al objeto. Éste marco de referencia conceptual en particular suele denominarse modelo de objetos (por ejemplo en [4]). La pregunta ahora es ¿qué es orientado a objeto?. En la siguiente sección se responderá a esta pregunta. 5 2.2 Conceptos Básicos En el Modelo Objeto existen cuatro elementos fundamentales, estos son: Abstracción, Encapsulamiento, Modularidad y Jerarquía; donde “un modelo que carezca de cualquiera de estos elementos no es orientado a objetos” [4] página 45. Y tres elementos secundarios: Tipos (tipificación), Concurrencia y Persistencia, donde “cada uno de ellos es una parte útil del modelo objeto pero no es esencial.” [4] página 45. Estos elementos parten de la noción de objeto, por ende definiremos primero este concepto y luego, los elementos del modelo objeto. 2.2.1 Objetos Los seres humanos estamos familiarizados con los objetos, los utilizamos diariamente y desde muy temprana edad aprendemos a identificarlos y reconocerlos [4]. Éstos pueden ser: • Una cosa Tangible y/o visible • Algo que puede comprenderse intelectualmente • Algo hacia lo que se dirige un pensamiento o acción. Un objeto posee un conjunto de características esenciales, llamadas propiedades o atributos [20]. Estos atributos son los que dan al objeto su naturaleza o tipo y hacen que el objeto no sea otro. Por ejemplo, un televisor tiene una figura 1: Ejemplo de objetos pantalla, un dispositivo encendido y apagado, un selector de canales, una antena, etc. televisor, no tuviera una de Si este antena y un selector de canales, no sería un televisor, sería un monitor de computador, que es otro tipo de objeto. 6 El Paradigma de la orientación a objeto – Conceptos Básicos Además de las propiedades, un objeto tiene un conjunto de servicios, operaciones o métodos [20], que definen el comportamiento del objeto, y actúan sobre sus atributos. Siguiendo el ejemplo del televisor, podríamos identificar los siguientes métodos: Encender, Apagar, Cambiar Canal, Sintonizar, Subir Volumen y Bajar Volumen. Si realizamos una comparación entre la programación orientada a objetos y la programación estructurada, podríamos decir que: en la programación orientada a objeto los atributos del objeto son como las variables y las estructuras de datos; mientras que los métodos de los objetos serían como las funciones o procedimientos que manipulan las estructuras de datos. La gran diferencia radica en que un objeto es una sola entidad compuesta por atributos y métodos, en cambio en la programación estructurada, las estructuras de datos y las funciones o procedimientos son cosas separadas. Todo objeto tiene un estado, un comportamiento bien definido y una identidad única [4]. Veamos qué significa cada concepto. 2.2.1.1 Estado Las propiedades de los objetos suelen ser estáticas, es decir, no cambian en el tiempo, aunque en algunos casos el objeto puede mutar y modificar su conjunto de propiedades (evolución [2]). Cada una de las propiedades de un objeto tiene algún valor y éste puede ser una cantidad u otro objeto. El valor de un objeto es la parte dinámica de la propiedad. Las propiedades del objeto son los que definen su estado. Grady Booch da la siguiente definición de estado: “El Estado de un objeto abarca todas las propiedades (normalmente estáticas) del mismo más los valores actuales (normalmente dinámicos) de cada una de estas propiedades” [4] página 98. 7 El Paradigma de la orientación a objeto – Conceptos Básicos Entonces, el estado de un objeto depende de los valores actuales de sus atributos. 2.2.1.2 Comportamiento y Mensajes El comportamiento de un objeto, representa su actividad visible y comprobable exteriormente, y él está definido en sus métodos. Los objetos se comportan de una manera conocida, esto permite que ellos interactúen entre sí, colaborando en pos de un objetivo. Esta interacción se logra a través del paso de mensajes, donde un mensaje es un mandato o solicitud que un objeto envía a otro indicándole que debe realizar una acción, lo que gatilla la ejecución de un método. Cada mensaje está compuesto por: destino, operación y parámetros [20], donde el destino es el objeto receptor, la operación es el método solicitado y los parámetros son el conjunto de datos necesarios para ejecutar el método. En la mayoría de los lenguajes de programación orientados a objetos, un mensaje tiene la forma: Destino.Operación(parámetro1, parámetro2, ... , parámetron). Siguiendo con el ejemplo del televisor, si tenemos un objeto llamado TV y quisiéramos ver el canal 13, tendríamos que pasar los siguientes mensajes: TV.encender() TV.cambiarCanal(13) Consideremos que el método encender(), sirve para prender y apagar el televisor, de esta manera, el comportamiento del objeto al invocar este método dependerá del estado actual del objeto, es decir, si él está encendido, entonces se apagará, y viceversa. Además, este método, cambiará el estado actual de encendido a apagado y viceversa. Esto se produce porque el comportamiento de un objeto altera el estado del mismo, y el estado actual del objeto afecta su comportamiento. Como resumen, veamos la siguiente definición de Grady Booch: “el comportamiento es como actúa y reacciona un objeto, en término de sus cambios de estados y paso de mensajes” [4] página 98. 8 El Paradigma de la orientación a objeto – Conceptos Básicos 2.2.1.3 Identidad La identidad es la característica del objeto que lo hace ser único y lo distingue del resto de los objetos. Es importante destacar que la identidad de un objeto es independiente de los valores actuales de sus atributos, por ejemplo si tenemos un objeto libro, y cambiamos el valor del ISBN, éste va a tener la misma identidad. En los lenguajes de programación orientados al objeto, los objetos se identifican con nombres de variables o a través de su OID (Object Identifier, identificador de objeto) generados por el sistema. Un nombre de variable es asignado por el programador, y el sistema se encarga de asociar ese nombre con el OID correspondiente. Utilizando los OID, los objetos pueden compartir otros objetos y se pueden crear redes entre ellos [2]. La Identidad de un Objeto hace que tengamos dos nociones de igualdad [2]: • Igualdad por identidad. Dos objetos son iguales si tienen el mismo OID, es decir, son el mismo. • Igualdad por valor. Dos objetos son iguales si los valores de sus atributos son recursivamente iguales. 2.2.2 Abstracción y Clase. La abstracción no sólo es utilizada en la orientación a objeto, ésta es una capacidad humana que nos permite simplificar la realidad de manera que sea más manejable. Ésta consiste en centrarnos en las características esenciales de un objeto, descartando detalles que no son pertinentes en ese momento. Una definición más formal sería : “Una abstracción denota las características esenciales de un objeto que lo distinguen de todos los demás tipos de objetos y proporciona así fronteras conceptuales nítidamente definidas respecto a la perspectiva del observador.” [4] página 46. En otras palabras, a través de una abstracción encontramos un conjunto de atributos y métodos que son la esencia de un objeto, descartando los detalles 9 El Paradigma de la orientación a objeto – Conceptos Básicos de su implementación por este momento. Los atributos y métodos identificados dependerán del dominio de la aplicación a desarrollar (perspectiva del observador). La manera en que representamos las abstracciones es a través de clases. Una clase es la especificación de los atributos y métodos que debe tener un objeto para pertenecer a esa clase, es decir es una abstracción del objeto. Al leer esta definición, da la impresión que los términos son sinónimos, pero no lo son. En algunos casos una abstracción será representada por una sola clase, pero en otros, se necesitarán varias clases. Por ejemplo consideremos la abstracción de un automóvil, con los atributos de: numPasajeros, propietario, numPuertas; y con los métodos: avanzar(), frenar(), doblarIz(), doblarDer(); figura 2:Clase Automóvil esta abstracción puede ser representada por una única clase como la mostrada en la figura 2. Por otro lado, si tratáramos de representar la abstracción Industria, con los atributos: Empleados, departamentos, muebles y útiles, maquinarias; y con los métodos: Comprar(), vender(), fabricar(), inventariar(), balance(); difícilmente podríamos representarlo a través de una sola clase. Al incorporar el concepto de clase a la definición de objeto podemos decir que: un objeto es una ocurrencia concreta (o instancia) de una clase, con una identidad única, donde todos los objetos que comparten un mismo conjunto de atributos y métodos están agrupados en una misma clase. 10 El Paradigma de la orientación a objeto – Conceptos Básicos 2.2.3 Encapsulamiento El encapsulamiento es uno de los pilares fundamentales en que se basa la orientación a objeto, atributos y métodos éste consiste en agrupar en una única entidad los de un objeto, ocultando los detalles de su implementación. Para lograr esto, el objeto tiene dos partes, una pública o visible denominada Interfaz y otra privada u oculta denominada implementación. Al momento de definir la clase del objeto, se establece qué atributos y métodos van a ser públicos, y cuáles privados. En el caso de los métodos públicos, el algoritmo que utilizan para ejecutar el trabajo queda oculto (forma parte de la implementación). Con el concepto de encapsulamiento podemos decir que la abstracción de un objeto corresponde a la interfaz de la clase encapsulada. Cabe destacar que un encapsulamiento real se logra solamente cuando todos sus atributos son privados, y la única forma de acceder a ellos, es a través de métodos públicos [2]. Si un objeto permite que sus atributos sean públicos pierde el control sobre los valores que le son asignados, no pudiendo realizar la validación de los datos que se ingresan, pudiendo ser asignados valores inválidos para esos atributos. Un encapsulamiento real, hace que los métodos públicos sean como una muralla que garantice la integridad y validez de los datos guardados por el objeto en sus atributos. Además permite modificar la estructura interna del objeto sin que estos cambios afecten a los que lo utilizan. con los algoritmos implementados por los métodos. Lo mismo pasa Esto nos trae como beneficio una facilidad de mantención, al minimizar los efectos colaterales producidos por cambios en la implementación del objeto. 2.2.4 Modularidad Como punto de partida tomaremos la definición de Booch que dice: “La modularidad es la propiedad que tiene un sistema que ha sido descompuesto en un conjunto de módulos cohesivos y débilmente acoplados.” [4] página 64. 11 El Paradigma de la orientación a objeto – Conceptos Básicos Para entender esta definición debemos comprender el significado de módulo, cohesión y acoplamiento desde la perspectiva de la orientación a objeto. Un módulo es un conjunto de clases o abstracciones que han sido agrupadas por guardar cierta relación lógica. El módulo pasa a ser un paquete que se puede diseñar, encapsulamiento. programar Por lo y compilar general los separadamente, módulos no son gracias al totalmente independientes, en la mayoría de los casos existen conexiones entre módulos. La cohesión se refiere al grado de relación lógica con el que se han agrupado las clases. Mientras más tengan en común las clases, en función del objetivo que persiguen, mayor es la cohesión. Existen varios grados de cohesión, siendo el más bajo la cohesión por coincidencia donde las clases son agrupadas sin ninguna relación; y el más alto la cohesión funcional donde las clases trabajan juntas para lograr un comportamiento bien delimitado [4]. El acoplamiento esta relacionado con el grado de dependencia que existen entre un módulo y los demás. Lo que se busca es minimizar el acoplamiento, para minimizar los efectos colaterales que pueda causar la modificación de un módulo. El Objetivo de la modularidad es hacer más manejable la programación a gran escala, basándose en la estrategia “dividir para vencer”. 2.2.5 Jerarquía de Clases Para modelar una determinada aplicación, no es suficiente definir todas las clases que la componen, además de eso es necesario describir las relaciones que existen entre ellas. Recordemos que todo sistema es un conjunto de partes que interactúan entre sí. Existen tres tipos básicos de relaciones entre clases [4]. La primera denota una relación “Es un”, por ejemplo un automóvil es un medio de transporte. A este tipo de relaciones se denomina herencia. La segunda denota una 12 El Paradigma de la orientación a objeto – Conceptos Básicos relación “parte de” por ejemplo un motor es parte de un automóvil. A este tipo de relación se denomina agregación. asociación, Y por último tenemos la que denota una relación semántica entre clases que de otro modo no existiría. Un ejemplo de esto es la relación que se produce entre una persona y un automóvil, cuando decimos que una persona es propietaria de uno o más automóviles, o una persona conduce un automóvil. El concepto de Jerarquía de clases también es uno de los pilares de la orientación a objeto. 2.2.5.1 Herencia La herencia permite definir una clase (llamada subclase) tomando como base la definición de otra (llamada superclase). La subclase hereda todos los atributos y operaciones de la superclase, a lo que agrega sus propios atributos y métodos [2]. Para construir este tipo de relaciones existen dos procesos, uno llamado generalización y otro llamado especialización. figura 3: Clases antes de la generalización Por ejemplo, si hemos identificado las siguientes clases: automóvil, bicicleta, camión (figura 3), podríamos extraer los atributos y operaciones que tienen en común y con ellos generar una clase llamada medios_de_transporte (figura 4) que pasaría a ser una superclase donde sus subclases automóvil, bicicleta y camión tendrían sólo los atributos y métodos que no fueron extraídos, puesto que ellos serán heredados de la superclase. Pensemos ahora que solamente tenemos la clase medios_de_transporte, podríamos decir que ella se divide en tres: medios de transporte Aéreos, 13 El Paradigma de la orientación a objeto – Conceptos Básicos terrestres y marítimos. Estas tres últimas clases son una especialización de medios de transporte. figura 4: Clases después de la generalización. La especialización es el proceso mediante el cual definimos una o más subclases a partir de una superclase, en cambio en la generalización definimos una superclase a partir de una o más subclases. Todos los objetos que pertenecen a una subclase también lo son de su superclase. Siguiendo el ejemplo anterior, podríamos decir, que un automóvil XX y una Bicicleta YY, pertenecen a clases distintas, pero a la vez tienen una clase en común que es Medios_de_transporte. Si en algún momento esperamos un objeto de la clase medios de transporte, XX e YY son perfectamente válidos. Esto no se limita a la superclase directa, también es válido para las indirectas, es decir, para la superclase de la superclase, y así sucesivamente. Cabe mencionar que, existe un tipo de superclase de la cual no se instancian objetos directamente, ellas reciben el nombre de superclase abstracta. Dependiendo del número de superclases que posea una clase, podemos definir dos tipos de herencia: Herencia simple y Herencia múltiple. La 14 El Paradigma de la orientación a objeto – Conceptos Básicos herencia simple es aquella donde una subclase puede tener solamente una superclase. En la herencia múltiple una subclase puede tener una o más superclases. La herencia trae los siguientes beneficios: • Elimina redundancia innecesaria. Al definir los atributos y métodos que son comunes a un grupo de objetos en una superclase se evita que estos estén repetidos en sus subclases. • Facilita la mantención. Al modificar una superclase se modifican automáticamente las subclases. • Facilita la localización de errores. Al construir una subclase basada en una superclase libre de errores, cualquier error que aparezca en la subclase va a ser producto de los métodos que esta introdujo. • Reutilización. Al poder heredar métodos y atributos no tenemos que definirlos nuevamente sólo reutilizarlos, lo que incrementa la productividad. Pero ninguno de estos beneficios serían significativos, si no existiera el polimorfismo. 2.2.5.1.1 Polimorfismo La idea de polimorfismo es: permitir que diferentes métodos tengan el mismo nombre [20]. Éste concepto esta íntimamente ligado al de herencia [2]. Pensemos en una superclase llamada figuras_geométricas, que tenga como subclases a triangulo, cuadrado, circulo. Pensemos que tenemos un arreglo de figuras geométricas y queramos dibujar cada una de ellas. Una alternativa es la siguiente implementación (en pseudo código): Inicio Defina A[1..10] de figuras_geométricas Llenar el arreglo A con figuras_geométricas Para i = 1 hasta 10 haga En caso que A[i] sea triangulo entonces A[i].dibujarTriangulo cuadrado entonces A[i].dibujarCuadrado circulo entonces A[i].dibujarCirculo fin en caso que Fin Para 15 El Paradigma de la orientación a objeto – Conceptos Básicos Fin Esto se puede simplificar enormemente. Podemos agregar un método llamado Dibujar en la superclase Figuras_geométricas. De esta manera todas las subclases tendrían este mismo método. subclases tiene un modo particular de Cada una de sus dibujarse, esto se logra redefiniendo la implementación del método. Esta redefinición también se denomina suplantación o anulación (overriding) porque la definición particular ocupa el lugar de la definición general (la suplanta) [2]. De esta manera logramos tener un mismo nombre de método, pero diferentes implementaciones de él. De este modo el algoritmo antes presentado se reduce a : Inicio Defina A[1..10] de figuras_geométricas Llenar el arreglo A con figuras_geométricas Para i = 1 hasta 10 haga A[i].Dibujar() Fin Esto trae como consecuencia un diseño más simple, una herencia más efectiva, y en casos como el ejemplo anterior, menos líneas de código. Para lograr esta nueva funcionalidad el sistema tiene que asociar la invocación al método con el método correspondiente en tiempo de ejecución. Esto se denomina ligadura tardía (late binding) [2]. Otra manera de lograr el polimorfismo es la sobrecarga de métodos. Esta consiste en tener dos o más métodos con el mismo nombre pero con distintos argumentos. En este caso no es necesario hacer una ligadura tardía, se puede hacer de manera normal, es decir, en momento de compilación (enlace más temprano). 2.2.5.2 Asociación Las clases se asocian para poder interactuar entre sí. La asociación es un enlace físico o conceptual que permite a un objeto ocupar los servicios (métodos) de otro. Este enlace es bidireccional a menos que se especifique lo contrario. Además este enlace posee una multiplicidad, por ejemplo: • Uno a uno 16 El Paradigma de la orientación a objeto – Conceptos Básicos • Cero a Muchos • Uno a muchos • Muchos a muchos. Una asociación entre clases es similar al concepto de relación entre entidades en un Diagrama Entidad-Relación [1]. 2.2.5.3 Agregación y Composición La agregación es una asociación que representa una relación Todo-Parte donde un objeto es parte de otro [39]. Al todo se le denomina agregado y sus partes pasan a ser atributos [4]. En la agregación, las partes, pueden tener una existencia independiente a la del agregado, pudiendo formar parte de varias clases, o de ninguna. Un ejemplo, es una comisión compuesta por varias personas, donde algunas de ellas, no participarán en una comisión, pero otras participarán en una o varias. La composición es una forma más fuerte de agregación, donde las partes sólo podrán pertenecer a un todo, y estás no existirán fuera del contexto del agregado. La única forma de acceder a las partes será a través del todo [4]. Por ejemplo podemos definir una clase bicicleta como una composición de las siguientes clases: rueda, pedal, manubrio, cadena, freno, asiento, marco. Donde la clase rueda puede ser un agregado de rayos, eje, cámara, neumático. Mediante la agregación y la composición podemos definir objetos arbitrariamente complejos. 2.2.6 Tipos (tipificación) Los conceptos de clases y tipos son muy similares, puesto que una clase es la implementación de un tipo, pero los tipos ponen énfasis en el significado de una abstracción de manera diferente: “Los tipos son la puesta en vigor de la clase de los objetos, de modo que los objetos de tipos distintos no pueden 17 El Paradigma de la orientación a objeto – Conceptos Básicos intercambiarse o, como mucho, pueden intercambiarse sólo de formas muy restringidas.” [4] página 74. El objetivo de contar con tipos, es evitar que se mezclen abstracciones de manera equivocada. Para lograrlo se debe realizar una comprobación de tipos, durante el proceso de compilación, que puede ser estricta o débil. Mediante la comprobación estricta de tipos se pueden detectar errores como: referencias a atributos inexistentes, invocación de métodos inexistentes, invocación de métodos con parámetros no válidos (Ej.: se espera un objeto del tipo automóvil, pero se recibe una del tipo persona), y cualquier otro que cause una incongruencia de tipos. En los lenguajes de comprobación débil de tipos, es posible ignorar o suprimir las reglas sobre los tipos. Existen lenguajes sin tipos como Smalltalk, donde los errores como invocación de método inexistente solo son reconocidos en tiempo de ejecución [4]. La ventaja de contar con un sistema que soporte la comprobación estricta es la detección temprana de errores de incongruencia de tipos en forma automática, puesto que un error de tipos en tiempo de ejecución no se manifiesta hasta que esa línea de código es ejecutada y eventualmente podríamos pasarla por alto en las pruebas de software. 2.2.7 Concurrencia La concurrencia es transversal a varios paradigmas de la computación. Ésta consiste en dividir un programa en dos o más procesos que se ejecutarán simultáneamente, logrando así una mayor eficiencia [6]. Se pueden distinguir dos tipos de procesos: pesados y ligeros (hilos), donde los procesos pesados consumen más recursos computacionales que los ligeros. Estos procesos se pueden ejecutar en una sola CPU, utilizando algún algoritmo de tiempo compartido, o en varias, logrando un proceso verdaderamente concurrente (o paralelo) [4]. 18 El Paradigma de la orientación a objeto – Conceptos Básicos Un modelo objeto que incorpora la concurrencia, considera que cada objeto puede ser representado por un proceso (o hilo) separado, pasando a ser un objeto activo. Los objetos activos funcionan independientemente, pero cooperando con el resto de los objetos, además, sincronizándose cuando sea necesario. A partir de esto se puede definir la concurrencia como: “La concurrencia es la propiedad que distingue un objeto activo de uno que no está activo.” [4] página 83. 2.2.8 Persistencia Cuando desarrollamos un software orientado a objeto y lo implementamos con algún lenguaje de programación apropiado nos encontramos con la siguiente pregunta ¿cómo puedo almacenar los objetos en memoria secundaria?. Esto sucede porque los lenguajes de programación orientados a objeto, en su mayoría, no permiten almacenar en forma directa un objeto o una jerarquía de objetos en memoria secundaria, y solamente los crean, manipulan y destruyen en memoria principal. Este es el problema de la persistencia, es decir, permitir que un objeto (o una jerarquía completa) sea almacenado en un soporte auxiliar no volátil, con el objetivo de ser usado nuevamente por la aplicación que lo creó o por otra aplicación [21]. Es impensable tener un software que cada vez que se inicie tenga que ingresar todos sus datos, como por ejemplo, una lista de clientes y de productos. 2.2.8.1 Formas de solucionar el problema de la persistencia Para solucionar el problema de la persistencia tenemos las siguientes alternativas [21]: • Archivos convencionales • Bases de datos relacionales • Bases de datos orientadas a objetos (puras y objeto- relacional). 2.2.8.1.1 Archivos convencionales Los archivos convencionales son los archivos básicos que puede manejar todo lenguaje de programación, estos no tienen ningún soporte directo 19 El Paradigma de la orientación a objeto – Conceptos Básicos para los objetos, por lo tanto tenemos que crear nuestros propios protocolos para almacenarlos. Un caso especial es el lenguaje Smalltalk el que cuenta con protocolos para escribir y leer objetos en discos [4]. En cualquiera de los dos casos, esta solución para la persistencia, es muy limitada, y solo sería aconsejable para entornos monousuarios que no necesiten manejar grandes cantidades de datos. 2.2.8.1.2 Bases de datos relacionales Un Software de administración de bases de datos relacional (SABDR) es nuestra segunda solución. Gracias a sus características (como concurrencia, transacciones, réplica, backup, auditoría, etc.) podemos implementar aplicaciones que necesiten mantener gran cantidad de información y manejo de múltiples usuarios concurrentes. En este caso también será necesario implementar la persistencia por nosotros mismos lo que incrementa la complejidad y el tiempo de desarrollo de nuestra aplicación y con ello, los costos. Ambas soluciones no permiten que los objetos persistentes sean independientes a la aplicación creada. Esto es necesario cuando varias aplicaciones distintas tienen que utilizar los mismos objetos. 2.2.8.1.3 Bases de datos orientadas a objetos. Hoy en día, existen en el mercado sistemas de bases de datos diseñadas para almacenar objetos, si bien no son perfectas, nos brindan una serie de características, que nos facilitan esta tarea (Ej.: O2, Object Store, Orión, etc). Además tienen características propias de los SABD y logran que el objeto persistente sea independiente de la aplicación. Esta independencia se logra almacenando las jerarquías de clases, junto con los objetos, en la base de datos. En las tres soluciones existe un problema denominado falta de correspondencia (impedance mismatch) [2], este se produce cuando 20 El Paradigma de la orientación a objeto – Conceptos Básicos alguna característica del lenguaje orientado a objeto no está presente en nuestra solución. La falta de correspondencia es completa en las dos primeras soluciones y en la tercera, ésta es casi nula. Dentro de esta línea existen dos tendencias importantes. Estas son las bases de datos objeto relacional (SABDOR) y las bases de datos orientadas a objetos puras (SABDOO). En el siguiente capítulo hablaremos con detenimiento sobre ambas. 21 3 Bases de datos Orientadas a Objeto 3.1 Introducción Los sistemas de Administración de bases de datos (SABD) nacen en los años 60, como solución a los problemas de manejo de archivos. Mario Piattini explica la evolución de los SABD definiendo tres generaciones [13]. La primera generación constituida por los sistemas jerárquicos (IMS, Total, etc.) y en Red (IDMS, AIM, IDS, etc.); la segunda por los sistemas relacionales (Oracle, DB2, Ingres, Informix, Sybase, etc.); y una tercera generación representada por los SADB orientados a objetos (SABDOO), deductivos, activos, multimedia, temporales, federadas, móviles y otros [40]. 3.1.1 Bases de datos relacionales La segunda generación ha sido motivo de investigación durante más de 20 años, consolidándose en el mercado. Los SABD relacionales tienen fundamentos teóricos sólidos introducidos por Codd en 1970 [10]. Estos se basan en una estructura de datos simple y uniforme llamada Relación. Los SABD relacionales se caracterizan por [10] • Persistencia. Esta característica es la esencia de las bases de datos, guardar la información en memoria secundaria hasta que sea explícitamente borrada. • Existencia de un catálogo o diccionario de datos. información sobre la estructura de las tablas Éste contiene (relaciones) y restricciones sobre la manipulación de los datos. • Abstracción de los datos. Los SABD ofrecen a los usuarios una representación conceptual de los datos, ocultando los detalles de almacenamiento. • Independencia entre programas y datos. Gracias a esto los datos pueden ser utilizados por distintos programas. Esto se logra almacenando la estructura de los archivos en la base de datos (catálogo). 23 Bases de Datos orientadas a objeto - Introducción • Manejo de múltiples vistas de usuario. Las vistas permiten mostrar a los usuarios solamente lo que necesitan o les es permitido “ver” de la base de datos. • Gestión de transacciones concurrentes. Esto permite que dos o más usuarios utilicen los mismos datos simultáneamente, en forma controlada. • Lenguaje de consulta SQL. Es un lenguaje declarativo que permite interrogar a la base de datos para buscar información. Se basan en el estándar SQL. 3.1.2 Bases de datos orientadas a objeto Los SABD orientados a objeto son la unión entre las características propias de las bases de datos y del paradigma de la orientación a objeto. De esta manera, las bases de datos no sólo almacenan información, sino que también algoritmos. Éstas nacen para solucionar el problema de la persistencia en forma eficiente (ver sección 2.2.6.1), requisito indispensable para aplicaciones como: CASE (Ingeniería de software asistida por computador), CAD/CAM (Diseño / fabricación asistidos por computador), SIG (Sistemas de información geográficos), multimedia, gestión de redes [14]; donde los sistemas relacionales han sido incapaces de satisfacer los requerimientos de dichas aplicaciones. Los SABDOO están creciendo rápidamente (sobre un 300% en 1995 [13]), pero todavía representan un mínimo porcentaje respecto al total de los sistemas instalados (5% del mercado mundial de bases de datos en 1997 [40]). 3.1.2.1 Nuevos requerimientos Por la necesidad de almacenar objetos persistentes, las Bases de datos OO deberán satisfacer un conjunto de requisitos que están estrechamente ligados a los conceptos básicos del paradigma de la orientación a objeto. Estos son: 24 Bases de Datos orientadas a objeto - Introducción • Definición de clases y extensión de los tipos de datos primitivos. La definición de clases y su jerarquía debe formar parte del catálogo, además es deseable que estas clases pasen a formar parte del sistema de tipos del SABD. • Lenguajes de programación computacionalmente completos. Los SABD deben contar con leguajes de programación o interfaces con lenguajes para implementar los métodos de los objetos. • Objetos complejos1. Para poder implementar este tipo de objetos es necesario contar con mecanismos que permitan tener atributos multivaluados (como listas, conjuntos, arreglos, etc.), identificadores únicos y referencias a otros objetos. • Herencia, Encapsulamiento y Polimorfismo. Estos son pilares fundamentales de la orientación a objeto, por lo que deben considerarse. • Manejar versiones de objetos y configuraciones. Cuando cambiamos los valores de los atributos, estos son reemplazados por los nuevos. En algunas aplicaciones es necesario conocer los valores históricos que han asumido los objetos, esto se logra manejando distintas versiones del objeto. Una configuración establece enlaces entre una versión de un objeto compuesto y las correspondientes versiones de sus objetos componentes. • Evolución de esquemas y de instancias. Los cambios en este tipo de sistemas son la regla más que una excepción. Para ello es necesario modificar proveer de mecanismos que permitan la definición de las clases y sus jerarquías (Evolución de esquemas), además de soportar la migración de instancias entre clases. • Transacciones de larga duración. Como los objetos y sus atributos pueden ser bastante grandes (Ej.: un video en una base de datos multimedia), las transacciones pueden ser bastante largas. Por eso se deben reconsiderar los mecanismos de recuperación ante un fallo y el control de la consistencia. 1 Objeto donde uno de sus atributos es otro objeto o un grupo de objetos. 25 Bases de Datos orientadas a objeto - Introducción • Mecanismos de autorización basados en la noción de objetos. los sistemas tradicionales, En los mecanismos de autorización están asociados a una relación o a una vista. Estos mecanismos no son del todo adecuados para un modelo orientado a objeto, el que necesita mecanismos asociados a la noción de objetos, considerando la herencia, versiones, objetos compuestos, etc. • Interactuar con sistemas existentes. Es necesario que estos nuevos sistemas sean capaces de acceder y manipular datos en sistemas existentes como bases de datos relacionales, debido al impacto que tiene en una organización la migración de datos. 26 3.2 Estándares de SABDOO En general, existen dos clases de normas, una de ellas llamada de Jure (por ley) que corresponde a aquellos estándares publicados por organizaciones dedicadas a ello y reconocidas internacionalmente (Ej.: ANSI, ISO, IEE); Y otras llamadas de facto (de hecho) que corresponden a aquellos estándares que realmente están en uso en el mercado sean estos o no, publicados por una organización formal de estandarización. Asociados a las bases de datos orientadas a objetos, existen dos estándares: SQL:1999 y ODMG v2.0 [13], el primero “de Jure” (y de facto) y el segundo “de facto”. Estos dos estándares siguen tendencias distintas, pero que están convergiendo. Estas tendencias son: • Extender los SABD relacionales para que soporten la orientación a objetos (SQL:1999); • confeccionar un SABDOO basado en un modelo de objetos “puro” que no extienda sistemas relacionales (ODMG-93). 3.2.1 Las tendencias: SABD relacionales “Extendidos” vs. SABDOO “Puros” Ambas tendencias tienen un manifiesto que recoge las características que debe tener un SABDOO, según su punto de vista. denomina El primero de ellos se “Manifiesto de los sistemas de bases de datos de tercera generación”, propuesto por Carey et al. en 1991 [13] [14], y es el que promueve extender los SABD relacionales. Este manifiesto presenta tres principios que se desarrollan en trece preposiciones. El siguiente resumen está sacado textualmente de [13]: 1. Principio: “además de los servicios tradicionales de gestión de datos, los SABD de la tercera generación proporcionaran gestión de objetos y reglas más ricos” • Un SABD de tercera generación debe tener un rico sistema de tipos 27 Bases de Datos orientadas a objeto – Estándares de SABDOO • La herencia es una buena idea • Las funciones (incluyendo procedimientos y métodos) y el encapsulamiento, son una buena idea. • Se deberían asignar identificadores de objeto para los registros sólo si no está disponible una clave primaria • Las reglas se convertirán en una característica primordial de los futuros sistemas. 2. Principio: “Los SABD de tercera generación deben subsumir a los SABD de segunda generación” • Un SABD de la tercera generación debe tener un lenguaje de acceso declarativo y de alto nivel • Deben existir dos formas de especificar las colecciones: por enumeración de sus miembros o mediante un lenguaje de consultas • Las vistas deben ser actualizables • Los indicadores de resultado no deben aparecer en los datos 3. Principio: “los SABD de tercera generación deben ser abiertos a otros subsistemas” • Un SABD de la tercera generación debe ser accesible desde múltiples lenguajes de alto nivel • Se debe soportar la persistencia de las variables • El SQL es una forma "intergaláctica" de expresión de datos • Las consultas y sus respuestas deben ser el nivel más bajo de comunicación entre un cliente y un servidor Este manifiesto deja en claro la idea de extender los sistemas relacionales actuales, manteniendo una compatibilidad hacia atrás. Este enfoque también llamado "evolutivo" es el que han adoptado la mayoría de los fabricantes de SABD relacionales (Oracle, Informix, Sybase, etc). A los SABDOO que siguen este enfoque también se les denomina SABD objeto relacional (ORDBMS o SABDOR), por combinar ambos paradigmas. La segunda tendencia se plasma en el manifiesto denominado "Manifiesto de los Sistemas de Bases de Datos Orientadas al Objeto", propuesto por 28 Bases de Datos orientadas a objeto – Estándares de SABDOO Atkinson en 1989 [13], es el que recoge el punto de vista de los SABDOO “puros”. Mario Piattini resume este manifiesto de la siguiente manera [13]: [En este manifiesto se recogen] trece "características obligatorias" o "reglas de oro" que deben cumplir todos los SABDOO, y que afectan a: • objetos complejos • identidad del objeto • encapsulamiento • tipos y clases • jerarquías de tipos o clases • anulación, sobrecarga y vinculación (binding) dinámica • completitud de cálculos • extensibilidad • persistencia • gestión del almacenamiento secundario • concurrencia • recuperación • facilidad de consulta "ad hoc" Junto a cinco "características opcionales" que sería deseable que presentaran: • herencia múltiple • verificación e inferencia del tipo • distribución • transacciones de diseño • versiones Además exponen "opciones abiertas" a los fabricantes de los SABDOO, como pueden ser: 1. paradigma de programación 2. sistema de representación 3. sistema de tipos 4. uniformidad De este manifiesto se desprende que el paradigma de la orientación a objeto debe ser soportado fuertemente, además no se mencionan los elementos del modelo relacional, ni tampoco una compatibilidad con los estándares actuales de bases de datos relacionales. Por esto podríamos decir que este 29 Bases de Datos orientadas a objeto – Estándares de SABDOO enfoque pretende crear un SABDOO puro, porque no mezcla orientación a objeto con los conceptos relacionales. Independiente del enfoque que adopten los SABDOO, el que “gane la batalla”, será aquel que: brinde el soporte más completo al paradigma de la orientación a objeto, sea compatible con las bases de datos relacionales, sea un SABD de alto rendimiento y logre una integración transparente entre el lenguaje de programación y la base de datos. En este momento, no existe un producto que reúna todas estas características, pero siguen trabajando en ello. 3.2.2 Estándar SQL:1999 El estándar SQL:1999 sigue el enfoque “evolutivo”, puesto que es la última versión aprobada del estándar SQL. Éste estándar incorpora varias características del paradigma de la orientación a objeto al enfoque relacional, pero no se limita a eso, puesto que incorpora otras características (véase el siguiente capítulo) y reestructura completamente la documentación del estándar, en vistas a una progresión más rápida en el futuro [8]. Durante su desarrollo se conoció como SQL 3, y a partir de los borradores de trabajo del estándar, se desarrollaron varios ORDBMS entre ellos: Oracle 8 de Oracle, Universal Server de Informix, DB2 Universal Database de IBM, Cloudscape de Cloudscape, entre otros. En el capítulo 5 se analizará el ORDBMS Oracle 8i. 3.2.2.1 Origen y evolución SQL nace a partir de SEQUEL, un lenguaje de consulta de bases de datos relacionales desarrollado por IBM en 1976-77 [10]. En 1979 aparece el primer SABDR comercial basado en SQL, Oracle. Luego de este, aparecieron muchos otros SABDR basados en SQL como: DB2, Interbase, Sybase, etc; y otros, que no lo tenían como lenguaje base, empiezan a ofrecer interfaces SQL. De esta manera, el SQL pasa a ser un estándar de facto [15]. 30 Bases de Datos orientadas a objeto – Estándares de SABDOO El primer estándar de Jure de SQL es el SQL/ANS (SQL-86), aprobado en 1986, por ANSI, al año siguiente fue aprobado por ISO. En 1989 se publica una nueva versión del estándar que incorpora una integridad referencial básica. En 1992 se aprueba la siguiente versión de SQL, llamada SQL-92. En esta versión, se incrementa sustancialmente la capacidad semántica del esquema relacional, añadiendo nuevos operadores, mejorando el tratamiento de errores incorporando normas para el SQL embebido. Dos partes adicionales se desarrollan también en 1992 el SQL/CLI y el SQL/PSM [8]. La última versión del estándar, fue aprobada por ANSI e ISO en 1999, llamándose SQL:1999 (No se llamó SQL-99 para no sufrir el problema del 2000), en este mismo año ISO aprueba el SQL/Multimedia. 3.2.2.2 Partes que lo componen El estándar está dividido en cinco documentos o partes: Número de Documento Nombre del documento 1. ANSI/ISO/IEC 9075-1-1999 Informatión Technology – Database Languages – SQL – part 1: Framework (SQL/Framework) 2. ANSI/ISO/IEC 9075-2-1999 Information Technology – Database Languages – SQL – part 2: Foundation (SQL/ Foundation) 3. ANSI/ISO/IEC 9075-3-1999 Information Technology – Database Languages – SQL – part 3: Call-level Interface (SQL/CLI) 4. ANSI/ISO/IEC 9075-4-1999 Information Technology – Database Languages – SQL – part4: Persistent Stored Modules (SQL / P SM) 5. ANSI/ISO/IEC 9075-5-1999 Information Technology – Database Languages – SQL – part 5: Host Language Bindings (SQL/Binding) Cada una de las partes puede ser adquirida por separado en http://web.ansi.org. 31 Bases de Datos orientadas a objeto – Estándares de SABDOO 3.2.3 Estándar ODMG 2.0 El estándar ODMG 2.0, es al que se acogen las empresas de SABDOO “Puros”, y a diferencia de SQL:1999, es un estándar de facto, cuya primera versión apareció hace siete años. 3.2.3.1 Origen y evolución En verano de 1991, se reunió un grupo de expertos que trabajaban en distintas empresas de SABDOO, para elaborar un estándar de facto, basado en las características que presentaban los productos existentes y que se pudiera publicar en un corto plazo. Así nació el ODMG (Object Data Management Group) que actualmente agrupa a los principales vendedores de SABDOO [13]: Object Design, Ontos, O2 Technology, Versant, Objectivity, POET Software y Servio Corporation; y que cuenta también con diversos revisores tanto de empresas (Andersen, Hewlett-Packard, EDS, Sybase, Texas Instruments o Persistence), como de universidades: Maier, Dewitt, Carey, Dittrich, Zdonik, Liskov, King, etc. En 1993 ODMG publica la primera versión de este estándar, el ODMG-93, una versión mejorada de este es publicada en 1995, el ODMG-93 v 1.2, en 1997 el ODMG 2.0 y la última versión publicada de este estándar es ODMG 3.0, publicada en el año 2000. 3.2.3.2 Partes que lo componen Este estándar se divide en las siguientes partes [19]: • Modelo de Objetos. Este está basado en el aprobado por el OMG (Object Management Group) y extendido con los conceptos propios de las bases de datos. • Lenguaje de Definición de Objetos. Se utiliza para describir la interfaz de los objetos. Su sigla es ODL (Object Definition Language) y está basado en IDL (Interface Definition Language) de CORBA (Common Object Request Broker Architecture). • Lenguaje de Consulta de Objetos. Este lenguaje es similar a SQL, pero no es compatible con él. Sus siglas originales son OQL (Object Query Language). 32 Bases de Datos orientadas a objeto – Estándares de SABDOO • Vinculación con el lenguaje C++ • Vinculación con el lenguaje Smalltalk • Vinculación con Java Para la vinculación con los lenguajes de programación este estándar ha procurado que el programador piense que utiliza un solo lenguaje. Esto lo logra integrando el lenguaje anfitrión con el lenguaje de manipulación de datos en forma natural y consistente [13]. Cabe destacar, que algunos SABDOO dan soporte a características que no son consideradas en el modelo objeto relacional, como la herencia múltiple (Ej.: IRIS, O2) y el manejo de versiones y configuraciones (Ej.: Orion). Puede ver un estudio comparativo, sobre las características de un grupo representativo de bases de datos orientadas a objeto en [2]. 33 4 La Orientación a Objeto en SQL:1999 4.1 Introducción En el capítulo anterior, explicamos la historia que hay detrás de SQL:1999, además de las partes que lo componen y dónde conseguirlo. profundizaremos más en el tema, Aquí partiendo por las organizaciones internacionales dedicadas a la estandarización de SQL:1999, las mejoras que introduce con respecto a su antecesor, y se evaluará el soporte que da a la Orientación a objeto, basándonos en el capítulo 2 y la sección 3.1.2.1. 4.1.1 Organizaciones preocupadas de la estandarización de SQL El primero en publicar un estándar para SQL (SQL-86) fue el instituto estadounidense para la estandarización ANSI (American National Standards Institute), y desde entonces ha sido un actor fundamental dentro del proceso de estandarización de SQL. Dentro de su esquema de trabajo, ANSI, no desarrolla las normas por si mismo, más bien acredita a otras organizaciones para que desarrollen los estándares bajo su auspicio y sus reglas [7] [8]. ANSI ha publicado varias normas referentes a tecnologías de la información a través de varios acreditados diferentes (SDO -Standards Developing Organization), incluyendo a IEEE y un grupo llamado NISO (National Information Standards Organization). La mayoría de los estándares asociados con las tecnologías de la información publicadas por ANSI fueron desarrollados por un grupo, conocido anteriormente con el nombre de X3 (entre 1961 – 1996) y ahora denominado NCITS (National Committee for Information Technology Standards). Este grupo crea comités técnicos que son los que realmente hacen el trabajo. Uno de estos comités es el H2 (anteriormente conocido como X3H2 y ahora como NCITS H2) que es el encargado de desarrollar una serie de estándares asociados a las bases de datos entre ellos RDA (Acceso a bases de datos remotas), SQL y SQL/MM. En el ámbito internacional, el principal cuerpo de desarrollo de estándares es ISO (International Standards Organization) y es quien tiene el rol oficial de desarrollo de normas internacionales. Este, al igual que ANSI, coopera con 35 La Orientación a Objeto en SQL:1999 - Introducción otras organizaciones, siendo una de ellas IEC (International Electro technical Comision). Del esfuerzo conjunto entre ISO e IEC nace el JTC1 (Joint Technical Committee 1) cuya misión es desarrollar y mantener estándares relacionados a tecnologías de la Información. El JTC1 crea subcomités (SCs) para distintas áreas de las tecnologías de la información. Hasta más o menos 1997 el SC21 era el responsable de SQL, pero luego de su disolución, esta responsabilidad fue asignada a un nuevo comité llamado SC32. Este Subcomité se encarga de los estándares asociados a las bases de datos y a los meta datos (RDO, SQL, SQL/MM, etc). Para esto crea, a su vez grupos de trabajo (Working Group), entre ellos destacamos el WG3 que es el encargado del estándar SQL y el WG4 que se encarga del SQL/MM. En total son 28 países los que integran el JTC1 y sus respectivos subcomités y grupos de trabajo. Alguno de estos miembros y sus institutos de estandarización son : Canadá (SCC), Australia (SAA), Brasil (ABNT), China (CSBTS), Francia (AFNOR), Alemania (DIN), Italia (UNI), Japón (JISC), Reino Unido (BSI), Estados Unidos (ANSI). Para más información sobre estas organizaciones y los procesos actuales de estandarización visite los siguientes sitios web: • American National Standards Institute (ANSI) o • National Committee for Information Technology Standards (NCITS) o • http://www.iso.ch International Electro technical Commission (IEC) o • http://www.ncits.org/tc_home/h2.htm International Organization for Standardization (ISO) o • http://www.ncits.org NCITS H2 o • http://www.ansi.org http://www.iec.ch/ Joint Technical Committee 1(JTC1) o http://www.jtc1.org/ 36 La Orientación a Objeto en SQL:1999 - Introducción 4.1.2 Nuevas características. Las nuevas características del estándar SQL:1999, se pueden dividir en “Características Relacionales” y “Características Orientadas a Objeto”. En esta sección nos limitaremos a describir las “Características relacionales y en la sección 4.2, explicaremos las referentes a la orientación a objeto. 4.1.2.1 Nuevos Tipos de Datos SQL:1999 tienen cuatro nuevo tipos de datos. El primero de estos tipos es el LARGE OBJET, o LOB. Este tipo tiene las siguientes variantes: CHARACTER LARGE OBJECT (CLOB) y BINARY LARGE OBJECT (BLOB). Otro nuevo tipo de datos es BOOLEAN que le permite a SQL grabar directamente los valores de verdad: verdadero (true), falso (false), y desconocido (Unknown) [25]. SQL:1999 también incorpora dos tipos compuestos: ARRAY y ROW [8] [25]. El tipo ARRAY nos permite guardar colecciones de valores directamente en una columna de una tabla de la base de datos. Por ejemplo: DIAS_DE_LA_SEMANA VARCHAR(10) ARRAY[7] El tipo ROW en SQL:1999 es una secuencia de una o más parejas (<Nombre Campo>,<Tipo de datos>) llamados campos (fields) [25] que permite almacenar valores estructurados en simples columnas de la base de datos. Por ejemplo: CREATE TABLE employee ( EMP_ID INTEGER, NAME ROW (GIVEN VARCHAR(30), FAMILY VARCHAR(30)), SALARY REAL ) SELECT E.NAME.FAMILY FROM employee E SQL:1999 agrega además otro tipos de datos llamados “tipos distintos”, que son tipos de datos definidos por el usuario (UDT en adelante) que se basan en tipos primitivos o en otros tipos distintos[25]. Por ejemplo: 37 La Orientación a Objeto en SQL:1999 - Introducción CREATE TYPE SHOW_SIZE AS INTEGER(3) FINAL CREATE TYPE IQ AS INTEGER(3) FINAL ... WHERE MY_SHOE_SIZE > MY_IQ // ERROR En este ejemplo, la última expresión arrojaría un error de sintaxis, puesto que MY_SHOE_SIZE y MY_IQ son de distinto tipo. 4.1.2.2 nuevos predicados En SQL:1999 existen dos nuevos predicados que no se relacionan con la orientación a objeto, el predicado SIMILAR y el predicado DISTINCT [8]. El predicado SIMILAR es utilizado para definir expresiones regulares para la búsquedas de patrones como lo hacen los programas UNIX. Por ejemplo: WHERE NAME SIMILAR TO ' (SQL-(86|89|92|99)) | (SQL(1|2|3)) ' En este ejemplo se emparejarían varios nombres dados al estándar SQL durante los años. [8]. El otro predicado nuevo, DISTINCT, es muy similar en operación al predicado ordinario UNIQUE de SQL; Dos valores nulos no son distintos (DISTINCT), si únicos (UNIQUE) [14]. 4.1.2.3 semántica reforzada SQL:1999 ha aumentado significativamente el alcance de vistas que pueden actualizarse directamente, facilitando así su utilización . Una limitación de SQL ha sido su incapacidad para ejecutar recursión [8] [24], pero esto ha sido solucionado en característica llamada Recursive Query. SQL:1999 proporcionando una Definir una consulta recursiva involucra escribir la expresión de consulta que usted quiere recurrente, entonces usa ese nombre en una expresión de consulta asociada. Por Ejemplo 38 La Orientación a Objeto en SQL:1999 - Introducción WITH RECURSIVE Q1 AS SELECT...FROM...WHERE..., Q2 AS SELECT...FROM...WHERE... SELECT...FROM Q1, Q2 WHERE... Otra funcionalidad incorporada es la noción de savepoints. Ellos permiten, a una aplicación, deshacer las acciones realizadas después del principio de un savepoint sin deshacer todas las acciones de una transacción entera[8]. SQL:1999 permite ROLLBACK TO SAVEPOINT y RELEASE SAVEPOINT [26]. 4.1.2.4 seguridad adicional La nueva característica de seguridad de SQL:1999 se encuentra en su capacidad de creación de roles (o papeles) [8]. Pueden concederse privilegios a los papeles y estos pueden ser identificadores de autorización individual o conceder privilegios de autorización a otros papeles. Por ejemplo [14]: CREATE ROLE Administrador CREATE ROLE Jefe_Supremo GRANT Administrador TO Miguel GRANT Administrador TO Jefe_Supremo 4.1.2.5 bases de datos activa SQL:1999 reconocen la noción de base de datos activa, aunque algunos años después que los SABD lo hicieran. Esta facilidad es proporcionada a través de una característica conocida como disparador (trigger). Un disparador es una facilidad que les permite a los diseñadores de la base de datos instruir al sistema para ejecutar ciertos procedimientos cada vez que una aplicación realice operaciones específicas en tablas particulares [8] [26]. Por ejemplo, podrían usarse disparadores para anotar todas las operaciones que cambian sueldos en una tabla empleado [8]: CREATE TRIGGER log_salupdate BEFORE UPDATE OF salary ON employees REFERENCING OLD ROW as oldrow NEW ROW as newrow FOR EACH ROW INSERT INTO log_table VALUES (CURRENT_USER, oldrow.salary, newrow.salary) 39 4.2 Soporte a la Orientación a objeto Para analizar el soporte de la orientación a objeto en SQL:1999, nos basaremos en los conceptos básicos de la orientación a objetos (ver 2) y en los requerimientos para bases de datos orientadas a objetos (ver 3.1.2.1 ). Por motivos didácticos el orden de los conceptos será diferente. La sintaxis de SQL:1999 relacionada con la orientación a objeto, se encuentra en el apéndice A. 4.2.1 Tipos, abstracción y clases El estándar unifica estos conceptos bajo el nombre de tipo estructurado, que es otro tipo de UDT. Un tipo estructurado encapsula atributos y métodos en una única entidad lógica, pero físicamente separados. Los atributos de un objeto persistente los almacenaremos en una tabla, y los métodos, junto con los demás procedimientos almacenados. Cabe destacar que esta separación será transparente para el usuario. Este nuevo tipo, pasará a ser uno más del sistema, pudiendo ser utilizado del mismo modo que un tipo primitivo. 4.2.1.1 Creación de tipos (CREATE TYPE) Para crear un tipo estructurado se utiliza el predicado CREATE TYPE [26], donde especificamos una lista de atributos, y opcionalmente, una lista de métodos (ver A.2.1). Por ejemplo, consideremos un tipo Punto, con los atributo: X, Y; y con los métodos: moverA(x,y), distanciaA(p), crear(x,y); su implementación sería: CREATE TYPE punto AS ( X INTEGER , Y INTEGER )INSTANTIABLE NOT FINAL METHOD moverA(x INTEGER, y INTEGER), METHOD distanciaA(p Punto) RETURNS FLOAT, STATIC METHOD crear(x INTEGER, y INTEGER) RETURNS punto 40 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto La cláusula INSTANTIBLE, permite crear instancias del tipo directamente. Si queremos prohibir esto (para implementar clases abstractas), indicamos NOT INSTANTIABLE. Esta cláusula es opcional (ver A.2.1), cuyo valor por defecto es INSTANTIABLE. La cláusula NOT FINAL, permite que el tipo sea utilizado como supertipo de otro (ver 4.2.6.3 Herencia y polimorfismo). Esta cláusula no es opcional. Todo tipo estructurado tiene asociado un correspondiente tipo de referencia. Este tipo almacena una secuencia de bytes, que indica el lugar donde está almacenado un objeto de ese tipo. El número de bytes es dependiente de la implementación del estándar. Un tipo de referencia puede ser utilizado en los mismos lugares que un tipo estructurado. Para definirlos utilizamos la palabra reservada REF, seguida por el nombre del tipo entre paréntesis. Por ejemplo, consideremos un tipo linea_recta, definido de la siguiente manera: CREATE TYPE linea_recta AS ( a REF(punto), b REF(punto) )INSTANTIABLE NOT FINAL con este tipo de datos, podemos crear redes de objetos. Por ejemplo, podemos crear una lista enlazada, un árbol binario, etc. Al definir un tipo estructurado, podemos indicar la manera en que el sistema construirá sus referencias. Para eso, debemos escoger entre tres formas de representación, definidas por la cláusula opcional <reference type specification> (ver A.2.1), que es indicada a continuación de la cláusula <finality>. • Las formas de representación son: USER GENERATED, la referencia se basa en un tipo primitivo, cuyo valor será proporcionado por el usuario. La sintaxis es: REF USING <predefined type>. Ejemplo: CREATE TYPE persona AS ( Rut CHAR(12) , Nombre CHAR(40) )INSTANTIABLE NOT FINAL REF USING INTEGER 41 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto • SYSTEM GENERATED, la referencia es generada automáticamente por el sistema. Este es el valor por defecto. La sintaxis es: REF IS SYSTEM GENERATED Ejemplo: CREATE TYPE persona AS ( Rut CHAR(12) , Nombre CHAR(40) )INSTANTIABLE NOT FINAL REF IS SYSTEM GENERATED • DERIVED, La referencia se basa en uno o más atributos. Es semejante a una clave primaria. La sintaxis es: REF FROM <list of attributes> Ejemplo: CREATE TYPE persona AS ( Rut CHAR(12) , Nombre CHAR(40) )INSTANTIABLE NOT FINAL REF FROM (Rut) Una limitación importante de los tipos de referencia, es que sólo pueden almacenar direcciones de objetos persistentes. 4.2.1.2 Atributos Un tipo estructurado puede tener uno o más atributos, cada uno con un nombre, un tipo de datos, y opcionalmente, un valor por defecto (con DEFAULT) [26]. No podemos definir dos atributos del mismo nombre, para un mismo tipo estructurado. El tipo de datos del atributo, puede ser cualquiera de SQL, incluyendo un UDT [14]. Por ejemplo, si quisiéramos crear un tipo Direccion con los atributos calle, numero, ciudad y region, sería. CREATE TYPE Direccion AS ( calle CHAR(40), numero CHAR(10), ciudad CHAR(30), region CHAR(30) )NOT FINAL Ahora, podemos crear un tipo empleado con un atributo del tipo Dirección de la siguiente manera: CREATE TYPE empleado AS ( rut CHAR(12), nombre CHAR(40), 42 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto sueldoBase INTEGER , anticipo INTEGER DEFAULT 0, fecha_ing DATE DEFAULT CURRENT_DATE, jefe REF(empleado), domicilio direccion )INSTANTIABLE NOT FINAL Para acceder al atributo de una instancia, lo hacemos de la misma manera que en JAVA, a excepción de los atributos del tipo REF(...). Por ejemplo, para acceder al atributo rut, de un empleado e, sería: e.rut, y esto en cualquier nivel de anidamiento, por ejemplo: e.domicilio.numero. Para acceder a los atributos cuyo tipo de datos sea REF(...), existe el operador ->. Por ejemplo, si queremos saber el nombre del jefe del empleado, sería: e.jefe->nombre No podemos asignar una visibilidad (Ej.: public, private, protected) a los atributos (característica que estuvo presente en el borrador de trabajo del año 1997). directo. A pesar de esto, el acceso a los atributos de un tipo no es Para cada atributo, el sistema genera dos funciones, una observadora y otra mutadora [26]. Por ejemplo, si decimos p.rut, lo que sucede en realidad es que estamos invocando a la función observadora rut(p). estamos Si realizamos una asignación como p.rut:=’13.131.344-6’, invocando implícitamente a la función mutadora rut(p,’13.131.344-6’). Lamentablemente, las funciones observadoras y mutadoras, no pueden ser sobrecargadas [14]. A diferencia de Java, los atributos estáticos no son soportados. 4.2.1.3 Métodos A contar del SQL-92, se introduce la noción de procedimiento almacenado, que corresponde a funciones o procedimientos definidos por el usuario, que pueden ser utilizados en sentencias SQL. SQL:1999 incorpora una tercera categoría de procedimiento almacenado, los métodos [8]. Un método, al igual que una función, posee un nombre, una lista de argumentos (que puede estar vacía) y un valor de retorno (opcional). La gran diferencia radica en que un método, está asociado a un UDT estructurado en particular, y no puede ser invocado Independientemente. 43 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Al definir los métodos en la creación del UDT estructurado, sólo declaramos su interfaz (función prototipo o encabezado). Para definir su implementación o cuerpo, utilizamos la sentencia CREATE METHOD (ver A.2.4). El código que implementa al método, puede ser escrito en SQL (usando las sentencias computacionalmente completas del SQL/PSM [47]) o en cualquiera de los lenguajes de programación más tradicionales, incluyendo JAVA [14]. Los métodos pueden ser: métodos de instancia, métodos constructores o métodos estáticos. 4.2.1.3.1 Métodos de Instancia Los métodos de instancia, son aquellos que serán invocados a partir de un objeto concreto. Para definirlos anteponenemos las palabras reservadas INSTANCE METHOD o simplemente METHOD, seguido por el nombre, la lista de parámetros (que puede estar vacía) y opcionalmente el tipo del valor de retorno. Cada elemento de la lista de parámetros está compuesto por: Modo, nombre y tipo de datos; donde el modo es la manera en la que se pasa el parámetro, que puede ser IN (entrada), OUT (salida), INOUT( entrada y salida). Los métodos de instancia, pueden ser sobrecargados. Por ejemplo, consideremos un tipo triángulo, definido por los tres vértices que lo componen, y con tres métodos, uno que calcule su área, y dos para calcular la distancia de un punto al lado más cercano del triángulo. CREATE TYPE triangulo AS ( A punto , B punto , C punto )INSTANTIABLE NOT FINAL METHOD Area() RETURNS FLOAT, INSTANCE METHOD distanciaA(IN p Punto) RETURNS FLOAT INSTANCE METHOD distanciaA(x FLOAT,y FLOAT) RETURNS FLOAT La Invocación de un método de instancia, se realiza de la misma forma que accedemos a un atributo. Por ejemplo, si quisiéramos saber el área de un triángulo t, sería: ...t.Area() 44 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Si t fuera una referencia a un triangulo, la invocación al método area() sería: ...t->Area() Los métodos de instancia incluyen, implícitamente un parámetro, cuyo nombre es SELF, y representa al objeto de la invocación. Opcionalmente podemos especificar otras características del método (ver <method characteristic> en A.2.1), las cuales se indican a continuación del encabezado. La primera de ellas (<language clause>) indica el lenguaje en el que se programará el método. Si omitimos esta cláusula, el lenguaje por defecto, es SQL. Por ejemplo, si el método área del tipo triangulo, mostrado anteriormente, lo quisiéramos programar en pascal, tendríamos que especificar: ... METHOD Area() RETURNS FLOAT LANGUAGE PASCAL ... La cláusula <parameter style clause> indica cómo debemos pasar los parámetros. Puede tomar dos valores posibles: • PARAMETER STYLE SQL. Los parámetros son pasados al estilo de SQL. Para las rutinas externas que utilicen este estilo, es necesario agregar algunos parámetros para el control de los valores nulos, valores de retorno y otros parámetros de control. En general, se necesitan 2n+4 parámetros para los procedimientos y 2n+6 para las funciones, donde n es el número de parámetros del método. • PARAMETER STYLE GENERAL. Cada parámetro del método, corresponde efectivamente a un parámetro en la implementación. Para las rutinas externas, es la manera más fácil de pasar parámetros, porque coinciden uno a uno. El problema existe con los valores nulos, que no son soportados, es decir, no podemos invocar al método con parámetros cuyos valores actuales, sean nulo. Cuando esta cláusula es omitida, se asume PARAMETER STYLE SQL como valor. Ejemplo: 45 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto ... METHOD Area() RETURNS FLOAT LANGUAGE PASCAL PARAMETER STYLE GENERAL ... La cláusula <deterministic characteristic> indica si el método es determinista o no. Un método es determinista, si en cada llamada con los mismos parámetros entrega el mismo resultado, por ejemplo, ...Math.sqrt(9). Es difícil que un método de instancia sea determinista, puesto que el estado actual del objeto, afecta su comportamiento, este comportamiento es más propio de los métodos estáticos. El valor por defecto de esta cláusla es NOT DETERMINISTIC. ... METHOD Area() RETURNS FLOAT LANGUAGE PASCAL PARAMETER STYLE GENERAL NOT DETERMINISTIC ... La cláusula <SQL-data access indication>, indica si el método manipula o no datos y sentencias de SQL. Los valores posibles son: NO SQL, CONTAINS SQL, READS SQL DATA, MODIFIES SQL DATA. Ejemplo: ... METHOD Area() RETURNS FLOAT LANGUAGE PASCAL PARAMETER STYLE GENERAL NOT DETERMINISTIC NOT SQL ... Los métodos que modifican datos de SQL (cláusula MODIFIES SQL DATA) no son permitidos en: • Constraint • Assertions • Sentencia CASE • Before Triggers • Cláusula de búsqueda en sentencias DELETE • Cláusula de búsqueda en sentencias UPDATE (aunque es permitido dentro de la cláusula SET) • Expresiones de consulta distintas a constructores de tablas. 46 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto La cláusula <null-call clause>, indica la forma de proceder, si todos los parámetros pasados al momento de la llamada son nulos. Para este caso existen dos alternativas, retornar NULL sin llamar al método (RETURNS NULL ON NULL INPUT) o llamar al método (CALLED ON NULL INPUT). El valor por defecto es CALLED ON NULL INPUT. Ejemplo: ... INSTANCE METHOD distanciaA(IN p Punto) RETURNS FLOAT RETURNS NULL ON NULL INPUT ... 4.2.1.3.2 Métodos estático Los métodos estáticos, son aquellos que serán invocados a partir de un tipo estructurado y no de un objeto. Para definirlos se anteponen las palabras reservadas STATIC METHOD seguido del nombre, la lista de parámetros (que puede estar vacía) y opcionalmente el tipo del valor de retorno. Un método estático, no posee el parámetro implícito self, porque son invocados a partir de un tipo y no de un objeto. Por esta razón, no podemos acceder a los atributos del tipo, desde un método estático. Al igual que en un método de instancia, los métodos estáticos pueden ser sobrecargados. Por ejemplo, consideremos el mismo tipo triángulo mostrado anteriormente, agregándole un método estático que permita crear un triángulo a partir de tres puntos: CREATE TYPE triangulo AS ( A punto , B punto , C punto )INSTANTIABLE NOT FINAL METHOD Area() RETURNS FLOAT, INSTANCE METHOD distanciaA(IN p Punto) RETURNS FLOAT STATIC METHOD crear(a Punto, b Punto, c Punto) RETURNS triangulo Para invocar al método crear se utiliza: ...triangulo::crear(a,b,c) considerando que a,b,c son instancias del tipo Punto previamente definidas. 47 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Al igual que en los métodos de instancia, podemos especificar las características opcionales descritas en <method characteristic> en A.2.1. 4.2.1.3.3 Constructores Cuando creamos un tipo estructurado con la cláusula INSTANTIABLE, el sistema define un método constructor por defecto [25]. Este método, es una función cuyo nombre coincide con el del tipo estructurado, no posee argumentos, y el valor de retorno es una instancia del tipo al que pertenece, donde el valor de cada atributo corresponde al valor por defecto, especificado al momento de su creación. Por ejemplo, para el tipo persona, el constructor por defecto es persona( ). El estándar SQL:1999, no permite los constructores definidos por el usuario, pero el borrador de trabajo de la futura versión del estándar (SQL:200n), ya lo incorpora. Existe un operador llamado NEW que nos permite invocar a un método de instancia que haga las veces de constructor. El método debe tener el mismo nombre que su tipo estructurado, y puede ser sobrecargado. Por ejemplo, consideremos el siguiente tipo persona: CREATE TYPE persona AS ( rut CHAR(12), nombre CHAR(40), )INSTANTIABLE NOT FINAL METHOD persona(r CHAR(12),n CHAR(40)) RETURNS persona Si queremos construir un objeto del tipo persona utilizando el operador NEW sería: ...new persona(’13.131.344-6’,’Miguel Romero Vásquez’) El operador NEW suple la limitación de constructores definidos por el usuario. 4.2.1.3.4 Creación de métodos (CREATE METHOD) Para cada método definido en el tipo estructurado, es necesario definir su código o enlazarlo con un programa externo. Para esto utilizamos la 48 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto sentencia CREATE METHOD (ver A.2.4). Por ejemplo, consideremos la siguiente implementación de un número complejo: CREATE TYPE complex AS ( rpart REAL DEFAULT 0, ipart REAL DEFAULT 0 )INSTANTIABLE FINAL METHOD suma(x complex) RETURNS complex, METHOD resta(x complex) RETURNS complex, METHOD mult(x complex) RETURNS complex, METHOD div(x complex) RETURNS complex; Un numero complejo consta de dos partes una real (atributo rpart) y otra imaginaria (atributo ipart), ambas dentro del rango de los números reales. Además existen una serie de operaciones matemáticas que se pueden aplicar a dichos números (métodos suma, resta, mult y div). Para crear los método del tipo utilizando SQL/PSM, sería: CREATE METHOD suma(x complex) RETURNS complex FOR complex BEGIN DECLARE co complex; SET co = complex(); SET co.rpart = self.rpart + x.rpart; SET co.ipart = self.ipart + x.ipart; RETURN co; END; CREATE METHOD resta(x complex) RETURNS complex FOR complex BEGIN DECLARE co complex; SET co = complex(); SET co.rpart = self.rpart - x.rpart; SET co.ipart = self.ipart - x.ipart; RETURN co; END; CREATE METHOD mult(x complex) RETURNS complex FOR complex BEGIN DECLARE co complex; SET co = complex(); SET co.rpart = rpart * x.rpart – ipart * x.ipart; SET co.ipart = rpart * x.ipart + ipart * x.rpart; RETURN co; END; CREATE METHOD div(x complex) RETURNS complex FOR complex BEGIN DECLARE z REAL; DECLARE co complex; SET z = x.rpart * x.rpart + x.ipart * x.ipart; SET co = complex(); SET co.rpart = (rpart * x.rpart + ipart * x.ipart)/z; SET co.ipart = (ipart * x.rpart - rpart * x.ipart)/z; 49 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto RETURN co; END; Como puede observar, la sintaxis es bastante sencilla. Parte con las palabras reservadas CREATE METHOD, seguida por el nombre del método, la lista de argumentos y opcionalmente la cláusula RETURNS junto al tipo del valor de retorno. La palabra reservada FOR nos permite indicar el tipo al que pertenece el método, en este caso complex. A este encabezado le sigue el cuerpo del método, que puede ser escrito en SQL (como en el ejemplo anterior) o en un lenguaje externo (C, Fortran, Ada, etc). Para aquellos métodos que son implementados en un lenguaje externo, es necesario indicar <external routine name>,<parameter style clause>,<external security clause> (ver A.2.4). Por ejemplo: Consideremos el mismo tipo complex, con el método suma externo. Lo primero sería indicar el lenguaje en el que está programado al momento de definir el tipo: CREATE TYPE complex AS ( rpart REAL DEFAULT 0, ipart REAL DEFAULT 0 )INSTANTIABLE FINAL METHOD suma(x complex) RETURNS complex, LANGUAGE C METHOD resta(x complex) RETURNS complex, METHOD mult(x complex) RETURNS complex, METHOD div(x complex) RETURNS complex; Luego, al implementar el método, indicar el nombre externo, y opcionalmente, el estilo del paso de parámetros y la cláusula de seguridad : CREATE METHOD suma(x complex) RETURNS complex FOR complex EXTERNAL NAME ‘/routines/suma’ PARAMETER STYLE GENERAL EXTERNAL SECURITY INVOKER Para poder invocar un método, el usuario debe tener el privilegio EXECUTE sobre el método (otorgado con la cláusula GRANT). La cláusula de seguridad indica al sistema qué privilegios considerar para permitir o negar la ejecución del método, como también, cada una de sus sentencias. Puede asumir tres valores posibles [46]: 50 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto • EXTERNAL SECURITY DEFINER. Considerar los privilegios del usuario que definió el método. • EXTERNAL SECURITY INVOKER. Considerar los privilegios del usuario que invoca al método. • EXTERNAL SECURITY IMPLEMENTATION DEFINED. Permite que la implementación decida qué privilegios utiliza, si los del usuario que definió el método o del que lo invoca. Este valor es asumido cuando se omite la cláusula de seguridad. Los métodos externos tienen varias ventajas frente a los métodos implementados con SQL [46]: • Permiten utilizar funciones que ya teníamos, o incorporar bibliotecas de funciones de otros lenguajes como Java, Pascal, C, Fortran, extendiendo las capacidades de SQL, sin la necesidad de programarlas en SQL. • Las rutinas externas son más portables (por ejemplo rutinas escritas en C o Java). • Las rutinas externas, permiten aprovechar las características y capacidades de un amplio conjunto de lenguajes. concurrencia, funciones matemáticas y Por ejemplo: estadísticas avanzadas, comunicación entre procesos, envío de correo electrónico, manejo de censores, etc. • Las rutinas externas permiten invocar las mismas funciones que utiliza el resto de nuestra aplicación, desde la base de datos. • Las rutinas externas permiten definir la cláusula EXTERNAL SECURITY. Pero tienen las siguientes desventajas que deben ser consideradas [46]: • Mover datos entre la rutina externa y el código SQL requiere un esfuerzo adicional de programación y a veces problemas, por la falta de correspondencia entre los tipos de datos. Por ejemplo el tipo Real de SQL soporta valores nulos, pero el tipo Real de C, no. • Para las rutinas externas que contienen código SQL, es necesaria la creación de nuevas sesiones de SQL, lo que puede ser bastante costoso. 51 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto • El tiempo de desarrollo puede incrementarse al tener que utilizar lenguajes externos, por el esfuerzo adicional de entrenamiento y depuración frente a los métodos desarrollados bajo SQL/PSM. 4.2.1.4 Modificación de tipos (ALTER TYPE) Con el predicado ALTER TYPE (ver A.2.3 <alter type statement>) podemos agregar o eliminar atributos y métodos a un tipo estructurado. Para ejemplificarlo utilizaremos el siguiente tipo: CREATE TYPE empleado AS ( rut CHAR(12), nombre CHAR(40), sueldoBase INTEGER , anticipo INTEGER DEFAULT 0, fecha_ing DATE DEFAULT CURRENT_DATE, domicilio direccion )INSTANTIABLE NOT FINAL 4.2.1.4.1 Agregar un atributo Si queremos agregar el atributo email al tipo empleado, sería: ALTER TYPE empleado ADD ATTRIBUTE email CHAR(50) No podremos agregar el atributo email si existe una o más columnas en alguna tabla relacional cuyo tipo de datos sea: • Empleado. • Un arreglo de cualquier supertipo o subtipo de empleado. • Cualquier supertipo o subtipo de T, siendo T un tipo que posee uno o más atributos que referencian a un empleado. • Un arreglo de cualquier supertipo o subtipo de T, siendo T un tipo que posee uno o más atributos que referencian a un empleado. Tampoco podremos agregar un atributo, si existe alguna tabla de objetos (ver 4.2.5.1 ) cuyo tipo base sea un subtipo (directo o no) de empleado. La definición de un atributo con ALTER TYPE utiliza la misma sintaxis que en CREATE TYPE. 52 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto 4.2.1.4.2 Eliminar un atributo Si queremos eliminar el atributo domicilio del tipo empleado sería: ALTER TYPE empleado DROP ATTRIBUTE domicilio RESTRICT En los siguientes casos, no podremos eliminar el atributo domicilio: • Si es el único que posee el tipo empleado. • Si es un atributo heredado. • Si existe una o más columnas en alguna tabla relacional cuyo tipo de datos sea: o Empleado. o Un arreglo de cualquier supertipo o subtipo de empleado, sea directo o no. o Cualquier supertipo o subtipo de T, sea directo o no. Siendo T un tipo que posee uno o más atributos que referencian a un empleado. o Un arreglo de cualquier supertipo o subtipo de T, sea directo o no. Siendo T un tipo que posee uno o más atributos que referencian a un empleado. • Si existe alguna typed table (ver 4.2.5.1 ) cuyo tipo base sea un subtipo (directo o no) de empleado. • • Si es utilizado directa o indirectamente en: o El cuerpo de un método, una función o un procedimiento, o un trigger, o la expresión booleana de una constraint o una assertion o la expresión de consulta que define una vista. Si es utilizado en una función de conversión definida por el usuario (ver 4.2.1.7 ) 4.2.1.4.3 Agregar un método Si queremos agregar el método SueldoLiquido() al tipo empleado, sería: ALTER TYPE empleado ADD METHOD SueldoLiquido() RETURNS INTEGER 53 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Para especificar el nuevo método, utilizamos la misma sintaxis que en CREATE TYPE. 4.2.1.4.4 Eliminar un método Consideremos el siguiente tipo: CREATE TYPE triangulo AS ( A punto , B punto , C punto )INSTANTIABLE NOT FINAL METHOD Area() RETURNS FLOAT, INSTANCE METHOD distanciaA(IN p Punto) RETURNS FLOAT INSTANCE METHOD distanciaA(x FLOAT,y FLOAT) RETURNS FLOAT Si queremos eliminar el método distanciaA del tipo triangulo sería: ALTER TYPE triangulo DROP METHOD distanciaA(Punto) FOR triangulo RESTRICT Para indicar el método a eliminar, tenemos que indicar su nombre y opcionalmente, los tipos de datos de los parámetros, separados por coma y entre paréntesis, seguido de eso, la palabra reservada FOR y el nombre del tipo al que pertenece. Esto último, es obligatorio cuando tenemos sobrecargado el método. 4.2.1.5 Eliminación de tipos (DROP TYPE) Mediante la sentencia DROP TYPE, podemos eliminar un tipo estructurado. Por ejemplo, si queremos eliminar el tipo punto, decimos: DROP TYPE punto CASCADE La cláusula CASCADE, indica que se deben borrar todas las dependencias que existan con este tipo. Alternativamente a CASCADE, existe la cláusula RESTRICT, que impide el borrado del tipo si existen dependencias. Por ejemplo: DROP TYPE punto RESTRICT 54 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto 4.2.1.6 Comparación de instancias Inicialmente, no podemos comparar dos instancias de un tipo estructurado (ej: A>B) . Para lograr esto, es necesario que el usuario defina una función que los compare y luego asociarla al tipo estructurado correspondiente. Para esto existe la sentencia CREATE ORDERING (ver A.2.8). Existen dos categorías de comparación: • EQUALS ONLY. Sólo se permiten comparaciones de igualdad (=) y desigualdad (<>). • ORDER FULL. Todas las comparaciones son permitidas (>,<,=,<>) Además, podemos escoger entre tres categorías de funciones de comparación: • STATE. La función de comparación es creada por el sistema. Dicha función tiene dos parámetros (las instancias a comparar) y retorna un Boolean. La función compara ambas instancias, atributo por atributo. Si coinciden los valores de todos los atributos entre las instancias, retorna true si no, false. • RELATIVE. El usuario debe especificar la función de comparación, la cual debe contar con dos parámetros (las instancias a comparar) y cuyo valor de retorno sea del tipo Integer. La función debe retornar 0 si ambas instancias son iguales, un valor positivo si la primera instancia es mayor que la segunda, y un valor negativo si es menor. • MAP. El usuario debe especificar la función de comparación, la cual debe contar con un sólo parámetro y debe retornar un tipo de dato primitivo. La función no se encarga de comparar las instancias, sólo indica la posición que tomaría la instancia pasada por parámetro, si ordenáramos el universo completo de las instancias. El sistema utiliza el valor devuelto para realizar la comparación. Al momento de definir una función de comparación debemos considerar lo siguiente: • STATE sólo puede ser utilizado con EQUALS ONLY. 55 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto • dentro de una jerarquía de tipos, STATE y RELATIVE deben ser especificados para el supertipo máximo (aquel que no tiene padre). • MAP puede ser especificado para más de un tipo, dentro de una jerarquía de tipos, pero todos ellos deben utilizar MAP, es decir, no podemos definir unos tipos con MAP y otros con STATE o RELATIVE. Para ejemplificarlo, utilizaremos el mismo tipo empleado utilizado anteriormente: CREATE TYPE empleado AS ( rut CHAR(12), nombre CHAR(40), sueldoBase INTEGER , anticipo INTEGER DEFAULT 0, fecha_ing DATE DEFAULT CURRENT_DATE, domicilio direccion )INSTANTIABLE NOT FINAL Si queremos definir una función de comparación del tipo STATE sería: CREATE ORDERING FOR empleado EQUALS ONLY BY STATE Con ella podremos hacer comparaciones de igualdad y desigualdad. Si lo que queremos es permitir todas las comparaciones, debemos utilizar ORDER FULL y definir una función del tipo RELATIVE o MAP. Por ejemplo: CREATE ORDERING FOR empleado ORDER FULL BY RELATIVE WITH FUNCTION empleado_relative(empleado,empleado) CREATE ORDERING FOR empleado ORDER FULL BY MAP WITH FUNCTION empleado_map(empleado) Una implementación válida de la función empleado_relative sería: CREATE FUNCTION empleado_relative(a empleado, b empleado) RETURNS INTEGER BEGIN DECLARE comp INTEGER; IF a.sueldoBase = b.sueldoBase THEN SET comp = 0; ELSE IF a.sueldoBase > b.sueldoBase THEN SET comp = 1; ELSE SET comp = - 1; END IF; END IF; RETURN comp; END 56 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Una implementación válida de la función empleado_map sería: CREATE FUNCTION empleado_map(a empleado) RETURNS INTEGER BEGIN RETURN a.sueldoBase; END Las funciones del tipo MAP y RELATIVE, también pueden ser usadas con EQUALS ONLY. Por ejemplo: CREATE ORDERING FOR empleado EQUALS ONLY BY RELATIVE WITH FUNCTION empleado_relative(empleado,empleado) CREATE ORDERING FOR empleado EQUALS ONLY BY MAP WITH FUNCTION empleado_map(empleado) Para entender mejor cómo trabajan estas funciones, consideremos la comparación “E1 = E2” siendo E1 y E2 instancias del tipo empleado. Internamente esta expresión será sustituida por otra, basada en la función de comparación correspondiente. Tenemos tres casos posibles: • Si la función es del tipo STATE, la expresión se sustituye por: o “SF(E1,E2)=TRUE”, siendo SF(...) la función definida por el sistema. • Si la función es del tipo MAP, se sustituye por: o • “empleado_map(E1) = empleado_map(E2)” Si la función es del tipo RELATIVE, se sustituye por: o “empleado_relative(E1,E2) = 0” 4.2.1.6.1 Eliminación de funciones de comparación Para eliminar la función de comparación de un tipo, utilizamos la sentencia DROP ORDERING (ver A.2.9). Por ejemplo, para eliminarla del tipo empleado sería: DROP ORDERING FOR empleado RESTRICT No podremos eliminar la función de comparación, si existe algún predicado que compare instancias del tipo empleado en: • métodos , Funciones o procedimientos • Vistas • Constraint 57 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto • Assertion • Trigger 4.2.1.7 Conversión de UDT (CAST definidos por el usuario) SQL siempre ha sido un lenguaje fuertemente tipado. Esto significa que no podemos mezclar tipos arbitrariamente en expresiones de comparación o en asignaciones, sino que debemos hacer una conversión explícita de tipos con la sentencia CAST. Para los tipos primitivos (ej.: INTEGER, BOLEAN, CHAR, etc), existen funciones CAST predefinidas. Por ejemplo, si queremos asignar un valor INTEGER a una variable SMALLINT dentro de una función sería: ... DECLARE a INTEGER, b SMALLINT; SET b = CAST (a AS SMALLINT); ... Para los tipos definidos por el usuario, no existen funciones CAST por defecto. Para solucionar esto, SQL-1999 permite al usuario definir funciones CAST para sus tipos con la sentencia CREATE CAST (ver A.2.6). Por ejemplo, consideremos que tenemos dos tipos estructurados, uno llamado T1 y otro T2, y queremos implementar un CAST para convertir valores de T1 a T2. Lo primero que tenemos que hacer, es crear una función con un único parámetro del tipo T1, cuyo valor de retorno sea del tipo T2, es decir: CREATE FUNCTION conver(a T1) RETURNS T2 BEGIN ... RETURN ... END; Y luego definimos el CAST de la siguiente manera: CREATE CAST(T1 AS T2) WITH FUNCTION convert(T1) AS ASSIGNMENT La cláusula AS ASSIGNMENT es opcional, y permite que la función de conversión sea llamada implícitamente en las asignaciones. Por ejemplo: ... DECLARE a T1, b T2; SET b = a; ... Si omitimos esta cláusula, tendremos que llamar explícitamente al CAST en las asignaciones. Por ejemplo: ... 58 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto DECLARE a T1, b T2; SET b = CAST (a AS T2); ... 4.2.1.7.1 Eliminación de CAST definidos por el usuario Para eliminar un CAST definido por el usuario existe la sentencia DROP CAST ( ver A.2.7). Por ejemplo: DROP CAST (T1 AS T2) RESTRICT No podremos eliminar el CAST definido por el usuario, si es utilizado en: • métodos , Funciones o procedimientos • Vistas • Constraint • Assertion • Trigger 4.2.2 Colecciones Una colección es una estructura que permite almacenar cero o más elementos de distinto o del mismo tipo de datos [26]. El término colección es genérico, y comprende varios tipos de colecciones como: arreglos, conjuntos, árboles, etc. El estándar SQL:1999 solamente soporta los arreglos. 4.2.2.1 Arreglos Un arreglo (array) es un conjunto de elementos del mismo tipo, donde cada elemento está en una posición determinada [31]. Al definir un arreglo, se especifica la cantidad máxima de elementos que puede tener (m en adelante). Las posiciones del arreglo comienzan a partir de 1 (uno), hasta n, siendo n el número de elementos que contiene el arreglo, que debe estar en el rango 1≤n≤m. Un arreglo, es un tipo de datos, que lo utilizamos de igual modo que un tipo primitivo. Por ejemplo, podemos crear un tipo polígono, que contenga un arreglo para sus vértices (del tipo punto), y los métodos de area(), y perímetro(), de la siguiente manera: 59 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto CREATE TYPE poligono ( vertices punto ARRAY [200] )INSTANTIABLE NOT FINAL METHOD Area() RETURN FLOAT, METHOD Perimetro() RETURN FLOAT Si queremos acceder al segundo vértice del poligono p, se escribe: ...p.vertices[2]. Podemos asignar varios elementos en un arreglo, simultáneamente. Por ejemplo, supongamos que hemos definido un arreglo notas, y quisiéramos asignarle las calificaciones: 100, 80 y 45 de una sola vez, esto sería: ... set Notas = ARRAY[100,80,45]; ... También existe un operador de concatenación (||), que nos permitirá construir un nuevo arreglo, a partir de dos o más. Para el mismo ejemplo anterior, pensemos que queremos agregar las notas al final del arreglo, esto sería: ... set Notas = Notas || ARRAY[100,80,45]; ... Podemos definir un arreglo a partir de cualquier tipo de datos, sea primitivo o no, siempre y cuando, no contenga o no sea un arreglo. Dos arreglos (C y D) son iguales (C = D), si ambos tienen el mismo número de elementos y C[i]=D[i], para i=1 hasta n 4.2.3 Encapsulamiento Como vimos en la sección 2.2.3, el encapsulamiento abarca tres aspectos: • Agrupar atributos y métodos en una sola entidad. • Distinguir entre interfaz(pública) e implementación (privada) • Asignar niveles de acceso individual a los atributos y métodos. El primer aspecto, es cubierto con el predicado CREATE TYPE. Esta es la parte pública del método. 60 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Con el predicado CREATE METHOD es cubierto el segundo punto. Gracias a esta división (publica y privada) podemos realizar cambios a la implementación de los métodos, sin que estos afecten a las aplicaciones que los utilizan. El tercer punto es cubierto medianamente por las funciones observadoras y mutadoras (ver 4.2.1.2 ) definidas por el sistema, puesto que estas impiden que los usuarios accedan a los atributos directamente. Lamentablemente, no podemos sobrescribir dichas funciones. Lo que falta para que el soporte al encapsulamiento sea completo, es la posibilidad de definir niveles de acceso a los atributos y métodos, como public, private y protected de Java. Sin embargo, el modelo de seguridad de SQL-1999, permite obtener una funcionalidad similar, pero no completa de lo anterior. Con respecto a los métodos, sólo los usuarios que tenga el privilegio EXECUTE podrán ejecutar un método, tanto para objetos persistentes como transitorios. Para otorgar el privilegio EXECUTE utilizamos la sentencia GRANT. Por ejemplo: GRANT EXECUTE ON METHOD sueldo_liquido() FOR empleado TO PUBLIC Podemos ejecutar métodos dentro de sentencias SELECT. Para ello debemos otorgar el permiso correspondiente. Por ejemplo: GRANT SELECT (METHOD sueldo_liquido() FOR empleado) ON TABLE tbl_emp TO PUBLIC Con respecto a los atributos, sólo podremos restringir el acceso a ellos sobre objetos persistentes, para los objetos transitorios, todos sus atributos serán públicos. Por defecto, un usuario no puede acceder a las filas de una tabla. Existen distintas sentencias que manipulan una tabla, para cada una de ellas existe un privilegio que debe ser otorgado. Por ejemplo: GRANT SELECT (rut,nombre,sueldo) ON TABLE tbl_emp TO PUBLIC GRANT INSERT (rut,nombre,sueldo) ON TABLE tbl_emp TO PUBLIC GRANT UPDATE (rut,nombre) ON TABLE tbl_emp TO PUBLIC GRANT DELETE ON TABLE tbl_emp TO PUBLIC 61 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto También podemos restringir el acceso de los atributos, creando vistas sobre las tablas. 4.2.4 Modularidad SQL:1999 cuenta con la noción de módulos, pero estos sólo proveen modularidad para las funciones y procedimientos almacenados, pero no para tipos. Sin embargo, podemos agrupar tipos basándonos en los esquemas de SQL, logrando así una funcionalidad similar. 4.2.5 Persistencia Para hacer persistir los objetos tenemos dos alternativas: • Almacenarlos como columnas en tablas relacionales • Almacenarlos en una tabla especial denominada typed table, donde cada fila es un objeto y cada columna un atributo del mismo. En las siguientes secciones veremos el manejo de tablas que almacenen objetos, tanto en columnas como en fila. Además de cómo trabajar con dichas instancias. 4.2.5.1 Manejo de Tablas El estándar incorpora varias características a las tablas relacionales. Además de permitir almacenar objetos como columnas, define tres tipos especiales de tablas: typed tables, subtablas y supertablas. En la presente sección, describiremos como definir dichas tablas. 4.2.5.1.1 Creación (CREATE TABLE) Como mencionamos anteriormente, podemos almacenar objetos como columnas de tablas relacionales. Por ejemplo, Consideremos la siguiente definición de un tipo direccion. 62 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto CREATE TYPE Direccion AS ( calle CHAR(40), numero CHAR(10), ciudad CHAR(30), region CHAR(30) )NOT FINAL Ahora, podemos crear una tabla relacional con una columna del tipo dirección. Como ejemplo, consideremos la definición de una tabla de personas de la siguiente manera: CREATE TABLE persona ( rut CHAR(12) PRIMARY KEY, nombre CHAR(40), fecha_nacim DATE, domicilio direccion ) Como podemos observar, es bastante sencillo definir columnas basadas en tipos estructurados. Los objetos almacenados como columnas no pueden ser referenciados por no poseer un OID. Solamente los objetos almacenados en una typed table pueden ser referenciados. Una typed table, es una tabla o vista construida en base a un tipo estructurado definido por el usuario. Para cada atributo del tipo base, es creada una columna con el mismo nombre y el mismo tipo de datos. Además de estas, se agrega otra, para almacenar el identificador de objeto. Esta columna tiene las restricciones UNIQUE y NOT NULL implícitas. Para ejemplificarlo consideremos el tipo empleado utilizado en secciones anteriores: CREATE TYPE empleado AS ( rut CHAR(12), nombre CHAR(40), sueldoBase INTEGER , anticipo INTEGER DEFAULT 0, fecha_ing DATE DEFAULT CURRENT_DATE, domicilio direccion )INSTANTIABLE NOT FINAL REF IS SYSTEM GENERATED METHOD SueldoLiquido() RETURNS INTEGER 63 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Para crear una typed table llamada tbl_emp basada en el tipo empleado sería: CREATE TABLE tbl_emp OF empleado (REF IS oid SYSTEM GENERATED) oid, es el nombre de la columna adicional que almacenará el identificador de objeto. identificador El nombre es puesto por el usuario y puede ser cualquier válido de columna. El tipo de datos de oid es REF(empleado), este no puede ser asignado por el usuario. Dependiendo de la forma que el tipo base de la tabla maneje las referencias, (SYSTEM GENERATED, USER GENERATED y DERIVED) se especificará: • REF IS oid SYSTEM GENERATED • REF IS oid USER GENERATED • REF IS oid DERIVED El valor que asumirá la columna oid, será asignado al insertar el objeto en la tabla, y no podrá ser modificado posteriormente. Si especificamos SYSTEM GENERATED o DERIVED, el valor de oid será definido automáticamente al momento de la inserción. En cambio, si es USER GENERATED, será entregado por el usuario. Si queremos hacer persistir objetos que tengan un supertipo (ver 4.2.6.3 ), tenemos la posibilidad de definir una estructura super tabla/subtabla. Por ejemplo, consideremos el tipo vendedor que es un subtipo de empleado definido de la siguiente manera: CREATE TYPE vendedor UNDER empleado AS ( comision FLOAT, viatico INTEGER )INSTANTIABLE NOT FINAL OVERRIDING METHOD SueldoLiquido() RETURNS INTEGER Ahora, podemos crear una subtabla de tbl_emp que almacene instancias del tipo vendedor. Para ello escribimos: CREATE TABLE tbl_vendedor OF vendedor UNDER tbl_emp 64 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Para que esta sentencia sea válida, tbl_emp, debe ser una typed table cuyo tipo de datos sea el supertipo directo de vendedor. Si observamos la definición hecha de tbl_emp anteriormente, veremos que estas condiciones se cumplen. En las subtablas, no podemos especificar la cláusula <self-referencing column specification>, esta es heredada de la supertabla. Las columnas de la supertabla, no son heredadas físicamente en la subtabla, pero si referenciadas. Como toda instancia de un subtipo, lo es también de su supertipo, cada objeto almacenado en una subtabla, también es almacenado en la supertabla. Debido a esto, físicamente, los valores de los atributos heredados de una instancia, serán almacenados en la supertabla y los valores más específicos del tipo, serán almacenados en la subtabla. Como es el mismo objeto almacenado en la supertabla y en la subtabla, poseerá el mismo OID en ambas. Es por esto que la forma de manejar el OID debe concordar en toda la Jerarquía de tablas. Cuando necesitamos recuperar un objeto en particular, el sistema se encarga de recopilar los valores desde las distintas tablas, automáticamente. La definición de estructuras jerárquicas de tablas, permite minimizar el impacto al extender las aplicaciones. Al definimos tablas relacionales, podemos especificar restricciones como NOT NULL, CHECK, PRIMARY KEY, sobre ellas. En las typed tables, estas restricciones también son permitidas, a excepción de PRIMARY KEY. Por ejemplo, si queremos restringir el atributo comisión a valores entre 0 y 100, seria: CREATE TABLE tbl_vendedor OF vendedor UNDER tbl_emp (CONSTRAINT com CHECK (comision BETWEEN 0 AND 100)) 4.2.5.1.2 Modificación (ALTER TABLE) 65 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Para modificar una tabla utilizamos el predicado ALTER TABLE (A.2.13). Esta sentencia nos permite: • Agregar y Eliminar columnas. • Agregar y elimina cláusula DEFAULT. • Agregar y eliminar cláusula SCOPE. • Agregar y eliminar restricciones (CONSTRAINT). Para la eliminación de elementos podemos especificar la cláusula CASCADE, que indica al sistema que, además de eliminar el elemento, elimine todas las dependencias existentes; o la cláusula RESTRIC, que impide el borrado del elemento al haber dependencias. Por ejemplo, si queremos eliminar la restricción com de la tabla tbl_vendedor sería: ALTER TABLE tbl_vendedor DROP CONSTRAINT com CASCADE Existen limitaciones a la hora de utilizar esta sentencia. Estas son: • No podremos agregar o eliminar columnas a una typed table • No podremos agregar o eliminar las cláusulas DEFAULT o SCOPE a una columna heredada en una subtabla. • No podremos agregar o eliminar las cláusulas DEFAULT o SCOPE a la columna que almacena el identificador de objeto. 4.2.5.1.3 Eliminación (DROP TABLE) Para eliminar una tabla utilizamos el predicado DROP TABLE (ver A.2.14). Cuando queremos eliminar una tabla, debemos indicarle al sistema qué hacer con las dependencias que existan de ella, como pueden ser, subtablas, triggers, vistas, etc. Para ello tenemos dos predicados: • CASCADE, que indica que se borren todas las dependencias de la tabla. • RESTRICT, impide que se borre la tabla si existen dependencias. La sintaxis es bastante sencilla. Por ejemplo, si queremos eliminar la tabla tbl_emp, sería: DROP TABLE tbl_emp CASCADE O bien: DROP TABLE tbl_emp RESTRICT 66 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto 4.2.5.2 Manejo de Instancias Para manipular las instancias que fueron almacenadas en una tabla, utilizamos las mismas sentencias del manejo de filas y columnas en tablas relacionales. Las operaciones sobre las instancias las podemos agrupar en: Inserción, selección, eliminación y modificación de objetos. Estas operaciones serán descritas en las secciones siguientes. 4.2.5.2.1 Inserción de objetos Para insertar un objeto, tanto en tablas relacionales como en typed table utilizamos la sentencia INSERT INTO (ver A.2.15). Veamos primero cómo insertar un objeto como columna de una tabla relacional. Para ello, consideremos las siguientes definiciones: CREATE TYPE direccion AS ( calle CHAR(40), numero CHAR(10), ciudad CHAR(30) )NOT FINAL METHOD direccion(ca CHAR(40),nu CHAR(10),ciu CHAR(30)) RETURNS direccion CREATE TABLE persona ( rut CHAR(12) PRIMARY KEY, nombre CHAR(40), domicilio direccion ) La inserción de una fila en la tabla persona sería: INSERT INTO persona VALUES(‘13131344-6’,’Miguel Romero’, NEW direccion(‘Emmanuel, pasaje san Damián’, ’1661’,’Chillán’) ) Mediante el operador NEW creamos un nuevo objeto del tipo direccion, el cual será asignado a la columna domicilio de la tabla persona. También podremos utilizar el constructor del tipo direccion, el que creará un objeto con los valores por defecto. Esta inserción sería: 67 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto INSERT INTO persona VALUES(‘13131344-6’,’Miguel Romero’,direccion()) La sintaxis para insertar un objeto como fila es muy similar. Por ejemplo, consideremos un tipo cliente y una tabla de clientes (tbl_cli), definidos de la siguiente manera: CREATE TYPE cliente AS ( rut CHAR(12), nombre CHAR(40), domicilio direccion )INSTANTIABLE NOT FINAL REF IS SYSTEM GENERATED CREATE TABLE tbl_cli OF cliente (REF IS oid SYSTEM GENERATED) Para insertar un cliente en esta tabla sería: INSERT INTO tbl_cli VALUES(‘13131344-6’,’Miguel Romero’, NEW direccion(‘Emmanuel, pasaje san Damián’, ’1661’,’Chillán’) ) al insertar este objeto, automáticamente se genera el valor para la columna OID, puesto que esta columna fue definida como SYSTEM GENERATED. Al momento de insertar, tenemos la posibilidad de sobrescribir el valor que el sistema asigna automáticamente como OID. Por ejemplo INSERT INTO tbl_cli (oid,rut,nombre,domicilio) OVERRIDING SYSTEM VALUE VALUES(145,‘13131344-6’,’Miguel Romero’, NEW dirección(‘Emmanuel, pasaje san Damián’, ’1661’,’Chillán’) ) Si al definir la tabla, especificamos que la columna es generada por el usuario, obligatoriamente tendremos que entregar el valor para la columna OID. Por ejemplo, consideremos la cláusula REF del tipo cliente como: ... REF USING NUMERIC(8) y la definición de la tabla como: CREATE TABLE tbl_cli OF cliente (REF IS oid USER GENERATED) 68 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto La inserción de un cliente sería: INSERT INTO tbl_cli (oid,rut,nombre,domicilio) VALUES(13131344,‘13131344-6’,’Miguel Romero’, NEW dirección(‘Emmanuel, pasaje san Damián’, ’1661’,’Chillán’) ) Para insertar un objeto en una typed table, el tipo estructurado base de la tabla, debe ser instanciable (INSTANTIABLE). Cuando insertamos un objeto en una typed table que posea una supertabla, automáticamente se insertará la fila correspondiente en la supertabla, con los valores para los atributos heredados. Para ello, será necesario que asignemos los valores tanto para los atributos heredados como para los más específicos, dentro del predicado INSERT. Por ejemplo, para insertar un vendedor en la tabla tbl_vendedor definida en la sección 4.2.5.1.1 sería: INSERT INTO tbl_vendedor (rut,nombre,SueldoBase, Anticipo, domicilio, comision, viatico) VALUES(’11.111.111-1’,’Luis Perez’,150000,10000, direccion(),12,35000) Si se excluyen valores para alguno de los atributos, estos asumirán el valor por defecto. Eso es lo que pasa en el ejemplo anterior con el atributo fecha_ing, que no fue especificado. 4.2.5.2.2 Selección de objetos (SELECT) La sentencia SELECT, nos permite obtener uno o más objetos almacenados en tablas, tanto relacionales como typed table. Por ejemplo, consideremos la tabla de persona definida en la sección anterior (ver 4.2.5.2.1 ), supongamos que queremos obtener el nombre y el domicilio de todas las personas que viven en Chillán, esto sería: SELECT nombre, domicilio.calle, domicilio.numero FROM persona WHERE domicilio.ciudad=’Chillán’ Como puede observar, podemos utilizar el atributo de un objeto, de la misma manera que las columnas de las tablas relacionales. pasa con los métodos. Lo mismo Por ejemplo, consideremos la tabla tbl_emp 69 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto definida en la sección 4.2.5.1.1 donde el tipo base es empleado. Si queremos obtener el RUT, el nombre y el sueldo líquido de todos los empleados que tengan sueldos superiores a $250.000, sería: SELECT e.rut,e.nombre,e.SueldoLiquido() FROM tbl_emp e WHERE e.SueldoLiquido() > 250000 Si aplicamos la sentencia SELECT sobre una typed table que posea subtablas, la búsqueda de filas se extenderá a todas las subtablas que posea. Por ejemplo, consideremos la tabla tbl_vendedor que es una subtabla de tbl_emp y cuyo tipo base es vendedor (vea 4.2.5.1.1 ). Para obtener a todos los empleados de la empresa, incluyendo a los vendedores, sería: SELECT e.rut,e.nombre,e.SueldoLiquido() FROM tbl_emp Gracias al polimorfismo (ver 4.2.6.3 ), la ejecución del método SueldoLiquido(), dependerá del tipo concreto del objeto ya sea empleado o vendedor. Para el ejemplo anterior, esta característica es fundamental, pues el cálculo del sueldo líquido de un vendedor, es diferente al de un empleado. Para excluir de la búsqueda a las subtablas de tbl_emp, utilizamos la cláusula ONLY, de la siguiente manera: SELECT * FROM ONLY(tbl_emp) También podemos restringir la búsqueda a la tabla tbl_vendedor, de la siguiente manera: SELECT * FROM tbl_emp WHERE DEREF(oid) IS OF(tbl_vendedor) Cuando tenemos un atributo o una columna que es una referencia a un objeto, podemos acceder a los atributos y métodos del objeto al que referencia, utilizando el operador ->. Por ejemplo, consideremos los siguientes tipos y sus respectivas tablas: CREATE TYPE punto AS ( X REAL, Y REAL 70 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto )INSTANTIABLE NOT FINAL REF IS SYSTEM GENERATED INSTANCE METHOD distanciaA(IN p Punto) RETURNS FLOAT CREATE TABLE tbl_pto OF punto (REF IS oid SYSTEM GENERATED) CREATE TYPE linea_recta AS ( A REF punto, B REF punto )INSTANTIABLE NOT FINAL REF IS SYSTEM GENERATED CREATE TABLE tbl_lineas OF linea_recta (REF IS oid SYSTEM GENERATED) Si queremos obtener las coordenadas de los puntos A y B de todas las líneas que tengan un largo mayor a 100, sería: SELECT L.A->X, L.A->Y, L.B->X, L.B->Y FROM tbl_lineas L WHERE L.A->distanciaA(B) > 100 El uso de referencias simplifica el manejo de consultas, puesto que disminuye la necesidad de utilizar JOIN. 4.2.5.2.3 Modificación de objetos Para modificar uno o más objetos, utilizamos el predicado UPDATE (ver A.2.16). Por ejemplo, consideremos la typed table tbl_emp definida en la sección 4.2.5.1.1 donde el tipo base es empleado; si queremos aumentar el sueldo en $25.000 a todos los empleados que tengan sueldos entre $100.000 y $ 200.000, sería: UPDATE tbl_emp SET SueldoBase = SueldoBase + 25000 WHERE SueldoBase BETWEEN 100000 AND 200000 Si aplicamos el predicado UPDATE sobre una supertabla, los cambios también se aplicarán a todos los objetos de las subtablas que cumplan con la condición definida en la cláusula WHERE Para evitar esto, utilizamos el predicado ONLY de la siguiente manera: UPDATE ONLY(tbl_emp) SET SueldoBase = SueldoBase + 25000 WHERE SueldoBase BETWEEN 100000 AND 200000 71 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Al igual que en la sentencia SELECT, en UPDATE, podemos restringir las tablas que serán utilizadas. Si en el ejemplo anterior, quisiéramos que UPDATE sólo afectara a la tabla tbl_vendedor sería: UPDATE tbl_emp SET SueldoBase = SueldoBase + 25000 WHERE DEREF(oid) IS OF(tbl_vendedor) AND SueldoBase BETWEEN 100000 AND 200000 Como toda fila de la tabla tbl_vendedor posee una fila correspondiente a sus atributos heredados en la tabla tbl_emp, el sistema ajustará automáticamente la fila correspondiente a la supertabla cuando utilicemos el predicado UPDATE sobre la tabla tbl_vendedor. Por ejemplo: UPDATE tbl_vendedor SET SueldoBase = SueldoBase + 25000 WHERE SueldoBase BETWEEN 100000 AND 200000 Si el tipo base de la tabla, posee un atributo que es un arreglo, podemos actualizarlo de dos maneras. La primera, tomando los elementos individualmente, por ejemplo, consideremos que el tipo base posee un atributo llamado arr que es un arreglo de 5 enteros: UPDATE tabla_de_arreglos SET arr[5] = 23, arr[2] = 12 WHERE arr[1]=0 Y la segunda, asignando un arreglo completo, por ejemplo: UPDATE tabla_de_arreglos SET arr = ARRAY[12,34,56,78,90] WHERE arr[1]=0 4.2.5.2.4 Eliminación de objetos Para eliminar uno o más objetos utilizamos el predicado DELETE A.2.17). (ver Este predicado borrará todas las filas que cumplan con la condición indicada por la cláusula WHERE, tanto en tablas relacionales como en typed tables. Por ejemplo, consideremos la tabla tbl_cli definida en la sección 4.2.5.2.1 Para eliminar a todos los clientes cuyo nombre contenga el string ‘miguel’, sería: DELETE FROM tbl_cli c WHERE c.nombre LIKE ’%miguel%’ 72 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Si ejecutamos la sentencia DELETE sobre una typed table que posea subtablas, también se eliminarán todos los objetos que cumplan con la condición en las subtablas. Por ejemplo, consideremos la tabla tbl_emp definida en la sección 4.2.5.1.1 que posee una subtabla denominada tbl_vendedor, si ejecutamos la sentencia: DELETE FROM tbl_emp e WHERE e.sueldobase < 100 Eliminará a todos los empleados y vendedores que tengan sueldos inferior a 100. Para limitar el borrado a la tabla tbl_emp, utilizamos la cláusula ONLY. Ejemplo: DELETE FROM ONLY(tbl_emp) e WHERE e.sueldobase < 100 Al igual que en UPDATE, podemos restringir las tablas que serán utilizadas por DELETE. Si para el ejemplo anterior, queremos eliminar solamente a los vendedores que cumplan con esa condición sería: DELETE FROM tbl_emp e WHERE DEREF(oid) IS OF(tbl_vendedor) AND e.sueldobase < 100 Al eliminar directamente una fila de una subtabla, automáticamente se eliminará la fila correspondiente en la supertabla. Por ejemplo: DELETE FROM tbl_vendedor v WHERE v.sueldobase < 100 4.2.6 Jerarquía de Clases 4.2.6.1 Asociación Como mencionamos en la sección 2.2.5.2 , el concepto de asociación es similar al de relación en el modelo entidad relación. Por eso, podemos utilizar las mismas estrategias para su implementación, pero con la ventaja de la existencia de las referencias de objeto, que son más eficientes que el manejo de clave foránea. Por ejemplo, consideremos la siguiente asociación: Vende Vendedor 1 Cliente * 73 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Para implementarla, agregaremos un atributo en el tipo cliente que sea una referencia al tipo vendedor. La implementación de los tipos quedaría: CREATE TYPE vendedor AS ( rut CHAR(12), nombre CHAR(40), sueldo NUMBER, comision NUMBER, domicilio direccion )INSTANTIABLE NOT FINAL CREATE TYPE cliente AS ( rut CHAR(12), nombre CHAR(40), telefono CHAR(20), domicilio dirección, vendedorAsignado REF(vendedor) )INSTANTIABLE NOT FINAL Otra alternativa es agregar un atributo clientes al tipo vendedor, que sea un arreglo de referencias a sus clientes. El único inconveniente es que debemos definir un largo máximo al arreglo. Lo anterior sería: CREATE TYPE vendedor AS ( rut CHAR(12), nombre CHAR(40), sueldo NUMBER, comision NUMBER, domicilio direccion, clientes REF(cliente) ARR [100] )INSTANTIABLE NOT FINAL Si la multiplicidad de la asociación es uno a uno, cualquiera de los tipos puede tener la referencia al otro. Si la multiplicidad de la asociación es Muchos a muchos, o la asociación en si misma tenga atributos, será necesario crear un tipo para implementarla. Por ejemplo, supongamos que la asociación entre un vendedor y un cliente fuera: vende Vendedor * Cliente * Cartera_de_clientes 74 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto La implementación de estos tres tipos sería: CREATE TYPE vendedor AS ( rut CHAR(12), nombre CHAR(40), sueldo NUMBER, comision NUMBER, domicilio direccion )INSTANTIABLE NOT FINAL CREATE TYPE cliente AS ( rut CHAR(12), nombre CHAR(40), telefono CHAR(20), domicilio direccion )INSTANTIABLE NOT FINAL CREATE TYPE cartera_de_clientes AS ( vendedorAsignado REF(vendedor), cliente REF(cliente) )INSTANTIABLE NOT FINAL 4.2.6.2 Agregación y Composición La implementación de la agregación es muy similar a la asociación. Pero se diferencia por existir una relación todo-parte, donde desde el todo queremos acceder a sus partes. En la Agregación, el todo no contiene físicamente a las partes, por esta razón, la implementación es a través de referencias de objetos. Consideremos por ejemplo la relación de agregación entre una carrera y sus asignaturas, donde una carrera puede tener muchas asignaturas y una asignatura puede pertenecer a varias carreras. La implementación sería: CREATE TYPE carrera AS ( codigo CHAR(12), nombre CHAR(40), Asignaturas REF(asignatura) ARR [100] )INSTANTIABLE NOT FINAL CREATE TYPE asignatura AS ( codigo CHAR(12), nombre CHAR(40), creditos NUMBER )INSTANTIABLE NOT FINAL Si la agregación es uno a uno no necesitaremos un arreglo, bastará con una referencia a la parte. 75 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Como en la composición las partes pertenecen a un único agregado y se destruyen junto con él, la mejor estrategia es implementarlo utilizando los tipos directamente. Por ejemplo, consideremos la relación entre un círculo y un punto, donde un círculo posee un único punto que es su centro. La implementación sería: CREATE TYPE circulo AS ( centro punto, radio NUMBER )INSTANTIABLE NOT FINAL Si la multiplicidad de la composición es uno a muchos, la implementación es por medio de un arreglo. Por ejemplo, consideremos la relación entre un polígono irregular y los puntos (vértices) que lo componen. Esto sería: CREATE TYPE circulo AS ( vertices punto ARR [100] )INSTANTIABLE NOT FINAL 4.2.6.3 Herencia y Polimorfismo SQL:1999 permite la herencia simple de tipos, es decir, un tipo puede poseer un único supertipo. La sintaxis es bastante sencilla (ver A.2.1), para entenderla mejor, veamos el siguiente ejemplo. Consideremos un tipo llamado figura_geométrica, con la siguiente definición: CREATE TYPE figura_geometricas AS ( color_linea CHAR(10), Color_fondo CHAR(10) )NOT FINAL METHOD area() RETURNS REAL METHOD perimetro() RETURNS REAL A partir de este tipo, podemos definir dos subtipo: triángulo y cuadrado, de la siguiente manera: CREATE TYPE punto AS ( X REAL, Y REAL )INSTANTIABLE NOT FINAL REF IS SYSTEM GENERATED INSTANCE METHOD distanciaA(IN p Punto) RETURNS FLOAT CREATE TYPE triangulo UNDER figura_geometrica AS ( a punto, 76 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto b punto, c punto ) INSTANTIABLE NOT FINAL OVERRIDING METHOD area() RETURNS REAL OVERRIDING METHOD perimetro() RETURNS REAL CREATE TYPE cuadrado UNDER figura_geometrica AS ( a punto, b punto, c punto, d punto ) INSTANTIABLE NOT FINAL OVERRIDING METHOD area() RETURNS REAL OVERRIDING METHOD perimetro() RETURNS REAL Para indicar el nombre del supertipo, especificamos la cláusula UNDER, seguido del nombre del supertipo correspondiente. Un tipo, no puede ser supertipo de si mismo. Tampoco podemos definir como supertipo a un subtipo del mismo, sea directo o no. Una característica esencial de la herencia es que los atributos y métodos del supertipo pasan a formar parte del subtipo. Por lo anterior, no podemos definir el nombre de un atributo igual a otro que se haya heredado. Con respecto a los métodos, estos pueden ser redefinidos en el subtipo. Esto es lo que indica la cláusula OVERRIDING que precede a la definición de los métodos en los tipos triangulo y cuadrado. Al momento de crear el método, será necesario indicar que el método está siendo sobrescrito. Por ejemplo, una implementación del método area() del tipo cuadrado sería; CREATE OVERRIDING METHOD area() RETURNS REAL FOR cuadrado BEGIN RETURN a.distanciaA(b) * b.distanciaA(c); END; La redefinición y la sobrecarga de métodos son la base del polimorfismo soportado por el estándar. Otra característica de la herencia es que toda instancia de un subtipo, es una instancia de su supertipo directo y también de los indirectos. Así tenemos que una instancia t del tipo triangulo también es una instancia del tipo figura_geométrica. Esto significa que la instancia t puede ser utilizada en cualquier lugar donde se espere una instancia del tipo figura_geométrica. 77 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Por ejemplo, si tenemos una tabla que posea una columna del tipo figura_geométrica: CREATE TABLE figuras ( id NUMBER PRIMARY KEY, fig figura_geometrica ) Podemos insertar una fila donde la columna fig almacene un cuadrado, y otra, donde almacene un triangulo, de la siguiente manera: INSERT INTO figuras VALUES(1,cuadrado()) INSERT INTO figuras VALUES(1,triangulo()) Si hacemos una consulta sobre la tabla figuras y ejecutamos el método area() de la columna fig, el sistema determinará el método correspondiente según el tipo concreto en tiempo de ejecución. SELECT id, fig.area() FROM figuras Además de las columnas, existen otros casos donde podemos sustituir el valor de una instancia del supertipo, por un valor de su subtipo. Por ejemplo, en los parámetros de una función, método o procedimiento. Una excepción a esta característica son las instancias almacenadas como fila en una typed table. Es decir, si tenemos una typed table tbl_fig cuyo tipo base sea figura_geometrica, no podremos insertar en ella instancias que no sean del tipo figura_geometrica. Sin embargo, podemos lograr la misma funcionalidad al definir jerarquías de tablas (ver 4.2.5 ). Al modificar un supertipo con la cláusula ALTER TYPE, estos cambios afectarán a sus subtipos. Por ejemplo, si agregamos un atributo a la clase figuras_geometricas, automáticamente, se agrega el atributo a los subtipos, triangulo y cuadrado. Lo mismo pasa con los métodos. 4.2.7 Concurrencia Como las versiones anteriores, SQL:1999 soporta la concurrencia de usuarios. Desde el punto de vista de la orientación a objetos, podríamos decir que 78 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto varios usuarios pueden manipular un mismo objeto a la vez o que varios objetos pueden ser manipulados por varios usuarios simultanéamele. Otro aspecto de la concurrencia es que los métodos puedan ser concurrentes, ya sea multihilo o multiproceso. Si desarrollamos los métodos con SQL/PSM, no podremos contar con esta característica. Pero si lo implementamos como una rutina externa, podremos utilizar algún lenguaje que soporte concurrencia, como Java o C. 4.2.8 Manejo de Versiones de objetos y configuraciones No soportado en el estándar, pero existe una especificación llamada SQL/Temporal prevista para el 2003 aproximadamente, que se encargará del manejo de bases de datos históricas, en ella debería estar soportada estas característica. 4.2.9 Evolución de esquemas y de Instancias La evolución de esquemas se refiere a la capacidad de modificar la estructura de los tipos, agregando, modificando o eliminando métodos o atributos y que estos cambios sean reflejados en todas las estructuras que dependan de él. La evolución de instancia apunta a dos cosas, la primera es que al alterar un tipo, dicho cambio debe verse reflejado en las instancias y la otra es contar con la posibilidad de migrar instancias entre tipos. La evolución de esquemas está completamente soportada mediante la sentencia ALTER TYPE. La evolución de instancias no es soportada completamente, debido a las restricciones impuestas al predicado ALTER TYPE con respecto a los atributos. Pero utilizando tablas temporales, lo podemos simular La migración de clases no es soportada directamente, pero podemos sacar una copia idéntica de él e insertarla en otra tabla, y eliminar el original. Esto es posible, gracias a la posibilidad de sobrescribir el OID. 79 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto 4.2.10 Transacciones y recuperación ante fallos El estándar implementa un robusto sistema de transacciones, incorporando en esta versión la noción de SAVEPOINT. Con respecto a la recuperación ante fallos, es una materia dependiente de las implementaciones del estándar. 4.2.11 Mecanismos de Autorización Los mecanismos de autorización están basados en las tablas y en los tipos. Se autoriza o restringe el acceso y manipulación de las tablas o tipos, como un todo, pero no se puede restringir el acceso sobre un objeto o una fila. 4.2.12 Compatibilidad con el modelo relacional El estándar ofrece una alta compatibilidad con el modelo relacional, no sólo por soportarlo directamente, sino por permitir el mezclado de tablas relacionales y tablas que almacenan objetos. Además podemos definir vistas de objetos sobre tablas relacionales, permitiendo definir una “capa” de orientación a objeto sobre nuestros datos relacionales. Las vistas de objeto también pueden ser creadas jerárquicamente. Por ejemplo, consideremos la tabla persona: CREATE TABLE persona ( rut CHAR(12) PRIMARY KEY, nombre CHAR(40), fecha_nacim DATE ) Para definir una vista de objetos sobre esta tabla tendremos que definir un tipo que represente a una fila de la tabla persona. Por ejemplo CREATE TYPE tipo_persona AS ( rut CHAR(12), nombre CHAR(40), fecha_nacim DATE )INSTANTIABLE NOT FINAL INSTANCE METHOD edad() RETURNS REAL 80 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto Ahora, podemos crear una vista de objetos llamada persona_view de la siguiente manera: CREATE WIEW persona_view OF tipo_persona REF IS oid SYSTEM GENERATED AS (SELECT * FROM persona) Esto facilita enormemente la migración de bases de datos relacionales a bases de datos orientadas a objeto, puesto que las aplicaciones actuales pueden continuar utilizando la tabla relacional, mientras que las nuevas aplicaciones orientadas a objeto podrán utilizar la vista de objeto, sin que se produzcan inconsistencias. 81 La Orientación a Objeto en SQL:1999 – Soporte a la Orientación a objeto 4.2.13 Limitaciones encontradas en SQL:1999 Al leer este capítulo podrá percatarse que el estándar tiene las siguientes limitaciones: • No podemos sobrecargar las funciones observadoras y mutadoras • No podemos especificar un nivel de encapsulamiento (public, private, protected) sobre los atributos y métodos. • No podemos definir constructores • No podemos definir atributos estáticos • Sólo contamos con los arreglos para definir colecciones. • No podemos definir arreglos de múltiples dimensiones (por ejemplo matrices). • Los tipos de referencia sólo pueden almacenar direcciones de objetos persistentes. • No podemos crear módulos o paquetes de objetos. • No contamos con herencia múltiple. • No está soportado el manejo de versiones y configuraciones de objetos. • No podemos modificar la definición de un atributo ni la interfaz de los métodos. 82 4.3 Evaluación En este capítulo evaluaremos la implementación que hace el estándar SQL:1999 del paradigma de la orientación a objeto. El objetivo de esta evaluación es determinar en qué porcentaje son soportadas las características de la orientación a objeto (ver 2) y los requerimientos para las bases de datos orientadas a objeto (ver 3.1.2.1), por parte del estandar SQL:1999. 4.3.1 Criterios de evaluación Para evaluar objetivamente el paradigma de la orientación a objeto en SQL1999, definimos un conjunto de criterios basados en las características del paradigma de la Orientación a Objeto (ver 2) y en los requerimientos para las bases de datos orientadas a objeto (ver 3.1.2.1). Los criterios de evaluación son: 1. Definición de tipos o clases a. Definición de atributos estáticos b. Definición de atributos de instancia c. Constructores definidos por el usuario d. Métodos estáticos e. Métodos de instancia f. Sobrecarga de métodos g. Definición de colecciones h. Clases abstractas 2. Encapsulamiento a. Atributos y métodos en una sola entidad b. Distinguir entre interfaz e implementación c. Visibilidad de métodos (Ej.: Public, private, protected) d. Visibilidad de atributos (Ej.: Public, private, protected) 3. Modularidad 4. Jerarquía de clases o tipos a. Asociación b. Agregación y Composición 83 Orientadas a Objeto en SQL:1999 – Soporte a la Orientación a objeto c. Herencia Simple d. Herencia Múltiple e. Polimorfismo 5. Concurrencia a. Métodos con múltiples hilos de control b. Concurrencia de usuarios. 6. Persistencia de objetos a. Creación b. Modificación c. Eliminación d. Consulta e. Identificadores de objetos 7. Versiones y configuraciones de objetos 8. Evolución de instancias 9. Evolución de esquemas a. Agregar Atributos b. Agregar Métodos c. Agregar Restricciones sobre objetos persistentes d. Eliminar Atributos e. Eliminar Métodos f. Eliminar Restricciones g. Modificar definición de atributos h. Modificar Interfaz de métodos i. Modificar Implementación de métodos j. Modificar Restricciones k. Deshabilitar y habilitar restricciones 10. Transacciones y recuperación ante fallos 11. Mecanismos de autorización basados en objetos 12. Compatibilidad con el modelo relacional. Cada criterio lo evaluaremos con los siguientes valores: Si la característica: Está presente No está presente, pero se puede simular No está presente Valoración 100% 60% 0% 84 Orientadas a Objeto en SQL:1999 – Soporte a la Orientación a objeto 4.3.2 Matriz de evaluación a. Definición de atributos estáticos Valoración 1. Definición de tipos o clases Final Parcial Características 95,00% 60% b. Definición de atributos de instancia 100% c. Constructores definidos por el usuario 100% d. Métodos estáticos 100% e. Métodos de instancia 100% f. Sobrecarga de métodos 100% g. Definición de colecciones 100% h. Clases abstractas 100% 2. Encapsulamiento 80,00% a. Atributos y métodos en una sola entidad 100% b. Distinguir entre interfaz e 100% implementación c. Visibilidad de métodos (Ej.:Public, 60% private, protected) d. Visibilidad de atributos (Ej.: Public, 60% private, protected) 3. Modularidad 60,00% 4. Jerarquía de clases o tipos 92,00% a. Asociación 100% b. Agregación y Composición 100% c. Herencia Simple 100% d. Herencia Múltiple e. Polimorfismo 60% 100% 5. Concurrencia 100,00% a. Métodos con múltiples hilos de control 100% b. Concurrencia de usuarios. 100% 85 Orientadas a Objeto en SQL:1999 – Soporte a la Orientación a objeto 6. Objetos Persistentes 100,00% a. Creación 100% b. Modificación 100% c. Eliminación 100% d. Consulta 100% e. Identificadores de objetos 100% 7. Versiones y configuraciones de objetos 0,00% 8. Evolución de instancias 60,00% 9. Evolución de esquemas 92,00% a. Agregar Atributos 100% b. Agregar Métodos 100% c. Agregar Restricciones sobre objetos 100% persistentes d. Eliminar Atributos 100% e. Eliminar Métodos 100% f. Eliminar Restricciones 100% g. Modificar definición de atributos 60% h. Modificar Interfaz de métodos 60% i. Modificar Implementación de métodos 100% j. Modificar Restricciones 100% 10. Transacciones y recuperación ante fallos 11. Mecanismos de autorización basados en 100% 60% objetos 12. Compatibilidad con el modelo relacional. Promedio 100% 78,25% 86 5 Oracle 8i y la Orientación a Objeto. 5.1 Introducción Oracle fue el primer servidor de bases de datos relacional, y desde entonces ha sido uno de los líderes de la industria, realizando grandes inversiones en investigación y desarrollo, además, participa activamente en la estandarización del lenguaje SQL. Entre las características más importantes de SABD Oracle 8i destacan [31]: • Entorno cliente / servidor y procesamiento distribuido. • Bases de datos de gran tamaño (Terabyte de datos). • Gran número de usuarios concurrente. • Conectividad con distintos sistemas operativos y distintas máquinas. • Alto desempeño en procesamiento de transacciones. • Alta disponibilidad, puede trabajar 24 horas por día sin una baja de desempeño y algunas operaciones como respaldos se pueden hacer sin “bajar” el servidor. • Disponibilidad controlada. • Se ajusta a los estándares de la industria. • Integridad de la base de datos reforzada. • Portabilidad y compatibilidad. • Capacidad de replica de bases de datos. A partir de la versión 8 de Oracle, comenzaron a incorporar características orientadas a objeto, como definición de tipos, creación de tablas y vistas de objeto, etc. En este capítulo se revisarán y analizarán los aspectos más importantes de Oracle 8i relacionados con la tecnología de objetos incorporada, para finalmente realizar una evaluación objetiva de esta tecnología. Como observación, asumiremos que el lector está familiarizado con SQL 92, especialmente en lo relacionado con la definición, manipulación y consulta de tablas relacionales y, por lo menos, posee una noción sobre los procedimientos almacenados. 88 5.2 Soporte a la Orientación a Objeto Para analizar el soporte de la orientación a objeto en ORACLE, como lo hicimos con SQL:1999, nos basaremos en los conceptos básicos de la orientación a objeto (ver 2) y en los requerimientos para las bases de datos orientadas a objeto (ver 3.1.2.1 ). En lo posible, mantendremos la misma estructura de presentación utilizada al analizar SQL:1999 para facilitar su comparación con Oracle. 5.2.1 Tipos, abstracción y clases Como explicamos en la sección 2.2.6 los conceptos de clase y tipo son distintos, pero muchos lenguajes no hacen dicha distinción e incorporan ambos conceptos bajo un mismo nombre ya sea el de clase o de tipo. En Oracle el nombre empleado es tipo objeto (object type), que es un tipo de datos definido por el usuario, que luego de ser descrito, pasa a formar parte del sistema de tipos del lenguaje, quedando disponible para todos los usuarios que tengan los privilegios de acceso correspondientes. Un tipo objeto implementa una abstracción y por ende está compuesto por [31]: 1. nombre, que lo identifica dentro de la base de datos y debe ser único dentro del esquema en que se está definiendo 2. Atributos, cada uno con un nombre y un tipo de datos, que puede ser un tipo primitivo u otro tipo definido por el usuario 3. métodos. Son funciones o procedimientos escritos en PL/SQL, Java o C. Los métodos escritos en PL/SQL o en Java son almacenados en la base de datos (de la misma manera que un procedimiento almacenado) y los métodos escritos en C son invocados externamente. Describiremos los atributos y métodos con detenimiento más adelante en las secciones 5.2.1.3 y 5.2.1.4 respectivamente. 89 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto 5.2.1.1 Estructura de un tipo objeto Un tipo objeto tiene dos partes: una especificación y un cuerpo. La especificación es la interfaz pública del tipo y en ella se declaran sus atributos y la especificación de sus métodos (sin su implementación). En el cuerpo se especifican e implementan los métodos que aparecieron en la especificación del tipo[32]. El cuerpo es la parte privada del tipo. especificación Interfaz Pública Declaración de Atributos Especificación de Métodos Cuerpo 1 Implementación Privada Cuerpo de los Métodos figura 5: Estructura de un tipo objeto. Esta separación permite modificar los algoritmos que implementan los métodos, sin que esto afecte a los usuarios del tipo (aplicaciones), siempre y cuando no alteremos su interfaz. De este modo, el cuerpo pasa a ser una “caja negra”, donde las aplicaciones clientes sólo conocen la interfaz del método. Como muestra la figura 5, sólo podemos declarar atributos en la especificación del tipo, y éstos se declaran antes que los métodos. No es necesario definir el cuerpo de un tipo cuando este no especifica métodos[32]. 5.2.1.2 Sintaxis para la creación de tipos Para definir la interfaz del tipo se utiliza la sentencia SQL CREATE TYPE Y para el cuerpo CREATE TYPE BODY [41]. No se pueden definir tipos o cuerpos de tipos en bloques PL/SQL, subprogramas o paquetes. Para describir la sintaxis usaremos la siguiente notación: 90 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto • Las palabras en mayúsculas son palabras reservadas • Los paréntesis cuadrados, [ ], indican que su contenido es opcional. • El paréntesis de llave, { }, agrupan elementos. • La barra vertical, |, indica que los elementos son alternativos (Es como un OR exclusivo o XOR). • Los puntos suspensivos, ..., indican repetición. 5.2.1.2.1 CREATE TYPE La sintaxis para el predicado CREATE TYPE es la siguiente [41][32]: CREATE [OR REPLACE] TYPE [schema.]type_name [AUTHID {CURRENT_USER | DEFINER}] {IS | AS} OBJECT ( attribute_name datatype [, attribute_name datatype]... [{MAP | ORDER} MEMBER function_spec,] [{MEMBER | STATIC} subprogram_spec [call_spec] [, {MEMBER | STATIC} subprogram_spec [call_spec]]...] ); Descripción de la sintaxis. OR REPLACE. Cláusula opcional que permite remplazar la definición anterior del tipo (si existe) con la nueva. No se puede reemplazar un tipo si existen tablas, vistas u otros tipos que dependan de él [41]. [schema.]type_name. Es el nombre del tipo, el cual puede ir precedido con el nombre del esquema en el que se quiere crear, separados por un punto. Si no se especifica el esquema, el tipo se crea en el esquema del usuario actual[41]. Estos son ejemplos de nombres válidos: Persona, esquema.persona, miguel.persona_tipo, etc. AUTHID CURRENT_USER o AUTHID DEFINER. Especifica si los métodos se ejecutan con los privilegios y en el esquema del usuario actual(AUTHID CURRENT_USER) o con los privilegios y en el esquema de usuario que definió el tipo (AUTHID DEFINER). Esta cláusula también se aplica al cuerpo del tipo [41]. La cláusula por defecto es AUTHID CURRENT_USER [31]. 91 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto attribute_name. Nombre del atributo que debe ser único dentro del tipo, pero puede repetirse en otros [31]. datatype. Tipo de dato del atributo que puede ser un tipo de dato definido por el usuario o bien un tipo de dato primitivo con la excepción de: ROWID, UROWID, LONG, LONG ROW, NCLOB, NCHAR y NBARCHAR2 [41]. MEMBER. Especifica que el método que se está declarando es del tipo miembro (ver sección 5.2.1.4.2 ), esto significa que su invocación será a partir de un objeto. Por ejemplo, si tenemos un tipo persona con un método miembro edad y un objeto p, para saber la edad de p se escribe: p.edad(). STATIC. Especifica que el método que se está declarando es del tipo Estático (ver sección 5.2.1.4.3 ). Un método estático, a diferencia de un método miembro, se invocan a partir del nombre del tipo, y no de un objeto. Tomando el ejemplo anterior, si el método edad fuera estático se invocaría: persona.edad(). Al no haber objeto de la invocación no podemos acceder a los atributos de este. [{MAP | ORDER} MEMBER function_spec,]. Especifica una función miembro que el sistema emplea para realizar comparaciones de objetos de este tipo. La función de comparación puede ser de dos tipos MAP u ORDER y un tipo puede tener sólo una de ellas[31]. Para mayor información vea la sección 5.2.1.4.4 subprogram_spec. Es la especificación de un procedimiento o de una función que consta de un nombre y de una lista opcional de parámetros. Además de lo anterior, las funciones cuentan con un valor de retorno. El formato para describir funciones es: FUNCTION name [(parameter [IN|OUT|IN OUT] datatype [, parameter [IN|OUT|IN OUT] datatype]…)] RETURN datatype y para procedimientos corresponde a: PROCEDURE name [(parameter [IN|OUT|IN OUT] datatype [, parameter [IN|OUT|IN OUT] datatype]…)] 92 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Donde name es el nombre de la función o procedimiento, parameter es el nombre del parámetro. IN indica que se debe especificar un valor para el argumento al invocar el método. OUT indica que el argumento es usado por el subprograma para devolver un valor al punto de llamada (o invocación del método), una vez terminada la ejecución. IN OUT indica que el argumento es utilizado como IN y OUT a la vez. datatype es el tipo de dato del parámetro RETURN datatype es el tipo de dato de retorno de la función. Al no especificar las cláusulas IN, OUT o IN OUT, el valor por defecto es IN. call_spec. Esta cláusula indica que el método está implementado en java o en C. Call_spec, especifica cómo llamar al método java o función C, y cómo trasladar los parámetros y los valores de retorno. Si para todos los métodos se especifica un call_spec, entonces no es necesario definir un cuerpo del tipo con CREATE TYPE BODY. El formato para el call_spec en el caso que el lenguaje sea java es: LANGUAJE JAVA NAME ’ string ’ En el caso que el lenguaje sea C: LANGUAJE C [NAME name] LIBRARY lib_name [WITH CONTEXT] [PARAMETERS ( parameters )] Para más información sobre métodos implementados con java o C, vea las secciones 5.2.1.5.2 ó 5.2.1.5.3 respectivamente. 93 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto 5.2.1.2.2 CREATE TYPE BODY La sintaxis para el predicado CREATE TYPE BODY es la siguiente: CREATE [OR REPLACE] TYPE BODY [schema.]type_name { IS | AS} {{MAP|ORDER}MEMBER function_spec {IS|AS} {pl/sql_block|call_spec}; |{MEMBER|STATIC}subprogram_spec{IS|AS}{pl/sql_block|call_spec};} [{MEMBER|STATIC}subprogram_spec{IS|AS}{pl/sql_block|call_spec};]... END; Descripción de la sintaxis. OR REPLACE. Funciona de la misma manera que en CREATE TYPE [41]. [schema.]type_name. El nombre del cuerpo debe coincidir con el nombre del tipo para el cual se define el cuerpo. MEMBER, STATIC, MAP, ORDER, function_spec, subprogram_spec. Tienen el mismo significado que en CREATE TYPE, además, estas especificaciones deben coincidir exactamente con las definidas en el predicado CREATE TYPE correspondiente. { pl/sql_block | call_spec }. Indican la manera en que se implementan los métodos. La cláusula pl/sql_block representa un bloque PL/SQL que contiene un conjunto de instrucciones que implementa el método en dicho lenguaje. Para el caso de call_spec, si éste aparece en CREATE TYPE, éste debe estar presente en CREATE TYPE BODY. Si no aparece quedamos libres de escoger el lenguaje de implementación de los métodos, y cambiarlo cuando queramos. Para una mejor comprensión de cómo se crea un tipo objeto, veamos el siguiente ejemplo tomado de [32], que corresponde a la implementación de un tipo para la manipulación de números complejos. Un numero complejo consta de dos partes, una real y otra imaginaria, ambas dentro del rango de los números reales. Además existen una serie de operaciones matemáticas que se pueden aplicar a dichos números. Las implementadas para este tipo son las cuatro operaciones básicas (suma, resta, multiplicación y división). El texto precedido con “- -“ es un comentario. 94 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto CREATE TYPE Complex AS OBJECT ( -- atributos rpart REAL, ipart REAL, -- métodos MEMBER FUNCTION suma (x Complex) RETURN Complex, MEMBER FUNCTION resta (x Complex) RETURN Complex, MEMBER FUNCTION mult (x Complex) RETURN Complex, MEMBER FUNCTION div (x Complex) RETURN Complex ); CREATE TYPE BODY Complex AS MEMBER FUNCTION suma (x Complex) RETURN Complex IS BEGIN RETURN Complex(rpart + x.rpart, ipart + x.ipart); END suma; MEMBER FUNCTION resta (x Complex) RETURN Complex IS BEGIN RETURN Complex(rpart - x.rpart, ipart - x.ipart); END resta; MEMBER FUNCTION mult (x Complex) RETURN Complex IS BEGIN RETURN Complex(rpart * x.rpart - ipart * x.ipart , rpart * x.ipart + ipart * x.rpart ); END mult; MEMBER FUNCTION div (x Complex) RETURN Complex IS z REAL := x.rpart**2 + x.ipart**2; BEGIN RETURN Complex((rpart*x.rpart + ipart*x.ipart)/z, (ipart*x.rpart - rpart*x.ipart)/z); END div; END; Como sugerencia, comparar esta implementación con la mostrada en la sección 4.2.1.3.4 SQL:1999. 5.2.1.3 Atributos Todo tipo objeto debe poseer a lo menos un atributo, y como máximo 1.000. Un atributo consta de un nombre y un tipo de dato, donde el nombre debe ser único dentro del tipo objeto, y el tipo de datos del atributo puede ser otro tipo definido por el usuario o un tipo primitivo, con las excepciones ROWID, UROWID, LONG, LONG ROW, NCLOB, NCHAR y NBARCHAR2. Entre los tipos de datos primitivos existe uno denominado REF, que permite almacenar una referencia a un tipo objeto. Gracias a él podemos definir un tipo objeto recursivo que es aquel donde uno o más de sus atributos tienen 95 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto como tipo de dato una referencia a si mismo. Por ejemplo, consideremos una lista enlazada con dos atributos, uno llamado info para almacenar un número y un atributo next que es una referencia a la lista siguiente. Para construirlo podemos escribir: CREATE OR REPLACE TYPE lista AS OBJECT ( info number, next REF lista ); Una referencia (REF), almacena un puntero lógico a un objeto, lo que nos permite acceder a él directamente. El manejo de las referencias en Oracle, es semejante al de JAVA. Por ejemplo, para obtener el atributo info de la lista siguiente (referenciada por next) a la lista L, seria: ... L.next.info ... Como en muchos lenguajes de programación (Ej.: c, pascal, etc) un tipo no puede tener un atributo del mismo tipo que él. Por ejemplo si intenta crear el tipo lista mostrado anteriormente, pero omitiendo la palabra REF obtendrá un error de compilación. Las siguientes líneas muestran la ejecución de este ejemplo en SQL*PLUS: SQL> create type lista as object ( 2 info number, 3 next lista --esta linea es ilegal. 4 ) 5 / Warning: Type created with compilation errors. SQL> show errors; Errors for TYPE LISTA: LINE/COL -------0/0 3/7 ERROR -----------------------------------------------------PL/SQL: Compilation unit analysis terminated PLS-00318: type "LISTA" is malformed because it is a non-REF mutually recursive type Si está interesado en crear tipos recursivos, le recomendamos la referencia bibliográfica [2], ahí explican cómo crear estructuras de red, árbol, listas enlazadas y de anillo en Oracle 8. Al observar los ejemplos se percatará que si comparamos la definición de atributos con la definición de columnas de una tabla, son muy similares, pero 96 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto existen diferencias notables. Para apreciar las diferencias y semejanzas veremos la siguiente definición de un tipo empleado y de una tabla empleado Creación del tipo empleado CREATE OR REPLACE TYPE ( rut nombre fecha_ingreso salario ); empleado AS OBJECT VARCHAR2(10), VARCHAR2(30), DATE, NUMBER Creación de la tabla empleado CREATE TABLE empleado ( rut VARCHAR2(10) CONSTRAINT pk_emp PRIMARY KEY, nombre VARCHAR2(30) CONSTRAINT nn_ename NOT NULL, fecha_ingreso DATE DEFAULT SYSDATE, sueldo NUMBER CONSTRAINT ck_suel CHECK(sueldo>95000) ); En ambas definiciones (atributos y columnas) especificamos un nombre y un tipo de datos, pero cuando definimos un tipo objeto, no podemos indicar valores iniciales ni imponer restricciones como NOT NULL a un atributo [32]. Estas restricciones podrán ser impuestas cuando definamos una tabla que almacene objetos. Otra restricción que tenemos a la hora de definir un tipo objeto, es que el tipo de datos de un atributo debe existir. En el siguiente ejemplo la primera sentencia CREATE TYPE es ilegal, porque el tipo Departamento todavía no existe: CREATE OR REPLACE TYPE Empleado AS OBJECT ( rut varchar2(9), nombre varchar2(20), dept REF Departamento – - esta linea es ilegal ); CREATE OR REPLACE TYPE Departamento AS OBJECT ( nombre varchar2(20), jefe Empleado ); Una solución podría ser definir primero el tipo Departamento, pero esto provocaría el mismo error al tratar de definir el atributo jefe de un tipo que 97 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto todavía no existe. Este problema se produce porque los dos tipos son mutuamente dependientes, es decir, uno depende del otro a través de una referencia [32]. Para solucionar este problema existe una sentencia CREATE TYPE especial, denominada forward type definition (definición de tipo adelantada) cuya sintaxis es la siguiente: CREATE [OR REPLACE] TYPE [schema.]type_name; El tipo objeto creado con esta sentencia es denominado tipo objeto incompleto, por no contar con atributos o métodos, los que serán definidos más adelante con la sentencia CREATE TYPE normal, dejando de ser incompleto. Siguiendo con el ejemplo anterior, ahora podemos solucionar el problema de la dependencia mutua al definir por adelantado el tipo Departamento: -- definición adelantada del tipo departamento CREATE OR REPLACE TYPE Departamento ; CREATE OR REPLACE TYPE Empleado AS OBJECT ( rut varchar2(9), nombre varchar2(20), dept REF Departamento -- No produce error. ); –- definición normal del tipo departamento CREATE OR REPLACE TYPE Departamento AS OBJECT ( nombre varchar2(20), jefe Empleado ); Es necesario señalar que no es posible definir un atributo a partir de un tipo incompleto, sólo se pueden definir referencias a tipos incompletos. Por ejemplo, si creamos un tipo departamento y luego tratamos de definir un atributo de este tipo, obtendremos un error de compilación. Las siguientes líneas fueron ejecutadas en SQL*PLUS, y ejemplifican lo antes descrito. SQL> create type departamento; 2 / Type created. SQL> create type Empleado as Object 2 ( 3 rut varchar2(9), 4 dep departamento –-esta linea es ilegal. 5 ); 98 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto 6 / Warning: Type created with compilation errors. SQL> show errors; Errors for TYPE EMPLEADO: LINE/COL -------0/0 4/8 ERROR ----------------------------------------------------PL/SQL: Compilation unit analysis terminated PLS-00311: the declaration of "DEPARTAMENTO" is incomplete or malformed Como consecuencia de lo anterior, no podemos tener dos tipos mutuamente dependientes si el atributo dependiente de uno de ellos (o de ambos) no es una referencia al otro tipo. 5.2.1.4 Métodos Los métodos no son más que funciones o procedimientos almacenados que se asocian a un tipo específico, y no pueden ser invocados independientemente. Existen cuatro categorías para los métodos estas son: Constructor, miembro, estático y comparación. 5.2.1.4.1 Constructor Todos los tipos objeto definidos por el usuario poseen un método constructor generado por el sistema, que es una función con el mismo nombre que el tipo. Los parámetros formales de esta función coinciden exactamente con los atributos del tipo, es decir, son declarados en el mismo orden, con el mismo nombre y del mismo tipo de dato que los atributos [32]. El valor de retorno del constructor es un nuevo objeto del mismo tipo al que pertenece el constructor, donde los valores de los atributos fueron inicializados con los valores pasados por parámetro al momento de llamar al constructor [31]. Por ejemplo, consideremos el siguiente tipo: CREATE OR REPLACE TYPE Persona AS OBJECT ( rut VARCHAR2(10), nombre VARCHAR2(30), 99 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto ); Si queremos crear un objeto Persona donde el rut sea 13.131.344-6 y el Nombre Miguel Romero, utilizaremos el método constructor de la siguiente manera ... Persona(‘13131344-6’,’Miguel Romero’) Como el método constructor es una función, sólo puede ser utilizar dentro de una expresión en la que se espera un objeto de ese tipo. Los constructores también pueden ser utilizados en sentencias SQL. Muchos lenguajes como JAVA, permiten la sobrecarga de constructores, esto no es posible en Oracle 8i, y tampoco podemos redefinirlo. 5.2.1.4.2 Miembro (member) Para especificar un método miembro se antepone la palabra reservada MEMBER, y como mencionamos anteriormente, estos son invocados a partir de un objeto. Los métodos miembro son definidos por el usuario al momento de especificar el tipo, pudiendo ser una función o un procedimiento. Por ejemplo, podemos definir un tipo punto para representar las coordenadas (X,Y) con los método moverA (cambia la ubicación del punto) y distanciaA (calcula la distancia a otro punto). El código para la creación del tipo es el siguiente: CREATE OR REPLACE TYPE Punto AS OBJECT ( X number, Y number, MEMBER PROCEDURE moverA(X IN number, Y IN number), MEMBER FUNCTION distanciaA(p IN Punto) RETURN NUMBER ); Hasta este momento, sólo hemos descrito la interfaz del método, falta su implementación. Las siguientes líneas implementan los métodos: CREATE OR REPLACE TYPE BODY Punto AS MEMBER PROCEDURE moverA(X IN number, Y IN number) IS BEGIN SELF.X:=X; SELF.Y:=Y; END moverA; MEMBER FUNCTION distanciaA(p IN Punto) RETURN NUMBER IS 100 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto -- Funcion POWER(m,n) retorna m elevado a n. -- Funcion SQRT(m) retorna la raiz cuadrada de m. -- Este metodo entrega la distancia euclidiana entre el -- objeto de la invocación y el punto p. BEGIN RETURN SQRT(POWER((x - p.x),2) + POWER((y - p.y),2)); END distanciaA; END; El primer parámetro de un método es siempre el objeto de la invocación llamado SELF cuyo tipo de dato es el mismo al que pertenece el método. El argumento SELF esta implícito al declarar un método de este tipo (no es necesario declararlo) [32]. Este parámetro nos permite acceder a los atributos o métodos del objeto de la invocación. El uso del parámetro SELF sólo es obligatorio cuando un método cuenta con un parámetro o una variable con el mismo nombre que un atributo. Por eso, en el método moverA se utiliza el parámetro SELF (ej: SELF.X:=X;), y en el método distanciaA es omitido (ej: POWER((x - p.x),2)). El modo por defecto para el parámetro SELF es IN para el caso de las funciones miembro e IN OUT para el caso de los procedimientos miembro [32]. Esto significa que no podemos actualizar los atributos del objeto en una función pero si en un procedimiento. Si queremos un pasaje de parámetros distinto será necesario declarar explícitamente el parámetro SELF, teniendo en cuenca lo siguiente: • SELF debe ser el primer argumento. • No se puede declarar el modo OUT para SELF. • SELF no puede ser de un tipo diferente al del tipo en el que se declara el método. Por ejemplo: CREATE TYPE Persona as OBJECT ( nombre VARCHAR(40), MEMBER FUNCTION setNombre(SELF IN OUT Persona, Nom IN VARCHAR) return BOOLEAN ); Las funciones miembro pueden ser invocadas desde sentencias SQL (SELECT, INSERT, DELETE, etc) siempre que el modo de todos sus parámetros sea IN, de lo contrario obtendremos el siguiente error: 101 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto SQL> select p.setNombre('Miguel Romero') from per p; select p.setNombre('Miguel Romero') from per p * ERROR at line 1: ORA-06572: Function SETNOMBRE has out arguments Los procedimientos miembro no pueden ser invocados desde sentencias SQL, sólo a través de objetos transitorios desde el lenguaje de programación. 5.2.1.4.3 Estático (Static) Un método estático al igual que un método miembro, puede ser un procedimiento o una función, pero a diferencia de este, no podemos declarar el parámetro SELF, implícita o explícitamente. Debido a esto, en un método estático no podemos acceder a los atributos del tipo, ni referenciar a SELF [32]. El uso más común de los métodos estáticos es crear funciones que hagan las veces de constructor, supliendo así la carencia de constructores definidos por el usuario. Por ejemplo para el tipo Punto descrito en la sección 5.2.1.4.2 el constructor por defecto construye un punto a partir de las coordenadas X e Y (ej.: Punto(3,5) ). Si quisiéramos construir un punto a partir de otro, podríamos hacerlo definiendo un método estático de la siguiente manera: CREATE OR REPLACE TYPE Punto AS OBJECT ( X number, Y number, MEMBER PROCEDURE moverA(X IN number, Y IN number), MEMBER FUNCTION distanciaA(p IN Punto) RETURN NUMBER, STATIC FUNCTION constructor(p IN Punto) RETURN PUNTO ); y luego lo invocaríamos como : ...Punto.constructor(P) siendo P un objeto del tipo Punto. 5.2.1.4.4 Comparación (Map u Order) 102 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Los valores de tipos de datos como REAL o CHAR tienen un orden predefinido (1,2,3 ó A,B,C... etc.), lo que permite su comparación (=, <, >, etc.) [32]. Pero las instancias de un tipo objeto no tienen un orden predefinido. Para poder realizar comparaciones, existen dos tipos de métodos que se definen anteponiendo las palabras reservadas MAP u ORDER. Un tipo objeto puede tener solamente una función de comparación ya sea MAP u ORDER. Un método de comparación del tipo MAP es una función miembro que no posee argumentos y cuyo valor de retorno es de uno de los siguientes tipos de datos escalares: DATE, NUMBER, VARCHAR2, o un tipo ANSI SQL como CHARACTER o REAL [32]. Por ejemplo, consideremos un tipo Racional para representar los números racionales (ej.: ½). CREATE TYPE Racional AS OBJECT ( num INTEGER, den INTEGER, MAP MEMBER FUNCTION convert RETURN REAL ); CREATE TYPE BODY Rational AS MAP MEMBER FUNCTION convert RETURN REAL IS BEGIN RETURN num / den ; END convert; END; Un método del tipo MAP deberá entrega la posición relativa que ocuparía el objeto si se ordenara el universo completo de objetos del mismo tipo. Para el caso anterior es muy conveniente una función de ordenación del tipo map, porque todo número racional puede ser convertido a un número real. Pero no todos los tipos objetos que definamos tendrán esa característica. Para todos los tipos objetos que no puedan ser convertidos a uno de los tipos escalares nombrados anteriormente existe otro tipo de método de comparación llamado ORDER. Un Método de tipo ORDER es una función miembro que tiene obligatoriamente dos parámetros: el parámetro SELF (ver sección 5.2.1.4.2 ), y otro objeto del mismo tipo. La función retorna un valor numérico, que 103 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto puede ser negativo, cero o positivo. Esto significa que el parámetro SELF es menor (negativo), igual (cero) o mayor (positivo) que el otro parámetro. Por ejemplo: CREATE TYPE Producto AS OBJECT ( id NUMBER, nombre VARCHAR2(40), ORDER MEMBER FUNCTION comparTo(p Producto) RETURN NUMBER ); CREATE TYPE BODY Producto AS ORDER MEMBER FUNCTION comparTo(p Producto) RETURN NUMBER BEGIN IF id < p.id THEN RETURN –1; ELSIF id > p.id THEN RETURN 1; ELSE RETURN 0; END IF; END comparTo; END; Si un método del tipo ORDER es llamado con un valor NULL, retorna un NULL. Gracias a los métodos de comparación, tanto MAP u ORDER, el sistema puede evaluar expresiones booleanas como X > Y o hacer comparaciones implícitas en las cláusulas GROUP BY, DISTINCT y ORDER BY. Si no se especifica una función miembro del tipo MAP u ORDER, Oracle no podrá hacer comparaciones en bloques PL/SQL, ni en las sentencias SQL, solamente podrá saber si dos objetos son iguales o no. Cuando definimos funciones miembro del tipo MAP, el sistema puede realizar ciertas optimizaciones. Por esta razón siempre que pueda utilice métodos del tipo MAP. 5.2.1.4.5 Sobrecarga de métodos. La sobrecarga de métodos como explicamos en la sección 2.2.5.1.1 , consiste en tener dos o más métodos con el mismo nombre pero con distintos parámetros formales, ya sea en número, orden, o tipo de dato. 104 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto La sobrecarga es soportada, únicamente, para los métodos miembro y estático. No podemos sobrecargar métodos si sus parámetros formales difieren únicamente en el modo (IN, OUT, IN OUT). Tampoco podemos sobrecargar funciones miembro, que difieran únicamente en el tipo de dato del valor de retorno [32]. Siguiendo con el ejemplo del tipo Punto, sobrecargaremos el método moverA, para que acepte un Punto como argumento. CREATE OR REPLACE TYPE ( X number, Y number, MEMBER PROCEDURE MEMBER PROCEDURE MEMBER FUNCTION ); Punto AS OBJECT moverA(X IN number, Y IN number), moverA(P IN Punto), distanciaA(p IN Punto) RETURN NUMBER 5.2.1.5 Alternativas para la implementación de los métodos Tenemos tres alternativas para implementar los métodos, PL/SQL, JAVA o C. Cada lenguaje tiene ventajas y desventajas comparativas, por esta razón, es importante escoger el que mejor se ajuste a nuestras necesidades, que podrían ser: facilidad de uso, disponibilidad de personal capacitado, portabilidad, compatibilidad con sistemas existentes o heredados [30]. Sin embargo, la elección podría estar estrechamente ligada a la naturaleza de la aplicación y la forma en que trabajará con Oracle. Bajo este punto de vista, considere lo siguiente [30]: • PL/SQL está especialmente diseñado para el procesamiento de transacciones SQL. • C es el lenguaje indicado para los métodos que realizan cálculos intensivos, gracias a su alta eficiencia y la posibilidad de programar a bajo nivel. • JAVA es altamente portable y seguro. Desde el punto de vista del rendimiento, PL/SQL se ejecuta ligeramente mejor que JAVA interpretado [6], pero en Oracle 9i, es posible compilar JAVA a código nativo, dicho código, se ejecutará diez veces más rápido que en PL/SQL. El Código nativo de JAVA posee un rendimiento similar al de los 105 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto métodos escritos con C. Esto se debe a que Oracle, primero pasa el código java a C, y luego lo compila. 5.2.1.5.1 Implementación de los métodos con PL/SQL PL/SQL es el lenguaje por defecto (no se necesita un call_spec) para la implementación de métodos y es el que hemos utilizado para la implementación de los ejemplos. Como debe haber apreciado implementar los métodos en PL/SQL resulta fácil, gracias a que está transparentemente incorporado en las sentencias CREATE TYPE y CREATE TYPE BODY. Otra característica que facilita el trabajo, es que los tipos definidos en SQL pueden ser utilizados directamente en PL/SQL sin la necesidad de importarlos o de declararlos. Además existe una correspondencia exacta de tipos. Ejemplo: CREATE or replace TYPE Empleado AS OBJECT ( rut VARCHAR2(10), nombre VARCHAR2(10), cargo VARCHAR2(9), fechaIng DATE, sueldo NUMBER(9), comision NUMBER(9), anticipo NUMBER(9), MEMBER FUNCTION sueldo_liquido RETURN NUMBER, MEMBER PROCEDURE aumento_sueldo (aumento NUMBER) ); CREATE OR REPLACE TYPE BODY Empleado IS MEMBER FUNCTION sueldo_liquido RETURN NUMBER IS BEGIN RETURN (sueldo + comision)-anticipo; END sueldo_liquido; MEMBER PROCEDURE aumento_sueldo (aumento NUMBER) IS BEGIN sueldo:=sueldo+aumento; END aumento_sueldo; END; 5.2.1.5.2 Implementación de métodos con JAVA Los métodos de un tipo objeto no pueden ser implementados directamente con el lenguaje java. Para esto debemos crear una clase (en java) con sus propios atributos y métodos, cargarla en el servidor, y luego, asociar los métodos del tipo objeto (en Oracle) con los de la clase java 106 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto correspondiente. Dicha asociación se logra mediante las cláusulas AS LANGUAGE JAVA NAME ‘...’, las cuales pueden ser especificadas en la sentencia CREATE TYPE y/o en CREATE TYPE BODY. Consideremos el mismo tipo empleado utilizado como ejemplo en la sección anterior (5.2.1.5.1 , pero asociando sus métodos con los de la clase EmpleadoBody previamente cargada en el servidor: CREATE or replace TYPE Empleado AS OBJECT ( rut VARCHAR2(10), nombre VARCHAR2(10), cargo VARCHAR2(9), fechaIng DATE, sueldo NUMBER(9), comision NUMBER(9), anticipo NUMBER(9), MEMBER FUNCTION sueldo_liquido RETURN NUMBER AS LANGUAGE JAVA NAME 'EmpleadoBody.sueldoLiquido() return java.math.BigDecimal', MEMBER PROCEDURE aumento_sueldo (aumento NUMBER) AS LANGUAGE JAVA NAME 'EmpleadoBody.aumentoSueldo(java.math.BigDecimal)' ); Al indicar cómo llamar al método de la clase java correspondiente, también se especifica el tipo (clase java) de retorno y el de cada uno de los parámetros. No se puede omitir la jerarquía de paquetes del tipo. Otra cosa importante es que los tipos deben ser compatibles con su contraparte de Oracle. Para ver en detalle la compatibilidad de tipos vea la referencia [43]. Cuando invoquemos un método (que no sea estático) sobre una instancia del tipo objeto Empleado, ésta invocará al método correspondiente, sobre una instancia de la clase EmpleadoBody. Para que los métodos se ejecuten correctamente, la instancia de la clase EmpleadoBody debe tener el mismo estado que su contraparte y los cambios de estado deben reflejarse en ambas instancias. Para lograr esto, la clase EmpleadoBody, deberá implementar la interfaz SQLData, la que define los métodos readSQL() y writeSQL() [43]. Estos métodos son invocados automáticamente por el driver JDBC. El método readSQL() permite a la instancia de EmpleadoBody leer los atributos de su contraparte para actualizar sus propios atributos. 107 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto El método writeSQL() permite que la instancia de EmpleadoBody escriba el valor de sus atributos en los de su contraparte. Otro método definido en la interfaz SQLData es getSQLTypeName() que le permite a JDBC conocer el tipo objeto al que representa. Y por último, es necesario agregar una variable de instancia del tipo String que guardará el nombre del tipo al que representa (pasado como parámetro en el método readSQL). Ejemplo: el siguiente código muestra la implementación de la clase EmpleadoBody, que fue creada directamente en el servidor con el predicado CREATE JAVA. CREATE import import import import import import import AND COMPILE JAVA SOURCE NAMED EmpleadoBody as java.sql.*; java.io.*; Oracle.sql.*; Oracle.jdbc.driver.*; Oracle.oracore.*; Oracle.jdbc2.*; java.math.*; public class EmpleadoBody implements SQLData { private String rut; private String nombre; private String cargo; private Date fechaIng; private BigDecimal sueldo; private BigDecimal comision; private BigDecimal anticipo; public BigDecimal sueldoLiquido() { BigDecimal liquido = sueldo; if (comision != null) liquido = liquido.add(comision); if (anticipo != null) liquido = liquido.subtract(anticipo); return liquido; } public void aumentoSueldo(BigDecimal aumento) { sueldo = sueldo.add(aumento); } // Implementacion de la interfaz SQLData String sql_type; public String getSQLTypeName() throws SQLException { return sql_type; } public void readSQL(SQLInput stream, String typeName) throws SQLException { sql_type = typeName; 108 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto rut nombre cargo fechaIng sueldo comision anticipo = = = = = = = stream.readString(); stream.readString(); stream.readString(); stream.readDate(); stream.readBigDecimal(); stream.readBigDecimal(); stream.readBigDecimal(); } public void writeSQL(SQLOutput stream) throws SQLException { stream.writeString(rut); stream.writeString(nombre); stream.writeString(cargo); stream.writeDate(fechaIng); stream.writeBigDecimal(sueldo); stream.writeBigDecimal(comision); stream.writeBigDecimal(anticipo); } } ; También podemos cargar la clase EmpleadoBody desde un archivo, ejecutando la utilidad loadjava, desde el interprete de comandos, de la siguiente manera: >Loadjava –u scott/tiger@zorro:1521:ORCL -v –r –t EmpleadoBody.java El parámetros –u scott/tigre@zorro:1521:ORCL es la url de conexión con la base de datos. El tipo se cargará en el esquema del usuario de la conexión. Para más información sobre la sentencia CREATE JAVA vea la referencia bibliográfica [41], para más información sobre loadjava vea [42]. 5.2.1.5.3 Implementación de métodos con C Implementar los métodos con C es mucho más difícil que con java, porque no existe una correspondencia directa entre tipos de Oracle y C. Otro problema es que los tipos primitivos de Oracle soportan valores nulos, pero los de C no. Para implementar los métodos con C debemos crear una biblioteca (archivo .DLL en Windows o .so en Solaris) que contenga las funciones que más tarde serán llamadas desde Oracle. 109 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Luego se crea un alias en el servidor que almacena la ruta externa de la biblioteca. Por ejemplo: CREATE LIBRARY agelib UNTRUSTED IS ’/tmp/scott1.so’; Y finalmente definimos el tipo y la llamada a la rutina externa de C CREATE OR REPLACE TYPE Person1_typ AS OBJECT ( Name VARCHAR2(30), B_date DATE, MEMBER FUNCTION calcAge_func RETURN NUMBER AS LANGUAGE C NAME "age" LIBRARY agelib WITH CONTEXT PARAMETERS ( CONTEXT, SELF,SELF INDICATOR STRUCT,SELF TDO, RETURN INDICATOR ) ); Para más información vea la referencia bibliográfica [30] y la [44]. 5.2.1.6 Modificación de tipos (ALTER TYPE) El predicado ALTER TYPE permite recompilar la especificación y/o el cuerpo de un tipo objeto, también nos permite agregarle nuevos métodos. Con ALTER TYPE no podemos modificar o eliminar los atributos o métodos existentes, tampoco agregar atributos [41]. La sintaxis para el predicado ALTER TYPE es la siguiente: ALTER TYPE [schema.]type_name {COMPILE [DEBUG] [{SPECIFICATION | BODY } ] | REPLACE AS OBJECT ( attribute_name datatype [, attribute_name datatype]... [{MAP | ORDER} MEMBER function_spec,] [{MEMBER | STATIC} subprogram_spec [call_spec] [, {MEMBER | STATIC} subprogram_spec [call_spec]]...] )} ; Donde: [schema.]type_name, es el nombre del tipo a modificar COMPILE, recompila la especificación y el cuerpo del tipo. Con las cláusulas opcionales SPECIFICATION o BODY se puede indicar que sólo se compile la especificación o el cuerpo. DEBUG, le indica al compilador de PL/SQL que genere y almacene código para ser utilizado con PL/SQL debugger de Oracle. 110 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto REPLACE AS OBJECT( ... ), permite agregar nuevos métodos al tipo que se está alterando. Dentro de los paréntesis se especifica el tipo, donde debemos incluir todos sus atributos y métodos actuales (sin ninguna modificación) y agregar los nuevos métodos. Ejemplos: Para los ejemplos, tomaremos como base el tipo Empleado especificado en la sección 5.2.1.5.1 1. Recompilación del tipo , para depuración: ALTER TYPE Empleado COMPILE DEBUG; 2. Recompilación de la especificación del tipo: ALTER TYPE Empleado COMPILE SPECIFICATION; 3. Recompilación del cuerpo del tipo: ALTER TYPE Empleado COMPILE BODY; 4. Agregando método setAnticipo: ALTER TYPE data_t REPLACE AS OBJECT ( rut VARCHAR2(10), nombre VARCHAR2(10), cargo VARCHAR2(9), fechaIng DATE, sueldo NUMBER(9), comision NUMBER(9), anticipo NUMBER(9), MEMBER FUNCTION sueldo_liquido RETURN NUMBER, MEMBER PROCEDURE aumento_sueldo (aumento NUMBER), MEMBER PROCEDURE setAnticipo(anticipo NUMBER) ); En el ejemplo 4, el texto que no está en negrita, corresponde a los atributos y métodos actuales, que se deben declarar pero no modificar. Para crear el cuerpo para el nuevo método, se debe emplear el predicado CREATE TYPE: CREATE OR REPLACE TYPE BODY Empleado IS MEMBER FUNCTION sueldo_liquido RETURN NUMBER IS BEGIN RETURN (sueldo + comision)-anticipo; END sueldo_liquido; MEMBER PROCEDURE aumento_sueldo (aumento NUMBER) IS BEGIN sueldo:=sueldo+aumento; END aumento_sueldo; 111 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto MEMBER PROCEDURE setAnticipo (anticipo NUMBER) IS BEGIN SELF.anticipo:=anticipo; END setAnticipo; END; Como queremos remplazar la definición anterior del cuerpo, incluimos la cláusula OR REPLACE. Junto con los nuevos métodos agregados con el predicado ALTER TYPE debemos incluir a todos los métodos actuales. Podemos modificar el cuerpo de los métodos actuales sin ninguna restricción, pero no podemos modificar su especificación, porque debe coincidir exactamente con lo descrito en ALTER TYPE. Y en CREATE TYPE. 5.2.1.7 Eliminación de tipos (DROP TYPE y DROP TYPE BODY) Para eliminar la especificación y el cuerpo de un tipo se utiliza la sentencia DROP TYPE, la sintaxis es la siguiente: DROP TYPE [schema.]type_name [FORCE] ; Donde : [schema.]type_name. es el nombre del tipo a eliminar [FORCE], fuerza el borrado del tipo en el caso que existan dependencias. Oracle marca como UNUSED todas las columnas que dependan del tipo que ha sido borrado. Las columnas marcadas como UNUSED son inaccesibles [41]. Para eliminar sólo el cuerpo del tipo existe el predicado DROP TYPE BODY, cuya sintaxis es: DROP TYPE BODY [schema.]type_name ; Donde : [schema.]type_name. es el nombre del cuerpo del tipo que será eliminado. 5.2.2 Tipo Colección 112 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Un tipo colección es otro tipo de dato definido por el usuario, que nos permite almacenar un número indefinido de elementos, todos de un mismo tipo [31]. Un tipo colección puede ser usado para definir: • Una columna en una tabla relacional • Un atributo en un tipo objeto. • Variables, parámetros o tipo de retorno de una función, en PL/SQL. En Oracle, existen dos tipos de colecciones: arreglos (VARRAYs) y tablas anidadas (nested table). Al igual que un tipo objeto, un tipo colección posee un método constructor definido por el sistema, cuyo nombre es el mismo que el de la colección, y los parámetros corresponden a los elementos que contendrá, separados por coma, todos del mismo tipo y que, obviamente, coincide con el tipo que es almacenado por la colección. Este constructor es una función que retorna una nueva colección con estos valores almacenados. Si no se pasan parámetros, se crea una colección vacía, que es diferente de una colección nula. No podemos agregar métodos o atributos a un tipo colección, si lo necesitamos, tendremos que crear un tipo objeto donde uno de sus atributos sea del tipo colección correspondiente, junto con los atributos y métodos que sean necesarios. 5.2.2.1 Métodos de un tipo colección. Además del constructor, el sistema define un conjunto de métodos miembro, que nos permitirán usar las colecciones fácilmente. Estos son [32]: • EXISTS(n), retorna verdadero si el elemento n existe, de lo contrario retorna falso. • COUNT, retorna el número actual de elementos de la colección. • LIMIT, para los varray retorna el número máximo de elementos que puede contener. Para las tablas anidadas retorna null, por no poseer un límite. 113 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto • FIRST y LAST, retornan el primer y el último índice de una colección respectivamente. Al no existir elementos retorna null, si únicamente hay uno, ambos retornan el mismo índice. • PRIOR(n) y NEXT(n), retornan el índice del antecesor (PRIOR) o sucesor (NEXT) al elemento ubicado en la posición n • EXTEND, EXTEND(n), EXTEND(n, i), permiten agrandar el tamaño de una colección. colección. El primero, agrega un elemento null al final de la El segundo, agrega n elementos null al final de la colección. Y el tercero, agrega n copias del elemento de la posición i, al final de la colección. • TRIM, TRIM(n), eliminan elementos desde el final de la colección. TRIM, elimina el último elemento. TRIM(n), elimina n elementos desde el final. • DELETE, DELETE(n), DELETE(m, n), también permite eliminar elementos, pero de una manera distinta. El primero elimina todos los elementos de la colección. El segundo elimina el elemento de la posición n en una tabla anidada. Y el tercero elimina los elementos desde m hasta n de una tabla anidada. Estos métodos no pueden ser invocados desde sentencias SQL, sólo podemos hacerlo desde bloques PL/SQL. Para invocar uno de estos métodos utilizamos la misma notación que en los tipos objeto. 5.2.2.2 VARRAY Un arreglo (array) es un conjunto de elementos del mismo tipo, donde cada elemento está en una posición determinada [31]. En Oracle, los arreglos son de largo variable, por eso son denominados VARRAY. Sin embargo, debemos indicar el máximo número de elementos que podrá contener el arreglo. La sintaxis para un varray es la siguiente [32]: TYPE type_name IS {VARRAY | VARYING ARRAY} (size_limit) OF element_type [NOT NULL]; Donde: Type_name, es el nombre del tipo de dato que el usuario está definiendo. 114 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Size_limit, es un valor (literal) entero positivo, que representa el número máximo de elementos que puede contener el array. Element_type, es cualquier tipo de PL/SQL con la excepción de [32]: • BINARY_INTEGER, PLS_INTEGER • BOOLEAN • BLOB, CLOB • LONG, LONG RAW • NATURAL, NATURALN • NCHAR, NCLOB, NVARCHAR2 • Tipos Objeto con atributos BLOB, CLOB, TABLE o VARRAY • POSITIVE, POSITIVEN • REF CURSOR • SIGNTYPE • STRING • TABLE • VARRAY Es necesario distinguir entre tamaño y límite, donde tamaño es el número de elementos que contiene la colección, y límite que es el número máximo de elementos que puede contener. Cuando creamos un objeto de algún tipo varray, la cantidad de memoria asignada estará en función de su tamaño y no de su límite. Por ejemplo, si quisiéramos crear un arreglo de puntos llamado arr_puntos con un límite de 100 elementos, sería: CREATE OR REPLACE TYPE arr_puntos AS VARRAY(100) OF PUNTO; Para crear un objeto del tipo Arr_puntos, usamos su constructor de la siguiente manera: ...Arr_puntos(Punto(3,4),Punto(5,1)); Esto creará un varray de tamaño dos, que contendrá a los puntos (3,4) y (5,1) en las posiciones 1 y 2, respectivamente. Si necesitamos agregar un nuevo punto a un varray existente tendremos que aumentar el tamaño del arreglo con el método EXTEND, dentro de un bloque PL/SQL. Por ejemplo: ... Polilinea := Arr_puntos(Punto(3,4),Punto(5,1)); 115 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Polilinea.EXTEND; -- agrego un elemento null al final. Polilinea(3):=Punto(8,5); ... Para eliminar el último elemento utilizamos el método TRIM, ... Polilinea.trim; ... No podemos utilizar los métodos delete(n) o delete(m,n) sobre un varray. Si queremos eliminar un elemento intermedio, tendremos que desplazar a la derecha a todos sus sucesores y luego eliminar el último elemento con el método TRIM. Con respecto al almacenamiento de un varray en la base de datos, este será almacenado en línea con la fila propietaria de la colección. Para aquellos varrays donde el tamaño exceda los 4K, el excedente se almacenará fuera de línea. 5.2.2.3 Tablas anidadas Una Tabla anidada es un conjunto de elementos, sin un orden particular, todos del mismo tipo. Ésta posee una única columna cuyo tipo puede ser un tipo primitivo o un tipo objeto. Si es un tipo objeto, la tabla puede ser vista también como una tabla de múltiples columnas, con una columna para cada atributo del tipo objeto. Sintaxis para una tabla anidada es TYPE type_name IS TABLE OF element_type [NOT NULL]; Type_name y element_type, tienen el mismo significado y restricciones que en los varrays. Oracle almacena una tabla anidada sin un orden particular, pero cuando recuperamos una tabla anidada, desde la base de datos, y la almacenamos en una variable de PL/SQL, a cada fila de la tabla se le asigna una posición o subíndice que parte de uno (1). Esto permite acceder a la tabla anidada como si fuera un varray, lo que simplifica su manipulación. Si manejamos la tabla anidada desde PL/SQL podremos acceder a los métodos de la colección, de la misma manera que con un varray. 116 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Ya vimos las semejanzas entre los varray y las tablas anidadas, ahora veremos en qué se diferencian [32]: • Un varray está limitado a un máximo número de elementos, pero las tablas anidadas no. • En un varray no podemos eliminar directamente elementos intermedios, pero en una tabla anidada sí. • Oracle almacena los datos de un varray en línea con la tabla (en la misma tabla), pero una tabla anidada es almacenada fuera de línea, en una tabla generada por el sistema. • Cuando almacenamos en la base de datos un varray, este retiene su orden y subíndices, pero las tablas anidadas no. • Desde SQL, no podemos insertar, modificar o eliminar, directamente, elementos de un varray, pero sí sobre una tabla anidada. • Podemos crear índices sobre tablas anidadas, pero no en un varray. Para más información sobre tablas anidadas vea las referencias bibliográficas [6], [30], [31], [32]. 5.2.3 Encapsulamiento Como puede ver en la sección 2.2.3 el encapsulamiento abarca tres aspectos: 1. Agrupar atributos y métodos en una sola entidad, 2. Distinguir entre interfaz (pública) e implementación (privada), 3. Asignar niveles de acceso individual a los atributos y métodos. Los aspectos 1 y 2 están cubiertos en Oracle 8i, a través de los predicados CREATE TYPE (ver 5.2.1.2.1 ) y CREATE TYPE BODY (ver 5.2.1.2.2 ). Lamentablemente, el tercer aspecto no es soportado directamente, pero a través de las vistas podemos restringir el acceso a los atributos. 117 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto 5.2.4 Modularidad En Oracle existe una estructura denominada modularidad a los programas escritos en PL/SQL. paquete, que brinda Lamentablemente, no podemos incluir un tipo objeto en estos paquetes. Sin embargo, podemos agrupar los tipos objeto a través de los esquemas de usuarios, logrando así la funcionalidad de un paquete. 5.2.5 Persistencia En todas las bases de datos relacionales la estructura encargada de persistir o almacenar información es la tabla. En Oracle podemos persistir objetos en columnas de tablas relacionales, o en filas de una tabla de objetos. Una tabla de objeto, es una clase especial de tabla que es construida en base a un tipo objeto, donde cada fila de la tabla será una instancia de él, y cada atributo del tipo, será una columna de la tabla [30]. Lamentablemente, si está interesado en construir una base de datos distribuida utilizando las capacidades de replica de Oracle, no podrá replicar las columnas de una tabla relacional que almacene objetos. Tampoco podrá replicar una tabla de objetos. Las operaciones básicas sobre tablas son : creación, modificación y eliminación. 5.2.5.1 Manejo de tablas (relacionales y de objetos) Como aclaración, no pretendemos explicar toda la sintaxis, para el manejo de tablas, por ser muy extenso, la intención es dar una idea clara de cómo trabajar con ellas. 5.2.5.1.1 Creación (Create Table) Como mencionamos anteriormente, podemos definir una columna de una tabla relacional en base a un tipo objeto. Por ejemplo: consideremos el tipo punto definido en secciones anteriores como: 118 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto CREATE OR REPLACE TYPE ( X number, Y number, MEMBER PROCEDURE MEMBER PROCEDURE MEMBER FUNCTION ); Punto AS OBJECT moverA(X IN number, Y IN number), moverA(P IN Punto), distanciaA(p IN Punto) RETURN NUMBER Pensemos ahora que necesitamos almacenar la información de un triangulo y su identificador, para lograr esto, creamos una tabla relacional de la siguiente manera: CREATE TABLE triangulo ( id VARCHAR2(10) CONSTRAINT pk_emp PRIMARY KEY, A Punto CONSTRAINT nn_A NOT NULL, B Punto CONSTRAINT nn_B NOT NULL, C Punto CONSTRAINT nn_C NOT NULL ); Como podemos observar, la definición de tablas relacionales sigue siendo la misma, y el tipo Punto puede ser visto como un tipo más del sistema. Otro aspecto importante es la simplificación de la tabla triángulo al usar el tipo punto. Para las aplicaciones orientadas a objeto, la forma más natural de almacenarlos es a través de tablas de objetos. Por ejemplo, consideremos los siguientes tipos: CREATE TYPE Empleado; CREATE TYPE Departamento AS OBJECT ( Nombre VARCHAR2(10), Jefe REF Empleado ); CREATE TYPE Empleado AS OBJECT ( rut VARCHAR2(10), nombre VARCHAR2(10), cargo VARCHAR2(9), fechaIng DATE, sueldo NUMBER(9), comision NUMBER(9), anticipo NUMBER(9), depto REF Departamento, MEMBER FUNCTION sueldo_liquido RETURN NUMBER, MEMBER PROCEDURE aumento_sueldo (aumento NUMBER) ); La construcción de tablas de objetos es muy sencilla, por ejemplo, podemos crear una tabla denominada tbl_emp para almacenar a los 119 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto empleados, y otra denominada tbl_dep para los departamentos, de la siguiente manera: CREATE TABLE tbl_emp OF Empleado; CREATE TABLE tbl_dep OF Departamento; Como puede observar, no definimos columnas para la tabla tbl_emp, ni para tbl_dep, porque estas se definen automáticamente a partir de los tipos Empleado y Departamento, respectivamente. Tampoco es posible agregar columnas adicionales. Al momento de definir las tablas de objeto, podemos imponer restricciones como: NOT NULL, UNIQUE, CHECK, o definir valores por defecto con DEFAULT sobre las columnas de una tabla de objeto, al igual que en las tablas relacionales. Por ejemplo: CREATE TABLE ( CONSTRAINT CONSTRAINT fechaIng CONSTRAINT ); tbl_emp OF Empleado rut_cons UNIQUE(RUT), sueldo_cons CHECK(sueldo IS NOT NULL), DEFAULT SYSDATE, anticipo_cons CHECK(anticipo >= 0) Otro tipo de restricción tiene que ver con las referencias de objeto a las cuales podemos imponer restricciones de integridad, como si fueran claves foráneas de tablas relacionales. Por ejemplo: CREATE TABLE tbl_emp ( FOREIGN KEY(depto) ); CREATE TABLE tbl_dep ( FOREIGN KEY (Jefe) ); OF Empleado REFERENCES tbl_dep ON DELETE CASCADE OF Departamento REFERENCES tbl_emp Cada instancia almacenada en una tabla de objeto posee un identificador de objeto único denominado OID [31]. Este identificador permite que el objeto almacenado pueda ser referenciado externamente, ya sea desde otros objetos, desde columnas de tablas relacionales o desde un lenguaje de programación como JAVA. En Oracle, podemos especificar que el OID sea generado por el sistema o una clave primaria. Un OID generado por el sistema es un número de 16 bytes de largo, que es único en toda la base de datos e independiente de los valores actuales de los atributos del objeto. Un OID basado en la clave primaria es único dentro de la tabla y 120 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto depende de los valores del objeto. El tipo de OID a utilizar se especifica al momento de crear la tabla de objeto utilizando las palabras reservadas OBJECT IDENTIFIER IS PRIMARY KEY u OBJECT IDENTIFIER IS SYSTEM GENERATED. Al no especificar el tipo de OID, este será generado por el sistema. Por ejemplo, si queremos definir un OID del tipo clave primaria para las instancias del tipo empleado, almacenadas en la tabla tbl_emp, tendremos que escribir lo siguiente: CREATE TABLE tbl_emp OF Empleado (rut PRIMARY KEY) OBJECT IDENTIFIER IS PRIMARY KEY; Aunque innecesario, podemos especificar que el identificador de objeto sea generado por el sistema, de la siguiente manera: CREATE TABLE tbl_emp OF Empleado OBJECT IDENTIFIER IS SYSTEM GENERATED; Como restricción no podemos especificar un OID en una tabla relacional, puesto que las filas no son objetos. 5.2.5.1.2 Modificación (Alter Table) Mediante el predicado ALTER TABLE [41] podemos agregar eliminar o modificar las columnas de una tabla relacional, pero no sobre una tabla de objetos. Además de alterar las columnas, el predicado ALTER TABLE permite agregar, modificar, eliminar o deshabilitar restricciones sobre tablas relacionales o sobre tablas de objeto. Por ejemplo, si queremos eliminar las restricciones sueldo_cons y anticipo_cons de la tabla tbl_emp sería: ALTER TABLE tbl_emp DROP (sueldo_cons, anticipo_cons); 5.2.5.1.3 Eliminación (Drop Table) Para la eliminación de tablas relacionales y de tablas de objetos, utilizamos el predicado DROP TABLE. permiten borrar la Las siguientes sentencias, nos tabla relacional triangulo y la tabla de objetos tbl_emp, creadas anteriormente DROP TABLE triangulo; DROP TABLE tbl_emp; 121 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Como puede observar la eliminación de una tabla de objeto es idéntica a la de una tabla relacional. 5.2.5.2 Manejo de objetos Luego que hayamos definido las tablas que almacenarán nuestros objetos, podemos empezar a trabajar con las instancias. Para esto contamos con varios predicados que nos permitirán insertar, consultar, modificar o eliminar objetos persistentes, que son los mismos que empleamos al trabajar con tablas relacionales, pero que tienen algunas características nuevas, para dar soporte al manejo de objetos. En los siguientes párrafos se describen dichos predicados. 5.2.5.2.1 Inserción de objetos (INSERT INTO) Para la inserción de objetos, utilizamos el predicado INSERT INTO, ya sea al insertarlo como columna en una tabla relacional o como una fila en una tabla de objetos. Si quisiéramos insertar una fila en la tabla triángulo, descrita en la sección 5.2.5.1.1 (creación de tablas) sería: INSERT INTO triangulo VALUES (“TR – 0001”,PUNTO(12,5),PUNTO(5,25),PUNTO(19,25)); Para aquellas columna definidas a partir de un tipo objeto, el sistema esperará una instancia de ese tipo, al momento de pasar el valor correspondiente a la columna. Para crearla, utilizaremos el constructor del tipo, como en el ejemplo anterior, donde utilizamos el constructor PUNTO, para asignarle valores a las columnas a, b, c, que representan a los vértices del triángulo. Para el caso de las tablas de objeto, la inserción es muy similar. Por ejemplo: si quisiéramos insertar un empleado a la tabla tbl_emp definida en la sección 5.2.5.1.1 (creación de tablas) sería: INSERT INTO tbl_emp Values(‘13131344-6’, ‘Miguel’, ‘Ingeniero’, SYSDATE, 600000, 10000, 0,null); 122 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto El orden de los valores debe coincidir con el orden de los atributos del tipo en el que se basa la tabla de objetos. Si un atributo está definido en base a otro tipo objeto, será necesario utilizar el constructor respectivo, al igual que en el primer ejemplo de esta sección (inserción de un triángulo), pudiendo anidar constructores, cuando sea necesario. Alternativamente, podemos utilizar el constructor del tipo objeto empleado para realizar la inserción. Lo anterior quedaría: INSERT INTO tbl_emp Values( Empleado(‘13131344-6’, ‘Miguel’, ‘Ingeniero’, SYSDATE, 600000,10000, 0,null) ); Para las columnas que sean del tipo REF, necesitaremos utilizar una subconsulta para obtener el valor correspondiente. Por ejemplo, consideremos la tabla de departamentos de la sección 5.2.5.1.1 (creación de tablas) , la inserción de una instancia de Departamento, quedaría: INSERT INTO tbl_dep Values( Departamento(‘Departamento de Informática’, (SELECT REF(e) FROM tbl_emp e WHERE e.rut = ‘13131344-6’) ) ); Para el empleado insertado anteriormente, será necesario actualizar el atributo depto, para que apunte al departamento correspondiente. Dicha actualización la mostraremos, más adelante, en la sección 5.2.5.2.3 (update). También podemos insertar objetos a partir de otra tabla, siempre y cuando sean compatibles. Por ejemplo: INSERT INTO tbl_emp select value(emp) from tbl_emp2 emp emp.nombre like ‘R%’; En este ejemplo se copian los objetos de la tabla emp2 cuyo atributo nombre empieza con R, pero a cada uno se le asignará un nuevo OID. Si hacemos la inserción desde PL/SQL, podemos obtener el OID del objeto insertado en la misma sentencia INSERT. Por ejemplo, consideremos que 123 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto queremos insertar un objeto del tipo Empleado previamente definido, llamado emp, en la tabla tbl_emp, y queremos guardar el OID del objeto insertado en la variable OID_Emp que también fue previamente definida, sería: ... insert into tbl_emp e values(emp) returning ref(e) into OID_emp; ... La cláusula returning ref es muy útil cuando tenemos un objeto que posee un atributo que es una referencia a otro, y queremos insertar ambos objetos en una misma transacción. 5.2.5.2.2 Selección de objetos (SELECT ... FROM) Podemos realizar consultas con el predicado SELECT sobre tablas de objeto como si fueran tablas relacionales, donde los atributos del objeto son las columnas de la tabla. Por ejemplo: SELECT * FROM tbl_emp emp WHERE emp.sueldo >100 Aquí se obtienen todos los empleados de la tabla tbl_emp (definida en la sección 5.2.5.1.1 ) cuyo sueldo base sea mayor a 100. Las consultas pueden invocar a las funciones miembro de un objeto y utilizarlas como si fueran columnas. Por ejemplo: SELECT rut,nombre, emp.sueldoLiquido() FROM tbl_emp emp WHERE emp.sueldoLiquido() < 100 Este ejemplo retorna el rut, el nombre y el sueldo líquido de todos los empleados de la tabla tbl_emp cuyo sueldo líquido es menor que 100. Esto también es válido para las tablas relacionales con columnas definidas a partir de un tipo objeto. Por Ejemplo: SELECT * FROM from triangulo t WHERE t.a.distanciaA(b) = 100 124 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Este ejemplo entrega todos los triángulos cuyo lado AB mida 100. La principal ventaja al usar referencias de objeto es la simplificación de las consultas. Por ejemplo, para saber el nombre del departamento al que pertenecen los empleados bastaría con: SELECT rut,nombre, emp.depto.nombre FROM tbl_emp emp; Bajo el enfoque tradicional, sería necesario utilizar un JOIN para obtener el nombre del departamento, pero acá basta con utilizar la referencia depto. 5.2.5.2.3 Modificación de objetos (UPDATE) Para modificar una instancia almacenada en una tabla de objetos utilizamos el predicado UPDATE. Por ejemplo: UPDATE tbl_emp emp SET emp.sueldo=emp.sueldo * 1.15 WHERE emp.sueldoLiquido() < 100 Aquí le aumentamos el sueldo base, en un 15%, a todos los empleados de la tabla_emp (definida en la sección 5.2.5.1.1 ) cuyo sueldo líquido es inferior a 100. También podemos reemplazar el valor de todos los atributos del objeto con los de otro. Por ejemplo UPDATE tbl_emp emp SET emp= Empleado(‘13131344-6’, ‘Miguel R.’, ‘Ingeniero’, SYSDATE, 800000,20000, 0,null) WHERE emp.rut =’13131344-6’ La forma más natural de modificar los atributos de un objeto es a través de sus métodos. Lamentablemente, estos métodos no podrán invocarse sobre objetos persistentes, debido a las restricciones impuestas por Oracle. Cuando explicamos la inserción de objetos, dejamos pendiente la actualización del atributo depto que es una referencia a una instancia de Departamento. Para realizar esta actualización sería: UPDATE tbl_emp emp SET emp.depto = ( SELECT REF(d) FROM tbl_dep 125 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto WHERE d.nombre =’Departamento de Informática’) WHERE emp.rut =’13131344-6’ 5.2.5.2.4 Eliminación de objetos (DELETE) Para eliminar objetos de una tabla de objetos utilizamos el predicado DELETE. Por ejemplo: DELETE FROM tbl_emp emp WHERE emp.sueldoLiquido() < 100 Aquí se eliminan todos los empleados cuyo sueldo líquido sea menor a 100. 5.2.6 Jerarquía de Clases 5.2.6.1 Asociación Para implementar las asociaciones utilizamos las referencias de objetos. Por ejemplo, supongamos que queremos implementar la asociación entre una persona y una organización: Participa Organización 1 Persona * Para implementar esta relación, será necesario agregar un atributo en el tipo objeto Persona que sea una referencia al tipo Organización. La implementación de los tipos quedaría: CREATE TYPE Organizacion AS OBJECT ( id NUMBER, Nombre VARCHAR2(30) ); CREATE TYPE Persona AS OBJECT ( rut NUMBER, Nombre VARCHAR2(30), Org REF Organizacion ); Si la multiplicidad de la asociación fuera uno a uno, cualquiera de los tipos puede tener la referencia al otro. 126 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Si la multiplicidad de la asociación fuera Muchos a muchos, o la asociación en sí misma tenga atributos, será necesario crear un tipo objeto para implementarla. Por ejemplo, supongamos que la asociación entre una organización y una persona fuera: Participa Organización Persona * * Participantes Y quisiéramos almacenar el rol o papel que desempeña la persona en la organización, los tipos objeto quedarían: CREATE TYPE Organizacion AS OBJECT ( id NUMBER, Nombre VARCHAR2(30) ); CREATE TYPE Persona AS OBJECT ( rut NUMBER, Nombre VARCHAR2(30) ); CREATE TYPE Participantes AS OBJECT ( persona REF Persona, Org REF Organizacion, Rol VARCHAR2(50) ); 5.2.6.2 Agregación y Composición La agregación podemos implementarla de la misma manera que una asociación. Pero, como la asociación refleja una relación todo-parte, en la mayoría de los casos, será más conveniente implementarla a través de un tipo colección que almacene referencias de objetos. Por ejemplo, podemos crear una tabla anidada llamada lista_per que almacene referencias a participantes, y crear una columna en Organización llamada lista del tipo Lista_per. La definición de tipos para este ejemplo quedaría: CREATE TYPE Persona AS OBJECT ( rut NUMBER, 127 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto Nombre VARCHAR2(30) ); CREATE TYPE lista_per AS TABLE OF REF Persona; CREATE TYPE Organizacion AS OBJECT ( id NUMBER, Nombre VARCHAR2(30), Lista lista_per ); La composición, por su naturaleza, no debe representarse a través de referencias de objetos, puesto que las partes sólo pertenecen a un agregado, y se destruyen junto con él. directamente. En lugar de eso, utilizamos los objetos Si el ejemplo anterior fuera una relación de agregación, tendríamos que definir la tabla anidada lista_per como una tabla de personas: CREATE TYPE lista_per AS TABLE OF Persona; Para aquellas composiciones, donde haya una asociación uno a uno el todo contendrá un atributo del tipo de datos del tipo asociado. Por ejemplo, si una organización tuviera una sola persona como miembro quedaría: CREATE TYPE Persona AS OBJECT ( rut NUMBER, Nombre VARCHAR2(30) ); CREATE TYPE Organizacion AS OBJECT ( id NUMBER, Nombre VARCHAR2(30), Per Persona ); 5.2.6.3 Herencia y Polimorfismo En Oracle 8i La herencia no es soportada, y la única forma de polimorfismo es la sobrecarga de métodos. En todo caso, si necesitamos modelar relaciones de herencia, podemos simularla a través de agregación o composición, y tendremos que dar un soporte manual al polimorfismo. Existen varias estrategias para realizar esto, por ejemplo, si A es la clase padre, y B, C sus subclases, podríamos crear un 128 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto tipo objeto para cada uno, donde B y C tendrían un atributo llamado super, que sería una referencia a A, el que nos permitiría acceder a sus atributos y métodos. El único problema con esta implementación es que no satisface una característica esencial de la herencia, donde una instancia de B o C también es de A, en la práctica, si tenemos una colección que almacena instancias de A, una instancia de B debería ser posible de almacenar. Una solución a esto es crear el tipo A como una composición, es decir, con un atributo para cada subclase, y una variable que indique el tipo particular de la instancia almacenada. Este último enfoque fue empleado por Oracle para implementar el SQL espacial [35], que es un conjunto de tipos objeto que dan soporte a la implementación de sistemas de información geográficos. 5.2.7 Concurrencia La concurrencia podemos verla desde dos puntos de vista: La • Como un método con múltiples hilos de ejecución. • Como varios usuarios manipulando el mismo objeto. concurrencia desde el primer punto de vista está resuelta, si implementamos el método con JAVA, puesto que este lenguaje implementa un robusto sistema para el manejo y sincronización de múltiples hilos de control. Desde el segundo punto de vista, la concurrencia también es soportada, permitiendo que varios usuarios utilicen y modifiquen el mismo objeto, permitiendo bloqueos manuales o automáticos que garantizan la consistencia y la integridad del objeto. 5.2.8 Manejo de Versiones de objetos y Configuraciones Lamentablemente, Oracle no provee mecanismos para manejar versiones y configuraciones de objetos. 129 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto 5.2.9 Evolución de esquemas y de Instancias La evolución de esquemas se refiere a la capacidad de modificar la estructura de los tipos, agregando, modificando o eliminando métodos o atributos y que estos cambios sean reflejados en todas las estructuras que dependan de él. La evolución de instancia apunta a dos cosas, la primera es que al alterar un tipo, dicho cambio debe verse reflejado en las instancias y la otra es contar con la posibilidad de migrar instancias entre tipos. En Oracle 8i, existe una forma restringida de evolución. A diferencia de una tabla relacional, un tipo no puede ser alterado, solamente podemos agregar métodos (vea 5.2.1.6 ), cambio que no afecta a los tipos, tablas o vistas que dependen de él. Esta carencia junto con la de herencia son las limitaciones más significativas que tiene Oracle, Aunque con un poco más de trabajo, podremos lograr esta funcionalidad. Una solución a este problema es crear una tabla relacional con los atributos del objeto como columna y una vista de objeto sobre la tabla relacional. Luego utilizaremos la vista de objeto como si fuera una tabla de objetos, y cuando necesitemos agregar, modificar o eliminar la definición de un atributo, lo haremos sobre la tabla relacional, y luego modificaremos nuestra vista de objeto. No se pueden migrar instancias entre tipos en Oracle 8i. 5.2.10 Transacciones y recuperación ante fallos. Estas características están presentes en Oracle desde varias versiones atrás, y de hecho son uno de sus puntos más fuertes. Una transacción, es una unidad lógica de trabajo que contiene una o más sentencias SQL que pueden ser todas aplicadas a la base de datos o ninguna. La utilidad de una transacción es que nos permite impedir que los cambios producidos por las sentencias SQL ya ejecutadas se apliquen a la base de 130 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto datos, al ocurrir alguna excepción, manteniendo así la consistencia y la integridad. En lo referente a la recuperación ante fallos, existen varios errores que Oracle soluciona automáticamente y otros requieren de una mínima intervención del usuario. Otra característica importante, es que la mayoría de los fallos pueden ser superados sin la necesidad de bajar el servidor. 5.2.11 Mecanismos de autorización Al igual que en SQL:1999, los mecanismos de autorización están basados en las tablas y en los tipos objeto. Se autoriza o restringe el acceso y manipulación de las tablas o tipos objeto, pero no se puede restringir el acceso sobre un objeto o una fila. 5.2.12 Compatibilidad con el modelo relacional Oracle posee una alta compatibilidad con el modelo relacional, permitiendo que interactúen las estructuras relacionales con las nuevas estructuras. Además permite la creación de vistas de objeto sobre tablas relacionales. Una vista de objetos, se basa en el resultado de una consulta y en un tipo objeto, que definirá las columnas de la vista. La consulta pude estar basada en varias tablas, sean relacionales o de objeto. Al consultar una vista de objetos, se obtendrá un conjunto de instancias del tipo objeto en el que se base la vista. Por ejemplo: supongamos que una empresa posea una tabla que almacena a todos sus departamentos, y que fue creada de la siguiente manera: CREATE TABLE departamento ( ID_Dep NUMBER (10) NOT NULL PRIMARY KEY, Nombre VARCHAR2 (50) ); Para crear una vista de objetos de esta tabla, primero tendremos que crear un tipo objeto cuyos atributos serán las columnas de la tabla departamento, y luego podremos crear nuestra vista de objeto. 131 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto CREATE TYPE dep_T ( ID_Dep NUMBER, Nombre VARCHAR2 (50) ); CREATE VIEW dep_ObjView OF dep_T WITH OBJECT OID (ID_Dep) AS SELECT ID_Dep, Nombre FROM DEPARTAMENTO; Al crear la vista, debemos indicar que cree el OID de los objetos a partir de la clave primaria de la tabla relacional. Crear esta clase de vistas permite acceder a toda la capacidad del manejo de objetos, como invocación de métodos y referencias a objetos, sin tener que migrar los datos de las tablas relacionales. 5.2.13 Limitaciones encontradas en Oracle 8i En Oracle 8i, encontramos las siguientes limitaciones, referente al manejo de objetos: • Un tipo no puede tener más de 1000 atributos • No podemos sobrecargar el constructor definido por el sistema, ni redefinirlo. Tampoco podrá crear nuevos constructores. • Los procedimientos miembro de un tipo, no pueden ser invocados desde SQL. • Las funciones miembro de un tipo, que posean uno o más parámetros pasados como INOUT o OUT, no pueden ser invocados desde SQL. • No podemos modificar la definición de atributos o métodos existentes de un tipo objeto. Sólo podemos agregar nuevos métodos. • No podemos replicar tablas de objetos. • No podemos tener colecciones que almacenen colecciones, ni colecciones de objetos que posean un atributo de un tipo de colección. • No podemos definir colecciones en base a ciertos tipos de datos como por ejemplo: BOOLEAN, BLOB, CLOB, LONG, LONG RAW, y otros. • No podemos especificar un nivel de encapsulamiento (private, public, protected) para los atributos, ni para los métodos. • No podemos definir Jerarquías de Herencia, ni simple, ni múltiple. 132 Oracle 8i y la orientación a objeto - Soporte a la orientación a objeto • No podemos duplicar las tablas de objeto en Oracle 8i. 133 5.3 Evaluación En este capítulo evaluaremos la implementación que hace el SABD Oracle 8i del paradigma de la orientación a objeto, utilizando los mismos criterios de la evaluación del estándar SQL:1999. El objetivo de esta evaluación es determinar en que porcentaje son soportadas las características de la orientación a objeto (ver 2) y los requerimientos para las bases de datos orientadas a objeto (ver 3.1.2.1), por parte del SABD Oracle 8i. Junto con ello, agregaremos la evaluación realizada para el estándar SQL:1999, para facilitar su comparación. 5.3.1 Criterios de evaluación Para la evaluación del soporte dado por Oracle 8i a la Orientación a Objeto, utilizaremos los mismos criterios y la misma valoración utilizada en el capítulo anterior para evaluar SQL-1999. Estos son: 1. Definición de tipos o clases a. Definición de atributos estáticos b. Definición de atributos de instancia c. Constructores definidos por el usuario d. Métodos estáticos e. Métodos de instancia f. Sobrecarga de métodos g. Definición de colecciones h. Clases abstractas 2. Encapsulamiento a. Atributos y métodos en una sola entidad b. Distinguir entre interfaz e implementación c. Visibilidad de métodos (Ej.: Public, private, protected) d. Visibilidad de atributos (Ej.: Public, private, protected) 3. Modularidad 4. Jerarquía de clases o tipos a. Asociación 134 Oracle 8i y la orientación a objeto – Evaluación b. Agregación y Composición c. Herencia Simple d. Herencia Múltiple e. Polimorfismo 5. Concurrencia a. Métodos con múltiples hilos de control b. Concurrencia de usuarios. 6. Persistencia de objetos a. Creación b. Modificación c. Eliminación d. Consulta e. Identificadores de objetos 7. Versiones y configuraciones de objetos 8. Evolución de instancias 9. Evolución de esquemas a. Agregar Atributos b. Agregar Métodos c. Agregar Restricciones sobre objetos persistentes d. Eliminar Atributos e. Eliminar Métodos f. Eliminar Restricciones g. Modificar definición de atributos h. Modificar Interfaz de métodos i. Modificar Implementación de métodos j. Modificar Restricciones k. Deshabilitar y habilitar restricciones 10. Transacciones y recuperación ante fallos 11. Mecanismos de autorización basados en objetos 12. Compatibilidad con el modelo relacional. Si la característica... Está presente No está presente, pero se puede simular No está presente Valoración 100% 60% 0% 135 Oracle 8i y la orientación a objeto – Evaluación 5.3.2 Matriz de evaluación Valoración Oracle 8i SQL:1999 85% a. Definición de atributos estáticos 95% 60% 60% 100% 100% 60% 100% d. Métodos estáticos 100% 100% e. Métodos de instancia 100% 100% f. 100% 100% 100% 100% 60% 100% b. Definición de atributos de instancia c. Constructores definidos por el Final Parcial 1. Definición de tipos o clases Final Parcial Características Valoración usuario Sobrecarga de métodos g. Definición de colecciones h. Clases abstractas 2. Encapsulamiento 80% a. Atributos y métodos en una sola 80% 100% 100% e 100% 100% c. Visibilidad de métodos (Ej.:Public, 60% 60% 60% 60% entidad b. Distinguir entre interfaz implementación private, protected) d. Visibilidad de atributos (Ej.: Public, private, protected) 3. Modularidad 60% 60% 4. Jerarquía de clases o tipos 82% 92% a. Asociación 100% 100% b. Agregación y Composición 100% 100% c. Herencia Simple 60% 100% d. Herencia Múltiple 60% 60% e. Polimorfismo 60% 100% 5. Concurrencia 100% 100% 136 Oracle 8i y la orientación a objeto – Evaluación a. Métodos con múltiples hilos de 100% 100% 100% 100% control b. Concurrencia de usuarios. 6. Objetos Persistentes 100% 100% a. Creación 100% 100% b. Modificación 100% 100% c. Eliminación 100% 100% d. Consulta 100% 100% e. Identificadores de objetos 100% 100% 7. Versiones y configuraciones de 0% 0% 8. Evolución de instancias 0% 60% 9. Evolución de esquemas 80% 92% objetos a. Agregar Atributos 60% 100% b. Agregar Métodos 100% 100% c. Agregar Restricciones sobre objetos 100% 100% d. Eliminar Atributos 60% 100% e. Eliminar Métodos 60% 100% 100% 100% g. Modificar definición de atributos 60% 60% h. Modificar Interfaz de métodos 60% 60% 100% 100% 100% 100% persistentes f. i. Eliminar Restricciones Modificar Implementación de métodos j. Modificar Restricciones 10. Transacciones y recuperación ante 100% 100% 60% 60% 100% 100% 70,58% 78,25% fallos 11. Mecanismos de autorización basados en objetos 12. Compatibilidad con el modelo relacional. Promedio 137 6 Una Aplicación de ejemplo 6.1 Introducción En este capítulo se describirá un prototipo desarrollado bajo el paradigma de la orientación a objeto, utilizando como lenguaje a Java y Oracle 8i para hacer persistir a los objetos. El prototipo esta relacionado con la gestión de recursos hídricos, tema que, a juicio personal, es muy interesante y complejo a la vez. Por esta razón y para una mejor comprensión del prototipo que expondremos más adelante, veamos primero lo siguiente. 6.1.1 Importancia del recurso hídrico [29] El mundo cuenta con grandes reservas de agua dulce, sobre 37 millones de Km3, que exceden cualquiera necesidad imaginable de la población humana, como por ejemplo los siguientes usos: riego, agua potable, generación de energía industrial y minería, navegación, recreación, control de calidad, control de crecidas, protección y conservación del medio ambiente, bebida animal, refrigeración de plantas nucleares, turismo, etc. Sin embargo, la mayor parte del recurso no nos es accesible, ya que más de las tres cuartas partes de las reservas se encuentran retenidas en los glaciares y en el hielo polar, y el resto muestra una distribución irregular de un lugar a otro y de una estación a otra. Es por lo anterior, que en la mayoría de las regiones del planeta sólo se puede alcanzar un nivel de aprovechamiento adecuado y rentable mediante una planificación y una gestión activa de los recursos hídricos. Aunque el agua cae del cielo, no es gratuita, y la intervención del hombre en un ciclo natural acarrea siempre un costo, precio que a veces resulta muy caro. 139 Una aplicación de ejemplo - Introducción La planificación de los recursos hídricos tiene por objetivo fundamental dar el máximo aprovechamiento al recurso en forma integral. 6.1.2 Gestión del recurso hídrico en Chile La Dirección General de Aguas (DGA) del Ministerio de Obras Públicas de Chile (MOP) es la encargada de planificar y gestionar los recursos hídricos con los que cuenta el país. Los objetivos de carácter general de la planificación de los recursos hídricos son [29]: Perfeccionar las técnicas de control y manejo del agua. Desarrollar y mejorar los métodos para conservar y aumentar las disponibilidades de agua en cantidad y distribución temporal. Desarrollar y mejorar los métodos de control de contaminación, de modo de proteger y mejorar la calidad de los recursos hídricos. Desarrollar y mejorar los métodos y la eficiencia de la recolección de los datos de terreno necesarios para lograr una adecuada planificación y diseño de las obras de aprovechamiento del agua. Desarrollar y mejorar los métodos de evaluación de los desarrollos de los recursos hidráulicos de modo de obtener una maximización de los beneficios socioeconómicos netos. Para llevar a cabo su tarea, la DGA necesita contar con herramientas que le permitan hacer pronósticos acertados, y predecir por ejemplo lo que sucederá al intervenir el cause de un río con algún nuevo proyecto (embalse, central hidroeléctrica, canales, etc). Algunas de estas herramientas corresponden a los modelos de simulación2 hidrológica operacional [29], los cuales representan los fenómenos hidrológicos en una cierta cuenca3, además, incorporan todas las obras hidráulicas existentes o futuras. 2 3 La técnica de reproducir la esencia de un sistema sin reproducir el sistema en si Territorio cuyas aguas afluyen todas a un mismo río, lago o mar 140 Una aplicación de ejemplo - Introducción 6.1.3 Modelación de cuencas y proceso de Simulación hidrológico operacional4. Para facilitar el proceso de simulación, existen modelos de tipo general que se caracterizan por tener previamente definidas las rutinas típicas de la simulación, de modo que el usuario sólo realiza lo siguiente [29]: • Definición de topología • Ingreso de datos para cada elemento del modelo • Procesar Para realizar la simulación es necesario contar con un modelo de la cuenca que nos interesa analizar (Definición de topología), La cual es modelada a través de un conjunto interconectado de objetos cada uno de los cuales representa un elemento hidráulico de la cuenca. Existen dos tipos de objetos: Fijos y Variables [29]. Los objetos fijos, tienen una ubicación preestablecida, que no se puede modificar, como tampoco sus atributos, porque estos objetos definen la topología básica de la cuenca (al modificarse dejan de modelar la cuenca). Los objetos fijos son los ríos o causes naturales más importantes y los Nodos o lugares donde confluyen aportes o extracciones de caudales. En los nodos se realiza un operación matemática denominada balance de caudal, cuya fórmula general es: Entrada – Salida = Variación de almacenamiento. Dos nodos consecutivos dentro de un mismo río definen los tramos de río. Por los tramos de ríos de la cuenca es por donde se mueven los caudales de un nodo a otro según la dirección del flujo [29]. 4 En esta sección no pretendemos enseñar a modelar cuencas reales, sólo dar los conocimientos básicos para el diagrama de una cuenca modelada. Tampoco se verá en forma detallada el proceso de simulación 141 Una aplicación de ejemplo - Introducción Considerando solamente los objetos fijos (ríos y nodos) podríamos tener un esquema de la modelación de una cuenca como el siguiente (figura 6) : Simbología: : Nodo : Tramo de Río figura 6 : Esquema de una cuenca en base a ríos y nodos Por su parte los objetos variables son posibles de ubicar en cualquier nodo y modificar sus atributos. Los objetos variables corresponden a diferentes elementos hidráulicos cuya función puede ser extracción, aporte, conducción, consumo, generación o regulación de caudales. Estos son [29]: • Aporte natural: Aporte de caudales a un nodo. • Canal: Permite la conducción de un caudal máximo entre dos nodos. (Son creados por el hombre). • Hoya intermedia: Aporte de caudales a un nodo que corresponden a aquellos caudales que son aportados a un tramo de río al cual el nodo pertenece. Esto se debe a que en el modelo las entradas o salidas de caudales sólo se pueden hacer en un nodo. • Central hidroeléctrica: Realiza una captación de agua desde un nodo inicial, genera energía eléctrica y entrega los caudales a un nodo final sin consumo. • Embalse: Obra de regulación de caudal que permite el almacenamiento de agua. • Zona de riego: Centro de demanda consuntiva de agua para el sector agrícola. • Captación de agua potable: Extracción de agua potable para el consumo. • Captación de uso industrial o minero: Extracción de agua para consumo Industrial o minero • Descarga contaminante: Aporte de caudales (contaminados) a un nodo. 142 Una aplicación de ejemplo - Introducción • Recuperación en el cauce: Punto donde se ha verificado una recuperación de caudal en un río, que se supondrán concentradas en un nodo específico. • Pérdida en el cauce: Punto donde se ha verificado una pérdida de caudal en un río, que se supondrán concentradas en un nodo específico. Ya explicamos todos los objetos que intervienen en el modelo de una cuenca, sólo falta agregar, que estas se pueden dividir en sectores. La figura 7 corresponde a un modelo real: La cuenca del río Maule, que por motivos de espacio, sólo se mostrará el sector uno de dicha cuenca (aproximadamente el 10% del diagrama total). En ella podemos apreciar la mayoría de los objetos aquí descritos. Este diagrama fue desarrollado por la Dirección General de Aguas del Ministerio de Obras Públicas. 143 Una aplicación de ejemplo - Introducción Simbología Nodo Nodo coincidente con estación pluviométrica Río o Estero Embalse C Central Hidroeléctrica Proyectada C Central Hidroeléctrica Canal Principal Derrames Zona de Riego Abreviaturas HI = Hoya Intermedia F = Filtraciones R = Rebases Ee = Entregas para Energía Er = Entregas para Riego G = Entregas generables por central Hidro. RN = Régimen Natural figura 7: Sector uno del esquema de la modelación de la cuenca del río Maule [29] 144 Una aplicación de ejemplo - Introducción 6.1.3.1 Simulación Para realizar la simulación hidrológica en la cuenca del río Maule, la DGA cuenta con un programa computacional, el cual toma como base información estadística, las modificaciones hechas por el usuario, como por ejemplo agregar un embalse, y un rango de años que indican el período de simulación. La unidad mínima en este modelo es el mes, puesto que todas las estadísticas de caudales se encuentran en esta unidad de tiempo. La simulación comienza desde aguas arriba hacia aguas abajo, nodo por nodo. En cada uno de los nodos se consideran todos los caudales de entrada y se distribuye el agua por todas las salidas del nodo, las cuales en algunos casos pasarán a ser entradas de otros nodos. Cada vez que se recorre el modelo completo, se pasa al siguiente mes de simulación. El algoritmo de simulación es el siguiente Inicio Para cada año de simulación haga Para cada mes Para cada nodo del modelo haga Operar Objetos que aportan caudal Distribuir caudales Opera embalses Balance hidrológico en el nodo Fin Para cada nodo Resultados de operación mensual Fin Para cada mes Resultados de operación anual Fin Para cada año Fin Además existe un proceso de simulación especial llamado simulación de temporada octubre a marzo [29]. Para desarrollarla se pronostican los caudales medios mensuales de cada uno de los objetos del modelo denominados “Hoya Intermedia” (H.I.) y Régimen natural (R.N), para cada uno de los meses de octubre a marzo del año en que se hace la simulación. Estos pronósticos, más los cambios en el modelo, son los datos de entrada para la simulación, la que es idéntica a la anterior, salvo que esta tiene un período fijo. 145 6.2 Descripción del Prototipo El prototipo consiste en una aplicación que implementa una versión reducida del modelo de simulación hidrológico operacional de tipo general, descrito en la sección anterior, realizando únicamente la simulación de temporada, para una cuenca que contemple los siguientes objetos: Nodos Ríos y tramos de río Embalses Central Hidroeléctrica de pasada Canal Principal Hoya Intermedia Régimen Natural Captación de agua potable A partir de estos objetos podemos simular una cuenca como la mostrada en la sección anterior (sector 1 de la cuenca del río Maule) si a esta excluyéramos las zonas de riego, los derrames y los nodos coincidentes con estación pluviométrica. Los requerimientos del prototipo son: Implementar una interfaz de usuario gráfica para mostrar el esquema de la cuenca Permitir la consulta de cualquier objeto de la cuenca pudiendo ser antes o después de la simulación. Realizar el proceso de simulación de temporada el que tendrá como salida un informe con los caudales medios mensuales para cada elemento (objeto) de la cuenca, en cada mes de simulación. Responder a consultas como: 1. Embalses bajo sus volúmenes mínimos y rebasados. 2. Energía generada. 146 Una aplicación de ejemplo – Descripción del prototipo 6.2.1 Descripción de los objetos soportados Para todos los objetos de la cuenca es necesario almacenar los atributos que describen su ubicación y el caudal para cada mes de la simulación. Cabe destacar que esta aplicación debe mostrar el esquema de una cuenca y no una cuenca, por esto, los datos de ubicación son en el plano cartesiano y no coordenadas terrestres como sucede en las aplicaciones SIG. Otro aspecto a considerar, es que la simulación de temporada está basada en demandas y no en derechos de agua, por esta razón, no es necesario almacenar la información referente a los derechos. 6.2.1.1 Nodos. El nodo es un objeto de tipo fijo, es decir que su ubicación y definición no es posible de alterar en el modelo una vez que se ha definido. En cada nodo es posible colocar cualquier objeto, a sola excepción de los cauces naturales que son fijos. Cada nodo representa un lugar de la cuenca donde confluyen aportes y extracciones, de modo que es posible efectuar un balance de caudales. Sólo en los nodos se puede efectuar balances hídricos en este modelo. Para individualizar cada nodo, cada uno de ellos se denomina con dos números separados por un guión. El primer número representa el sector, en tanto que el segundo corresponde al número correlativo dentro del sector, esto es útil cuando se tiene una cuenca dividida en subcuencas o sectores. Cabe destacar que el número correlativo dentro del sector se debe definir de modo que todos los caudales afluentes y extracciones estén determinados previamente, para efectuar el respectivo balance hídrico. De este modo, se puede definir la secuencia de los balances en los nodos, utilizando el mencionado número correlativo. 147 Una aplicación de ejemplo – Descripción del prototipo Esquema i-j Información que se desea almacenar Código del nodo, que se indica como 'NO i-j", donde 'i" es el número del sector, 'j" es el número correlativo del nodo en el sector. Cota del nodo. 6.2.1.2 Ríos y tramos de río. Este es un objeto de tipo fijo al igual que los nodos. Corresponde a los ríos, esteros o cauces naturales en general, los cuales pueden atravesar por varios nodos en su trayecto. Dependiendo del número de nodos por los cuales pase el río, se definen tramos, que se designan en forma correlativa, desde aguas arriba hacia aguas abajo. En el modelo de simulación, este es un objeto que permite la conducción del caudal entre un nodo inicial y un nodo final, sin restricción de capacidad de conducción. Esquema En el esquema, el rió se indica con una línea de trazo más grueso. i-j Rio XXXX (Tramo K) m-n Información que se desea almacenar Código del río Nombre del río Tramos del río, Nodo inicial y nodo final. 148 Una aplicación de ejemplo – Descripción del prototipo 6.2.1.3 Embalses. Este objeto representa una obra de regulación de caudal, que puede tener varias entradas, tales como una hoya aportante o caudales en régimen natural, como también canales alimentadores. El objeto embalse contempla cinco tipo de salidas diferentes, a saber: Filtraciones (F) Entregas para energía, sin generar (Ee) Entregas para riego (Er) Rebases (R) Caudal Generado (G) El objeto embalse se ubica en un cierto nodo, pero cada una de sus salidas puede definirse en nodos diferentes. EV Esquema F R Ee Er G Abreviaturas: Ev: Evaporación del embalse R : Rebases Ee: Entrega para energía (sin generar) Er: Entrega para riego G : Caudal generado en el embalse (central a pié de presa). F : Filtraciones del embalse. Información que se desea almacenar Código del objeto Nodo 149 Una aplicación de ejemplo – Descripción del prototipo Capacidad máxima del embalse (volumen útil), en millones de metros cúbicos. Evaporación bandeja mensual, en milímetros. Valores de superficie (hectáreas) y volúmenes (millones de metros cúbicos). Coeficiente de embalse. (Normalmente se utiliza 0,7). Valores de caudal máximo posible de entregar por válvulas en m3/s y volumen almacenado en millones de m3. (para verificación de entrega máxima del embalse). Valores de filtración en litros por segundo y volumen del embalse, en millones de metros cúbicos. Nodos de llegada de cada una de las salidas del embalse. (Son 5 valores) Capacidad máxima de conducción de las entregas Er (riego), Ee (energía) y G (generada). Volumen inicial del embalse, en el primer mes de la simulación, en millones de metros cúbicos. Curva de alerta. Consiste en la definición de los volúmenes mínimos que debe tener el embalse en cada mes, como condición deseable. Si no se incluye este dato, no se verificará esta restricción de curva de alerta. 6.2.1.4 Central Hidroeléctrica de pasada Este objeto representa una central hidroeléctrica de pasada, que capta sus aguas desde un nodo inicial y las entrega a un nodo final, sin consumo de agua. Esquema i-j C m-n Información que se desea almacenar Código del objeto Nodo inicial. Nodo Final. 150 Una aplicación de ejemplo – Descripción del prototipo Caudal máximo de generación en m3/s. Caudal mínimo medio anual, en m3/s. 6.2.1.5 Canal Principal Este objeto permite la conducción de un caudal máximo, con una eficiencia de conducción dada, entre dos nodos. Esquema En el esquema, el objeto “canal" se representa a través de una flecha con trazo doble, que indica el sentido del escurrimiento. Canal Principal i-j i-j Información que se desea almacenar Código del objeto Denominación del canal Nodo inicial Nodo final o punto de llegada Capacidad de conducción en m3/s Eficiencia de conducción en porcentaje. Parámetros ‘a’, ‘b’ y ‘c’ de la captación del canal en un cauce natural y que representa el caudal posible de captar por el canal en función del caudal del río, según se indica en la figura siguiente : CAUDAL CAPTADO (m3/s) a CAUDAL DEL RIO(m3/s) b c 151 Una aplicación de ejemplo – Descripción del prototipo 6.2.1.6 Hoya Intermedia Corresponde a una estadística de caudales medios mensuales, que ingresan o aportan al nodo y representan el aporte de la hoya intermedia de un río o cauce natural entre dos nodos. Esquema H.I. i-j Información que se desea almacenar Código del Objeto Denominación de la Hoya Intermedia o nombre del río Código del Nodo Estadística de caudales. Código del río al cual aporta la H.I. 6.2.1.7 Régimen Natural Corresponde a una estadística de caudales medios mensuales, en régimen natural, que ingresan o aportan al nodo. Esquema R.N i-j Información que se desea almacenar Código del objeto Denominación de los aportes Nodo Estadística de caudales. 152 Una aplicación de ejemplo – Descripción del prototipo 6.2.1.8 Captación de Agua Potable Este objeto representa una extracción de agua potable desde un nodo, para el consumo. Requiere los valores de caudales medios mensuales de las extracciones. Esquema i-j A.P. Información que se desea almacenar Código del Objeto Código del Nodo Caudales de extracción medio mensual 153 6.3 Diseño del prototipo Para el diseño del prototipo utilizamos la notación UML, pero no completa, solamente los diagramas de clases y los diagramas de colaboración. Para los lectores no familiarizados con UML en el apéndice B encontrará una breve descripción. Una aclaración importante, por falta de información y de tiempo, no fue posible encontrar información sobre la correcta operación de un embalse, y la fórmula con la que se convierte un caudal a kilo watt. Por lo tanto, estos dos aspectos del prototipo están desarrollados a partir de una fórmula incorrecta, por lo tanto los resultados que pueda entregar el modelo de simulación, serán incorrectos. En todo caso bastaría con corregir los métodos que incorporan dichas fórmulas y el modelo quedaría correcto. Tenga en cuenta que el objetivo de este prototipo es probar las capacidades y limitaciones de Oracle 8i para el manejo de objetos y como interactúa este con Java y no dar una solución acabada y óptima para el problema planteado. 6.3.1 Diagramas de clases 6.3.1.1 Clases implementadas en Oracle 8i Comenzaremos analizando el conjunto de clases que representa a los objetos de la cuenca, y que serán creadas en Oracle 8i para hacer persistir a los objetos. Para cada uno de los objetos de la cuenca soportados por el prototipo, crearemos una clase. De este modo tenemos las siguientes: Nombre de la Clase Descripción Cuenca Representa a una cuenca Nodo Representa a un nodo de la cuenca Río Representa un Río de la cuenca Embalse Representa a un embalse de la cuenca Salida_Emb Representa a los flujos de salida de un embalse Regimen_Natural Representa a un Régimen Natural 154 Una aplicación de ejemplo – Diseño del prototipo Hoya_Inter Representa a una Hoya Intermedia Cap_Agua_Pot Representa una Captación de agua potable Central_hidro Representa una Central Hidroeléctrica Canal Representa un Canal Representa un tramo de Río, que transporta un Tramo caudal desde un nodo inicial a un nodo final Al analizar el problema, descubrimos que existe un conjunto de clases que en términos generales son Flujos de caudales que aportan y/o extraen caudales de un nodo. A partir de esto construimos el siguiente esquema de generalización: Flujo Aportante Salida_Emb Regimen_Natural Extraccion Hoya_Inter Cap_Agua_Pot Apor_extrac Central_Hidro Canal Tramo Además de la jerarquía de generalización mostrada anteriormente, existen otras relaciones entre las clases. Estas relaciones son: • Un Río posee uno o muchos tramos • Un embalse esta ubicado en un único nodo • Un Embalse posee siempre cinco flujos de salida • Un nodo posee uno o muchos flujos de entrada que le aportan caudales, y uno o muchos flujos de salida que le extraen caudales. • Dependiendo del tipo de flujo, éste puede poseer un nodo inicial, de donde extrae un caudal y un nodo final a donde aporta un caudal. El nodo inicial debe ser distinto al final, y un flujo debe poseer por lo menos un nodo (sea inicial o final). 155 Una aplicación de ejemplo – Diseño del prototipo El siguiente diagrama muestra dichas relaciones: entrada Tramo 1..* Flujo Aporta 1..* 0..1 Nodo_Inicial Salida 1..* Extrae Nodo_Final 1 5 1 * Está 0..1 1 Rio Nodo 0..1 Salida_Emb Embalse 1 Cuenca Una cuenca es un conjunto de nodos, flujos, embalses, etc, pero como todos estos objetos están enlazados directa o indirectamente con el nodo, podemos representar una cuenca como un conjunto de nodos. El conjunto de clases mostrado hasta ahora corresponde a las abstracciones del dominio del problema, cuyos objetos deberán ser almacenados en la base de datos. El siguiente diagrama muestra las clases a nivel de implementación, incluyendo los atributos y métodos para cada clase. Además de otras clases necesarias para la implementación. El tipo de dato de los atributos corresponde a los tipos de Oracle. 156 Una aplicación de ejemplo – Diseño del prototipo Como la razón de ser de este prototipo es evaluar las capacidades de Oracle 8i en el soporte de objetos, la simulación es ejecutada directamente en el servidor. La clase arr_caudales, almacena los caudales para cada uno de los meses de la simulación (6 meses, de octubre a marzo). Las clases Graf_nodo y Graf_cuenca, forman parte la jerarquía de clases definidas para describir la geometría de cada uno de los objetos del diagrama de la cuenca. El siguiente Diagrama de clases muestra dicha jerarquía : 157 Una aplicación de ejemplo – Diseño del prototipo Como Oracle8i, no implementa la herencia, tuvimos que simularla a través de la composición. La estrategia utilizada fue incluir en la superclase un atributo para cada una de sus subclases, junto con otro que indicara el tipo actual del objeto. Esto nos permite dar soporte a la substituibilidad de tipos, es decir, permitir que donde se espere una instancia de una superclase, también sea válida una instancia de la subclase. La misma estrategia utilizamos para implementar la jerarquía de herencia de la clase flujo. El siguiente diagrama muestra dicha implementación 158 Una aplicación de ejemplo – Diseño del prototipo El código necesario para implementar todas estas clases en Oracle 8i, se encuentra en el anexo C.1, junto con ello, una explicación del tipo, de sus atributos y métodos. 159 Una aplicación de ejemplo – Diseño del prototipo Para almacenar los objetos, se crearon cinco tablas, estas son: • Tbl_Cuenca. Almacena objetos del tipo Cuenca • Tbl_Rio. Almacena objetos del tipo Río • Tbl_Nodo. Almacena objetos del tipo Nodo • Tbl_Flujo. Almacena objetos del tipo Flujo • Tbl_Emb. Almacena objetos del tipo Embalse Como puede observar, no es necesario crear una tabla para cada subclase del tipo flujo. Esto se debe a que el tipo flujo encapsula a todas las subclases. El código para la implementación de estas tablas en Oracle 8i se encuentra en el anexo C.2. Ahí también encontrará un ejemplo de inserción para cada uno de los tipos existentes. 160 Una aplicación de ejemplo – Diseño del prototipo 6.3.1.2 Clases implementadas en Java Existe una utilidad llamada JPUB (Java Publisher) [34] que permite exportar los tipos creados en Oracle 8i a Java. Por cada tipo objeto, esta utilidad crea dos clases java, una para almacenar referencias al objeto y otra para almacenar al objeto. Este conjunto de clases permiten el intercambio de objetos entre Java y Oracle en forma transparente. Por ejemplo, para exportar el tipo apor_extrac definido en Oracle a Java sería: $ jpub –user=scott/tigre –sql=apor_extrac:AporExtracOra Esto genera las clases AporExtracOra.java y AporExtracOraRef.java, descritas en los anexos C.4.4.1 y C.4.4.2, respectivamente. Para desarrollar el prototipo, lo primero fue generar las clases java con JPUB, las cuales fueron agrupadas en el paquete objcuenca. Para eso fue necesario agregar una línea de código (package objcuenca;) a cada una de las clases. En C.4.4 encontrará una lista completa de las clases generadas con JPUB para el prototipo. Para mostrar visualmente cada uno de los objetos de la cuenca, definimos la Interface Graficable y una clase para la superficie de dibujo llamada LienzoCuenca. JPanel LienzoCuenca -lasFiguras : Vector -zoom : int +addFigura(f : Graficable) : void 0..* +buscarFigura(x : int, y : int) : Graficable +clearFiguras() : void +dibujarFiguras(g : Graphics ) : void +getZoom() : int +setZoom(zoom : int) : void +paint(g : Graphics) : void +reloadFiguras() : void +setVectorFiguras(f : Vector) : void «Interface» Graficable 1..* +dibujar(d : Dibujante) : void +dimensionMinima() : Dimension +dlgAtributos() : void +getZoom() : int +loadFigura() : void +pertenece(p : Punto) : boolean +setZoom(zoom : int) : void 161 Una aplicación de ejemplo – Diseño del prototipo Para lograr una independencia entre la superficie de dibujo y el dibujado de los objetos, creamos una clase llamada Dibujante, que es la encargada de dibujar en una instancia de LienzoCuenca, de manera que Graficable, no accede directamente al lienzo, sino que lo haga a través de Dibujante Dibujante -colorLinea : Color -g : Graphics -zoom : int +drawCuadrado(cuad : CuadradoOra) : void +drawElipse(e : ElipseOra) : void +drawPolilinea(p : ArrPuntosOra) : void +drawPolilinea(p : PolilineaOra) : void +drawPolilineaDoble(p : ArrPuntosOra) : void +drawPolilineaDoble(p : PolilineaOra) : void +drawPolilineaGruesa(p : ArrPuntosOra) : void +drawPolilineaGruesa(p : PolilineaOra) : void +drawTriangulo(t : TrianguloOra) : void +setColorLinea(c : Color) : void +drawString(s : String, x : int, y : int, tam : int) : vo Para cada objeto de la cuenca que es mostrado gráficamente fue creada una clase para mostrar los atributos descriptivos del objeto. JDialog frmEmbalse frmSalidaEmb frmHoyaInter frmFlujo frmRegNat FrmCapAguaPot frmNodo FrmCanal FrmTramo FrmCentralHidro Las pantallas que representan estos diálogos, las podemos encontrar en C.3.9 al C.3.16. Como las clases generadas con JPUB, no pueden ser mostradas directamente sobre el LienzoCuenca, creamos tres clases GuiEmbalse, GuiNodo, GuiFlujo, como subclases de EmbalseOraRef, NodoOraRef, FlujoOraRef respectivamente; y las tres implementan la interfaz Graficable. La clase GuiFlujo encapsula a todos los flujos. El siguiente diagrama muestra dicha relación: 162 Una aplicación de ejemplo – Diseño del prototipo «Interface» Graficable NodoOraRef EmbalseOraRef FlujoOraRef GuiEmbalse GuiNodo -codigo : String -frm : frmNodo -graf : ElipseOra -zoom : int +dibujar(d : Dibujante) : void +dimensionMinima() : Dimension +dlgAtributos() : void +getZoom() : int +loadFigura() : void +pertenece(p : Punto) : boolean +setZoom(zoom : int) : void 1 1 1 ElipseOra 1 frmNodo GuiFlujo -alerta : boolean -frm : frmNodo -graf : ElipseOra -zoom : int -alertaEmb() : boolean +dibujar(d : Dibujante) : void +dimensionMinima() : Dimension +dlgAtributos() : void +getZoom() : int +loadFigura() : void +pertenece(p : Punto) : boolean +setZoom(zoom : int) : void -alertaFlujo() : boolean -colineales(a : Punto, b : Punto, c : Punto) : boolean -dibujarCentHidro(d : Dibujante) : void -dibujarFlujo(d : Dibujante) : void -dimensionMinima(fl : PolilineaOra) : Dimension -dimensionMinimaCh() : Dimension -dimensionMinimaGFlujo() : Dimension -pertenece(arr : ArrPuntosOra, p : Punto) : boolean -pertenece(gch : GrafCentHidroOra, p : Punto) : boolean -setZoomCentHidro(zoom : int) : void -setZoomGrafFlujo(zoom : int) : void 1 1 GrafCuencaOra -alerta : boolean -codigo : String -frm : frmNodo -graf : ElipseOra -zoom : int -alertaEmb() : boolean +dibujar(d : Dibujante) : void +dimensionMinima() : Dimension +dlgAtributos() : void +getZoom() : int +loadFigura() : void +pertenece(p : Punto) : boolean +setZoom(zoom : int) : void 1 1 1 ElipseOra 1 frmEmbalse 1 1 frmFlujo Como puede observar, utilizamos las clases que referencian a un objeto y no las que almacenan directamente el objeto. Esto se debe a que estas últimas, almacenan una copia del objeto persistente, lo que dificulta la modificación del objeto y la consistencia entre la vista del usuario y la base de datos. Por el contrario, las referencias de objetos, permiten a las aplicaciones obtener y modificar el objeto persistente referenciado en cualquier momento, a través de los métodos setValue(C) (actualiza el objeto referenciado con C) y getValue() (obtiene el objeto referenciado). 163 Una aplicación de ejemplo – Diseño del prototipo La pantalla principal del prototipo es la clase frame1 cuyo código fuente puede ser visto en C.4.2.1. El siguiente diagrama muestra la relación que existe entre Frame1 y las demás clases. JPanel 1 BDObjCuenca 1 Frame1 CuencaOraRef 1 1 1 1 FrmConeccion 1 1 LienzoCuenca La clase Frame1, además de los atributos que son parte de la interfaz gráfica estándar, cuenta con cuatro atributos, uno es del tipo CuencaOraRef, que almacena una referencia a la cuenca que actualmente está siendo desplegada. Otro es del tipo FrmConexion, que permite desplegar la ventana de conexión y almacenar los datos necesarios para realizarla. Otro es del tipo LienzoCuenca que almacena y muestra los objetos de la cuenca. Y por último, un atributo del tipo BDObjCuenca. La clase BDObjCuenca es una especialización de la clase ConectorJdbc que encapsula la conexión entre java y la base de datos, almacenando los datos básicos para la conexión. Estos son: el nombre del usuario (atributo usr), la contraseña (passwd), el driver a utilizar para la conexión (driver) y la URL JDBC que indica la dirección de la base de datos (url). El Atributo Connection, almacena la conexión entre java y la base de datos. Los métodos de la clase ConectorJdbc permiten abrir y cerrar una conexión, consultar el estado de la conexión (abierta o cerrada), obtener y modificar el estado de los atributos (set y get). La clase BDObjCuenca, no incorpora nuevos atributos, pero sí nuevos métodos que permiten obtener los objetos de una cuenca, ingresar cuencas, ríos y flujos a la base de datos, eliminar ríos y cuencas. El siguiente diagrama, muestra dicha jerarquía: 164 Una aplicación de ejemplo – Diseño del prototipo ConectorJdbc connection : OracleConnection driver : String passwd : String url : String usr : String +close() : void +getConnection() : Connection +getDriver() : String +getOracleConnection() : OracleConnection +getPasswd() : String +getUrl() : String +getUsr() : String +isClosed() : boolean +isOpen() : boolean +open(url : String, driverName : String, user : String, passwd : String) : void +setDriver(driver : String) : void +setPasswd(passwd : String) : void +setUrl(url : String) : void +setUsr(usr : String) : void BDObjCuenca +eliminar(c : CuencaOra) : void +eliminar(c : RioOra) : void +getAllEmbalseoraRef(c : CuencaOra) : Vector +getAllFigRef(c : CuencaOra) : Vector +getAllFlujosOraRef(c : CuencaOra) : Vector +getAllNodoOraRef(c : CuencaOra) : Vector +getCuenca(codigo : String) : CuencaOra +getCuencaRef(codigo : String) : CuencaOraRe +getRioRef(codigo : String) : RioOraRef +Insertar(c : CuencaOra) : void +insertar(flujo : FlujoOra) : GuiFlujo +Insertar(c : RioOra) : void La siguiente pantalla corresponde a Frame1, en ella podemos apreciar el comportamiento final de las clases Frame1, LienzoCuenca, GuiNodo, GuiFlujo y GuiEmbalse. 165 Una aplicación de ejemplo – Diseño del prototipo La parte blanca, donde se dibujan los objetos de la cuenca, corresponde a una instancia de la clase LienzoCuenca. Las flechas corresponden a instancias de la clase GuiFlujo, los óvalos a GuiNodo y los triángulos a GuiEmbalse. La misma imagen, pero de un tamaño más grande, puede ser vista en C.3.3 Las ventanas (C.3.2), restantes: Frame1_AboutBox FrmCuenca (ver C.3.4), (C.3.17), FrmAbrirCuenca FrmModificarRio (C.3.7), FrmResultConsulta (C.3.6), FrmSimular (ver C.3.5), FrmZoom (ver C.3.5), son llamadas desde Frame1, dependiendo de la opción del menú utilizada por el usuario. Para conocer las opciones del menú disponible vea las pantallas desde C.3.18 a la C.3.23 6.3.2 Diagramas de colaboración 6.3.2.1 Simulación El método que realiza la simulación corresponde a Simular() del tipo definido en Oracle Cuenca. Este método es ejecutado en el servidor, y es invocado desde la aplicación java a partir de un objeto del tipo CuencaOra. Antes de mostrar el diagrama de colaboración para este método, veremos el contexto en el que es invocado. Cuando el usuario escoge la opción del menú cuenca (D.3.18) “Iniciar simulación...” de la clase Frame1. Esta opción abre la ventana de simulación (FRMSimular), pasándole como parámetro una referencia de Frame1 y otra al atributo dataBase de Frame1 que almacena la conexión abierta entre el programa y la automáticamente, base es de datos. invocado Al abrir la ventana FRMSimular, por su el sistema método this_windowOpened(...), que a su vez invoca al método iniciarSim() de la clase FRMSimular. El método inicairSim() obtiene la cuenca mostrada por Frame1 y la almacena en una instancia llamada cuenca que es del tipo CuencaOra. Luego invoca al método Simular() sobre Cuenca, el cual realiza 166 Una aplicación de ejemplo – Diseño del prototipo la simulación. Finalizado el método Simular(), es invocado el método reloadFiguras() de la instancia lienzo, (que es un atributo de Frame1 del tipo LienzoCuenca) para actualizar los objetos mostrados. El siguiente Diagrama de colaboración muestra la ejecución del método Simular(), el cual es ejecutado en la base de datos. La iteración es de 1..6 porque son 6 mesde de simulación. En cada mes, invocaremos el método distribuirCaudal(i) sobre todos los nodos de la cuenca Simular() Conjunto de nodos que pertenecen a la cuenca ordenados. 1*:[i:=1..6] [para cada] no:=siguiente( ) :Cuenca «cursor Sql» :Nodo 2.4a: [existe embalse] entrada(Ent, i ) :Embalse 2: distribuirCaudal(i) 2.4b*: [no existe embalse] fs:=siguiente() 2.3: setCaudal(Ent, i ) no:Nodo 2.6b*: [no existe embalse] fs:=siguiente() 2.5b: [no existe embalse] dem:=dem+Demanda(Ent, i ) :arr_caudal «cursor Sql» :Flujo f:Flujo 2.7b: [no existe embalse] entrada(prop, i) prop, contiene el caudal proporcional a la demanda del flujo Atributo del nodo no 2.2: Ent=Ent+Salida( i ) 2.1*: [para cada] f:=siguiente( ) f:Flujo «cursor Sql» no:Nodo :Flujo conjunto de flujos que aportan caudales a un nodo El método simular() de la cuenca, invoca al método distribuirCaudal(...), sobre cada uno de sus nodos, para cada mes de la simulación. Para la distribución de caudales, el nodo calcula el total de caudal de entrada a partir de los flujos que le aportan caudal. Para ello, invoca al método salida(...) 167 Una aplicación de ejemplo – Diseño del prototipo sobre ellos. Luego, almacena dicho caudal en su arreglo de caudales, invocando el método SetCaudal(...) sobre el atributo caudales. Si el nodo tiene un embalse, todo el caudal de entrada es aportado a él, para ello invoca al método del embalse entrada(...). Si no hay un embalse en el nodo, entonces, se distribuye el caudal de entrada, proporcional a la demanda de los flujos que extraen caudal del nodo. Para calcular la demanda total, invoca el método demanda(...) sobre todos los flujos que le extraen caudal, y acumula su resultado. Luego, es asignado el caudal que le corresponde a cada uno de los flujos con el método entrada(...). 6.3.3 Limitaciones Encontradas 6.3.3.1 Limitaciones de PL/SQL • No podemos navegar a través de los atributos del tipo REF de un tipo. • No podemos actualizar un atributo de un objeto persistente directamente, tenemos que utilizar UPDATE • No podemos modificar un atributo en funciones. • Sólo podemos invocar métodos del tipo procedimiento en objetos transitorios. No podemos utilizar esta clase de métodos dentro de predicados SELECT, UPDATE, O DELETE. 6.3.3.2 Limitaciones de Oracle y la exportación de tipos • Cuando exporto un tipo a java con JPUB, todos los métodos son ejecutados por el servidor. 168 7 Conclusiones A pesar de las limitaciones del estándar para el manejo de objetos, este obtiene una buena calificación en nuestra evaluación, un 78,25%. Lo que nos permite concluir que, en la próxima versión del estándar (SQL:200n), estará cubriendo prácticamente el 100% de las características que debe tener una base de datos orientada a objeto. Por otro lado, Oracle 8i obtiene una calificación de 70,58%, más baja que el estándar, esto se debe principalmente a la falta de herencia, un encapsulamiento incompleto, y el deficiente soporte a la evolución de esquemas. El estándar, con sus capacidades y limitaciones, es lo suficientemente robusto para permitir que los desarrolladores lo utilicen para hacer persistir sus objetos, puesto que las limitaciones son pocas y en áreas no fundamentales. Pero Oracle 8i, por su falta de herencia, hace que el desarrollo de aplicaciones orientadas a objeto en él sea muy complejo y poco productivo. Es de esperar que en uno o dos años más ya existan productos que implementen todas las instrucciones del estándar SQL:1999. Uno de los beneficios de la tecnología de objetos incluida en el estándar SQL:1999 y en Oracle 8i es permitir que los usuarios incrementen el conjunto de tipos de datos provisto por el sistema. Esto permite la reutilización del código, lo que disminuye el tiempo de desarrollo y la complejidad. Además, trae como consecuencia una mayor eficiencia a la hora de mantener sistemas, porque bastaría con modificar un tipo (ALTER TYPE) para que automáticamente se modifiquen todas las tablas y tipos que dependen de él. Lamentablemente dicha actualización no está presente en Oracle 8i aunque cuenta con el predicado ALTER TYPE, este sólo permite agregar nuevos métodos, lo cual es muy limitado. En SQL:1999 esto sí es posible, pero tiene algunas restricciones. La herencia y el polimorfismo, también traen beneficios a las bases de datos, puesto que permiten extender el sistema fácilmente para que se adapte a los cambios de requerimientos. Permitiendo así un desarrollo incremental, lo que disminuye la complejidad del desarrollo. Estas características están presentes en SQL:1999, pero no en Oracle 8i. 170 Conclusiones Otra de las capacidades de SQL:1999 y de Oracle 8i, son las vistas de objetos. Estas permiten definir una capa de orientación a objeto sobre datos relacionales, lo que elimina la necesidad de migrar las tablas relacionales a tablas de objetos. El contar con el estándar SQL:1999 garantiza a las aplicaciones una compatibilidad, portabilidad e independencia entre las distintas bases de datos que implementan el estándar Una limitación del estándar SQL:1999 y Oracle 8i es que carece de la funcionalidad para manejar versiones y configuraciones de objetos. Esta es la desventaja más grande entre las bases de datos objeto-relacional y las bases de datos orientadas a objeto puras. Gracias a la implementación del prototipo del capítulo 6, podemos deducir que la interacción que logramos entre Oracle y Java es bastante transparente, gracias a la utilidad JPUB, que permite generar las clases Java a partir de los tipos definidos en Oracle. El presente trabajo puede ser complementado con una investigación similar a ésta pero sobre el estándar ODMG-93 y algunas bases de datos orientadas a objeto puras que lo implementen. Y a partir ello realizar una comparación entre los estándares y los productos para determinar cuál de los dos resuelve mejor el problema de la persistencia. Otro trabajo interesante sería, realizar la misma descripción y análisis hecho para Oracle 8i, sobre una o más bases de datos objeto relacional, como puede ser Informix o DB2 DE IBM, y luego realizar una comparación entre ellas. 171 Apéndice A: Sintaxis de SQL:1999 Relacionada con la OO. A.1 Leyenda Para la descripción del lenguaje se utiliza una sintaxis extendida de la gramática BNF (Backus Normal Form o Forma normal de Backus) [25]. En la gramática BNF, cada elemento sintáctico es conocido como un símbolo no terminal, el cual es definido por el significado de una regla de producción [25]. Esto define el elemento en términos de una fórmula consistente de: caracteres, cadenas de caracteres, y elementos sintácticos que pueden ser usados para formar una instancia del [25]. La simbología es la siguiente: Símbolo Significado < > Las palabras encerradas en estos símbolos, representa un símbolo no terminal. El operador de definición es utilizado en una regla de ::= producción para separar el elemento definido por la regla y su definición. A la izquierda del operador, aparece el símbolo no terminal, y a la derecha, la fórmula que lo define. [ ] Los corchetes indican que el elemento en una fórmula es { } Las llaves agrupan elementos en una fórmula. | Indica que la porción de la fórmula que aparece después de la ... Indica que el elemento opcional. barra es una alternativa a la porción que precede a la barra. que aparece antes de los puntos suspensivos, puede ser repetido n veces La sintaxis expuesta en este apéndice es tan sólo un extracto del estándar, por ser éste de gran tamaño (1.196 páginas). A.2 Sentencias A.2.1 Creación de tipos (CREATE TYPE) Especifica un tipo de datos definido por el usuario. Formato: <user-defined type definition> ::= CREATE TYPE <user-defined type body> <user-defined type body> ::= <user-defined type name> [ <subtype clause> ] [ AS <representation> ] [ <instantiable clause> ] <finality> [ <reference type specification> ] [ <cast option> ] [ <method specification list> ] <subtype clause> ::= UNDER <supertype name> <supertype name> ::= <user-defined type> <representation> ::= <predefined type>| <member list> <member list> ::= <left paren> <member> [ { <comma> <member> }... ] <right paren> <member> ::= <attribute definition> <instantiable clause> ::= INSTANTIABLE | NOT INSTANTIABLE <finality> ::= FINAL | NOT FINAL <reference type specification> ::= <user-defined representation> | <derived representation> | <system-generated representation> <user-defined representation> ::= REF USING <predefined type> [ <ref cast option> ] <derived representation> ::= REF FROM <list of attributes> <system-generated representation> ::= REF IS SYSTEM GENERATED <ref cast option> ::= [ <cast to ref> ] [ <cast to type> ] <cast to ref> ::= CAST <left paren> SOURCE AS REF <right paren> WITH <cast to ref identifier> Apéndice A – Sintaxis SQL:1999 <cast to ref identifier> ::= <identifier> <cast to type> ::= CAST <left paren> REF AS SOURCE <right paren> WITH <cast to type identifier> <cast to type identifier> ::=<identifier> <list of attributes> ::= <left paren> <attribute name> [ { <comma> <attribute name> }...] <right paren> <cast option> ::= [ <cast to distinct> ] [ <cast to source> ] <cast to distinct> ::= CAST <left paren> SOURCE AS DISTINCT <right paren> WITH <cast to distinct identifier> <cast to distinct identifier> ::= <identifier> <cast to source> ::= CAST <left paren> DISTINCT AS SOURCE <right paren> WITH <cast to source identifier> <cast to source identifier> ::= <identifier> <method specification list> ::= <method specification> [ { <comma> <method specification> }... ] <method specification> ::= <original method specification> | <overriding method specification> <original method specification> ::= <partial method specification> [ SELF AS RESULT ] [ SELF AS LOCATOR ] [ <method characteristics> ] <overriding method specification> ::= OVERRIDING <partial method specification> <partial method specification> ::= [ INSTANCE | STATIC ] METHOD <method name> <SQL parameter declaration list> <returns clause> [ SPECIFIC <specific name> ] <method characteristics> ::= <method characteristic>... <method characteristic> ::= <language clause> | <parameter style clause> | <deterministic characteristic> | <SQL-data access indication> | <null-call clause> | <transform group specification> 175 Apéndice A – Sintaxis SQL:1999 <language clause> ::= LANGUAGE <language name> <language name> ::= ADA |C |COBOL | FORTRAN | MUMPS | PASCAL | PLI | SQL A.2.2 Definición de atributos Define los atributos de un tipo estructurado Formato: <attribute definition> ::= <attribute name> <data type> [ <reference scope check> ] [ <attribute default> ] [ <collate clause> ] <attribute default> ::= <default clause> <default clause> ::= DEFAULT <default option> <default option> ::= <literal> | <datetime value function> | USER | CURRENT_USER | CURRENT_ROLE | SESSION_USER | SYSTEM_USER | CURRENT_PATH | <implicitly typed value specification> A.2.3 Modificación de tipos (ALTER TYPE) Permite agregar y eliminar atributos o métodos. Formato: <alter type statement> ::= ALTER TYPE <path-resolved user-defined type name> <alter type action> <alter type action> ::= <add attribute definition> | <drop attribute definition> | <add original method specification> | <add overriding method specification> | <drop method specification> <add attribute definition> ::= ADD ATTRIBUTE <attribute definition> <drop attribute definition> ::= DROP ATTRIBUTE <attribute name> RESTRICT <add original method specification> ::= ADD <original method specification> <add overriding method specification> ::= 176 Apéndice A – Sintaxis SQL:1999 ADD <overriding method specification> <drop method specification> ::= DROP <specific routine designator> RESTRICT <drop data type statement> ::= DROP TYPE <path-resolved user-defined type name> <drop behaviour> <drop behaviour> ::= CASCADE | RESTRICT <specific routine designator> ::= SPECIFIC <routine type> <specific name> | <routine type> <member name> [ FOR <path-resolved user-defined type name> ] <routine type> ::= ROUTINE | FUNCTION | PROCEDURE | [ INSTANCE | STATIC ] METHOD <member name> ::= <schema qualified routine name> [ <data type list> ] <data type list> ::= <left paren> [ <data type> [ { <comma> <data type> }... ] ] <rige paren> A.2.4 Creación de métodos, funciones y procedimientos Permite definir métodos, funciones y procedimientos almacenados. Formato: <SQL-invoked routine> ::= <schema routine> <schema routine> ::= <schema procedure> | <schema function> <schema procedure> ::= CREATE <SQL-invoked procedure> <schema function> ::= CREATE <SQL-invoked function> <SQL-invoked procedure> ::= PROCEDURE <schema qualified routine name> <SQL parameter declaration list> <routine characteristics> <routine body> <SQL-invoked function> ::= { <function specification> | <method specification designator> } <routine body> <SQL parameter declaration list> ::= <left paren> [ <SQL parameter declaration> [ { <comma> <SQL parameter declaration> }... ] ] <right paren> <SQL parameter declaration> ::= [ <parameter mode> ] [ <SQL parameter name> ] <parameter type> 177 Apéndice A – Sintaxis SQL:1999 [ RESULT ] <parameter mode> ::= IN | OUT | INOUT <parameter type> ::= <data type> [ <locator indication> ] <locator indication> ::= AS LOCATOR <function specification> ::= FUNCTION <schema qualified routine name> <SQL parameter declaration list> <returns clause> <routine characteristics> [ <dispatch clause> ] <method specification designator> ::= [ INSTANCE | STATIC ] METHOD <method name> <SQL parameter declaration list> [ <returns clause> ] FOR <path-resolved user-defined type name> <routine characteristics> ::=[ <routine characteristic>... ] <routine characteristic> ::= <language clause> | <parameter style clause> | SPECIFIC <specific name> | <deterministic characteristic> | <SQL-data access indication> | <null-call clause> | <transform group specification> | <dynamic result sets characteristic> <dynamic result sets characteristic> ::= DYNAMIC RESULT SETS <maximum dynamic result sets> <parameter style clause> ::= PARAMETER STYLE <parameter style> <dispatch clause> ::= STATIC DISPATCH <returns clause> ::= RETURNS <returns data type> [ <result cast> ] <result cast> ::= CAST FROM <result cast from type> <result cast from type> ::= <data type> [ <locator indication> ] <returns data type> ::= <data type> [ <locator indication> ] <routine body> ::= <SQL routine body>| <external body reference> <SQL routine body> ::= <SQL procedure statement> <external body reference> ::= EXTERNAL [ NAME <external routine name> ] [ <parameter style clause> ] [ <external security clause> ] <external security clause> ::= EXTERNAL SECURITY DEFINER | EXTERNAL SECURITY INVOKER 178 Apéndice A – Sintaxis SQL:1999 | EXTERNAL SECURITY IMPLEMENTATION DEFINED <parameter style> ::= SQL | GENERAL <deterministic characteristic> ::= DETERMINISTIC | NOT DETERMINISTIC <SQL-data access indication> ::= NO SQL | CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA <null-call clause> ::= RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT <maximum dynamic result sets> ::= <unsigned integer> <transform group specification> ::= TRANSFORM GROUP { <single group specification> | <multiple group specification> } <single group specification> ::= <group name> <multiple group specification> ::= <group specification> [ { <comma> <group specification> }... ] <group specification> ::= <group name> FOR TYPE <path-resolved user-defined type name> A.2.5 Modificación de métodos, funciones y procedimientos Permite modificar la definición de una función, procedimiento o de un método. Formato: <alter routine statement> ::= ALTER <specific routine designator> <alter routine characteristics> <alter routine behaviour> <alter routine characteristics> ::= <alter routine characteristic>... <alter routine characteristic> ::= <language clause> | <parameter style clause> | <SQL-data access indication> | <null-call clause> | <dynamic result sets characteristic> | NAME <external routine name> <alter routine behaviour> ::= RESTRICT <drop routine statement> ::= DROP <specific routine designator> <drop behaviour> <specific routine designator> ::= SPECIFIC <routine type> <specific name> | <routine type> <member name> [ FOR <path-resolved user-defined type name> ] 179 Apéndice A – Sintaxis SQL:1999 <routine type> ::= ROUTINE | FUNCTION | PROCEDURE | [ INSTANCE | STATIC ] METHOD <member name> ::= <schema qualified routine name> [ <data type list> ] <data type list> ::= <left paren> [ <data type> [ { <comma> <data type> }... ] ] <rige paren> A.2.6 CAST definido por el usuario Permite crear un CAST definido por el usuario Formato: <user-defined cast definition> ::= CREATE CAST <left paren> <source data type> AS <target data type> <right paren> WITH <cast function> [ AS ASSIGNMENT ] <cast function> ::= <specific routine designator> <source data type> ::= <data type> <target data type> ::= <data type> <specific routine designator> ::= SPECIFIC <routine type> <specific name> | <routine type> <member name> [ FOR <path-resolved user-defined type name> ] <routine type> ::= ROUTINE | FUNCTION | PROCEDURE | [ INSTANCE | STATIC ] METHOD <member name> ::= <schema qualified routine name> [ <data type list> ] <data type list> ::= <left paren> [ <data type> [ { <comma> <data type> }... ] ] <rige paren> 180 Apéndice A – Sintaxis SQL:1999 A.2.7 Eliminación de un CAST Permite eliminar un CAST definido por el usuario Formato: <drop user-defined cast statement> ::= DROP CAST <left paren> <source data type> AS <target data type> <right paren> <drop behaviour> A.2.8 Definición del ORDERING de un tipo. Define la forma en que el sistema debe comparar dos instancias de un mismo tipo. Formato: <user-defined ordering definition> ::= CREATE ORDERING FOR <path-resolved user-defined type name> <ordering form> <ordering form> ::= <equals ordering form> | <full ordering form> <equals ordering form> ::=EQUALS ONLY BY <ordering category> <full ordering form> ::=ORDER FULL BY <ordering category> <ordering category> ::= <relative category> | <map category> | <state category> <relative category> ::= RELATIVE WITH <relative function specification> <map category> ::= MAP WITH <map function specification> <state category> ::= STATE [ <specific name> ] <relative function specification> ::= <specific routine designator> <map function specification> ::= <specific routine designator> <specific routine designator> ::= SPECIFIC <routine type> <specific name> | <routine type> <member name> [ FOR <path-resolved user-defined type name> ] <routine type> ::= ROUTINE | FUNCTION | PROCEDURE 181 Apéndice A – Sintaxis SQL:1999 | [ INSTANCE | STATIC ] METHOD <member name> ::= <schema qualified routine name> [ <data type list> ] <data type list> ::= <left paren> [ <data type> [ { <comma> <data type> }... ] ] <rige paren> A.2.9 Eliminación de un user definer ordering Permite eliminar la definición de un user definer ordering. Formato: <drop user-defined ordering statement> ::= DROP ORDERING FOR <path-resolvedEuser-defined type name> <drop behaviour> A.2.10 Funciones de transformación de tipos Permite definir una o más funciones de transformación para tipos definidos por el usuario. Formato: <transform definition> ::= CREATE { TRANSFORM | TRANSFORMS } FOR <path-resolved user-defined type name> <transform group>... <transform group> ::= <group name> <left paren> <transform element list> <right paren> <group name> ::= <identifier> <transform element list> ::= <transform element> [ <comma> <transform element> ] <transform element> ::= <to sql> | <from sql> <to sql> ::= TO SQL WITH <to sql function> <from sql> ::= FROM SQL WITH <from sql function> <to sql function> ::= <speciic routine designator> <from sql function> ::= <specific routine designator> <specific routine designator> ::= SPECIFIC <routine type> <specific name> | <routine type> <member name> [ FOR <path-resolved user-defined type name> ] <routine type> ::= ROUTINE | FUNCTION 182 Apéndice A – Sintaxis SQL:1999 | PROCEDURE | [ INSTANCE | STATIC ] METHOD <member name> ::= <schema qualified routine name> [ <data type list> ] <data type list> ::= <left paren> [ <data type> [ { <comma> <data type> }... ] ] <rige paren> A.2.11 Eliminación de funciones de transformación Permite eliminar funciones de transformación de tipos definidos por el usuario. Formato: <drop transform statement> ::= DROP { TRANSFORM | TRANSFORMS } <transforms to be dropped> FOR <path-resolved user-defined type name> <drop behaviour> <transforms to be dropped> ::= ALL | <transform group element> <transform group element> ::= <group name> A.2.12 Definición de Tablas (CREATE TABLE) Permite definir tablas, tanto relacionales como typed tables, subtablas, y supertablas. Formato: <table definition> ::= CREATE [ <table scope> ] TABLE <table name> <table contents source> [ ON COMMIT <table commit action> ROWS ] <table contents source> ::= <table element list> | OF <user-defined type> [ <subtable clause> ] [ <table element list> ] <table scope> ::= <global or local> TEMPORARY <global or local> ::= GLOBAL | LOCAL <table commit action> ::= PRESERVE | DELETE <table element list> ::= <left paren> <table element> [ { <comma> <table element> }... ] <right paren> 183 Apéndice A – Sintaxis SQL:1999 <table element> ::= <column definition> | <table constraint definition> | <like clause> | <self-referencing column specification> | <column options> <self-referencing column specification> ::= REF IS <self-referencing column name> <reference generation> <reference generation> ::= SYSTEM GENERATED | USER GENERATED | DERIVED <self-referencing column name> ::= <column name> <column options> ::= <column name> WITH OPTIONS <column option list> <column [ [ [ [ option list> ::= <scope clause> ] <default clause> ] <column constraint definition>... ] <collate clause> ] <subtable clause> ::= UNDER <supertable clause> <supertable clause> ::= <supertable name> <supertable name> ::= <table name> <like clause> ::= LIKE <table name> <column definition> ::= <column name> { <data type> | <domain name> } [ <reference scope check> ] [ { <default clause> | <identity column specification> } ] [ <column constraint definition>... ] [ <collate clause> ] <column constraint definition> ::= [ <constraint name definition> ] <column constraint> [ <constraint characteristics> ] <column constraint> ::= NOT NULL | <unique specification> | <references specification> | <check constraint definition> <reference scope check> ::= REFERENCES ARE [ NOT ] CHECKED [ ON DELETE <reference scope check action> ] <reference scope check action> ::= <referential action> <identity column specification> ::= 184 Apéndice A – Sintaxis SQL:1999 GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ <start and increment> ] <start and increment> ::= <left paren> <start or increment 1> [ <comma> <start or increment 2> ] <right paren> <start or increment 1> ::= <start or increment> <start or increment 2> ::= <start or increment> <start or increment>::= <start specification> | <increment specification> <start specification> ::= START WITH <start value> <increment specification> ::= INCREMENT BY <increment> <start value> ::= <signed numeric literal> <increment> ::= <signed numeric literal> <table constraint definition> ::= [ <constraint name definition> ] <table constraint> [ <constraint characteristics> ] <table constraint> ::= <unique constraint definition> | <referential constraint definition> | <check constraint definition> <unique constraint definition> ::= <unique specification> <left paren> <unique column list> <right paren> | UNIQUE ( VALUE ) <unique specification> ::= UNIQUE | PRIMARY KEY <unique column list> ::= <column name list> <referential constraint definition> ::= FOREIGN KEY <left paren> <referencing columns> <right paren> <references specification> <references specification> ::= REFERENCES <referenced table and columns> [ MATCH <match type> ] [ <referential triggered action> ] <match type> ::= FULL | PARTIAL | SIMPLE <referencing columns> ::= <reference column list> <referenced table and columns> ::= <table name> [ <left paren> <reference column list> <right paren> ] 185 Apéndice A – Sintaxis SQL:1999 <reference column list> ::= <column name list> <referential triggered action> ::= <update rule> [ <delete rule> ] | <delete rule> [ <update rule> ] <update rule> ::= ON UPDATE <referential action> <delete rule> ::= ON DELETE <referential action> <referential action> ::= CASCADE | SET NULL | SET DEFAULT | RESTRICT | NO ACTION <check constraint definition> ::= CHECK <left paren> <search condition> <right paren> A.2.13 Modificación de tablas (ALTER TABLE) Permite cambiar la definición de una tablas. Formato: <alter table statement> ::= ALTER TABLE <table name> <alter table action> <alter table action> ::= <add column definition> | <alter column definition> | <drop column definition> | <add table constraint definition> | <drop table constraint definition> <add column definition> ::= ADD [ COLUMN ] <column definition> <alter column definition> ::= ALTER [ COLUMN ] <column name> <alter column action> <alter column action> ::= <set column default clause> | <drop column default clause> | <add column scope clause> | <drop column scope clause> <set column default clause> ::= SET <default clause> <drop column default clause> ::= DROP DEFAULT <add column scope clause> ::= ADD <scope clause> 186 Apéndice A – Sintaxis SQL:1999 <drop column scope clause> ::= DROP SCOPE <drop behaviour> <drop column definition> ::= DROP [ COLUMN ] <column name> <drop behaviour> <add table constraint definition> ::= ADD <table constraint definition> <drop table constraint definition> ::= DROP CONSTRAINT <constraint name> <drop behaviour> A.2.14 Eliminación de tablas (DROP TABLE) Permite eliminar una tabla. Formato: <drop table statement> ::= DROP TABLE <table name> <drop behaviour> A.2.15 Inserción de filas (INSERT INTO) Permite insertar una fila en una tabla. Formato: <insert statement> ::= INSERT INTO <insertion target> <insert columns and source> <insertion target> ::=<table name> <insert columns and source> ::= <from subquery> | <from constructor> | <from default> <from subquery> ::= [ <left paren> <insert column list> <right paren> ] [ override clause> ] <query expression> <from constructor> ::= [ <left paren> <insert column list> <right paren> ] [ <override clause> ] <contextually typed table value constructor> <override clause> ::= OVERRIDING USER VALUE | OVERRIDING SYSTEM VALUE <from default> ::= DEFAULT VALUES <insert column list> ::= <column name list> 187 Apéndice A – Sintaxis SQL:1999 <contextually typed table value constructor> ::= VALUES <contextually typed row value expression list> <contextually typed row value expression list> ::= <contextually typed row value expression> [ { <comma> <contextually typed row value expression> }... ] <contextually typed row value expression> ::= <row value special case> | <contextually typed row value constructor> <row value special case> ::= <value specification> | <value expression> A.2.16 Actualización de filas (UPDATE) Permite modificar el contenido de una fila existente. Formato: <update statement: searched> ::= UPDATE <target table> SET <set clause list> [ WHERE <search condition> ] <set clause list> ::= <set clause> [ { <comma> <set clause> }... ] <set clause> ::= <update target> <equals operator> <update source> | <mutated set clause> <equals operator> <update source> <update target> ::= <object column> | <object column> <left bracket or trigraph> <simple value specification> <right bracket or trigraph> <object column> ::= <column name> <mutated set clause> ::= <mutated target> <period> <method name> <mutated target> ::= <object column> | <mutated set clause> <update source> ::= <value expression> | <contextually typed value specification> A.2.17 Eliminación de filas (DELETE FROM) Permite modificar el contenido de una fila existente. Formato: 188 Apéndice A – Sintaxis SQL:1999 <delete statement: searched> ::= DELETE FROM <target table> [ WHERE <search condition> ] <target table> ::= <table name> | ONLY <left paren> <table name> <right paren> 189 Apéndice B: UML B.1 ¿Qué es UML? UML cuya sigla significa Unified Modeling Language (Lenguaje Unificado de Modelado) fue desarrollado por Grady Booch, Jim Rumbaugh e Ivar Jacobson en respuesta a una serie de necesidades [6]. UML es un lenguaje que permite especificar, visualizar, construir y documentar artefactos de sistemas de software, como también, modelar negocios y otros sistemas no software [6]. El UML representa una colección de las mejores prácticas de ingeniería que han demostrado ser exitosas en el modelamiento de sistemas grandes y complejos [37]. Como se manifiesta en el documento UML Sumary [37], los objetivos principales que se perseguían al diseñar UML fueron: “Proporcionar a los usuarios un lenguaje de modelización visual listo para ser utilizado que sirviera para desarrollar e intercambiar modelos con significado. Proporcionar mecanismos de extensibilidad y especialización para extender los conceptos esenciales. Ser independiente de cualquier lenguaje de programación y de cualquier proceso de desarrollo. Proporcionar una base formal para comprender el lenguaje de modelización. Fomentar el crecimiento del mercado de las herramientas orientadas a objeto Dar soporte a conceptos de nivel superior, tales como, colaboración, marcos de trabajo, patrones y componentes. Integrar las mejores prácticas” En UML existen diversos diagramas, estos son [6]: Diagrama de clases. En este diagrama se definen las clases y cómo se relacionan entre sí. Diagrama de Objetos. conjunto de objetos. Es un diagrama de clases para sólo un Apéndice C: UML Diagrama de casos de uso. Un caso de uso muestra la interacción entre distintos actores (por ejemplo clientes, empleado) y casos de uso. Diagrama de secuencias. Un diagrama de secuencia muestra una interacción de los objetos dispuestos en una secuencia temporal. Diagrama de colaboración. Un diagrama de colaboración muestra los objetos y mensajes que se han pasado entre dichos objetos para llevar a cabo alguna función. Diagramas de estados. Los diagramas de estado son diagramas estándares de transición de estados. Muestran en qué estado puede encontrarse un objeto y qué puede hacer que dicho objeto cambie de estado. Diagrama de actividad. Un diagrama de actividad es un tipo de diagrama de flujo. Representa puntos de operación y de decisión. Diagrama de realización. Un diagrama de realización muestra los componentes del sistema y la forma en que interactúan entre sí. El diagrama de realización puede mostrar los componentes software o hardware del sistema. Estos diagramas se suelen agrupar en vistas y las vistas se agrupan en tres áreas [39]: • Clasificación estructural. Describe los elementos del sistema y sus relaciones, proporcionando la base para el comportamiento dinámico. • Comportamiento dinámico. describe el comportamiento de un sistema en el tiempo. • Gestión del modelo. Describe la organización del propio modelo en unidades jerárquicas denominadas paquetes. Existen varias construcciones previas que permiten extender la capacidad de UML pero de forma limitada. Estas construcciones son transversales a todas las vistas. La siguiente tabla [39] describe la agrupación antes descrita. 192 Apéndice C: UML Área Vista Diagrama estructural vista estática diagrama de clases vista de casos de uso diagrama de casos de uso vista de implementación diagrama de componentes vista de despliegue Dinámica diagrama de despliegue Vista de máquinas de diagrama de estados estados Vista de actividad Diagrama de actividad Vista de interacción Diagrama de secuencia Diagrama de colaboración Gestión del modelo Vistas de gestión del Diagrama de clases modelo Extensión del modelo Todas Todos En la siguiente sección presentaremos la notación para los diagramas utilizados para diseñar el prototipo. B.1.1 Diagramas de clases Los diagramas de clases son la base de los demás diagramas de UML. Ellos describen la relación que existe entre las distintas clases, siendo las principales: herencia, agregación, composición y asociación. La notación para una clase es un rectángulo con tres compartimentos separados con líneas verticales, el superior para el nombre, el central para los atributos y el inferior para sus métodos (ver figura 8): figura 8: Ejemplo de clase [27] 193 Apéndice C: UML Para indicar la visibilidad de un atributo o método, anteponemos uno de estos caracteres: + (público), - (privado), # (protegido). Además, Si el atributo o el método es estático, se subraya. Es posible ocultar uno o más compartimientos (preservando el del nombre), para facilitar la comprensión del diagrama. También, podemos ocultar aquellos métodos y atributos que forman parte de la implementación y no del dominio del problema. Esto permitirá definir un mismo diagramas, en distintos niveles de detalle (ver figura 9): Detalles suprimidos f i g u r a Detalle a nivel de análisis Detalles a nivel de implementación Figura 9: Clases en distintos niveles de detalle [27] B.1.1.1 Compartimentos adicionales Es posible agregar compartimentos definidos por el usuario o predefinidos, para mostrar otras propiedades del modelo como: reglas del negocio, excepciones, responsabilidades, etc [27]. Un compartimiento adicional, se señala con un nombre en la parte superior de él, que indique en forma clara su contenido (ver figura 10). Los compartimentos para los atributos y métodos, no necesitan llevar un nombre, aunque pude ser necesario para evitar ambigüedades, cuando es suprimido uno de ellos (ver figura 10) 194 Apéndice C: UML figura 10: Clase con compartimentos adicionales [27] B.1.1.2 Estereotipos Los estereotipos permiten extender la semántica de algún elemento de UML, por ejemplo las clases. Estos permiten definir restricciones adicionales, o indicar un modo de uso particular. Para denotar un estereotipo dentro de una clase, se escribe el nombre del estereotipo entre comillas («»), sobre el nombre de la clase. Por ejemplo, en la figura 11 tenemos una clase llamada PenTracker, que tiene el estereotipo «control». figura 11: Ejemplo de clase con estereotipo [27] 195 Apéndice C: UML B.1.1.3 Asociaciones Para representar las asociaciones de dos clases, utilizamos una línea continua que las une. En cada extremo de la asociación colocamos la multiplicidad y el rol que ocupa la clase en la asociación. El nombre de la asociación lo colocamos a lo largo de la línea pudiendo indicar la dirección en la que se debe leer el nombre con una punta de flecha triangular negra. Cuando la asociación posee atributos, entonces creamos una clase de asociación, la cual es indicada por una línea punteada que une la asociación a su clase (ver figura 12). Nombre Asociación Multiplicida Nombre del Rol Clase Asociación Autoasociación figura 12: Ejemplo de Asociaciones entre clases [27] [39] Una clase puede estar asociada consigo misma. autoasociación. En UML, esto se llama En el ejemplo anterior, tenemos una autoasociación en la clase trabajo. B.1.1.4 Agregación y composición Para la agregación utilizamos un rombo en blanco y para la composición un rombo negro, en el extremo correspondiente al compuesto, es decir, a la clase que posee a los componentes. Por ejemplo, consideremos la siguiente relación de agregación entre un Polígono y un Punto: 196 Apéndice C: UML 0..* Poligono Punto 3..* figura 13: Ejemplo de agregación Un polígono, posee 3 o más puntos que son sus vértices, y un punto puede pertenecer a muchos polígonos o a ninguno. En la composición, la multiplicidad en el extremo del compuesto puede ser como máximo una. Por ejemplo, veamos la relación de composición entre una Bicicleta y sus partes: 1 Pedal 1 Bicicleta 2 2 Freno 1 2 1 Rueda Marco 1 Asiento 1 Manubrio figura 14: Ejemplo de composición B.1.1.5 Generalización Para la generalización dibujamos como una flecha desde el hijo hacia el padre, utilizando un triángulo blanco en la punta. Por ejemplo, consideremos la siguiente jerarquía de generalización, con empleado como superclase y con las subclases gerente y vendedor (ver figura 15): 197 Apéndice C: UML Empleado Gerente Vendedor figura 15: Ejemplo generalización B.1.1.6 Restricciones y comentarios Una restricción es una expresión encerrada entre llaves que impone ciertas condiciones al modelo. Las restricciones pueden ser expresadas utilizando el lenguaje natural, notación de teoría de conjuntos, lenguajes de restricciones o lenguajes de programación. Para los comentarios se utilizan un rectángulo que tiene doblada la esquina superior derecha, pero también en ellos podemos incluir restricciones (ver figura 16). figura 16: Ejemplo de restricciones y comentarios [39] 198 Apéndice C: UML B.1.2 Diagrama de colaboración Los diagramas de colaboración nos permiten mostrar como interactúan los objetos de las distintas clases para implementar una operación. En un diagrama, en solamente mostramos los objetos que implementación de la operación, el resto se omite. participan la La notación de estos diagramas se compone de: • Objetos participantes • Paso de mensajes • Relaciones entre los objetos. • Roles de clasificación • Roles de asociación. Los objetos participantes son representados por un rectángulo, con el nombre del objeto, seguido por dos puntos (:) y el rol de clasificación que es el nombre de la clase del objeto, subrayado. El rol de asociación describe un enlace dentro de una colaboración. Los mensajes se muestran como flechas, ligadas a las líneas de la relación, que conectan a los objetos. La secuencia de mensajes la indicamos con número secuenciales que preceden a las descripciones del mensaje. Ejemplo: 199 Apéndice C: UML B.1.3 Para Más información. si necesita más información revise las referencia bibliográfica [6], [11], [27], [37] y [38]. 200 Apéndice C: Código Fuente del Prototipo y Pantallas Código Fuente C.1 Creación de tipos Los tipos serán mostrados en el orden que deben ser creados (CREATE TYPE). Como no podemos utilizar un tipo si este no está creado, debemos crear primero todos los tipos, y después sus cuerpos. Pero, por motivos didácticos hemos agrupado la creación de un tipo con la de su cuerpo, aunque esto causaría errores de compilación. C.1.1 array_caudal. Este es un tipo distinto que define un arreglo de seis elementos del tipo number. CREATE OR REPLACE TYPE array_caudal AS VARRAY(6) OF NUMBER / show errors C.1.2 arr_caudales Este tipo almacena los caudales de octubre a marzo. ATRIBUTOS Nombre Descripción Caudal Arreglo para los caudales de octubre a marzo (6 elementos) del tipo arr_caudal METODOS Nombre Descripción setCaudal(v,m) Modifica la posición M del arreglo caudal con el valor V getCaudal(m):num Retorna el valor de la posición m del arreglo caudal init() inicializa el arreglo con valor 0 para cada elemento CÓDIGO FUENTE 202 Código Fuente CREATE OR REPLACE TYPE arr_caudales as OBJECT ( -- atributos caudal array_caudal, -- métodos MEMBER PROCEDURE setCaudal(v IN NUMBER,m IN NUMBER), MEMBER FUNCTION getCaudal(m IN NUMBER) RETURN NUMBER, MEMBER PROCEDURE init ) / show errors CREATE OR REPLACE TYPE BODY arr_caudales AS -- modifica la posición M del arreglo caudales con el valor V MEMBER procedure setCaudal(v IN NUMBER,m IN NUMBER) IS BEGIN IF (m<=6) and (m>=1) then caudal(m) := v; else RAISE_APPLICATION_ERROR(-21100,'indice Fuera de Rango'); end if; END setCaudal; -- Retorna el valor v de la posición m MEMBER FUNCTION getCaudal(m IN NUMBER) RETURN NUMBER BEGIN RETURN (caudal(m)); END getCaudal; IS -- pone en cero el arreglo MEMBER Procedure init IS BEGIN FOR i IN 1..6 LOOP caudal(i):=0; END LOOP; END init; END; / show errors 203 Código Fuente C.1.3 Punto Este tipo almacena un punto de coordenadas (x,y) ATRIBUTOS Nombre Descripción X Coordenada x del punto. Y Coordenada y del punto. CÓDIGO FUENTE CREATE OR REPLACE TYPE Punto as OBJECT ( X NUMBER, --coordenada x del punto Y NUMBER --coordenada y del punto ) / show errors C.1.4 arr_puntos Crea un tipo de arreglo de 100.000 puntos. CREATE OR REPLACE TYPE / show errors arr_puntos AS VARRAY(100000) OF PUNTO C.1.5 Polilinea Este tipo almacena un conjunto de puntos que forman una polilínea (varios trazos de línea recta unidos) ATRIBUTOS Nombre Descripción LosPuntos Arreglo para almacenar los vértices que forman la polilinea Npuntos Entero que indica el número de vértices METODOS Nombre Descripción INIT() Inicializa la estructura. AddP(p) Agrega el punto p al final del arreglo LosPuntos. AddP(p,i) Agrega el punto p en la posición i del arreglo LosPuntos. Desplazando los puntos desde i hasta el final. indexOf(P):NUM Devuelve la posición del punto p. 204 Código Fuente GETP(i):punto Devuelve el punto de la posición i. SETP(p,i) Remplaza el punto que esta en i con p DELP(i) Borra el punto que está en i, desplazando los demás puntos a la izquierda para eliminar el hueco CÓDIGO FUENTE CREATE OR REPLACE TYPE Polilinea as OBJECT ( -- atributos LOSPUNTOS ARR_PUNTOS, NPUNTOS NUMBER, ESTILO NUMBER, -- métodos MEMBER PROCEDURE INIT, MEMBER PROCEDURE ADDP(p in Punto), EMBER PROCEDURE ADDP(p in Punto,i in number), MEMBER FUNCTION indexOf(P in punto) RETURN NUMBER, MEMBER FUNCTION GETP(i in number) RETURN PUNTO, MEMBER PROCEDURE SETP(p in Punto,i in number), MEMBER PROCEDURE DELP(i in number) ) / show errors CREATE OR REPLACE TYPE BODY Polilinea AS MEMBER PROCEDURE init IS BEGIN NPUNTOS := 0; LOSPUNTOS := ARR_PUNTOS(NULL); ESTILO := 1; END init; MEMBER PROCEDURE ADDP(p in Punto) IS BEGIN IF ((p<>null)and(NPUNTOS <= 100000)) then NPUNTOS := NPuntos + 1; lospuntos.extend; --agrega un casillero null al final LosPuntos(NPUNTOS):= P; ELSE RAISE_APPLICATION_ERROR(-21000,'Error al insertar el punto'); END IF; END ADDP; MEMBER PROCEDURE ADDP(p in Punto,i in number) IS cont number := npuntos; BEGIN IF ((P<>null)and((NPUNTOS <= 100000)and((i<=cont) and (i>=1)))) then NPUNTOS := NPuntos +1; lospuntos.extend; 205 Código Fuente ELSE END IF; END ADDP; while cont>=i loop LosPuntos(cont+1):=LosPuntos(cont); cont := cont - 1; end loop; LosPuntos(i):=P; RAISE_APPLICATION_ERROR(-21001,'Error al insertar el punto'); MEMBER FUNCTION indexOf(P in punto) RETURN NUMBER IS cont number:=1; BEGIN While (cont<=NPUNTOS) and ((P.x<>LosPuntos(cont).x) or (P.y<>LosPuntos(cont).y)) loop cont := cont + 1; end loop; if cont>NPUNTOS THEN return(-1); else return(cont); end if; END INDEXOF; MEMBER FUNCTION GETP(i in number) RETURN PUNTO IS BEGIN return(LosPuntos(i)); END GETP; MEMBER PROCEDURE SETP(p in Punto,i in number) is BEGIN LosPuntos(I) := P; END SETP; MEMBER PROCEDURE DELP(i in number) IS cont number := i; BEGIN IF (i<=npuntos) and (i>=1) then while cont<npuntos loop LosPuntos(cont):=LosPuntos(cont+1); cont := cont + 1; end loop; NPUNTOS := NPuntos - 1; lospuntos.trim;--elimina el ultimo elemento ELSE RAISE_APPLICATION_ERROR(-21001,'indice fuera de rango'); END IF; END DELP; END; / show errors 206 Código Fuente C.1.6 Cuadrado Cuadrado representado por sus cuatro vértices ATRIBUTOS Nombre Descripción a Punto del extremo superior izquierdo del cuadrado b Punto del extremo superior derecho del cuadrado c Punto del extremo inferior izquierdo del cuadrado d Punto del extremo inferior derecho del cuadrado CÓDIGO FUENTE CREATE OR REPLACE TYPE Cuadrado as OBJECT ( --propiedades a PUNTO, b PUNTO, c PUNTO, d PUNTO ) / show errors C.1.7 Elipse Elipse representada por su centro, ancho (paralelo al eje x) y su alto (paralelo al eje Y). ATRIBUTOS Nombre Descripción Centro Punto que indica el centro de la elipse. Ancho Entero que indica el ancho de la elipse. Alto Entero que indica el alto de la elipse. CÓDIGO FUENTE CREATE OR REPLACE TYPE elipse as OBJECT ( --propiedades Centro PUNTO, ANCHO NUMBER, ALTO NUMBER ) 207 Código Fuente / show errors C.1.8 Triangulo Triangulo representado por sus tres vértices. ATRIBUTOS Nombre Descripción a Punto que representa el vértice superior del triángulo b Punto que representa el vértice inferior izquierdo del triangulo c Punto que representa el vértice inferior derecho del triangulo CÓDIGO FUENTE CREATE OR REPLACE TYPE Triangulo as OBJECT ( --propiedades a PUNTO, b PUNTO, c PUNTO ) / show errors 208 Código Fuente C.1.9 Graf_flujo Este tipo representa un flujo genérico ATRIBUTOS Nombre Descripción pl Arreglo para almacenar los vértices que forman el flujo. Estilo Entero que indica el estilo de dibujo del flujo, que puede ser gruesa=0, simple=1 doble=2 CÓDIGO FUENTE CREATE OR REPLACE TYPE Graf_flujo as OBJECT ( Pl ARR_PUNTOS, Estilo NUMBER ) / show errors C.1.10 graf_cent_hidro Este objeto representa los atributos gráficos de una central hidroeléctrica ATRIBUTOS Nombre Descripción PL1 Polilínea que representa al canal de entrada PL2 Polilínea que representa al canal se salida CUAD Cuadrado que representa a la central hidroeléctrica. CÓDIGO FUENTE CREATE OR REPLACE TYPE graf_cent_hidro as OBJECT ( --atributos PL1 POLILINEA,--canal de entrada PL2 POLILINEA,--canal salida CUAD Cuadrado --representa el cuadrado central ) / show errors 209 Código Fuente C.1.11 graf_nodo Representación gráfica de un nodo ATRIBUTOS Nombre Descripción Elip Elipse que representa al nodo CÓDIGO FUENTE CREATE OR REPLACE TYPE graf_nodo as OBJECT ( Elip Elipse ) / show errors C.1.12 graf_emb Representación gráfica de un embalse ATRIBUTOS Nombre Descripción TR Triángulo que representa al embalse CÓDIGO FUENTE CREATE OR REPLACE TYPE graf_emb as OBJECT ( TR TRIANGULO ) / show errors C.1.13 graf_Cuenca Supertipo de las clases Graf_emb, Graf_flujo, Graf_nodo y Graf_Cent_Hidro, que son las representaciones gráficas de los objetos de la cuenca. ATRIBUTOS Nombre Descripción ColorLinea Indica el color de la figura. Tipo Entero que indica el tipo concreto de gráfico GFL Almacena un objeto del tipo Graf_Flujo. GCH Almacena un objeto del tipo Graf_Cent_hidro. 210 Código Fuente GNO Almacena un objeto del tipo Graf_nodo GEM Almacena un objeto del tipo Graf_emb CÓDIGO FUENTE CREATE OR REPLACE TYPE graf_Cuenca as OBJECT ( ColorLinea Varchar(15), Tipo Number, GFL Graf_Flujo, GCH Graf_Cent_Hidro, GNO Graf_Nodo, GEM Graf_Emb ) / show errors C.1.14 Cuenca Este tipo almacena los datos de una cuenca ATRIBUTOS Nombre Descripción Codigo Código de la cuenca Nombre Nombre de la cuenca METODOS Nombre Descripción simular() Realiza la simulación de la cuenca CÓDIGO FUENTE CREATE OR REPLACE TYPE cuenca as OBJECT ( --propiedades CODIGO VARCHAR2(40), NOMBRE VARCHAR2(50), --metodos MEMBER PROCEDURE SIMULAR ) / show errors CREATE OR REPLACE TYPE BODY CUENCA AS MEMBER PROCEDURE SIMULAR IS -- cursor con los nodos pertenecientes a la cuenca CURSOR NODO_C (cod varchar2) IS SELECT ROWID FROM TBL_NODO N 211 Código Fuente WHERE N.MYCUENCA=(SELECT REF(C) FROM TBL_CUENCA C WHERE C.CODIGO=COD) ORDER BY N.ORDEN()FOR UPDATE; nodo_rec nodoObj n i fecha UROWID; nodo; number; number; varchar2(80); BEGIN FOR I IN 1..6 LOOP -– recorre cada uno de los meses de la -- simulación open NODO_C(codigo); LOOP FETCH NODO_C INTO NODO_rec; exit when NODO_C%NOTFOUND; SELECT VALUE(e) INTO nodoObj FROM TBL_NODO e WHERE ROWID=NODO_rec; if NodoObj is not null then NodoObj.DistribCaudal(i); end if; -- actualiza el nodo persistente con el nodo transitorio NodoObj utilizado para UPDATE TBL_nodo e SET e=NodoObj WHERE ROWID=NODO_rec; COMMIT; END LOOP; close NODO_C; END LOOP; -- fin del for COMMIT; END SIMULAR; END; / show errors 212 Código Fuente C.1.15 Nodo Este tipo es la abstracción de un nodo de la cuenca. ATRIBUTOS Nombre Descripción Codigo Código del nodo Caudales Arreglo con los caudales de entrada para cada mes de la simulación. Graf Almacena los atributos gráficos del nodo MyCuenca Almacena una referencia al nodo al que pertenece. METODOS Nombre Descripción orden():num Indica la posición que ocuparía el nodo, si ordenamos a todos los nodos existentes DistribuirCaudal(m) Calcula el total de caudal de entrada y lo distribuye entre todos los objetos que extraen su caudal, el caudal de entrada es almacenado en caudales(m). CÓDIGO FUENTE CREATE OR REPLACE ( --atributos CODIGO COTA CAUDALES GRAF MYCUENCA TYPE nodo as OBJECT VARCHAR2(40) , NUMBER , ARR_CAUDALES , GRAF_NODO , REF CUENCA , -- métodos MAP MEMBER FUNCTION Orden RETURN NUMBER, MEMBER PROCEDURE DistribCaudal(m in number) ) / show errors CREATE OR REPLACE TYPE BODY nodo AS --convierte el código de la forma "NO I-J" en el Num real I.J MAP MEMBER function orden RETURN number IS strCod varchar2(40); begin strCod:=REPLACE(SUBSTR(LTRIM(RTRIM(codigo)),4),'-',''); RETURN (TO_NUMBER(strCod)); end orden; 213 Código Fuente -- Distribuye el caudal de entrada al nodo por todas las -- salidas que tenga MEMBER procedure distribCaudal(m IN number) is -- definición de variables entrada number:=0; demanda number:=0; resto number:=0; n number:=0; fl_rec UROWID; emb_rec UROWID; fl flujo; emb Embalse; valor number:=0; --definición de cursores CURSOR Emb_c(COD VARCHAR2) IS select ROWID from tbl_emb e where e.mynodo= (select Ref(n) from tbl_nodo n where n.codigo=COD) FOR UPDATE; CURSOR flujo_cursor(COD VARCHAR2) IS select ROWID from tbl_flujo f where (f.tipo!=1)and (f.nodo_ini= (select Ref(n) from tbl_nodo n where n.codigo=COD)) FOR UPDATE; -- Esta función calcula las entradas del nodo function caudalEntradas(mes in number,cod varchar2) return number is CURSOR ENT_C(M NUMBER,C VARCHAR2) IS select f.salida(M),rowid from tbl_flujo f where f.tipo!=2 and f.nodo_fin=(select ref(n) from tbl_nodo n where n.codigo=c); ent number:=0; valor number:=0; begin open ENT_C(mes,cod); loop FETCH ENT_C INTO valor,id; exit when ENT_C%NOTFOUND; ent:=ent + valor; end loop; close ENT_C; return( ent ); end; -- Esta función calcula las demandas del nodo function demandaAlNodo(e in number,mes in number, cod in varchar2)return number is CURSOR DEM_C(ent number,m number,c VARCHAR2) is select f.demanda(ent,m) from tbl_flujo f where f.tipo!=1 and f.nodo_ini=(select ref(n) from tbl_nodo n where n.codigo=cod); 214 Código Fuente valor number:=0; dem number:=0; begin end; BEGIN open DEM_C(e,mes,cod); loop FETCH DEM_C INTO valor; exit when DEM_C%NOTFOUND; dem:=dem + valor; end loop; close DEM_C; return(dem); entrada:=caudalEntradas(m,codigo); demanda:=demandaAlNodo(Entrada,m,codigo); caudales.setCaudal(entrada,m); -- si el nodo tiene un embalse entonces no puede tener -- otras salidas. así que todo el caudal de entrada va -- para el embalse. n:=0; -- numero de embalses open EMB_C(codigo); loop fetch emb_c into emb_rec; exit when emb_c%notfound; n:=n+1; end loop; close EMB_C; if n>0 then -- si el nodo tiene uno o más embalses open EMB_C(codigo); loop FETCH EMB_C INTO EMB_rec; exit when EMB_C%NOTFOUND; SELECT VALUE(e) INTO EMB FROM TBL_EMB e WHERE ROWID=EMB_rec; valor:=entrada/N; EMB.entrada(valor, M); UPDATE TBL_EMB e SET e=emb WHERE ROWID=EMB_rec; COMMIT; end loop; close EMB_C; else -- SI NO TIENE EMBALSE -> PROCESAR SUS FLUJOS. -- entregar caudales a flujos demandantes, en -- forma proporcional a sus demandas. n:=0; if demanda > 0 then --si hay flujos demandantes. open flujo_cursor(codigo); 215 Código Fuente loop FETCH flujo_cursor INTO fl_rec; exit when flujo_cursor%NOTFOUND; SELECT VALUE(F) INTO FL FROM TBL_FLUJO F WHERE ROWID=fl_rec; if fl.demanda(entrada,m) > 0 then valor:=entrada*(fl.demanda(entrada,m)/ demanda ); fl.entrada(valor,M); UPDATE TBL_FLUJO F SET F=FL WHERE ROWID=fl_rec; COMMIT; Else n:=n+1;--cuento el numero de flujos que --no demandan caudal. end if; end loop; close flujo_cursor; else -- si no hay demanda, es necesario contar los -- flujos que no demandan caudal open flujo_cursor(codigo); loop FETCH flujo_cursor INTO fl_rec; exit when flujo_cursor%NOTFOUND; SELECT VALUE(F) INTO FL FROM TBL_FLUJO F WHERE ROWID=fl_rec; if fl.demanda(entrada,m) = 0 then n:=n+1; -- cuento el numero de flujos -- que no demandan caudal. end if; end loop; close flujo_cursor; end if; -- caudal restante para los flujos no demandantes -- como los ríos Resto:=entrada - demanda; if resto < 0 then resto:=0; end if; if N >0 then --si existen flujos no demandantes. open flujo_cursor(codigo); loop FETCH flujo_cursor INTO fl_rec; exit when flujo_cursor%NOTFOUND; SELECT VALUE(F) INTO FL FROM TBL_FLUJO F WHERE ROWID=fl_rec; if fl.demanda(entrada,m) = 0 then valor:=Resto/N; fl.entrada(valor,M); UPDATE TBL_FLUJO F SET F=FL WHERE ROWID=fl_rec; COMMIT; 216 Código Fuente end if; end loop; close flujo_cursor; end if; end if; END distribCaudal; END; / show errors C.1.16 Rio Este tipo nos permite almacenar los datos generales de un Río. ATRIBUTOS Nombre Descripción codigo Código que identifica al río nombre Nombre del río CÓDIGO FUENTE CREATE OR REPLACE TYPE Rio as OBJECT ( --atributos CODIGO VARCHAR2(40), NOMBRE VARCHAR2(50) ) / show errors C.1.17 Hoya_inter Este tipo es la abstracción de una Hoya Intermedia ATRIBUTOS Nombre Descripción codigo Código que identifica la hoya intermedia nombre Nombre de la hoya intermedia Ref_rio_aportante Referencia al río al que pertenece la hoya intermedia Caudales Arreglo con los caudales que aporta la hoya intermedia en cada mes de la simulación. METODOS 217 Código Fuente Nombre Descripción ToVarchar2():cad Entrega una cadena de caracteres formada por la palabra “hoya_Inter“ concatenado con el código de la hoya intermedia salida(m):num Entrega el caudal que es aportado por la hoya intermedia a un nodo en el mes m. CÓDIGO FUENTE CREATE OR REPLACE TYPE Hoya_inter as OBJECT ( --atributos CODIGO VARCHAR2(40), NOMBRE VARCHAR2(50), REF_RIO_APORTE REF RIO , CAUDALES ARR_CAUDALES, --métodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER ) / show errors CREATE OR REPLACE TYPE BODY Hoya_inter AS MEMBER FUNCTION toVarchar2 return varchar2 is begin return('Hoya_inter '||codigo); end toVarchar2; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN return(Caudales.getcaudal(m)); END Salida; END; / show errors 218 Código Fuente C.1.18 Regimen_Natural Este tipo es la abstracción de un régimen natural. ATRIBUTOS Nombre Descripción codigo Código que identifica al régimen natural nombre Nombre del régimen natural Caudales Arreglo con los caudales que aporta el régimen natural en cada mes de la simulación. METODOS Nombre Descripción ToVarchar2():cad Entrega una cadena de caracteres formada por la palabra “Regimen_Natural “ concatenado con el código del régimen natural. salida(m):num Entrega el caudal que es aportado por el régimen natural a un nodo en el mes m. CÓDIGO FUENTE CREATE OR REPLACE TYPE Regimen_Natural as OBJECT ( --atributos CODIGO VARCHAR2(40), NOMBRE VARCHAR2(50), CAUDALES ARR_CAUDALES, --métodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER ) / show errors CREATE OR REPLACE TYPE BODY Regimen_Natural AS MEMBER FUNCTION toVarchar2 return varchar2 is BEGIN return('Regimen_Natural'||codigo); END toVarchar2; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN return(Caudales.getcaudal(m)); END Salida; END; / 219 Código Fuente show errors C.1.19 Salida_Emb Este tipo es la abstracción de una salida de un embalse ATRIBUTOS Nombre Descripción tipo Tipo de la salida de embalse, que puede ser Ee,Er,F,R,G codigoemb Código del embalse al que pertenece el flujo Caudales Arreglo con los caudales que recibe la salida desde el embalse en cada mes de la simulación. METODOS Nombre Descripción ToVarchar2():cad Entrega una cadena de caracteres formada por la cadena “Salida_Emb “, concatenado con CodigoEmb”-“tipo. salida(m):num Entrega el caudal que aporta la salida del embalse a un nodo para el mes m de la simulación. CÓDIGO FUENTE CREATE OR REPLACE ( --atributos Tipo CODIGOEMB CAUDALES --métodos MEMBER FUNCTION MEMBER FUNCTION ) / show errors TYPE Salida_Emb as OBJECT VARCHAR2(2) , VARCHAR2(40), ARR_CAUDALES, --Ee,Er,F,R,G toVarchar2 return varchar2, SALIDA(m in number) RETURN NUMBER CREATE OR REPLACE TYPE BODY Salida_Emb AS MEMBER FUNCTION toVarchar2 return varchar2 is begin return('Salida_Emb '||codigoEMB||'-'||tipo); end toVarchar2; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN return(Caudales.getcaudal(m)); END Salida; END; 220 Código Fuente / show errors C.1.20 Aportante Esta es la implementación mediante agregación del supertipo de: Hoya_Inter, Regimen_natural, Salida_emb. ATRIBUTOS Nombre Descripción tipo Número que indica el tipo concreto de un objeto aportante, que puede ser: 1=Hoya_Inter, 2=Regimen_natural, 3=Salida_emb. HI Almacena un objeto del tipo Hoya_Inter. RN Almacena un objeto del tipo Regimen_Natural. SE Almacena un objeto del tipo Salida_Emb. METODOS Nombre Descripción ToVarchar2():cad Invoca al método ToVarchar2(), sobre HI, RN o SE dependiendo del tipo correspondiente. salida(m):num Invoca al método salida(), sobre HI, RN o SE dependiendo del tipo correspondiente. CÓDIGO FUENTE CREATE OR REPLACE TYPE Aportante as OBJECT ( --atributos Tipo NUMBER, HI Hoya_inter, RN Regimen_Natural, SE Salida_Emb, --metodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER ) / show errors CREATE OR REPLACE TYPE BODY Aportante AS MEMBER FUNCTION toVarchar2 return varchar2 is begin IF tipo=1 THEN 221 Código Fuente return(HI.toVarchar2()); ELSIF tipo=2 THEN return(RN.toVarchar2()); ELSIF tipo=3 THEN return(SE.toVarchar2()); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; end toVarchar2; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN IF tipo=1 THEN return(HI.salida(m)); ELSIF tipo=2 THEN return(RN.salida(m)); ELSIF tipo=3 THEN return(SE.salida(m)); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; END Salida; END; / show errors C.1.21 Canal Esta es la abstracción de un canal ATRIBUTOS Nombre Descripción codigo Código del canal nombre Nombre del canal cap_conducción Cantidad máxima de M3 por segundo que es posible transportar por el canal. Eficiencia Porcentaje que indica la cantidad de agua de salida con referencia a la entrada. A Máximo caudal posible de captar B Caudal mínimo que debe tener el río para que el canal pueda captar agua. Si el caudal del río es menor a B, entonces el caudal captado es cero. C Caudal del río que hace que el caudal captado sea máximo. 222 Código Fuente Caudales Arreglo con los caudales de entrada al canal. METODOS Nombre Descripción ToVarchar2():cad Entrega una cadena compuesta por “Canal “ concatenado con Codigo. salida(m):num Entrega la cantidad que el canal aporta al nodo final en el mes m Demanda(caudal,m) Entrega la cantidad de agua que demanda el canal sobre el caudal del río pasado como parámetro. El parámetro m, es pasado por compatibilidad del método con los otros tipos del mismo supertipo. Entrada(caudal,m) Cantidad de caudal que entra en el canal desde el nodo inicial en el mes m. CÓDIGO FUENTE CREATE OR REPLACE TYPE Canal as OBJECT ( --atributos CODIGO VARCHAR2(40), NOMBRE VARCHAR2(50), CAP_CONDUCION NUMBER , EFICIENCIA NUMBER , A NUMBER , B NUMBER , C NUMBER , CAUDALES ARR_CAUDALES, --métodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER, MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER, MEMBER PROCEDURE ENTRADA(caudal in number,m in number) ) / show errors CREATE OR REPLACE TYPE BODY Canal AS MEMBER FUNCTION toVarchar2 return varchar2 is begin return('Canal '||codigo); end; MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER IS BEGIN 223 Código Fuente IF caudal<=b THEN return(0); ELSIF caudal>c THEN return(a); ELSE -- Calcula el valor del caudal captado en base a la -- ecuación de la recta que pasa por los punto (b,0) y -- (c,a). return(((A / (c-b))*(caudal - c))+A); END IF; END DEMANDA; MEMBER PROCEDURE ENTRADA(caudal in number,m in number)IS BEGIN CAUDALES.SetCaudal(caudal,m); END ENTRADA; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN return((Caudales.getcaudal(m)*Eficiencia)/100); END Salida; END; / show errors C.1.22 Tramo Esta es la abstracción de un tramo de río ATRIBUTOS Nombre Descripción MyRio Referencia al río al cual corresponde el tramo Num_Tramo Número del tramo. La numeración comienza desde aguas arriba hacia aguas abajo. Caudales Arreglo con los caudales de entrada al tramo de río 224 Código Fuente METODOS Nombre Descripción ToVarchar2():cad Entrega una cadena compuesta por “Tramo Nº”, concatenado con el número del tramo y “del río “ seguido del nombre del río. salida(m):num Caudal aportado al nodo final por el tramo de río. Demanda(caudal,m) Siempre devuelve cero, puesto que un tramo de río no tiene una demanda de caudal. Los parámetros son mantenidos por compatibilidad. Entrada(caudal,m) Cantidad de caudal que entra en el tramo de río desde el nodo inicial en el mes m. CÓDIGO FUENTE CREATE OR REPLACE TYPE Tramo as OBJECT ( --atributos MYRIO Ref RIO , NUM_TRAMO NUMBER , CAUDALES ARR_CAUDALES, --metodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER, MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER, MEMBER PROCEDURE ENTRADA(caudal in number,m in number) ) / show errors CREATE OR REPLACE TYPE BODY Tramo AS MEMBER FUNCTION toVarchar2 return varchar2 is nomrio varchar2(40); BEGIN select r.codigo into nomrio from tbl_rio r where ref(r)=myrio; return('Tramo N '||to_char(num_tramo)|| ' del rio'||nomrio); END toVarchar2; MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER IS BEGIN return(0); END DEMANDA; 225 Código Fuente MEMBER PROCEDURE ENTRADA(caudal in number,m in number)IS BEGIN CAUDALES.SetCaudal(caudal,m); END ENTRADA; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN return(Caudales.getcaudal(m)); END Salida; END; / show errors C.1.23 Central_hidro Este tipo es la abstracción de una central hidroeléctrica. ATRIBUTOS Nombre Descripción codigo Código de la central hidroeléctrica nombre Nombre de la central hidroeléctrica caudal_max_gen Caudal de entrada que permite la máxima generación hidroeléctrica por parte de la central. caudal_min Caudal mínimo de entrada a la central hidroeléctrica que permite su funcionamiento. Caudales Arreglo con los caudales de entrada a la central hidroeléctrica. METODOS Nombre Descripción ToVarchar2():cad Entrega una cadena compuesta por “Central Hidro ”, concatenado con el código de la central. salida(m):num Caudal aportado al nodo final por la central en el mes m. Kw(m):num Función que entrega la energía generada para el mes m. Por falta de información, este método no utiliza la fórmula correcta para calcular los KW generados por la central. Demanda(caudal,m) La demanda de la central es constante, y corresponde a cauda_max_gen. Entrada(caudal,m) Cantidad de caudal que entra en la central hidroeléctrica desde el nodo inicial en el mes m. 226 Código Fuente CÓDIGO FUENTE CREATE OR REPLACE TYPE Central_hidro as OBJECT ( --atributos CODIGO VARCHAR2(40), NOMBRE VARCHAR2(50), CAUDAL_MAX_GEN NUMBER , CAUDAL_MIN NUMBER , CAUDALES ARR_CAUDALES, --métodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION KW(m in number) RETURN NUMBER, MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER, MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER, MEMBER PROCEDURE ENTRADA(caudal in number,m in number) ) / show errors CREATE OR REPLACE TYPE BODY Central_hidro as MEMBER FUNCTION toVarchar2 return varchar2 is begin return('Central_hidro '||codigo); end; MEMBER FUNCTION KW(m in number) RETURN NUMBER is Begin --NOTA: ESTA FORMULA NO ES CORRECTA. return(Caudales.getcaudal(m)*1.86); end KW; MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER IS BEGIN return(Caudal_max_gen); END DEMANDA; MEMBER PROCEDURE ENTRADA(caudal in number,m in number)IS BEGIN CAUDALES.SetCaudal(caudal,m); END ENTRADA; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN return(Caudales.getcaudal(m)); END Salida; END; / show errors 227 Código Fuente C.1.24 Apor_extrac Este tipo corresponde a la implementación del supertipo de: Canal, Tramo y Central_Hidro. ATRIBUTOS Nombre Descripción Tipo Número que indica el tipo concreto que está almacenado. Los valores que puede tomar son: 1=Canal, 2=Tramo, 3=Central_hidro CA Almacena una instancia de Canal. TR Almacena una instancia de Tramo. CH Almacena una instancia de Central_hidro. METODOS Nombre Descripción ToVarchar2():cad Invoca a la función ToVarchar2() sobre el atributo CA, TR o CH dependiendo del atributo tipo salida(m):num Invoca a la función salida(m) sobre el atributo CA, TR o CH dependiendo del atributo tipo Demanda(caudal,m) Invoca a la función demanda(caudal,m) sobre el atributo CA, TR o CH dependiendo del atributo tipo Entrada(caudal,m) Invoca a la función Entrada(caudal,m) sobre el atributo CA, TR o CH dependiendo del atributo tipo CÓDIGO FUENTE CREATE OR REPLACE TYPE Apor_extrac as OBJECT ( --atributos Tipo NUMBER, CA Canal, TR Tramo, CH Central_hidro, --métodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER, MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER, MEMBER PROCEDURE ENTRADA(caudal in number,m in number) 228 Código Fuente ) / show errors CREATE OR REPLACE TYPE BODY Apor_extrac AS MEMBER FUNCTION toVarchar2 return varchar2 is begin IF tipo=1 THEN return(CA.toVarchar2()); ELSIF tipo=2 THEN return(TR.toVarchar2()); ELSIF tipo=3 THEN return(CH.toVarchar2()); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; end toVarchar2; MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER IS BEGIN IF tipo=1 THEN return(CA.demanda(caudal,m)); ELSIF tipo=2 THEN return(TR.demanda(caudal,m)); ELSIF tipo=3 THEN return(CH.demanda(caudal,m)); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; END DEMANDA; MEMBER PROCEDURE ENTRADA(caudal in number,m in number)IS BEGIN IF tipo=1 THEN CA.ENTRADA(caudal,m); ELSIF tipo=2 THEN TR.ENTRADA(caudal,m); ELSIF tipo=3 THEN CH.ENTRADA(caudal,m); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; END ENTRADA; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN IF tipo=1 THEN return(CA.salida(m)); ELSIF tipo=2 THEN return(TR.salida(m)); ELSIF tipo=3 THEN 229 Código Fuente ELSE return(CH.salida(m)); RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; END Salida; END; / show errors C.1.25 Cap_agua_Pot Este tipo representa la abstracción de una captación de agua potable Central_Hidro. ATRIBUTOS Nombre Descripción codigo Código de la captación de agua potable nombre Nombre de la captación de agua potable. Caudal_de_extrac Arreglo con la demanda de extracción para cada mes de la simulación. Caudales Caudales extraídos del nodo inicial para cada mes de la simulación. METODOS Nombre Descripción ToVarchar2():cad Entrega una cadena compuesta por “Cap_agua_Pot ”, concatenado con el código de la captación de agua potable. Demanda(caudal,m) Entrega la cantidad de agua demandada, según el arreglo caudal_de_extrac. Entrada(caudal,m) Permite asignar el caudal captado desde el nodo inicial en el mes m. CÓDIGO FUENTE CREATE OR REPLACE TYPE Capt_Agua_Pot as OBJECT ( --atributos CODIGO VARCHAR2(40), NOMBRE VARCHAR2(50), Caudal_de_extrac ARR_CAUDALES, 230 Código Fuente CAUDALES ARR_CAUDALES, --métodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER, MEMBER PROCEDURE ENTRADA(caudal in number,m in number) ) / show errors CREATE OR REPLACE TYPE BODY Capt_Agua_Pot AS MEMBER FUNCTION toVarchar2 return varchar2 is begin return('Capt_agua_Pot '||codigo); end; MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER IS BEGIN return(Caudal_de_extrac.getCaudal(m)); END DEMANDA; MEMBER PROCEDURE ENTRADA(caudal in number,m in number)IS BEGIN CAUDALES.SetCaudal(caudal,m); END ENTRADA; END; / show errors C.1.26 Extracción Este tipo corresponde a la implementación del supertipo de cap_agua_pot. Este tipo representa a todos los flujos que extraen caudales del nodo. ATRIBUTOS Nombre Descripción Tipo Número que indica el tipo concreto que está almacenado. Los valores que puede tomar son: 1=Cap_agua_pot CA Almacena una instancia de Cap_agua_pot. METODOS Nombre Descripción 231 Código Fuente ToVarchar2():cad Invoca a la función ToVarchar2() sobre el atributo CA, TR o CH dependiendo del atributo tipo Demanda(caudal,m) Invoca a la función Demanda(caudal,m) sobre el atributo CA Entrada(caudal,m) Invoca a la función Entrada(caudal,m) sobre el atributo CA CÓDIGO FUENTE CREATE OR REPLACE TYPE Extraccion as OBJECT ( --atributos Tipo NUMBER, CA Capt_Agua_Pot, --métodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER, MEMBER PROCEDURE ENTRADA(caudal in number,m in number) ) / show errors CREATE OR REPLACE TYPE BODY Extraccion AS MEMBER FUNCTION toVarchar2 return varchar2 is begin IF tipo=1 THEN return(CA.toVarchar2()); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; end toVarchar2; MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER IS BEGIN IF tipo=1 THEN return(CA.demanda(caudal,m)); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; END DEMANDA; MEMBER PROCEDURE ENTRADA(caudal in number,m in number)IS BEGIN IF tipo=1 THEN CA.ENTRADA(caudal,m); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; 232 Código Fuente END ENTRADA; END; / show errors C.1.27 Flujo Este tipo corresponde a la implementación del supertipo de: Aportante, Extraccion y Apor_extrac. ATRIBUTOS Nombre Descripción Nodo_ini Referencia al nodo inicial del flujo. Nodo_fin Referencia al nodo final del flujo. Graf Almacena los atributos gráficos del flujo. Tipo Número que indica el tipo concreto que está almacenado. Los valores que puede tomar son: 1=Aportante, 2=extraccion, 3=Apor_extrac AP Almacena una instancia de Aportante. EX Almacena una instancia de Extraccion. APEX Almacena una instancia de Apor_extrac. METODOS Nombre Descripción ToVarchar2():cad Invoca a la función ToVarchar2() sobre el atributo AP, EX o APEX dependiendo del atributo tipo salida(m):num Invoca a la función salida(m) sobre el atributo AP, EX o APEX dependiendo del atributo tipo Demanda(caudal,m) Invoca a la función demanda(caudal,m) sobre el atributo AP, EX o APEX dependiendo del atributo tipo Entrada(caudal,m) Invoca a la función Entrada(caudal,m) sobre el atributo AP, EX o APEX dependiendo del atributo tipo CÓDIGO FUENTE CREATE OR REPLACE TYPE Flujo as OBJECT ( 233 Código Fuente --atributos NODO_INI Ref NODO , NODO_FIN Ref NODO , GRAF Graf_Cuenca, Tipo NUMBER, AP Aportante, EX Extraccion, APEX Apor_extrac, --métodos MEMBER FUNCTION toVarchar2 return varchar2, MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER, MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER, MEMBER PROCEDURE ENTRADA(caudal in number,m in number) ) / show errors CREATE OR REPLACE TYPE BODY Flujo AS MEMBER FUNCTION toVarchar2 return varchar2 is begin IF tipo=1 THEN return(ap.toVarchar2()); ELSIF tipo=2 THEN return(EX.toVarchar2()); ELSIF tipo=3 THEN return(APEX.toVarchar2()); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; end toVarchar2; MEMBER FUNCTION DEMANDA(caudal in number,m in number) RETURN NUMBER IS BEGIN IF tipo=1 THEN RAISE_APPLICATION_ERROR(-20111,'No se puede invocar el método demanda(c,m):n sobre un tipo Aportante'); ELSIF tipo=2 THEN return(EX.demanda(caudal,m)); ELSIF tipo=3 THEN return(APEX.demanda(caudal,m)); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; END DEMANDA; MEMBER PROCEDURE ENTRADA(caudal in number,m in number)IS BEGIN IF tipo=1 THEN RAISE_APPLICATION_ERROR(-20111,'No se puede invocar el metodo Entrada(c,m) sobre un tipo Aportante'); 234 Código Fuente ELSIF tipo=2 THEN EX.ENTRADA(caudal,m); ELSIF tipo=3 THEN APEX.ENTRADA(caudal,m); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; END ENTRADA; MEMBER FUNCTION SALIDA(m in number) RETURN NUMBER IS BEGIN IF tipo=1 THEN return(AP.salida(m)); ELSIF tipo=2 THEN RAISE_APPLICATION_ERROR(-20111,'No se puede invocar el método Salida(m):n sobre un tipo Extracione'); ELSIF tipo=3 THEN return(APEX.salida(m)); ELSE RAISE_APPLICATION_ERROR(-22000,'Tipo inexistente corregir campo tipo'); END IF; END Salida; END; / show errors C.1.28 Embalse Este tipo es la abstracción de un embalse. ATRIBUTOS Nombre Descripción codigo Código del embalse myNodo Referencia al nodo en el que está ubicado el embalse. volumenUtil Volumen del embalse que puede ser utilizado. capmax Capacidad máxima del embalse. Evap Almacena la evaporación mensual del embalse Superficie Hectarias que abarca el embalse VolumenTot Volumen total de caudal en el embalse Coef Coeficiente del embalse, que por lo general es 0.7 EntregaMaxPorV Caudal máximo de salida por válvula. VolumenInicial Volumen con el que cuenta el embalse antes de la simulación. 235 Código Fuente Curva_alerta Arreglo con los valores mínimos de caudales para cada mes de la simulación Graf Atributos gráficos del embalse Caudales Arreglo con los caudales de entrada para el embalse para cada uno de los meses de simulación. Filtraciones Salida del embalse por concepto de filtraciones Rebases Salida del embalse por concepto de rebases EntregaEner Salida del embalse para generación hidroeléctrica EntregaRieg Salida del embalse por riego EntregaGen Salida del embalse que representa al caudal utilizado en la generación hidroeléctrica en la central al pie del embalse. METODOS Nombre Descripción ToVarchar2():cad Entrega una cadena formada por “Embalse” concatenado con el código del embalse. Entrada(caudal,m) Permite asignar el caudal entregado por el nodo al embalse en el mes m. CÓDIGO FUENTE CREATE OR REPLACE ( --atributos CODIGO MYNODO VOLUMENUTIL CAPMAX EVAP SUPERFICIE VOLUMENTOT COEF ENTRAGAMAXPORV VOLUMENINICIAL CURVA_ALERTA GRAF CAUDALES FILTRACIONES REBASES ENTREGAENER ENTREGARIEG ENTREGAGEN TYPE Embalse as OBJECT VARCHAR2(40) Ref NODO , NUMBER , NUMBER , NUMBER , NUMBER , NUMBER , NUMBER , NUMBER , NUMBER , ARR_CAUDALES, GRAF_EMB, ARR_CAUDALES, REF FLUJO, REF FLUJO, REF FLUJO , REF FLUJO , REF FLUJO , , --METODOS MEMBER FUNCTION toVarchar2 return varchar2, MEMBER PROCEDURE ENTRADA(caudal in number,m in number) 236 Código Fuente ) / show errors CREATE OR REPLACE TYPE BODY Embalse AS MEMBER FUNCTION toVarchar2 return varchar2 is begin return('Embalse '||codigo); end toVarchar2; --Este método no es la forma real en que se opera un – --embalse. MEMBER PROCEDURE ENTRADA(caudal in number,m in number) IS F number; R number; Ee number; Er number; Eg number; Vac number:=0; mes number; Procedure entregarCaudal(c in number,m in number,RFL in ref flujo) is fl flujo; begin --ENTREGAGEN.caudales.setCaudal(Eg,m) select value(p) into fl from tbl_flujo p where ref(p)=RFL; fl.ap.se.caudales.setCaudal(c,m); update tbl_flujo p set p= fl where ref(p)=RFL; end; BEGIN caudales.setCaudal(caudal,M); if (M = 1) then Vac := VolumenInicial; else mes:=1; while mes < M loop Vac :=Vac + caudales.getCaudal(mes); mes:=mes+1; end loop; end if; R:=(caudal + Vac ) - CapMax; if (r < 0 ) then r:=0; end if; Vac := caudal - r; F := Vac * 0.005; Vac := Vac - F; 237 Código Fuente Ee Er Eg := Caudal / 3; := Caudal / 3; := Caudal - Ee - Er; entregarCaudal(F,m,FILTRACIONES); entregarCaudal(R,m,REBASES); entregarCaudal(Ee,m,ENTREGAENER); entregarCaudal(Er,m,ENTREGARIEG); entregarCaudal(Eg,m,ENTREGAGEN); END entrada; END; / show errors 238 Código Fuente C.2 Definición de Tablas de objetos e inserción de instancias C.2.1 Tabla: tbl_cuenca Create table tbl_cuenca OF cuenca ( CODIGO NOT NULL PRIMARY KEY ) / show errors C.2.2 Tabla: tbl_nodo create table tbl_nodo OF nodo ( CODIGO NOT NULL PRIMARY KEY, FOREIGN KEY (myCuenca) REFERENCES tbl_cuenca ON DELETE CASCADE ) / show errors C.2.3 Tabla: tbl_rio create table tbl_rio OF rio ( CODIGO NOT NULL PRIMARY KEY ) / show errors C.2.4 Tabla: tbl_Flujo create table tbl_Flujo OF Flujo ( FOREIGN KEY (nodo_ini) REFERENCES tbl_nodo ON DELETE CASCADE, FOREIGN KEY (nodo_fin) REFERENCES tbl_nodo ON DELETE CASCADE ) / show errors C.2.5 Tabla: tbl_Emb create table ( FOREIGN KEY FOREIGN KEY FOREIGN KEY FOREIGN KEY tbl_Emb OF Embalse (mynodo) REFERENCES tbl_nodo ON DELETE CASCADE, (FILTRACIONES) REFERENCES tbl_flujo ON DELETE CASCADE, (REBASES) REFERENCES tbl_flujo ON DELETE CASCADE, (ENTREGAENER) REFERENCES tbl_flujo ON DELETE CASCADE, 239 Código Fuente FOREIGN KEY (ENTREGARIEG) REFERENCES tbl_flujo ON DELETE CASCADE, FOREIGN KEY (ENTREGAGEN) REFERENCES tbl_flujo ON DELETE CASCADE ) / show errors C.2.6 Inserción de una Cuenca insert into tbl_cuenca values(cuenca('01','Cuenca del Rio Maule')) / C.2.7 Inserción de un Rio insert into tbl_rio values(rio('R00','Estero [sin nombre]')) / C.2.8 Inserción de un Nodo Insert into tbl_nodo values ( nodo('NO 1-1',2500, arr_caudales(array_caudal(0,0,0,0,0,0)), graf_nodo(elipse(Punto(387,87),39,24)), (select REF(C) from tbl_cuenca C where c.codigo='01') ) ) / C.2.9 Inserción de un Embalse y sus salidas insert into tbl_emb values ( embalse('EM1',(select REF(N) from tbl_nodo n where n.codigo='NO 1-1'), 100000,150000,20000,10,150000, 0.7,20000,1000, arr_caudales(array_caudal(1000,1200,1300,1400,1500,1600)), GRAF_EMB(TRIANGULO(PUNTO(387,99),PUNTO(354,138),PUNTO(425,138))), arr_caudales(array_caudal(0,0,0,0,0,0)),null,null,null,null,null ) ) / -- insertando las 5 salidas del embalse insert into tbl_flujo values ( flujo(null,(select REF(n) from tbl_nodo n where n.codigo='NO 1-2'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(360,138), Punto(378,229)),1),null,null,null),1, aportante(3,null,null,SALIDA_EMB('F','EM1', arr_caudales(array_caudal(0,0,0,0,0,0)))), null,null) ) / 240 Código Fuente insert into tbl_flujo values ( flujo(null,(select REF(n) from tbl_nodo n where n.codigo='NO 1-2'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(375,138), Punto(382,228)),1),null,null,null),1, aportante(3,null,null,SALIDA_EMB('Ee','EM1', arr_caudales(array_caudal(0,0,0,0,0,0)))), null,null) ) / insert into tbl_flujo values ( flujo(null,(select REF(n) from tbl_nodo n where n.codigo='NO 1-2'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(387,138), Punto(387,228)),1),null,null,null),1, aportante(3,null,null,SALIDA_EMB('R','EM1', arr_caudales(array_caudal(0,0,0,0,0,0)))), null,null) ) / insert into tbl_flujo values ( flujo(null,(select REF(n) from tbl_nodo n where n.codigo='NO 1-2'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(400,138), Punto(393,228)),1),null,null,null),1, aportante(3,null,null,SALIDA_EMB('Er','EM1', arr_caudales(array_caudal(0,0,0,0,0,0)))), null,null) ) / insert into tbl_flujo values ( flujo(null,(select REF(n) from tbl_nodo n where n.codigo='NO 1-2'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(417,138), Punto(399,230)),1),null,null,null),1, aportante(3,null,null,SALIDA_EMB('G','EM1', arr_caudales(array_caudal(0,0,0,0,0,0)))), null,null) ) / -- actualizando las referencias en el embalse update tbl_emb e set filtraciones=(select REF(f) from tbl_flujo f where (f.ap.se.codigoemb='EM1')and(f.ap.se.tipo='F')) WHERE e.codigo='EM1' / update tbl_emb e set Rebases=(select REF(f) from tbl_flujo f 241 Código Fuente where (f.ap.se.codigoemb='EM1')and(f.ap.se.tipo='R')) WHERE e.codigo='EM1' / update tbl_emb e set EntregaEner=(select REF(f) from tbl_flujo f where (f.ap.se.codigoemb='EM1')and(f.ap.se.tipo='Ee')) WHERE e.codigo='EM1' / update tbl_emb e set EntregaRieg=(select REF(f) from tbl_flujo f where (f.ap.se.codigoemb='EM1')and(f.ap.se.tipo='Er')) WHERE e.codigo='EM1' / update tbl_emb e set EntregaGen=(select REF(f) from tbl_flujo f where (f.ap.se.codigoemb='EM1')and(f.ap.se.tipo='G')) WHERE e.codigo='EM1' / C.2.10 Inserción de un Regimen Natural insert into tbl_flujo values ( flujo(null,(select REF(n) from tbl_nodo n where n.codigo='NO 1-1'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(432,65), Punto(398,77)),1),null,null,null),1,aportante(2,null, Regimen_natural('RN 1-1','Afluentes Laguna Maule', arr_caudales(array_caudal(5000,6000,7000,7000,8000,2000))), null), null,null) ) / C.2.11 Inserción de una Hoya Intermedia insert into tbl_flujo values ( flujo(null,(select REF(n) from tbl_nodo n where n.codigo='NO 1-5'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(272,391), Punto(308,391)),1),null,null,null), 1,aportante(1,Hoya_Inter('HI 1-5','-', (select ref(r) from tbl_rio r where r.codigo='R11'), arr_caudales(array_caudal(9230,8980,7520,6520,9860,7510))), null,null), null,null) ) / 242 Código Fuente C.2.12 Inserción de una Captación de Agua Potable insert into tbl_flujo values ( flujo((select REF(n) from tbl_nodo n where n.codigo='NO 1-6'), null, graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(404,366), Punto(444,382)),1),null,null,null), 2, null, EXTRACCION(1, CAPT_AGUA_POT('CAPT AGUA 01','Captación de agua potable 01', arr_caudales(array_caudal(8560,7320,6980,5680,9760,1230)), arr_caudales(array_caudal(0,0,0,0,0,0)))) ,null ) ) / C.2.13 Inserción de una Central Hidroeléctrica insert into tbl_flujo values ( flujo( (select REF(n) from tbl_nodo n where n.codigo='NO 1-2'), (select REF(n) from tbl_nodo n where n.codigo='NO 1-6'), graf_cuenca('Negro',2,null, graf_cent_hidro(polilinea(arr_puntos(Punto(406,243), Punto(432,257),Punto(432,276)),3,2), polilinea(arr_puntos(Punto(432,300), Punto(432,311),Punto(400,351)),3,2), cuadrado(Punto(420,276),Punto(444,276), Punto(420,300),Punto(444,300))),null,null), 3,null,null, apor_extrac(3,null,null, Central_hidro('CI 1-1','Central los condores',50000,1000, arr_caudales(array_caudal(0,0,0,0,0,0)) )) ) ) / C.2.14 Inserción de un Tramo de Rio insert into tbl_flujo values (flujo((select REF(n) from tbl_nodo n where n.codigo='NO 1-2'), (select REF(n) from tbl_nodo n where n.codigo='NO 1-6'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(387,252), Punto(387,348)),3),null,null,null),3,null,null, apor_extrac(2,null,Tramo( (select ref(r) from tbl_rio r where r.codigo='R11'), 2,arr_caudales(array_caudal(0,0,0,0,0,0))),null )) ) / 243 Código Fuente C.2.15 Inserción de un Canal insert into tbl_flujo values ( flujo( (select REF(n) from tbl_nodo n where n.codigo='NO 1-11'), (select REF(n) from tbl_nodo n where n.codigo='NO 1-12'), graf_cuenca('Negro',1,graf_flujo(arr_puntos(Punto(159,474), Punto(241,474)),2),null,null,null), 3,null,null, apor_extrac( 1, Canal('CA 2','Rama Cipreses',1000,100,1000,0,3000, arr_caudales(array_caudal(0,0,0,0,0,0))) ,null,null ) ) ) / 244 Código Fuente C.3 Pantallas. C.3.1 Datos de Conexión C.3.2 Abrir cuenca existente 245 Código Fuente C.3.3 Ventana Principal, después de abrir una cuenca. (Frame1.java) C.3.4 Agregar Cuenca 246 Código Fuente C.3.5 Ventana simulación al finalizar C.3.6 Nodos y sus caudales 247 Código Fuente C.3.7 Modificando un Rio (FrmModificarRio.java y FrmRio.java) FrmModificarRio.java FrmRio.java C.3.8 Acerca de (Frame1_AboutBox.java) 248 Código Fuente C.3.9 Propiedadas de un Canal (FrmCanal.java) C.3.10 Propiedades de una Captación de Agua Potable (FrmCapAguaPot.java) C.3.11 Propiedades de una central hidroeléctrica (FrmCentralHidro.java) 249 Código Fuente C.3.12 Propiedades de una hoya intermedia (frmHoyaInter.java) C.3.13 Propiedades de un régimen natural (frmRegNat.java) C.3.14 Propiedades de una salida de embalse (frmSalidaEmb.java) 250 Código Fuente C.3.15 Propiedades de un tramo de río (FrmTramo.java) C.3.16 Propiedades de un nodo (frmNodo.java) C.3.17 Ventana para la modificación del Zoom (FrmZoom.java) 251 Código Fuente C.3.18 Menu Cuenca C.3.19 Menu Río C.3.20 Menu Zoom C.3.21 Menu Insertar 252 Código Fuente C.3.22 Menu Consultas C.3.23 Menu Ayuda 253 Código Fuente C.4 Código Java. C.4.1 Paquete consultorSql C.4.1.1 Clase: ConectorJDBC //Nombre del archivo ConectorJDBC.java package consultorSql; import java.sql.*; import Oracle.jdbc.driver.*; import Oracle.sql.*; public class ConectorJDBC { OracleConnection connection=null; String url; String usr; String driver; String passwd; public ConectorJDBC() { } public ConectorJDBC(String url, String driverName,String user, String passwd) { this.url = url; this.driver = driverName; this.usr = user; this.passwd = passwd; { /*----------------------------* Funcion mutadora de Usr */ public void setUsr(String usr) { this.usr=usr; } /**-----------------------------* Funcion mutadora de passwd */ public void setPasswd(String passwd) { this.passwd = passwd; } /**------------------------------* Funcion mutadora de Url 254 Código Fuente */ public void setUrl(String url) { this.url = url; } /**------------------------------* Funcion mutadora de driver */ public void setDriver(String driver) { this.driver = driver; } /**------------------------------* Funcion observadora de Usr */ public String getUsr() { return this.usr; } /**------------------------------* Funcion observadora de passwd */ public String getPasswd() { return this.passwd; } /**------------------------------* Funcion observadora de Url */ public String getUrl() { return this.url ; } /**------------------------------* Funcion observadora de driver */ public String getDriver() { return this.driver; } /**---------------------------------------------------------* Abre la coneccion a la base de datos con los atributos de 255 Código Fuente * coneccion. */ public void open() throws Exception { Class.forName(driver); System.out.println("conectando a la base de datos..."); connection = (OracleConnection) DriverManager.getConnection(url, usr, passwd); } /**---------------------------------------------------------* Abre la coneccion a la base de datos con los atributos de * coneccion. */ public void open(String url, String driverName, String user, String passwd) throws Exception { Class.forName(driverName); System.out.println("conectando a la base de datos..."); connection =(OracleConnection) DriverManager.getConnection(url, user, passwd); } /**----------------------------------------------------------* Cierra la conneccion a la base de datos. */ public void close() throws SQLException { System.out.println("Cerrando la conneccion a la base de datos"); connection.close(); } public Connection getConnection() { return connection; } public OracleConnection getOracleConnection() { return connection; } public boolean isClosed() throws SQLException { return ((this.connection==null) ||(connection.isClosed())); } public boolean isOpen() throws SQLException { 256 Código Fuente } } return !((this.connection==null)|| (connection.isClosed())); C.4.1.2 Clase: FrmConexion //Nombre del archivo FrmConexion.java package consultorSql; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class FrmConexion extends JDialog { JPanel jPanel1 = new JPanel(); JButton btSalir = new JButton(); JPanel jPanel2 = new JPanel(); GridLayout gridLayout1 = new GridLayout(); JPanel jPanel3 = new JPanel(); JPanel jPanel4 = new JPanel(); JPanel jPanel5 = new JPanel(); JPanel jPanel6 = new JPanel(); JLabel jLabel1 = new JLabel(); JPasswordField paswd = new JPasswordField(); JLabel jLabel2 = new JLabel(); JTextField url = new JTextField(); JLabel jLabel3 = new JLabel(); JTextField driver = new JTextField(); JLabel jLabel4 = new JLabel(); FlowLayout flowLayout1 = new FlowLayout(); FlowLayout flowLayout2 = new FlowLayout(); FlowLayout flowLayout3 = new FlowLayout(); FlowLayout flowLayout4 = new FlowLayout(); JTextField user = new JTextField(); JPanel jPanel7 = new JPanel(); JPanel jPanel8 = new JPanel(); JPanel jPanel9 = new JPanel(); private ConectorJDBC conector; JButton btConectar = new JButton(); public FrmConexion() { try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } 257 Código Fuente public FrmConexion(Frame po,ConectorJDBC conector) { super(po); try { jbInit(); this.conector=conector; } catch(Exception e) { e.printStackTrace(); } } public FrmConexion(Frame po) { super(po); try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } private void jbInit() throws Exception { btSalir.setPreferredSize(new Dimension(89, 25)); btSalir.setMinimumSize(new Dimension(89, 25)); btSalir.setText("Salir"); btSalir.setMnemonic('S') ; btSalir.setMaximumSize(new Dimension(89, 25)); btSalir.addKeyListener(new java.awt.event.KeyAdapter() { public void keyReleased(KeyEvent e) { btSalir_keyReleased(e); } }); btSalir.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { btSalir_actionPerformed(e); } }); jPanel2.setLayout(gridLayout1); gridLayout1.setRows(4); jLabel1.setText("Nombre de Usuario"); jLabel1.setPreferredSize(new Dimension(127, 15)); jLabel1.setMinimumSize(new Dimension(127, 15)); 258 Código Fuente jLabel1.setMaximumSize(new Dimension(127, 15)); paswd.setPreferredSize(new Dimension(215, 19)); paswd.setMinimumSize(new Dimension(215, 19)); paswd.setText("romero"); paswd.setMaximumSize(new Dimension(215, 19)); paswd.addFocusListener(new java.awt.event.FocusAdapter() { public void focusGained(FocusEvent e) { paswd_focusGained(e); } public void focusLost(FocusEvent e) { paswd_focusLost(e); } }); paswd.addKeyListener(new java.awt.event.KeyAdapter() { public void keyReleased(KeyEvent e) { paswd_keyReleased(e); } }); jLabel2.setText("Contraseña"); jLabel2.setPreferredSize(new Dimension(127, 15)); jLabel2.setMinimumSize(new Dimension(127, 15)); jLabel2.setMaximumSize(new Dimension(127, 15)); url.setPreferredSize(new Dimension(215, 19)); url.setMinimumSize(new Dimension(215, 19)); url.setSelectionStart(0); url.setText("jdbc:Oracle:thin:@zorro:1521:ORCL"); url.setSelectionEnd(0); url.setMaximumSize(new Dimension(215, 19)); url.addFocusListener(new java.awt.event.FocusAdapter() { public void focusGained(FocusEvent e) { url_focusGained(e); } public void focusLost(FocusEvent e) { url_focusLost(e); } }); url.addKeyListener(new java.awt.event.KeyAdapter() { public void keyReleased(KeyEvent e) { url_keyReleased(e); } }); jLabel3.setText("URL JDBC"); jLabel3.setPreferredSize(new Dimension(127, 15)); jLabel3.setMinimumSize(new Dimension(127, 15)); 259 Código Fuente jLabel3.setMaximumSize(new Dimension(127, 15)); driver.setPreferredSize(new Dimension(215, 19)); driver.setMinimumSize(new Dimension(215, 19)); driver.setText("Oracle.jdbc.driver.OracleDriver"); driver.setMaximumSize(new Dimension(215, 19)); driver.addFocusListener(new java.awt.event.FocusAdapter() { public void focusGained(FocusEvent e) { driver_focusGained(e); } public void focusLost(FocusEvent e) { driver_focusLost(e); } }); driver.addKeyListener(new java.awt.event.KeyAdapter() { public void keyReleased(KeyEvent e) { driver_keyReleased(e); } }); jPanel3.setLayout(flowLayout1); jPanel4.setLayout(flowLayout2); jPanel5.setLayout(flowLayout3); jPanel6.setLayout(flowLayout4); flowLayout1.setAlignment(FlowLayout.LEFT); flowLayout2.setAlignment(FlowLayout.LEFT); flowLayout3.setAlignment(FlowLayout.LEFT); flowLayout4.setAlignment(FlowLayout.LEFT); user.setPreferredSize(new Dimension(215, 19)); user.setMinimumSize(new Dimension(215, 19)); user.setText("miguel"); user.setMaximumSize(new Dimension(215, 19)); user.addFocusListener(new java.awt.event.FocusAdapter() { public void focusGained(FocusEvent e) { user_focusGained(e); } public void focusLost(FocusEvent e) { user_focusLost(e); } }); user.addKeyListener(new java.awt.event.KeyAdapter() { public void keyPressed(KeyEvent e) { user_keyPressed(e); } }); jLabel4.setText("Driver JDBC"); 260 Código Fuente } jLabel4.setPreferredSize(new Dimension(127, 15)); jLabel4.setMinimumSize(new Dimension(127, 15)); jLabel4.setToolTipText(""); jLabel4.setMaximumSize(new Dimension(127, 15)); jPanel2.setBorder(BorderFactory.createEtchedBorder()); this.setModal(true); this.setTitle("Datos de Conección"); jPanel1.setMinimumSize(new Dimension(10, 10)); btConectar.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { btConectar_actionPerformed(e); } }); btConectar.addKeyListener(new java.awt.event.KeyAdapter() { public void keyReleased(KeyEvent e) { btConectar_keyReleased(e); } }); btConectar.setText("Conectar"); btConectar.setMnemonic('C'); this.getContentPane().add(jPanel1, BorderLayout.SOUTH); jPanel1.add(btConectar, null); jPanel1.add(btSalir, null); this.getContentPane().add(jPanel2, BorderLayout.CENTER); jPanel2.add(jPanel3, null); jPanel3.add(jLabel1, null); jPanel3.add(user, null); jPanel2.add(jPanel4, null); jPanel4.add(jLabel2, null); jPanel4.add(paswd, null); jPanel2.add(jPanel5, null); jPanel5.add(jLabel3, null); jPanel5.add(url, null); jPanel2.add(jPanel6, null); jPanel6.add(jLabel4, null); jPanel6.add(driver, null); this.getContentPane().add(jPanel7, BorderLayout.WEST); this.getContentPane().add(jPanel8, BorderLayout.EAST); this.getContentPane().add(jPanel9, BorderLayout.NORTH); void user_keyPressed(KeyEvent e) { if (e.getKeyChar()=='\n') { paswd.requestFocus(); } } void paswd_keyReleased(KeyEvent e) 261 Código Fuente { if (e.getKeyChar()=='\n') { url.requestFocus(); } } void url_keyReleased(KeyEvent e) { if (e.getKeyChar()=='\n') { driver.requestFocus(); } } void driver_keyReleased(KeyEvent e) { if (e.getKeyChar()=='\n') { btSalir.requestFocus(); } } void btSalir_actionPerformed(ActionEvent e) { this.hide(); } void user_focusGained(FocusEvent e) { user.setSelectionStart(0) ; user.setSelectionEnd(user.getText().length()) ; } void paswd_focusGained(FocusEvent e) { paswd.setSelectionStart(0) ; paswd.setSelectionEnd(paswd.getPassword().length); } void url_focusGained(FocusEvent e) { url.setSelectionStart(0) ; url.setSelectionEnd(url.getText().length()) ; } void driver_focusGained(FocusEvent e) { driver.setSelectionStart(0) ; 262 Código Fuente driver.setSelectionEnd(driver.getText().length()) ; } void user_focusLost(FocusEvent e) { user.setSelectionStart(0) ; user.setSelectionEnd(0) ; } void paswd_focusLost(FocusEvent e) { paswd.setSelectionStart(0) ; paswd.setSelectionEnd(0) ; } void url_focusLost(FocusEvent e) { url.setSelectionStart(0) ; url.setSelectionEnd(0) ; } void driver_focusLost(FocusEvent e) { paswd.setSelectionStart(0) ; paswd.setSelectionEnd(0) ; } /** * entregan los datos de coneccion */ public String getUsr() { return user.getText(); } public String getPasswd() { return new String(paswd.getPassword()); } public String getUrl() { return url.getText(); } public String getDriver() { return driver.getText(); } void btSalir_keyReleased(KeyEvent e) 263 Código Fuente { if (e.getKeyChar()=='\n') { this.hide(); } } void btConectar_actionPerformed(ActionEvent e) { try { conector.setDriver(this.getDriver()); conector.setUrl(this.getUrl()); conector.setUsr(this.getUsr() ); conector.setPasswd(this.getPasswd()); if(conector.isOpen()) { conector.close(); conector.open(); } else { conector.open(); } this.hide(); } catch (Exception ex) { JOptionPane.showMessageDialog(this,"Error al conectar: "+ex) ; } } void btConectar_keyTyped(KeyEvent e) { } void btConectar_keyPressed(KeyEvent e) { } void btConectar_keyReleased(KeyEvent e) { } } //habilita o deshabilita el boton salir public void enbableBtSalir(boolean enabled) { btSalir.setEnabled(enabled); } 264 Código Fuente C.4.1.3 Clase: FrmConsultor package consultorSql; import java.awt.*; import javax.swing.*; import java.awt.event.*; public class FrmConsultor extends JDialog { GridBagLayout gridBagLayout3 = new GridBagLayout(); JScrollPane jScrollPane1 = new JScrollPane(); JScrollPane jScrollPane2 = new JScrollPane(); JButton jButton1 = new JButton(); JButton jButton2 = new JButton(); JTextArea jTextArea1 = new JTextArea(); sqlQuery sqlQuery1 = new sqlQuery(); String usr; String passwd; String url; String driver; /** * Constructor */ public FrmConsultor() { try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } /** * Construcctor para el FrmConsultor */ public FrmConsultor(String usr,String passwd,String url,String driver) { try { jbInit(); } catch(Exception e) { e.printStackTrace(); } sqlQuery1.setUsr(usr) ; sqlQuery1.setPasswd(passwd) ; sqlQuery1.setUrl(url) ; sqlQuery1.setDriver(driver) ; sqlQuery1.setActive(true) ; 265 Código Fuente } /** * Construcctor para el FrmConsultor */ public FrmConsultor(Frame po,String usr,String passwd,String url,String driver) { super(po); try { jbInit(); } catch(Exception e) { e.printStackTrace(); } sqlQuery1.setUsr(usr) ; sqlQuery1.setPasswd(passwd) ; sqlQuery1.setUrl(url) ; sqlQuery1.setDriver(driver) ; sqlQuery1.setActive(true) ; } private void jbInit() throws Exception { this.getContentPane().setLayout(gridBagLayout3); this.setModal(true); this.setTitle("Consultas SQL"); this.addWindowListener(new java.awt.event.WindowAdapter() { public void windowActivated(WindowEvent e) { this_windowActivated(e); } }); jButton1.setNextFocusableComponent(jButton2); jButton1.setText("Ejecutar"); jButton1.setMnemonic('E') ; jButton1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jButton1_actionPerformed(e); } }); jButton2.setNextFocusableComponent(jTextArea1); jButton2.setText("Salir"); jButton2.setMnemonic('S') ; jButton2.addKeyListener(new java.awt.event.KeyAdapter() { public void keyReleased(KeyEvent e) { jButton2_keyReleased(e); } 266 Código Fuente }); jButton2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jButton2_actionPerformed(e); } }); jTextArea1.setNextFocusableComponent(jButton1); jTextArea1.setText("Select * from dual"); jTextArea1.setToolTipText("escriba la consulta, para ejecutarla utilize alt + e o el botón Ejecutar"); sqlQuery1.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); sqlQuery1.setDoubleBuffered(true); sqlQuery1.setUsr("miguel"); sqlQuery1.setPasswd("romero"); sqlQuery1.setUrl("jdbc:Oracle:thin:@zorro:1521:ORCL"); sqlQuery1.setDriver("Oracle.jdbc.driver.OracleDriver"); sqlQuery1.setQuery("Select * from Dual"); sqlQuery1.setActive(true); this.getContentPane().add(jScrollPane1, new GridBagConstraints(0, 3, GridBagConstraints.REMAINDER, 10, 1.0, 0.666666666 ,GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(3, 7, 7, 7), 0, 0)); jScrollPane1.getViewport().add(sqlQuery1, null); this.getContentPane().add(jButton1, new GridBagConstraints(1, 0, 1, 1,0.0,0.0,GridBagConstraints.NORTHEAST, GridBagConstraints.BOTH, new Insets(7, 3, 3, 7), 0, 0)); this.getContentPane().add(jButton2, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0,GridBagConstraints.NORTHEAST, GridBagConstraints.BOTH, new Insets(3, 3, 3, 7), 0, 0)); this.getContentPane().add(jScrollPane2, new GridBagConstraints(0, 0, 1, 3, 0.6, 0.23,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(7, 7, 3, 3), 93, 18)); jScrollPane2.getViewport().add(jTextArea1, null); } void jButton2_actionPerformed(ActionEvent e) { this.hide() ; } void jButton2_keyReleased(KeyEvent e) { if (e.getKeyChar()=='\n') { this.hide(); } } void jButton1_actionPerformed(ActionEvent e) { 267 Código Fuente ejecutarConsulta(); } void ejecutarConsulta() { String query=jTextArea1.getText().trim() ; sqlQuery1.setQuery(query) ; sqlQuery1.exeQuery(); } } void this_windowActivated(WindowEvent e) { jTextArea1.requestFocus(); } C.4.1.4 Clase: JDBCAdapter //Nota: esta clase pertenece a los ejemplos de JDK 1.2, // pero tiene algunas modificaciones para ser utilizada en este proyecto package consultorSql; import import import import java.util.Vector; java.sql.*; javax.swing.table.AbstractTableModel; javax.swing.event.TableModelEvent; public class JDBCAdapter extends AbstractTableModel { Connection connection; Statement statement; ResultSet resultSet; String[] columnNames = {}; Vector rows = new Vector(); ResultSetMetaData metaData; public JDBCAdapter() { } public JDBCAdapter(String url, String driverName, String user, String passwd) { try{ open(url,driverName,user,passwd); } catch (Exception e) { System.err.println(e); } } 268 Código Fuente /** * Ejecuta la consulta pasada por parametro. */ public void executeQuery(String query) throws SQLException { if (connection == null || statement == null) { SQLException e=new SQLException("Base de datos Cerrada"); System.err.println("No hay base de datos que ejecute la consulta, debe conectarse a una"); throw e; } resultSet = statement.executeQuery(query); metaData = resultSet.getMetaData(); int numberOfColumns = metaData.getColumnCount(); columnNames = new String[numberOfColumns]; // Get the column names and cache them. // Then we can close the connection. for(int column = 0; column < numberOfColumns; column++) { columnNames[column] = metaData.getColumnLabel(column+1); } // Get all rows. rows = new Vector(); while (resultSet.next()) { Vector newRow = new Vector(); for (int i = 1; i <= getColumnCount(); i++) { newRow.addElement(resultSet.getObject(i)); } rows.addElement(newRow); } fireTableChanged(null); // Tell the listeners a new table // has arrived. } /********************************************************************** * Abre la coneccion a la base de datos con los atributos de coneccion. */ public void open(String url, String driverName, String user, String passwd) throws Exception { Class.forName(driverName); System.out.println("conectando a la base de datos..."); connection = DriverManager.getConnection(url, user, passwd); statement = connection.createStatement(); } 269 Código Fuente /** * Cierra la conneccion a la base de datos. */ public void close() throws SQLException { System.out.println("Cerrando la conneccion a la base de datos"); resultSet.close(); statement.close(); connection.close(); } protected void finalize() throws Throwable { close(); super.finalize(); } //////////////////////////////////////////////////////////////////////// // // // Implementacion de la interfaz TableModel // //////////////////////////////////////////////////////////////////////// // // MetaData public String getColumnName(int column) { if (columnNames[column] != null) { return columnNames[column]; } else { return ""; } } public Class getColumnClass(int column) { int type; try { type = metaData.getColumnType(column+1); } catch (SQLException e) { return super.getColumnClass(column); } switch(type) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: return String.class; case Types.BIT: return Boolean.class; case Types.TINYINT: 270 Código Fuente case Types.SMALLINT: case Types.INTEGER: return Integer.class; case Types.BIGINT: return Long.class; case Types.FLOAT: case Types.DOUBLE: return Double.class; case Types.DATE: return java.sql.Date.class; } default: return Object.class; } public boolean isCellEditable(int row, int column) { try { return metaData.isWritable(column+1); } catch (SQLException e) { return false; } } public int getColumnCount() { return columnNames.length; } // Data methods public int getRowCount() { return rows.size(); } public Object getValueAt(int aRow, int aColumn) { Vector row = (Vector)rows.elementAt(aRow); return row.elementAt(aColumn); } public String dbRepresentation(int column, Object value) { int type; if (value == null) { return "null"; } try { type = metaData.getColumnType(column+1); } catch (SQLException e) { 271 Código Fuente } return value.toString(); switch(type) { case Types.INTEGER: case Types.DOUBLE: case Types.FLOAT: return value.toString(); case Types.BIT: return ((Boolean)value).booleanValue() ? "1" : "0"; case Types.DATE: return value.toString().trim(); default: return "\""+value.toString()+"\""; } } public void setValueAt(Object value, int row, int column) { try { String tableName = metaData.getTableName(column+1); if (tableName == null) { System.out.println("Table name returned null."); } String columnName = getColumnName(column); String query = "update "+tableName+ " set "+columnName+" = "+dbRepresentation(column, value)+ " where "; // We don't have a model of the schema so we don't know the // primary keys or which columns to lock on. To demonstrate // that editing is possible, we'll just lock on everything. for(int col = 0; col<getColumnCount(); col++) { String colName = getColumnName(col); if (colName.equals("")) { continue; } if (col != 0) { query = query + " and "; } query = query + colName +" = "+ dbRepresentation(col, getValueAt(row, col)); } System.out.println(query); //System.out.println("Not sending update to database"); // statement.executeQuery(query); } catch (SQLException e) { // e.printStackTrace(); System.err.println("Update failed"); } Vector dataRow = (Vector)rows.elementAt(row); dataRow.setElementAt(value, column); 272 Código Fuente } } C.4.1.5 Clase: OldJTable //Nota: esta clase pertenece a los ejemplos de JDK 1.2, // pero tiene algunas modificaciones para ser utilizada en este proyecto package consultorSql; import import import import import import import import java.lang.Thread; java.util.*; java.awt.*; java.awt.event.*; javax.swing.*; javax.swing.event.*; javax.swing.plaf.*; javax.swing.table.*; /** * The OldJTable is an unsupported class containing some methods that * were deleted from the JTable between releases 0.6 and 0.7 */ public class OldJTable extends JTable { /* * A new convenience method returning the index of the column in the * co-ordinate space of the view. */ public int getColumnIndex(Object identifier) { return getColumnModel().getColumnIndex(identifier); } // // // // Methods deleted from the JTable because they only work with the DefaultTableModel. public TableColumn addColumn(Object columnIdentifier, int width) { return addColumn(columnIdentifier, width, null, null, null); } public TableColumn addColumn(Object columnIdentifier, Vector columnData) { return addColumn(columnIdentifier, -1, null, null, columnData); } public TableColumn addColumn(Object columnIdentifier, int width, TableCellRenderer renderer,TableCellEditor editor) 273 Código Fuente { } return addColumn(columnIdentifier, width, renderer, editor, null); public TableColumn addColumn(Object columnIdentifier, int width, TableCellRenderer renderer, TableCellEditor editor, Vector columnData) { checkDefaultTableModel(); // Set up the model side first DefaultTableModel m = (DefaultTableModel)getModel(); m.addColumn(columnIdentifier, columnData); } // The column will have been added to the end, so the index of the // column in the model is the last element. TableColumn newColumn = new TableColumn(m.getColumnCount()-1, width, renderer, editor); super.addColumn(newColumn); return newColumn; public void removeColumn(Object columnIdentifier) { super.removeColumn(getColumn(columnIdentifier)); } public void addRow(Object[] rowData) { checkDefaultTableModel(); ((DefaultTableModel)getModel()).addRow(rowData); } public void addRow(Vector rowData) { checkDefaultTableModel(); ((DefaultTableModel)getModel()).addRow(rowData); } public void removeRow(int rowIndex) { checkDefaultTableModel(); ((DefaultTableModel)getModel()).removeRow(rowIndex); } public void moveRow(int startIndex, int endIndex, int toIndex) { checkDefaultTableModel(); ((DefaultTableModel)getModel()).moveRow(startIndex, endIndex, toIndex); } public void insertRow(int rowIndex, Object[] rowData) { checkDefaultTableModel(); ((DefaultTableModel)getModel()).insertRow(rowIndex, rowData); } public void insertRow(int rowIndex, Vector rowData) { 274 Código Fuente } checkDefaultTableModel(); ((DefaultTableModel)getModel()).insertRow(rowIndex, rowData); public void setNumRows(int newSize) { checkDefaultTableModel(); ((DefaultTableModel)getModel()).setNumRows(newSize); } public void setDataVector(Vector newData, Vector columnIds) { checkDefaultTableModel(); ((DefaultTableModel)getModel()).setDataVector(newData, columnIds); } public void setDataVector(Object[][] newData, Object[] columnIds) { checkDefaultTableModel(); ((DefaultTableModel)getModel()).setDataVector(newData, columnIds); } protected void checkDefaultTableModel() { if(!(dataModel instanceof DefaultTableModel)) throw new InternalError("In order to use this method, the data model must be an instance of efaultTableModel."); } // // // Methods removed from JTable in the move from identifiers to ints. public Object getValueAt(Object columnIdentifier, int rowIndex) { return super.getValueAt(rowIndex, getColumnIndex(columnIdentifier)); } public boolean isCellEditable(Object columnIdentifier, int rowIndex) { return super.isCellEditable(rowIndex, getColumnIndex(columnIdentifier)); } public void setValueAt(Object aValue, Object columnIdentifier, int rowIndex) { super.setValueAt(aValue, rowIndex,getColumnIndex(columnIdentifier)); } public boolean editColumnRow(Object identifier, int row) { return super.editCellAt(row, getColumnIndex(identifier)); } public void moveColumn(Object columnIdentifier, Object targetColumnIdentifier) 275 Código Fuente { } moveColumn(getColumnIndex(columnIdentifier), getColumnIndex(targetColumnIdentifier)); public boolean isColumnSelected(Object identifier) { return isColumnSelected(getColumnIndex(identifier)); } public TableColumn addColumn(int modelColumn, int width) { return addColumn(modelColumn, width, null, null); } public TableColumn addColumn(int modelColumn) { return addColumn(modelColumn, 75, null, null); } /** * Creates a new column with <I>modelColumn</I>, <I>width</I>, * <I>renderer</I>, and <I>editor</I> and adds it to the end of * the JTable's array of columns. This method also retrieves the * name of the column using the model's <I> * getColumnName(modelColumn) </I> method, and sets the both the * header value and the identifier for this TableColumn accordingly. * <p> * The <I>modelColumn</I> is the index of the column in the model * which will supply the data for this column in the table. This, * like the <I>columnIdentifier</I> in previous releases, does not * change as the columns are moved in the view. * <p> * For the rest of the JTable API, and all of its associated * classes, columns are referred to in the co-ordinate system of the * view, the index of the column in the model is kept inside the * TableColumn and is used only to retrieve the information from the * appropraite column in the model. * <p> * * @param modelColumn The index of the column in the model * @param width The new column's width. Or -1 to use * the default width * @param renderer The renderer used with the new column. * Or null to use the default renderer. * @param editor The editor used with the new column. * Or null to use the default editor. */ public TableColumn addColumn(int modelColumn, int width, TableCellRenderer renderer, TableCellEditor editor) { TableColumn newColumn = new TableColumn(modelColumn, width, renderer, editor); addColumn(newColumn); return newColumn; 276 Código Fuente } public boolean editColumnRow(int columnIndex, int rowIndex) { return super.editCellAt(rowIndex, columnIndex); } public boolean editColumnRow(int columnIndex, int rowIndex, EventObject e) { return super.editCellAt(rowIndex, columnIndex, e); } } // End Of Class OldJTable C.4.1.6 Clase sqlQuery //Nota: esta clase pertenece a los ejemplos de JDK 1.2, // pero tiene algunas modificaciones para ser utilizada en este proyecto package consultorSql; import java.sql.SQLException; import javax.swing.JOptionPane; import javax.swing.JTable; import java.awt.event.*; import javax.swing.JWindow; public class sqlQuery extends JTable { private TableSorter sorter=new TableSorter(); private JDBCAdapter dataBase=new JDBCAdapter(); private String usr=""; private String passwd=""; private String url=""; private String driver=""; private String query=""; private boolean active=false; /** * Constructor por derfecto del sqlQuery */ public sqlQuery() { this.setModel(sorter); sorter.addMouseListenerToHeaderInTable(this); sorter.setModel(dataBase) ; try { jbInit(); } catch(Exception e) { e.printStackTrace(); } 277 Código Fuente } /** * Funcion mutadora de Usr */ public void setUsr(String usr) { this.usr=usr; } /** * Funcion mutadora de passwd */ public void setPasswd(String passwd) { this.passwd = passwd; } /** * Funcion mutadora de Url */ public void setUrl(String url) { this.url = url; } /** * Funcion mutadora de driver */ public void setDriver(String driver) { this.driver = driver; } /** * Funcion mutadora de query */ public void setQuery(String query) { this.query = query; } /** * Funcion mutadora de active */ public void setActive(boolean active) { if (active) { if ((url!="") && (driver!="") && ( usr !="") && (passwd !="") && (query!="")) { try { dataBase.open(url,driver,usr,passwd); 278 Código Fuente dataBase.executeQuery(query) ; sorter.setModel(dataBase); } catch (Exception e) { System.err.println(e); active=false; } } else active=false; } else { } try { dataBase.close() ; } catch (Exception e) { System.err.println("base de datos no se pudo cerrar"); System.err.println(e); active=false; } } this.active = active; /** * Funcion observadora de Usr */ public String getUsr() { return this.usr; } /** * Funcion observadora de passwd */ public String getPasswd() { return this.passwd; } /** * Funcion observadora de Url */ public String getUrl() { return this.url ; } /** * Funcion observadora de driver */ 279 Código Fuente public String getDriver() { return this.driver; } /** * Funcion observadora de active */ public boolean getActive() { return this.active; } /** * Funcion observadora de query */ public String getQuery() { return this.query; } /** * Funcion que ejecuta la consulta del atributo query */ public void exeQuery() { if (!this.active) {this.setActive(true);} try { dataBase.executeQuery(query); } catch (SQLException e){ JOptionPane.showMessageDialog(this,e.getMessage()) ; } } private void jbInit() throws Exception { this.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); this.addFocusListener(new java.awt.event.FocusAdapter() { public void focusGained(FocusEvent e) { this_focusGained(e); } }); } void this_focusGained(FocusEvent e) { } 280 Código Fuente } C.4.1.7 Clase: TableMap //Nota: esta clase pertenece a los ejemplos de JDK 1.2, // pero tiene algunas modificaciones para ser utilizada en este proyecto package consultorSql; import javax.swing.table.*; import javax.swing.event.TableModelListener; import javax.swing.event.TableModelEvent; public class TableMap extends AbstractTableModel implements TableModelListener { protected TableModel model; public TableModel return model; } getModel() { public void setModel(TableModel model) { this.model = model; model.addTableModelListener(this); } // By default, Implement TableModel by forwarding all messages // to the model. public Object getValueAt(int aRow, int aColumn) { return model.getValueAt(aRow, aColumn); } public void setValueAt(Object aValue, int aRow, int aColumn) { model.setValueAt(aValue, aRow, aColumn); } public int getRowCount() { return (model == null) ? 0 : model.getRowCount(); } public int getColumnCount() { return (model == null) ? 0 : model.getColumnCount(); } public String getColumnName(int aColumn) { return model.getColumnName(aColumn); } public Class getColumnClass(int aColumn) { return model.getColumnClass(aColumn); } public boolean isCellEditable(int row, int column) { return model.isCellEditable(row, column); 281 Código Fuente } // // Implementation of the TableModelListener interface, // By default forward all events to all the listeners. public void tableChanged(TableModelEvent e) { fireTableChanged(e); } } C.4.1.8 Clase: TableSorter //Nota: esta clase pertenece a los ejemplos de JDK 1.2, // pero tiene algunas modificaciones para ser utilizada en este proyecto package consultorSql; import java.util.*; import javax.swing.table.TableModel; import javax.swing.event.TableModelEvent; // Imports for picking up mouse events from the JTable. import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.InputEvent; import javax.swing.JTable; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; public class TableSorter extends TableMap { int indexes[]; Vector sortingColumns = new Vector(); boolean ascending = true; int compares; public TableSorter() { indexes = new int[0]; // For consistency. } public TableSorter(TableModel model) { setModel(model); } public void setModel(TableModel model) { super.setModel(model); reallocateIndexes(); } public int compareRowsByColumn(int row1, int row2, int column) { 282 Código Fuente Class type = model.getColumnClass(column); TableModel data = model; // Check for nulls Object o1 = data.getValueAt(row1, column); Object o2 = data.getValueAt(row2, column); // If both values are null return 0 if (o1 == null && o2 == null) { return 0; } else if (o1 == null) { // Define null less than everything. return -1; } else if (o2 == null) { return 1; } /* We copy all returned values from the getValue call in case an optimised model is reusing one object to return many values. The Number subclasses in the JDK are immutable and so will not be used in this way but other subclasses of Number might want to do this to save space and avoid unnecessary heap allocation. */ if (type.getSuperclass() == java.lang.Number.class) { Number n1 = (Number)data.getValueAt(row1, column); double d1 = n1.doubleValue(); Number n2 = (Number)data.getValueAt(row2, column); double d2 = n2.doubleValue(); if (d1 < d2) return -1; else if (d1 > d2) return 1; else return 0; } else if (type { Date d1 long n1 Date d2 long n2 == java.util.Date.class) = = = = (Date)data.getValueAt(row1, column); d1.getTime(); (Date)data.getValueAt(row2, column); d2.getTime(); if (n1 < n2) return -1; else if (n1 > n2) return 1; else return 0; } else if (type == String.class) { 283 Código Fuente String s1 = (String)data.getValueAt(row1, column); String s2 = (String)data.getValueAt(row2, column); int result = s1.compareTo(s2); if (result < 0) return -1; else if (result > 0) return 1; else return 0; } else if (type { Boolean boolean Boolean boolean } else { } } == Boolean.class) bool1 = (Boolean)data.getValueAt(row1, column); b1 = bool1.booleanValue(); bool2 = (Boolean)data.getValueAt(row2, column); b2 = bool2.booleanValue(); if (b1 == b2) return 0; else if (b1) // Define false < true return 1; else return -1; Object v1 = data.getValueAt(row1, column); String s1 = v1.toString(); Object v2 = data.getValueAt(row2, column); String s2 = v2.toString(); int result = s1.compareTo(s2); if (result < 0) return -1; else if (result > 0) return 1; else return 0; public int compare(int row1, int row2) { compares++; for(int level = 0; level < sortingColumns.size(); level++) { Integer column = (Integer)sortingColumns.elementAt(level); int result = compareRowsByColumn(row1, row2, column.intValue()); if (result != 0) return ascending ? result : -result; } return 0; } public void reallocateIndexes() 284 Código Fuente { int rowCount = model.getRowCount(); // Set up a new array of indexes with the right number of elements // for the new data model. indexes = new int[rowCount]; } // Initialise with the identity mapping. for(int row = 0; row < rowCount; row++) indexes[row] = row; public void tableChanged(TableModelEvent e) { System.out.println("Sorter: tableChanged"); reallocateIndexes(); } super.tableChanged(e); public void checkModel() { if (indexes.length != model.getRowCount()) { System.err.println("Sorter not informed of a change in model."); } } public void sort(Object sender) { checkModel(); compares = 0; // n2sort(); // qsort(0, indexes.length-1); //similar a mergesort; shuttlesort((int[])indexes.clone(), indexes, 0, indexes.length); System.out.println("Compares: "+compares); } public void n2sort() { for(int i = 0; i < getRowCount(); i++) { for(int j = i+1; j < getRowCount(); j++) { if (compare(indexes[i], indexes[j]) == -1) { swap(i, j); } } } } // // // // // This is a home-grown implementation which we have not had time to research - it may perform poorly in some circumstances. It requires twice the space of an in-place algorithm and makes NlogN assigments shuttling the values between the two arrays. The number of compares appears to vary between N-1 and 285 Código Fuente // NlogN depending on the initial order but the main reason for // using it here is that, unlike qsort, it is stable. public void shuttlesort(int from[], int to[], int low, int high) { if (high - low < 2) { return; } int middle = (low + high)/2; shuttlesort(to, from, low, middle); shuttlesort(to, from, middle, high); int p = low; int q = middle; /* This is an optional short-cut; at each recursive call, check to see if the elements in this subset are already ordered. If so, no further comparisons are needed; the sub-array can just be copied. The array must be copied rather than assigned otherwise sister calls in the recursion might get out of sinc. When the number of elements is three they are partitioned so that the first set, [low, mid), has one element and and the second, [mid, high), has two. We skip the optimisation when the number of elements is three or less as the first compare in the normal merge will produce the same sequence of steps. This optimisation seems to be worthwhile for partially ordered lists but some analysis is needed to find out how the performance drops to Nlog(N) as the initial order diminishes - it may drop very quickly. */ if (high - low >= 4 && compare(from[middle-1], from[middle]) <= 0) { for (int i = low; i < high; i++) { to[i] = from[i]; } return; } } // A normal merge. for(int i = low; i < high; i++) { if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) { to[i] = from[p++]; } else { to[i] = from[q++]; } } public void swap(int i, int j) { int tmp = indexes[i]; 286 Código Fuente } indexes[i] = indexes[j]; indexes[j] = tmp; // The mapping only affects the contents of the data rows. // Pass all requests to these rows through the mapping array: // "indexes". public Object getValueAt(int aRow, int aColumn) { checkModel(); return model.getValueAt(indexes[aRow], aColumn); } public void setValueAt(Object aValue, int aRow, int aColumn) { checkModel(); model.setValueAt(aValue, indexes[aRow], aColumn); } public void sortByColumn(int column) { sortByColumn(column, true); } public void sortByColumn(int column, boolean ascending) { this.ascending = ascending; sortingColumns.removeAllElements(); sortingColumns.addElement(new Integer(column)); sort(this); super.tableChanged(new TableModelEvent(this)); } // There is no-where else to put this. // Add a mouse listener to the Table to trigger a table sort // when a column heading is clicked in the JTable. public void addMouseListenerToHeaderInTable(JTable table) { final TableSorter sorter = this; final JTable tableView = table; tableView.setColumnSelectionAllowed(false); MouseAdapter listMouseListener = new MouseAdapter() { public void mouseClicked(MouseEvent e) { TableColumnModel columnModel = tableView.getColumnModel(); int viewColumn = columnModel.getColumnIndexAtX(e.getX()); int column =tableView.convertColumnIndexToModel(viewColumn); if(e.getClickCount() == 1 && column != -1) { System.out.println("Sorting ..."); int shiftPressed=e.getModifiers()&InputEvent.SHIFT_MASK; boolean ascending = (shiftPressed == 0); sorter.sortByColumn(column, ascending); } } }; JTableHeader th = tableView.getTableHeader(); 287 Código Fuente } } th.addMouseListener(listMouseListener); 288 Código Fuente C.4.2 Paquete cuencamaule En esta sección mostraremos algunas clases, puesto que la mayoría son 100% generadas por el RAD JBUILDER 4, a partir del diseño visual de las ventanas. C.4.2.1 Clase: Frame1 package cuencamaule; import import import import import import import import import java.awt.*; java.awt.event.*; javax.swing.*; figuras.*; consultorSql.*; objcuenca.*; java.util.*; javax.swing.border.*; java.sql.Connection; public class Frame1 extends JFrame { boolean insert=false; int tipoInsert=0; private static final int INSERT_CANAL=1; private static final int INSERT_CAP_AGUA_POT=2; private Graficable figInsert; private int countClick=0; private ArrPuntosOra puntos; JPanel contentPane; CuencaOraRef CuencaActualRef; BDObjCuenca dataBase=new BDObjCuenca(); JMenuBar jMenuBar1 = new JMenuBar(); JMenu jMenuFile = new JMenu(); JMenuItem jMenuFileExit = new JMenuItem(); JMenu jMenuHelp = new JMenu(); JMenuItem jMenuHelpAbout = new JMenuItem(); JToolBar jToolBar = new JToolBar(); JButton btAbrir = new JButton(); JButton jButton2 = new JButton(); JButton jButton3 = new JButton(); ImageIcon image1; ImageIcon image2; ImageIcon image3; JLabel statusBar = new JLabel(); BorderLayout borderLayout1 = new BorderLayout(); JScrollPane FigurasSP = new JScrollPane(); JMenuItem menuAbrirCuenca = new JMenuItem(); JMenu jMenu1 = new JMenu(); JMenuItem mnuNodo = new JMenuItem(); JMenuItem menuNueva = new JMenuItem(); JMenuItem mnuEm = new JMenuItem(); JMenuItem mnuHI = new JMenuItem(); 289 Código Fuente JMenuItem mnRN = new JMenuItem(); JMenuItem mnuTr = new JMenuItem(); JMenuItem mnuCAP = new JMenuItem(); JMenuItem mnuCanal = new JMenuItem(); JMenuItem mnuCentHidro = new JMenuItem(); JMenuItem menuModificarCuenca = new JMenuItem(); JMenu jMenu2 = new JMenu(); JRadioButtonMenuItem jRadioButtonMenuItem2 = new JRadioButtonMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem3 = new JRadioButtonMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem4 = new JRadioButtonMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem5 = new JRadioButtonMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem7 = new JRadioButtonMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem8 = new JRadioButtonMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem9 = new JRadioButtonMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem10 = new JRadioButtonMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem6 = new JRadioButtonMenuItem(); JMenu jMenu3 = new JMenu(); JMenuItem mnuEmbBajos = new JMenuItem(); JMenuItem mnuEnGen = new JMenuItem(); JRadioButtonMenuItem jRadioButtonMenuItem11 = new JRadioButtonMenuItem(); JMenuItem jMenuSimular = new JMenuItem(); JMenuItem jMenuItem17 = new JMenuItem(); JMenuItem jMenuItem2 = new JMenuItem(); JMenuItem jMenuItem18 = new JMenuItem(); JMenuItem jMenuItem19 = new JMenuItem(); FrmConexion datosConeccion; LienzoCuenca lienzo = new LienzoCuenca(); JPanel jPanel1 = new JPanel(); JPanel jPanel3 = new JPanel(); Border border1; JPanel jPanel4 = new JPanel(); GridBagLayout gridBagLayout2 = new GridBagLayout(); JLabel jlMensaje = new JLabel(); JPanel jPanel5 = new JPanel(); GridBagLayout gridBagLayout1 = new GridBagLayout(); JLabel jlPunto = new JLabel(); JPanel jPanel2 = new JPanel(); JMenu jMenu4 = new JMenu(); JMenuItem menuNuevoRio = new JMenuItem(); JMenuItem menuModificarRio = new JMenuItem(); /**Construct the frame*/ public Frame1() { enableEvents(AWTEvent.WINDOW_EVENT_MASK); try { 290 Código Fuente jbInit(); } } catch(Exception e) { e.printStackTrace(); } /**Component initialization*/ private void jbInit() throws Exception { image1 = new ImageIcon(cuencamaule.Frame1.class.getResource("openFile.gif")); image2 = new ImageIcon(cuencamaule.Frame1.class.getResource("closeFile.gif")); image3 = new ImageIcon(cuencamaule.Frame1.class.getResource("help.gif")); contentPane = (JPanel) this.getContentPane(); contentPane.setLayout(borderLayout1); this.setSize(new Dimension(647, 452)); this.addWindowListener(new java.awt.event.WindowAdapter() { public void windowOpened(WindowEvent e) { this_windowOpened(e); } }); this.setTitle("Modelo de Simulación Hidrologico Operacional"); statusBar.setText(" "); jMenuFile.setText("Cuenca"); jMenuFile.setMnemonic('C'); jMenuFileExit.setAccelerator(javax.swing.KeyStroke.getKeyStroke(88, java.awt.event.KeyEvent.CTRL_MASK, true)); jMenuFileExit.setText("Salir"); jMenuFileExit.setMnemonic('S'); jMenuFileExit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { jMenuFileExit_actionPerformed(e); } }); jMenuHelp.setText("Ayuda"); jMenuHelp.setMnemonic('A'); jMenuHelpAbout.setText("Acerca de..."); jMenuHelpAbout.setMnemonic('c'); jMenuHelpAbout.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { jMenuHelpAbout_actionPerformed(e); } }); btAbrir.setIcon(image1); btAbrir.setToolTipText("Open File"); btAbrir.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { 291 Código Fuente btAbrir_actionPerformed(e); } }); jButton2.setIcon(image2); jButton2.setToolTipText("Simular"); jButton3.setIcon(image3); jButton3.setToolTipText("Ayuda"); FigurasSP.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); FigurasSP.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); FigurasSP.getViewport().setBackground(Color.white); menuAbrirCuenca.setText("Abrir..."); menuAbrirCuenca.setMnemonic('A'); menuAbrirCuenca.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { menuAbrirCuenca_actionPerformed(e); } }); jMenu1.setText("Insertar"); jMenu1.setMnemonic('I'); mnuNodo.setEnabled(false); mnuNodo.setText("Nodo"); mnuNodo.setMnemonic('N'); mnuNodo.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnuNodo_actionPerformed(e); } }); menuNueva.setAccelerator(javax.swing.KeyStroke.getKeyStroke(78, java.awt.event.KeyEvent.CTRL_MASK, true)); menuNueva.setText("Nueva..."); menuNueva.setMnemonic('N'); menuNueva.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { menuNueva_actionPerformed(e); } }); mnuEm.setEnabled(false); mnuEm.setText("Embalse"); mnuEm.setMnemonic('E'); mnuEm.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnuEm_actionPerformed(e); 292 Código Fuente } }); mnuHI.setEnabled(false); mnuHI.setText("Hoya Intermedia"); mnuHI.setMnemonic('H'); mnuHI.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnuHI_actionPerformed(e); } }); mnRN.setEnabled(false); mnRN.setText("Regimen Natural"); mnRN.setMnemonic('g'); mnRN.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnRN_actionPerformed(e); } }); mnuTr.setEnabled(false); mnuTr.setText("Tramo"); mnuTr.setMnemonic('T'); mnuTr.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnuTr_actionPerformed(e); } }); mnuCAP.setEnabled(false); mnuCAP.setText("Captación Agua Potable"); mnuCAP.setMnemonic('C'); mnuCAP.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnuCAP_actionPerformed(e); } }); mnuCanal.setEnabled(false); mnuCanal.setText("Canal"); mnuCanal.setMnemonic('a'); mnuCanal.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnuCanal_actionPerformed(e); } }); mnuCentHidro.setEnabled(false); mnuCentHidro.setText("Central Hidroeléctrica"); 293 Código Fuente mnuCentHidro.setMnemonic('l'); mnuCentHidro.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnuCentHidro_actionPerformed(e); } }); menuModificarCuenca.setEnabled(false); menuModificarCuenca.setText("Modificar..."); menuModificarCuenca.setMnemonic(77); menuModificarCuenca.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { menuModificarCuenca_actionPerformed(e); } }); jMenu2.setText("Zoom"); jMenu2.setMnemonic('Z'); jRadioButtonMenuItem2.setEnabled(false); jRadioButtonMenuItem2.setText("15%"); jRadioButtonMenuItem2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem2_actionPerformed(e); } }); jRadioButtonMenuItem3.setEnabled(false); jRadioButtonMenuItem3.setText("30%"); jRadioButtonMenuItem3.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem3_actionPerformed(e); } }); jRadioButtonMenuItem4.setEnabled(false); jRadioButtonMenuItem4.setText("50%"); jRadioButtonMenuItem4.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem4_actionPerformed(e); } }); jRadioButtonMenuItem5.setEnabled(false); jRadioButtonMenuItem5.setText("75%"); jRadioButtonMenuItem5.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { 294 Código Fuente jRadioButtonMenuItem5_actionPerformed(e); } }); jRadioButtonMenuItem7.setEnabled(false); jRadioButtonMenuItem7.setText("125%"); jRadioButtonMenuItem7.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem7_actionPerformed(e); } }); jRadioButtonMenuItem8.setEnabled(false); jRadioButtonMenuItem8.setText("150%"); jRadioButtonMenuItem8.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem8_actionPerformed(e); } }); jRadioButtonMenuItem9.setEnabled(false); jRadioButtonMenuItem9.setText("200%"); jRadioButtonMenuItem9.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem9_actionPerformed(e); } }); jRadioButtonMenuItem10.setEnabled(false); jRadioButtonMenuItem10.setText("300%"); jRadioButtonMenuItem10.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem10_actionPerformed(e); } }); jRadioButtonMenuItem6.setEnabled(false); jRadioButtonMenuItem6.setSelected(true); jRadioButtonMenuItem6.setText("100%"); jRadioButtonMenuItem6.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem6_actionPerformed(e); } }); jMenu3.setText("Consultas"); jMenu3.setMnemonic('o'); mnuEmbBajos.setEnabled(false); mnuEmbBajos.setText("Embalse Bajo sus Volumens Minimos..."); mnuEmbBajos.setMnemonic('E'); mnuEmbBajos.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { 295 Código Fuente mnuEmbBajos_actionPerformed(e); } }); mnuEnGen.setEnabled(false); mnuEnGen.setText("Energía Generada..."); mnuEnGen.setMnemonic('n'); mnuEnGen.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { mnuEnGen_actionPerformed(e); } }); ButtonGroup group = new ButtonGroup(); jRadioButtonMenuItem11.setEnabled(false); jRadioButtonMenuItem11.setText("Personalizado..."); jRadioButtonMenuItem11.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jRadioButtonMenuItem11_actionPerformed(e); } }); jMenuSimular.setEnabled(false); jMenuSimular.setText("Iniciar Simulación..."); jMenuSimular.setMnemonic(73); jMenuSimular.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jMenuSimular_actionPerformed(e); } }); jMenuItem17.setText("Conección..."); jMenuItem17.setMnemonic('o'); jMenuItem17.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jMenuItem17_actionPerformed(e); } }); jMenuItem2.setText("SQL..."); jMenuItem2.setMnemonic('S'); jMenuItem2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jMenuItem2_actionPerformed(e); } }); jMenuItem18.setText("Contenido..."); 296 Código Fuente jMenuItem18.setMnemonic('C'); jMenuItem19.setText("Búsqueda..."); jMenuItem19.setMnemonic('B'); jPanel4.setLayout(gridBagLayout2); jPanel3.setLayout(gridBagLayout1); jMenu4.setText("Rio"); jMenu4.setMnemonic(82); menuNuevoRio.setText("Nuevo..."); menuNuevoRio.setMnemonic(78); menuNuevoRio.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { menuNuevoRio_actionPerformed(e); } }); menuModificarRio.setText("Modificar..."); menuModificarRio.setMnemonic(77); menuModificarRio.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { menuModificarRio_actionPerformed(e); } }); lienzo.setBackground(Color.white); lienzo.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(MouseEvent e) { lienzo_mouseClicked(e); } }); group.add(jRadioButtonMenuItem2); group.add(jRadioButtonMenuItem3); group.add(jRadioButtonMenuItem4); group.add(jRadioButtonMenuItem5); group.add(jRadioButtonMenuItem6); group.add(jRadioButtonMenuItem7); group.add(jRadioButtonMenuItem8); group.add(jRadioButtonMenuItem9); group.add(jRadioButtonMenuItem10); group.add(jRadioButtonMenuItem11); jMenuFile.add(menuNueva); jMenuFile.add(menuAbrirCuenca); jMenuFile.add(menuModificarCuenca); jMenuFile.addSeparator(); jMenuFile.add(jMenuItem17); jMenuFile.add(jMenuSimular); jMenuFile.addSeparator(); jMenuFile.add(jMenuFileExit); 297 Código Fuente } jMenuHelp.add(jMenuItem18); jMenuHelp.add(jMenuItem19); jMenuHelp.add(jMenuHelpAbout); jMenuBar1.add(jMenuFile); jMenuBar1.add(jMenu4); jMenuBar1.add(jMenu2); jMenuBar1.add(jMenu1); jMenuBar1.add(jMenu3); jMenuBar1.add(jMenuHelp); this.setJMenuBar(jMenuBar1); contentPane.add(FigurasSP, BorderLayout.CENTER); FigurasSP.getViewport().add(lienzo, null); contentPane.add(jToolBar, BorderLayout.NORTH); jToolBar.add(btAbrir); jToolBar.add(jButton2); jToolBar.add(jButton3, null); contentPane.add(jPanel3, BorderLayout.SOUTH); jPanel3.add(jlMensaje, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0 ,GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 12, 4, 8), 0, 0)); jPanel3.add(jlPunto, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0 ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(4, 12, 4, 12), 0, 0)); contentPane.add(jPanel5, BorderLayout.EAST); contentPane.add(jPanel2, BorderLayout.WEST); jMenu1.add(mnuNodo); jMenu1.add(mnuTr); jMenu1.add(mnuEm); jMenu1.add(mnuHI); jMenu1.add(mnRN); jMenu1.add(mnuCAP); jMenu1.add(mnuCanal); jMenu1.add(mnuCentHidro); jMenu2.add(jRadioButtonMenuItem2); jMenu2.add(jRadioButtonMenuItem3); jMenu2.add(jRadioButtonMenuItem4); jMenu2.add(jRadioButtonMenuItem5); jMenu2.add(jRadioButtonMenuItem6); jMenu2.add(jRadioButtonMenuItem7); jMenu2.add(jRadioButtonMenuItem8); jMenu2.add(jRadioButtonMenuItem9); jMenu2.add(jRadioButtonMenuItem10); jMenu2.add(jRadioButtonMenuItem11); jMenu3.add(mnuEmbBajos); jMenu3.add(mnuEnGen); jMenu3.add(jMenuItem2); jMenu4.add(menuNuevoRio); jMenu4.add(menuModificarRio); public void jMenuFileExit_actionPerformed(ActionEvent e) { System.exit(0); } 298 Código Fuente public void jMenuHelpAbout_actionPerformed(ActionEvent e) { Frame1_AboutBox dlg = new Frame1_AboutBox(this); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.setModal(true); dlg.show(); } /**Overridden so we can exit when window is closed*/ protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); if (e.getID() == WindowEvent.WINDOW_CLOSING) { jMenuFileExit_actionPerformed(null); } } void jRadioButtonMenuItem1_actionPerformed(ActionEvent e) { lienzo.setZoom(5); } void jRadioButtonMenuItem2_actionPerformed(ActionEvent e) { lienzo.setZoom(15); } void jRadioButtonMenuItem3_actionPerformed(ActionEvent e) { lienzo.setZoom(30); } void jRadioButtonMenuItem4_actionPerformed(ActionEvent e) { lienzo.setZoom(50); } void jRadioButtonMenuItem5_actionPerformed(ActionEvent e) { lienzo.setZoom(75); } void jRadioButtonMenuItem6_actionPerformed(ActionEvent e) { lienzo.setZoom(100); } void jRadioButtonMenuItem7_actionPerformed(ActionEvent e) { lienzo.setZoom(125); } void jRadioButtonMenuItem8_actionPerformed(ActionEvent e) { lienzo.setZoom(150); } 299 Código Fuente void jRadioButtonMenuItem9_actionPerformed(ActionEvent e) { lienzo.setZoom(200); } void jRadioButtonMenuItem10_actionPerformed(ActionEvent e) { lienzo.setZoom(300); } void jRadioButtonMenuItem11_actionPerformed(ActionEvent e) { //llamar al dialogo de zoom FrmZoom dlg = new FrmZoom(this, lienzo); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.show(); } void menuNueva_actionPerformed(ActionEvent e) { try { if ((dataBase==null)||(dataBase.isClosed())) { String usr=datosConeccion.getUsr(); String psswd=datosConeccion.getPasswd(); String url=datosConeccion.getUrl(); String drv=datosConeccion.getDriver(); dataBase=new BDObjCuenca(url,drv,usr,psswd); try{dataBase.open() ;} catch(Exception err) { JOptionPane.showMessageDialog(this,err.getMessage()); err.printStackTrace(System.err) ; } } FrmCuenca dlg=new FrmCuenca(this.dataBase,null); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.pack(); dlg.show(); } catch (Exception ex) { JOptionPane.showMessageDialog(this, "Error al conectar a la base de datos "+ex.getMessage()) ; } 300 Código Fuente } void abrirCuenca() { try { if ((dataBase==null)||(dataBase.isClosed())) { String usr=datosConeccion.getUsr(); String psswd=datosConeccion.getPasswd(); String url=datosConeccion.getUrl(); String drv=datosConeccion.getDriver(); dataBase=new BDObjCuenca(url,drv,usr,psswd); dataBase.open() ; } FRMAbrirCuenca dlg = new FRMAbrirCuenca(this,dataBase); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.pack(); dlg.show(); } catch(Exception err) { JOptionPane.showMessageDialog(this,err.getMessage()); err.printStackTrace(System.err) ; } } /** * Abre una cuenca. */ void menuAbrirCuenca_actionPerformed(ActionEvent e) { jlMensaje.setText("Abriendo la cuenca..."); abrirCuenca(); jlMensaje.setText(" "); } void menuModificarCuenca_actionPerformed(ActionEvent e) { try { if ((dataBase==null)||(dataBase.isClosed())) { String usr=datosConeccion.getUsr(); String psswd=datosConeccion.getPasswd(); String url=datosConeccion.getUrl(); String drv=datosConeccion.getDriver(); dataBase=new BDObjCuenca(url,drv,usr,psswd); try{dataBase.open() ;} catch(Exception err) { 301 Código Fuente JOptionPane.showMessageDialog(this,err.getMessage()); err.printStackTrace(System.err) ; } } if (CuencaActualRef==null) { JOptionPane.showMessageDialog(this,"Debe Abrir una cuenca Primero") ; } else { FrmCuenca dlg=new FrmCuenca(this.dataBase,this.CuencaActualRef); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.pack(); dlg.show(); } } } catch (Exception ex) { JOptionPane.showMessageDialog(this, "Error al conectar a la base de datos "+ex.getMessage()) ; } finally { if(FrmCuenca.accionRealizada==FrmCuenca.ELIMINAR) { setEnabledMenu(false); this.setTitle("Modelo de Simulacion Hidrologico operacional"); this.CuencaActualRef=null; } } /** * pone en enable o disable todos los itemes del zoom */ private void setEnabledMenu(boolean enabled) { jRadioButtonMenuItem2.setEnabled(enabled); jRadioButtonMenuItem3.setEnabled(enabled); jRadioButtonMenuItem4.setEnabled(enabled); jRadioButtonMenuItem5.setEnabled(enabled); jRadioButtonMenuItem6.setEnabled(enabled); jRadioButtonMenuItem7.setEnabled(enabled); jRadioButtonMenuItem8.setEnabled(enabled); jRadioButtonMenuItem9.setEnabled(enabled); jRadioButtonMenuItem10.setEnabled(enabled); jRadioButtonMenuItem11.setEnabled(enabled); 302 Código Fuente this.menuModificarCuenca.setEnabled(enabled); this.jMenuSimular.setEnabled(enabled); this.mnRN.setEnabled(enabled); this.mnuCanal.setEnabled(enabled); this.mnuCAP.setEnabled(enabled); this.mnuCAP.setEnabled(enabled); this.mnuNodo.setEnabled(enabled); this.mnuCentHidro .setEnabled(enabled); this.mnuTr.setEnabled(enabled); this.mnuEm.setEnabled(enabled); this.mnuHI.setEnabled(enabled); this.mnuEmbBajos.setEnabled(enabled); this.mnuEnGen.setEnabled(enabled); } /**************************************************************** * * Llama al dialogo de conección. * */ void jMenuItem17_actionPerformed(ActionEvent e) { Dimension dlgSize = datosConeccion.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); datosConeccion.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); datosConeccion.pack() ; datosConeccion.show(); } /************************************************************ * Realiza la simulación. */ void jMenuSimular_actionPerformed(ActionEvent e) { try { if(this.CuencaActualRef !=null) { if ((dataBase==null)||(dataBase.isClosed())) { String usr=datosConeccion.getUsr(); String psswd=datosConeccion.getPasswd(); String url=datosConeccion.getUrl(); String drv=datosConeccion.getDriver(); dataBase=new BDObjCuenca(url,drv,usr,psswd); dataBase.open(); } FRMSimular dlg=new FRMSimular(this,dataBase); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); 303 Código Fuente dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.pack() ; dlg.show() ; } else {JOptionPane.showMessageDialog(this,"La cuenca está cerrada, ábrala nuevamente"); } } } catch (Exception err) { JOptionPane.showMessageDialog(this,err.getMessage()); err.printStackTrace(System.err) ; } /********************************************************** * */ void mnuNodo_actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"Por Implementar... "); } void jMenuItem6_actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"Por Implementar... "); } void mnuTr_actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"Por Implementar... "); } void mnuEm_actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"Por Implementar... "); } void mnuHI_actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"Por Implementar... "); } void mnRN_actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"Por Implementar... "); } void mnuCAP_actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"Por Implementar... "); 304 Código Fuente } void mnuCanal_actionPerformed(ActionEvent e) { this.insert=true; this.tipoInsert=INSERT_CANAL; this.setEnabledMenu(false); } void mnuCentHidro_actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"Por Implementar... "); } void mnuEmbBajos_actionPerformed(ActionEvent e) { try { frmResultConsulta frm=new frmResultConsulta(); frm.sqlQuery1.setUrl(this.dataBase.getUrl()); frm.sqlQuery1.setDriver(this.dataBase.getDriver()); frm.sqlQuery1.setUsr(this.dataBase.getUsr()); frm.sqlQuery1.setPasswd(this.dataBase.getPasswd()); frm.setTituloInforme("Embalses Bajo volumenes mínimos"); frm.sqlQuery1.setQuery( "Select "+ "emb.codigo\"CODIGO\""+ " from tbl_emb emb"+ " Where emb.myNodo.myCuenca.codigo=\'" + this.getCuencaRef().getValue().getCodigo()+ "\'"+ "and "+ " ( emb.curva_Alerta.getCaudal(1)>emb.caudales.getCaudal(1)"+ " Or emb.curva_Alerta.getCaudal(2)>emb.caudales.getCaudal(2)"+ " Or emb.curva_Alerta.getCaudal(3)>emb.caudales.getCaudal(3)"+ " Or emb.curva_Alerta.getCaudal(4)>emb.caudales.getCaudal(4)"+ " Or emb.curva_Alerta.getCaudal(5)>emb.caudales.getCaudal(5)"+ " Or emb.curva_Alerta.getCaudal(6)>emb.caudales.getCaudal(6)"+ " ) " ); frm.pack(); frm.setLocation((int)(this.getLocation().getX()20),(int)(this.getLocation().getY()-10)); frm.show(); } catch (Exception ex) { JOptionPane.showMessageDialog(this,"error en FrmSimular: "+ ex.getMessage()) ; } } void mnuEnGen_actionPerformed(ActionEvent e) { try 305 Código Fuente { frmResultConsulta frm=new frmResultConsulta(); frm.sqlQuery1.setUrl(this.dataBase.getUrl()); frm.sqlQuery1.setDriver(this.dataBase.getDriver()); frm.sqlQuery1.setUsr(this.dataBase.getUsr()); frm.sqlQuery1.setPasswd(this.dataBase.getPasswd()); frm.setTituloInforme("Energia Generada en KW mes"); frm.sqlQuery1.setQuery( "Select "+ "n.apex.ch.codigo\"CODIGO\","+ "n.apex.ch.nombre\"NOMBRE\","+ " to_char(n.apex.ch.KW(1), \'999,999,999.99\') " + "\"OCT\","+ " to_char(n.apex.ch.KW(2), \'999,999,999.99\') " + "\"NOV\","+ " to_char(n.apex.ch.KW(3), \'999,999,999.99\') " + "\"DIC\","+ " to_char(n.apex.ch.KW(4), \'999,999,999.99\') " + "\"ENE\","+ " to_char(n.apex.ch.KW(5), \'999,999,999.99\') " + "\"FEB\","+ " to_char(n.apex.ch.KW(6), \'999,999,999.99\') " + "\"MAR\""+ " from tbl_flujo n"+ " Where n.apex.tipo=3 and "+ " ( n.nodo_ini.myCuenca.codigo=\'" + this.getCuencaRef().getValue().getCodigo() + "\'" + " Or n.nodo_fin.myCuenca.codigo=\'" + this.getCuencaRef().getValue().getCodigo() + "\'" + " ) " ); frm.pack(); frm.setLocation((int)(this.getLocation().getX()20),(int)(this.getLocation().getY()-10)); frm.show(); } catch (Exception ex) { JOptionPane.showMessageDialog(this,"error en FrmSimular: "+ ex.getMessage()) ; } } void jMenuItem2_actionPerformed(ActionEvent e) { String usr=datosConeccion.getUsr(); String passwd=datosConeccion.getPasswd(); String url=datosConeccion.getUrl(); String driver=datosConeccion.getDriver(); FrmConsultor dlg = new FrmConsultor(this,usr,passwd,url,driver); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.pack() ; dlg.show(); } 306 Código Fuente public void setCuencaRef(CuencaOraRef cr) { try { jlMensaje.setText("Obteniendo Figuras..."); CuencaActualRef=cr; lienzo.clearFiguras(); Vector figuras=dataBase.getAllFigRef(cr.getValue()); jlMensaje.setText("Cargando Figuras..."); lienzo.setVectorFiguras(figuras); jlMensaje.setText(" ") ; this.setTitle("Cuenca Actual : "+this.CuencaActualRef.getValue().getCodigo()+"-"+ this.CuencaActualRef.getValue().getNombre()); this.setEnabledMenu(true); } catch (Exception err) { JOptionPane.showMessageDialog(this,"En setCuencaRef() :"+err.getMessage()); err.printStackTrace(System.err) ; } } public CuencaOraRef getCuencaRef() { return CuencaActualRef; } void btAbrir_actionPerformed(ActionEvent e) { abrirCuenca(); } void this_mouseMoved(MouseEvent e) { jlPunto.setText("(" + e.getX() + "," + e.getY() +")") ; } void FigurasSP_mouseMoved(MouseEvent e) { jlPunto.setText("(" + e.getX() + "," + e.getY() +")") ; } void lienzo_mouseMoved(MouseEvent e) { jlPunto.setText("(" + e.getX() + "," + e.getY() +")") ; } /********************************************************************** * */ void menuNuevoRio_actionPerformed(ActionEvent e) { 307 Código Fuente try { if ((dataBase==null)||(dataBase.isClosed())) { String usr=datosConeccion.getUsr(); String psswd=datosConeccion.getPasswd(); String url=datosConeccion.getUrl(); String drv=datosConeccion.getDriver(); dataBase=new BDObjCuenca(url,drv,usr,psswd); dataBase.open() ; } FrmRio dlg=new FrmRio(this.dataBase,null); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.pack(); dlg.show(); } catch (Exception ex) { JOptionPane.showMessageDialog(this, "Error al conectar a la base de datos "+ex.getMessage()) ; } } /*********************************************************************** * */ void menuModificarRio_actionPerformed(ActionEvent e) { try { if ((dataBase==null)||(dataBase.isClosed())) { String usr=datosConeccion.getUsr(); String psswd=datosConeccion.getPasswd(); String url=datosConeccion.getUrl(); String drv=datosConeccion.getDriver(); dataBase=new BDObjCuenca(url,drv,usr,psswd); dataBase.open() ; } FrmModificarRio dlg=new FrmModificarRio(this,dataBase); Dimension dlgSize = dlg.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); dlg.pack(); dlg.show(); } catch (Exception ex) { 308 Código Fuente } } JOptionPane.showMessageDialog(this, "Error al conectar a la base de datos : "+ex.getMessage()) ; /** * Crea un canal con valores por defectos */ private FlujoOra crearCanal(Graficable noIni,Graficable noFin) { try { FlujoOra flujo=new FlujoOra(this.dataBase.getConnection()); flujo.setNodoIni((NodoOraRef)noIni); flujo.setNodoFin((NodoOraRef)noFin); flujo.setTipo(new java.math.BigDecimal(3)); //actualizadondo el atributo graf GrafCuencaOra gc=new GrafCuencaOra(dataBase.getConnection()); GrafFlujoOra gf=new GrafFlujoOra(dataBase.getConnection()); gf.setEstilo(new java.math.BigDecimal(2)); //este punto se ingresa para que el arreglos //de puntos no sea null //pero el valor sera corregido mas adelante. this.puntos=new ArrPuntosOra(new PuntoOra[1]); PuntoOra p = new PuntoOra(dataBase.getConnection()); p.setX(new java.math.BigDecimal(0)); p.setY(new java.math.BigDecimal(0)); this.puntos.setElement(p,0); gf.setPl(this.puntos); gc.setColorlinea("negro") ; gc.setTipo(new java.math.BigDecimal(1)); gc.setGfl(gf); flujo.setGraf(gc); //actualizando el tipo apex AporExtracOra apex=new AporExtracOra(this.dataBase.getConnection()); apex.setTipo(new java.math.BigDecimal(1)); CanalOra ca=new CanalOra(dataBase.getConnection()); ca.setA(new java.math.BigDecimal(0)); ca.setB(new java.math.BigDecimal(0)); ca.setC(new java.math.BigDecimal(0)); ca.setCodigo("CA nuevo"); ca.setNombre("Canal en proceso de ingreso"); ca.setCapConducion(new java.math.BigDecimal(0)); ca.setEficiencia(new java.math.BigDecimal(0)); ArrCaudalesOra arrCaudales=new ArrCaudalesOra(dataBase.getConnection()); ArrayCaudalOra arr=new ArrayCaudalOra(new 309 Código Fuente java.math.BigDecimal[6]); arr.setElement(new java.math.BigDecimal(0),0); arr.setElement(new java.math.BigDecimal(0),1); arr.setElement(new java.math.BigDecimal(0),2); arr.setElement(new java.math.BigDecimal(0),3); arr.setElement(new java.math.BigDecimal(0),4); arr.setElement(new java.math.BigDecimal(0),5); arrCaudales.setCaudal(arr); ca.setCaudales(arrCaudales) ; apex.setCa(ca); flujo.setApex(apex); return flujo; } catch (Exception ex) { JOptionPane.showMessageDialog(null,"eror al crear canal por defecto : "+ex); } return null; } //******************************************************************** // void lienzo_mouseClicked(MouseEvent e) { try { boolean esNodo=false; if (this.insert) { if (this.tipoInsert==this.INSERT_CANAL) { Graficable f=lienzo.buscarFigura(e.getX(),e.getY()) ; try {esNodo=(f.getClass().getName()=="objcuenca.GuiNodo");} catch (Exception ex) {esNodo=false;} //si es el primer punto, debe ser un nodo. if(this.countClick ==0) { if(esNodo) { FlujoOra flujo=this.crearCanal(f,f); this.figInsert=(Graficable)dataBase.insertar(flujo); this.countClick++; } else { JOptionPane.showMessageDialog(this,"El Primer punto del canal debe ser del nodo inicial"); } } else 310 Código Fuente { GuiFlujo flref=(GuiFlujo)this.figInsert; FlujoOra flujo=flref.getValue(); if(esNodo) { if(this.countClick > 2) { //actualizo el nodo final //this.jlMensaje.setText("Nodo Final marcado"); flujo.setNodoFin((NodoOraRef)f); this.countClick=0; this.insert=false; this.tipoInsert=0; flref.setValue(flujo); flref.loadFigura(); this.figInsert=(Graficable)flref; this.figInsert.dlgAtributos(); this.setEnabledMenu(true); this.jlMensaje.setText(" "); } else { JOptionPane.showMessageDialog(this,"Minimo se necesitan dos puntos(sin contar nodo inicial y final)"); } } else { GrafCuencaOra gc=flujo.getGraf();; GrafFlujoOra gf=flujo.getGraf().getGfl();; PuntoOra p = new PuntoOra(dataBase.getConnection()); int x=(e.getX()*100)/lienzo.getZoom(); int y=(e.getY()*100)/lienzo.getZoom(); p.setX(new java.math.BigDecimal(x)); p.setY(new java.math.BigDecimal(y)); //extiendo el arreglo, asignandole uno nuevo. PuntoOra[] arrPuntos=new PuntoOra[countClick]; // traslado los valores originales para // que no se pierdan for(int i=0;i<countClick-1;i++) { arrPuntos[i]=this.puntos.getElement(i); } arrPuntos[countClick-1]=p; this.puntos.setArray(arrPuntos); gf.setPl(this.puntos); gc.setGfl(gf); flujo.setGraf(gc); countClick++; flref.setValue(flujo); flref.loadFigura(); this.figInsert=(Graficable)flref; 311 Código Fuente } } if(countClick==3){this.lienzo.addFigura(figInsert);} if(countClick>2) { this.figInsert.dibujar(new Dibujante(lienzo.getGraphics(),lienzo.getZoom())); } } } else { lienzo.Click(e); } } catch (Exception ex) { JOptionPane.showMessageDialog(this,"Error al insertar el canal:"+ex); ex.printStackTrace(System.err); } } void this_windowOpened(WindowEvent e) { //llamar al dialogo de conección, try { datosConeccion=new FrmConexion(this,dataBase); Dimension dlgSize = datosConeccion.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); datosConeccion.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); datosConeccion.enbableBtSalir(false); datosConeccion.pack() ; datosConeccion.show(); } finally { datosConeccion.enbableBtSalir(true); } } /******************************************************/ } 312 Código Fuente C.4.2.20 Clase: MainGui package cuencamaule; import javax.swing.UIManager; import java.awt.*; public class MainGui { boolean packFrame = false; /**Construct the application*/ public MainGui() { Frame1 frame = new Frame1(); // Validate frames that have preset sizes // Pack frames that have useful preferred size info, e.g. from their // layout if (packFrame) { frame.pack(); } else { frame.validate(); } //Center the window Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension frameSize = frame.getSize(); if (frameSize.height > screenSize.height) { frameSize.height = screenSize.height; } if (frameSize.width > screenSize.width) { frameSize.width = screenSize.width; } frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2); frame.setVisible(true); } } /**Main method*/ public static void main(String[] args) { try { UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName()); } catch(Exception e) { e.printStackTrace(); } new MainGui(); } 313 Código Fuente C.4.3 Paquete Figuras C.4.3.1 Clase: Cuadrilatero package figuras; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class Cuadrilatero extends FigGeo { private Punto a,b,c,d; //----------------------------------------------public Cuadrilatero(Punto A,Punto B,Punto C,Punto D) { super();a=A;b=B; c=C; d=D;} //--------------------------------------------------------//Redefiniendo los métodos de FigGeo public void dibujar(Graphics g) { int x[]={a.x,b.x,c.x,d.x}; int y[]={a.y,b.y,c.y,d.y}; g.drawPolygon(x,y,4); } //-------------------------------------------------------------public boolean pertenece(Punto p) { Punto inicio,fin,direccion; int coorx,cnt=0,np=4; Punto q[]={a,b,c,d}; for(int i=0;i<np;i++) { inicio=p.restar(q[i%np]); fin=p.restar(q[(i+1)%np]); if((inicio.y * fin.y) <=0) { if((inicio.y!=0) || (fin.y!=0)) { direccion = inicio.restar(fin); coorx = (inicio.x * direccion.y inicio.y * direccion.x); if(direccion.y<0) coorx*=-1; if(coorx==0) {cnt =1; break;} if(coorx > 0) { cnt++; if((inicio.y < 0 && fin.y==0)|| (inicio.y==0 && fin.y<0)) cnt --; } } 314 Código Fuente } } return ((cnt %2)!=0); }//fin función //------------------------------------------------------------public void dlgAtributos() { JOptionPane.showMessageDialog(null,"soy un cuadrilatero"); } //------------------------------------------------------------public void setZoom(int z) { //modificando los atributos a,b,c,d int actual=getZoom(); a.x=(a.x * z)/actual; a.y=(a.y * z)/actual; b.x=(b.x * z)/actual; b.y=(b.y * z)/actual; c.x=(c.x * z)/actual; c.y=(c.y * z)/actual; d.x=(d.x * z)/actual; d.y=(d.y * z)/actual; super.setZoom(z); } //---------------------------------------------------------//coordenada x mas lejana al origen public int xMayor() { int mayor=a.x>b.x?a.x:b.x; mayor=mayor>c.x?mayor:c.x; mayor=mayor>d.x?mayor:d.x; return mayor; } //-------------------------------------------------------------//coordenada y mas lejana al origen public int yMayor() { int mayor=a.y>b.y?a.y:b.y; mayor=mayor>c.y?mayor:c.y; mayor=mayor>d.y?mayor:d.y; return mayor; } //------------------------------------------------------- } public String toString() { return "{"+a+b+c+d+"}"; } 315 Código Fuente C.4.3.2 Clase: Dibujante package figuras; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import objcuenca.*; import java.sql.SQLException; import javax.swing.JOptionPane; import java.math.*; import java.awt.Color; public class Dibujante { private Graphics g; private int zoom=100; private Color colorLinea=Color.black; public Dibujante(Graphics g,int zoom) { this.g=g; this.zoom=zoom; } public void setColorLinea(Color c) { this.colorLinea=c; } /** * Dibuja una polilinea a partir de una PolilineaOra */ public void drawPolilinea(PolilineaOra p) { try { g.setColor(colorLinea); int nPuntos=p.getNpuntos().intValue() ; int xCor[]=new int[nPuntos]; int yCor[]=new int[nPuntos]; for(int i=0;i<nPuntos;i++) { xCor[i]=p.getLospuntos().getElement(i).getX().intValue(); yCor[i]=p.getLospuntos().getElement(i).getY().intValue(); } g.drawPolyline(xCor,yCor,nPuntos); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } /** * Dibuja una polilinea a partir de un ArrPuntosOra */ 316 Código Fuente public void drawPolilinea(ArrPuntosOra p) { try { g.setColor(colorLinea); int nPuntos=p.length(); int xCor[]=new int[nPuntos]; int yCor[]=new int[nPuntos]; for(int i=0;i<nPuntos;i++) { xCor[i]=p.getElement(i).getX().intValue(); yCor[i]=p.getElement(i).getY().intValue(); } g.drawPolyline(xCor,yCor,nPuntos); int H=xCor[nPuntos-1]; int K=yCor[nPuntos-1]; int X=xCor[nPuntos-2]; int Y=yCor[nPuntos-2]; //el radio es 10 cuando el zoom es 100, por regla de tres simple //r=10*zoom/100 al simplificar queda r=zoom/10; int R=this.zoom/10; int anch=40; double angulo; if((X-H)==0) { angulo=270;} else { angulo = Math.toDegrees(Math.atan2((Y-K),(X-H)));} if ((angulo==0)&&(X<H)){angulo=180;} //ancho de la flecha en grados. // los grados en fillArc se incrementan en sentido contrario que en // el plano cartesiano, por esa razón ajusto el ángulo // inicial con 360-angulo this.g.fillArc(H-R,K-R,R*2,R*2,(int)(360-angulo-(anch/2)),anch); } } catch (Exception err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } /************************************************************* * Dibuja una polilinea doble */ public void drawPolilineaDoble(PolilineaOra p) { try { drawPolilineaDoble(p.getLospuntos() ); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); 317 Código Fuente } err.printStackTrace(System.err) ; } /************************************************************* * Dibuja una polilinea doble a partir de un ArrPuntosOra */ public void drawPolilineaDoble(ArrPuntosOra p) { try { g.setColor(colorLinea); int nPuntos=p.length(); int xCor[]=new int[nPuntos]; int yCor[]=new int[nPuntos]; for(int i=0;i<nPuntos;i++) { xCor[i]=p.getElement(i).getX().intValue(); yCor[i]=p.getElement(i).getY().intValue(); } g.drawPolyline(xCor,yCor,nPuntos); int H=xCor[nPuntos-1]; int K=yCor[nPuntos-1]; int X=xCor[nPuntos-2]; int Y=yCor[nPuntos-2]; //el radio es 10 cuando el zoom es 100, por regla de tres simple //r=10*zoom/100 al simplificar queda r=zoom/10; int R=this.zoom/10; int anch=40; double angulo; if((X-H)==0) { angulo=270;} else { angulo = Math.toDegrees(Math.atan2((Y-K),(X-H)));} if ((angulo==0)&&(X<H)){angulo=180;} // los grados en fillArc se incrementan en sentido contrario // que en el plano cartesiano, por esa razón ajusto el ángulo // inicial con 360-angulo this.g.fillArc(H-R,K-R,R*2,R*2,(int)(360-angulo-(anch/2)),anch); //El radio de la circunferencia rellena del inicio es de 3 al 100% R=(3*zoom)/100; this.g.fillArc(xCor[0]-R,yCor[0]-R,R*2,R*2,0,360); } } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } /** * Dibuja una polilinea Gruesa 318 Código Fuente */ public void drawPolilineaGruesa(PolilineaOra p) { try { this.drawPolilinea(p.getLospuntos()); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } /** * Dibuja una polilinea Gruesa a partir de un ArrPuntosOra */ public void drawPolilineaGruesa(ArrPuntosOra p) { this.drawPolilinea(p) ; } public void drawCuadrado(CuadradoOra cuad) { try { g.setColor(colorLinea); PuntoOra a=cuad.getA() ; PuntoOra b=cuad.getB() ; PuntoOra c=cuad.getC() ; PuntoOra d=cuad.getD() ; int nPuntos=4; int xCor[]={a.getX().intValue() ,b.getX().intValue(), d.getX().intValue(), c.getX().intValue()}; int yCor[]={a.getY().intValue() ,b.getY().intValue(), d.getY().intValue(), c.getY().intValue()}; g.drawPolygon(xCor,yCor,nPuntos); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } public void drawTriangulo(TrianguloOra t) { try { g.setColor(colorLinea); PuntoOra a=t.getA() ; PuntoOra b=t.getB() ; PuntoOra c=t.getC() ; int nPuntos=3; int xCor[]={a.getX().intValue(),b.getX().intValue(), c.getX().intValue()}; 319 Código Fuente int yCor[]={a.getY().intValue(),b.getY().intValue(), c.getY().intValue()}; g.drawPolygon(xCor,yCor,nPuntos); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } public void drawElipse(ElipseOra e) { try { g.setColor(colorLinea); PuntoOra cent = e.getCentro() ; int alto = e.getAlto().intValue(); int ancho = e.getAncho().intValue(); int esqIzX = cent.getX().intValue()- (ancho/2); int esqIzY = cent.getY().intValue()- (alto/2); g.drawOval(esqIzX,esqIzY,ancho,alto); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } public void drawString(String s,int x,int y,int tam) { g.setColor(colorLinea); Font f=new Font("Helvetica",Font.BOLD,tam); g.setFont(f); g.drawString(s,x,y); } public void drawCenteredString(String s,int x, int y, int w,int h, int tam) { g.setColor(colorLinea); Font f=new Font("Helvetica",Font.BOLD,tam); g.setFont(f); FontMetrics fm = g.getFontMetrics(); int xx = x + (w -fm.stringWidth(s))/2; int yy = y +(fm.getAscent() + (h -(fm.getAscent()+ fm.getDescent()))/2); g.drawString(s,xx,yy); } } 320 Código Fuente C.4.3.3 Clase: Elipse package figuras; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class Elipse extends FigGeo { private Punto esqIz; private int ancho,alto; //---------------------------------------------------public Elipse(Punto c,int an,int al) { super();esqIz=c; ancho=an; alto = al; } //---------------------------------------------------//Redefiniendo los métodos de FigGeo public void dibujar(Graphics g) { g.drawOval(esqIz.x,esqIz.y,ancho,alto); } //--------------------------------------------------------public boolean pertenece(Punto p) { Punto c=new Punto(esqIz.x+ancho/2,esqIz.y+alto/2); int an=ancho/2; int al=alto/2; if ((((Math.pow((p.x-c.x),2))/(Math.pow(an,2)))+ ((Math.pow((p.y-c.y),2))/(Math.pow(al,2))))<=1) {return true;} else {return false;} }//fin función //-----------------------------------------------------------public void dlgAtributos() { JOptionPane.showMessageDialog(null,"soy una Elipse"); } public String toString() { return "{"+esqIz+","+ancho+","+alto+"}"; } //------------------------------------------------------------------//Esta funcion acutaliza las propiedades de la elipse para que 321 Código Fuente //esten acordes al nuevo Zoom aplicando la regla de 3 simple public void setZoom(int z) { //modificando los atributos esqIz,alto,ancho int actual=getZoom(); esqIz.x=(esqIz.x * z)/actual; esqIz.y=(esqIz.y * z)/actual; ancho=(ancho * z)/actual; alto =(alto * z)/actual; super.setZoom(z); } //------------------------------------------------------------//coordenada x mas lejana al origen public int xMayor() { return esqIz.x+ancho; } //----------------------------------------------------------//coordenada y mas lejana al origen public int yMayor() { return esqIz.y+alto ; } } 322 Código Fuente C.4.3.4 Clase: FigGeo package figuras; import java.awt.*; import javax.swing.*; import java.awt.Dimension; abstract class FigGeo { private int zoom; public FigGeo(){zoom=100;} public abstract void dibujar(Graphics g); public abstract boolean pertenece(Punto p); public abstract void dlgAtributos(); public abstract int xMayor(); public abstract int yMayor(); //------------------------------------------------------------public void setZoom(int z) { zoom=z; } //------------------------------------------------------------public int getZoom() { return zoom; } //------------------------------------------------------------public boolean esContenidoEn(Dimension d,Dimension nueva) { int xM=xMayor()+12;//12 pixeles de margen derecho int yM=yMayor()+12;//12 pixeles de margen inferir nueva.width=d.width>xM?d.width:xM; nueva.height=d.height>yM?d.height:yM; } return ((d.width>xM)&(d.height>yM)); } C.4.3.5 Interfaz: Graficable package figuras; import java.awt.Dimension; public interface Graficable { public void setZoom(int zoom); public int getZoom(); public void dibujar(Dibujante d); public void dlgAtributos(); public Dimension dimensionMinima(); public boolean pertenece(Punto p); public void loadFigura(); } 323 Código Fuente C.4.3.6 Clase: LienzoCuenca package figuras; import import import import import import import javax.swing.JPanel; java.awt.*; javax.swing.*; javax.swing.event.*; javax.swing.border.*; java.awt.event.MouseEvent; java.util.*; public class LienzoCuenca extends JPanel { //----------------------------------------------------------------// Variables de Instancia private Vector lasFiguras; private int zoom=100; public LienzoCuenca() { lasFiguras= new Vector(); setBackground(new Color(255,231,184)); this.setOpaque(false); //this.addMouseListener( new MouseInputAdapter(){ // public void mouseClicked(MouseEvent e){ Click(e);}}); } //--------------------------------------------------------------public void paint(Graphics g) { // g.setColor(new Color(15,23,100)); dibujarFiguras(g); } //---------------------------------------------------------------public void Click(MouseEvent e) { try { if ((e.getClickCount() == 1)&&(!e.isConsumed())) { int x = e.getX(); int y = e.getY(); Graficable f = buscarFigura(x,y); if (f!=null) { f.dlgAtributos(); } e.consume(); } } finally { this.repaint(); 324 Código Fuente } } public void setVectorFiguras(Vector f) { lasFiguras=f; setZoom(100); } //--------------------------------------------------------------------public void clearFiguras() { lasFiguras.clear() ; } //-------------------------------------------------------------------public void addFigura(Graficable f) { lasFiguras.addElement(f); } //-------------------------------------------------------------------/** * debuelve la referencia de la figura a la cual le pertenece el punto * dado por las coordenadas x,y. * en caso de existir ninguna, debuelve null. */ public Graficable buscarFigura(int x,int y) { Graficable f=null;boolean ok=false; Enumeration FigIter=lasFiguras.elements(); while((FigIter.hasMoreElements())&&(!ok)) { f=(Graficable)FigIter.nextElement(); if (f.pertenece(new Punto(x,y))){ok=true;} } if ( ok ) {return f;} else {return null;} } //-------------------------------------------------------------------/** * Dibuja las figuras contenidas en el vector lasFiguras * * */ public void dibujarFiguras(Graphics g) { Dibujante pincel=new Dibujante(g,this.zoom); Graficable f; Enumeration FigIter=lasFiguras.elements(); while (FigIter.hasMoreElements()) { f=(Graficable)FigIter.nextElement(); //Redimiensiono el lienzo si la figura no es contenida 325 Código Fuente Dimension df=f.dimensionMinima() ; Dimension dAct=this.getSize() ; int x=df.width>dAct.width ? df.width : dAct.width ; int y=df.height > dAct.height ? df.height : dAct.height; Dimension nueva=new Dimension(x,y); if ((df.width>dAct.width)||(df.height > dAct.height)) {this.setSize(nueva);} f.dibujar(pincel) ; } } //--------------------------------------------------------------------public void reloadFiguras() { Enumeration figIter=lasFiguras.elements(); Graficable f; while(figIter.hasMoreElements()) { f=(Graficable)figIter.nextElement(); f.loadFigura(); } this.repaint(); } //-------------------------------------------------------------------public void setZoom(int z) { zoom=z; Graficable f; Enumeration FigIter=lasFiguras.elements(); while (FigIter.hasMoreElements()) { f=(Graficable)FigIter.nextElement(); f.setZoom(zoom); } this.setSize(1024,768); repaint(new Rectangle(getSize())); } //-----------------------------------------------------------------public int getZoom() { return zoom; } } 326 Código Fuente C.4.3.7 Clase: Poligono package figuras; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class Poligono extends FigGeo { private int x[]; private int y[]; private int nPuntos=0; //----------------------------------------------public Poligono(int xP[],int yP[],int n) { super(); x=(int[])xP.clone(); y=(int[])yP.clone(); nPuntos=n; } //--------------------------------------------------------//Redefiniendo los métodos de FigGeo public void dibujar(Graphics g) { g.drawPolygon(x,y,nPuntos); } //------------------------------------------------------------------private Punto[] arrPuntos() { Punto arr[]=new Punto[nPuntos]; for (int i=0;i<nPuntos;i++) { arr[i]=new Punto(x[i],y[i]); } return arr; } public boolean pertenece(Punto p) { Punto inicio,fin,direccion; int coorx,cnt=0,np=nPuntos; Punto q[]=arrPuntos(); for(int i=0;i<np;i++) { inicio=p.restar(q[i%np]); fin=p.restar(q[(i+1)%np]); if((inicio.y * fin.y) <=0) { if((inicio.y!=0) || (fin.y!=0)) { direccion = inicio.restar(fin); coorx = (inicio.x * direccion.y 327 Código Fuente } } inicio.y * direccion.x); if(direccion.y<0) coorx*=-1; if(coorx==0) {cnt =1; break;} if(coorx > 0) { cnt++; if((inicio.y < 0 && fin.y==0)|| (inicio.y==0 && fin.y<0)) cnt --; } } return ((cnt %2)!=0); }//fin función //--------------------------------------------------------------public void dlgAtributos() { JOptionPane.showMessageDialog(null,"soy un Poligono"); } //--------------------------------------------------------------public void setZoom(int z) { //modificando los atributos a,b,c,d int actual=getZoom(); for(int i=0;i<nPuntos;i++) { x[i]=(x[i] * z)/actual; y[i]=(y[i] * z)/actual; } super.setZoom(z); } //--------------------------------------------------------------//coordenada x mas lejana al origen public int xMayor() { int mayor=x[0]; for(int i=0;i<nPuntos;i++) { mayor=x[i]>mayor?x[i]:mayor; } return mayor; } //--------------------------------------------------------------//coordenada y mas lejana al origen public int yMayor() { int mayor=y[0]; for(int i=0;i<nPuntos;i++) { mayor=y[i]>mayor?y[i]:mayor; 328 Código Fuente } return mayor; } } //------------------------------------------------------------public String toString() { String s="{"; for(int i=0;i<nPuntos;i++) { s+=new Punto(x[i],y[i])+","; } s=s.substring(0,s.length()-1)+"}"; return s; } C.4.3.8 Clase: PoliLinea package figuras; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class PoliLinea extends FigGeo { private int nPuntos; private int[] xCor; private int[] yCor; private int estilo; public static final int SIMPLE=1; public static final int DOBLE =2; public static final int GRUESA=0; private int error=250; //Constructores //--------------------------------------------------------public PoliLinea(PoliLinea p,int e) { super(); estilo=e; this.nPuntos=p.nPuntos; xCor=new int[nPuntos]; yCor=new int[nPuntos]; for(int i=0;i<nPuntos;i++) { xCor[i]=p.xCor[i]; yCor[i]=p.yCor[i]; 329 Código Fuente } } public PoliLinea(int[] x,int[] y,int nPuntos,int e) { super(); estilo=e; this.nPuntos=nPuntos; xCor=new int[nPuntos]; yCor=new int[nPuntos]; for(int i=0;i<nPuntos;i++) { xCor[i]=x[i]; yCor[i]=y[i]; } } //--------------------------------------------------------//mueve la polilínea sumando o restando x e y a las //cordenadas correspondientes public void moverEn(int x,int y) { for(int i=0;i<nPuntos;i++) { xCor[i]+=x; yCor[i]+=y; } } //--------------------------------------------------------//Redefiniendo los métodos de FigGeo public void dibujar(Graphics g) { PoliLinea n1=new PoliLinea(this,SIMPLE); PoliLinea n2=new PoliLinea(this,SIMPLE); { switch(estilo) case case case SIMPLE: g.drawPolyline(xCor,yCor,nPuntos); error=250; break; DOBLE: n1.moverEn(2,0); n2.moverEn(-2,0); g.drawPolyline(n1.xCor,n1.yCor,nPuntos); g.drawPolyline(n2.xCor,n2.yCor,nPuntos); error=500; break; GRUESA: n1.moverEn(1,0); g.drawPolyline(n1.xCor,n1.yCor,nPuntos); 330 Código Fuente } } g.drawPolyline(xCor,yCor,nPuntos); error=300; break; g.drawPolyline(n1.xCor,n1.yCor,nPuntos); g.drawPolyline(n2.xCor,n2.yCor,nPuntos); /**-----------------------------------------------------------------* p1,p2 linea de p1 a p2 * p3 el punto a consultar. * el mouse no es tan presiso, por eso la comparación * no es == a "0"(exacto) */ private boolean colineales(Punto a,Punto b,Punto c) { return (Math.abs((a.x * b.y) - (a.y * b.x) + (a.y * c.x) (a.x * c.y) + (b.x * c.y) - (b.y*c.x))<error); } //------------------------------------------------------------------public boolean pertenece(Punto p) { boolean resultado=false; for (int i=0;i<(nPuntos-1);i++) { if(colineales(vertice(i),vertice(i+1),p)) { resultado=true; break; } } return resultado; }//fin función //---------------------------------------------------------------public void dlgAtributos() { JOptionPane.showMessageDialog(null,"soy una polilinea"); } //------------------------------------------------------------------public int[] getXCor() { return this.xCor; } //--------------------------------------------------------------------public void setXCor(int[] xCor) { 331 Código Fuente this.xCor = xCor; } //------------------------------------------------------------------public int[] getYCor() { return this.yCor; } //-------------------------------------------------------------------public void setYCor(int[] yCor) { this.yCor = yCor; } //-------------------------------------------------------------------public int getNPuntos() { return this.nPuntos; } //-------------------------------------------------------------------public void setNPuntos(int nPuntos) { this.nPuntos = nPuntos; } //------------------------------------------------------------------public Punto vertice(int i) { if ((i>=0) && (i<nPuntos)) {return (new Punto(xCor[i],yCor[i]));} else { return null;} } //------------------------------------------------------------------public String toString() { String s="{"; for(int i=0;i<nPuntos;i++) s+="("+xCor[i]+","+yCor[i]+")"; s+="}"; return s; } //------------------------------------------------------------------//Esta funcion acutaliza las propiedades de la polilinea para que //esten acordes al nuevo Zoom public void setZoom(int z) { //modificando los atributos xCor, yCor int actual=getZoom(); 332 Código Fuente { } for(int i=0;i<nPuntos;i++) xCor[i]=(xCor[i] * z)/actual; yCor[i]=(yCor[i] * z)/actual;; super.setZoom(z); } //------------------------------------------------------------------//coordenada x mas lejana al origen public int xMayor() { int mayor=xCor[0]; for(int i=0;i<nPuntos;i++) { mayor=mayor>xCor[i]?mayor:xCor[i]; } return mayor; } //-------------------------------------------------------------//coordenada y mas lejana al origen public int yMayor() { int mayor=yCor[0]; for(int i=0;i<nPuntos;i++) { mayor=mayor>yCor[i]?mayor:yCor[i]; } return mayor; } } C.4.3.9 Clase: Punto package figuras; public class Punto { public int x, y; //construye el punto (0,0) public Punto() { x=0;y=0; } //construye el punto a partir de las cordenadas x e y public Punto(int x, int y) { this.x=x;this.y=y; } //Construye el punto a partir de otro public Punto(Punto P) { x=P.x;y=P.y; 333 Código Fuente } //Calcula la distancia entre el punto y otro pasado //como argumento public double distanciaA(Punto p) { int dx=p.x - this.x; int dy=p.y - this.y; return Math.sqrt(dx*dx+dy*dy); } public void asignar(Punto p) { x=p.x;y=p.y; } public void asignar(int x, int y) { this.x=x;this.y=y; } public Punto { return } public Punto { return } } restar(Punto p) (new Punto(x-p.x,y-p.y)); sumar(Punto p) (new Punto(x+p.x,y+p.y)); public String toString() { return ("("+x+","+y+")"); } C.4.3.10 Clase: Triangulo package figuras; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class Triangulo extends FigGeo { private Punto a; private Punto b; private Punto c; //Constructores //--------------------------------------------------------Triangulo() { super(); a=new Punto(15,20); b=new Punto(15,100); 334 Código Fuente c=new Punto(50,60); } //--------------------------------------------------------Triangulo(Punto d,Punto e,Punto f) { super();a=d;b=e;c=f; } //--------------------------------------------------------//Redefiniendo los métodos de FigGeo public void dibujar(Graphics g) { int x[]={a.x,b.x,c.x}; int y[]={a.y,b.y,c.y}; g.drawPolygon(x,y,3); } //----------------------------------------------------------public boolean pertenece(Punto p) { Punto inicio,fin,direccion; int coorx,cnt=0,np=3; Punto q[]={a,b,c}; for(int i=0;i<np;i++) { inicio=p.restar(q[i%np]); fin=p.restar(q[(i+1)%np]); if((inicio.y * fin.y) <=0) { if((inicio.y!=0) || (fin.y!=0)) { direccion = inicio.restar(fin); coorx = (inicio.x * direccion.y inicio.y * direccion.x); if(direccion.y<0) coorx*=-1; if(coorx==0) {cnt =1; break;} if(coorx > 0) { cnt++; if((inicio.y < 0 && fin.y==0)|| (inicio.y==0 && fin.y<0)) cnt --; } } } } return ((cnt %2)!=0); }//fin función //----------------------------------------------------------public void dlgAtributos() { JOptionPane.showMessageDialog(null,"soy un Triangulo"); 335 Código Fuente } //-------------------------------------------------------------//Esta funcion acutaliza las propiedades del triangulo para //que esten acordes al nuevo Zoom aplicando la regla de 3 simple // por ejemplo: // valor actual -----> valor Actual del Zoom // nuevo valor -----> nuevo zoom // nuevo valor = (valor actual * nuevo zoom)/valor actual zoom public void setZoom(int z) { //modificando los atributos a,b,c int actual=getZoom(); a.x=(a.x * z)/actual; a.y=(a.y * z)/actual; b.x=(b.x * z)/actual; b.y=(b.y * z)/actual; c.x=(c.x * z)/actual; c.y=(c.y * z)/actual; super.setZoom(z); } //----------------------------------------------------------public int xMayor() {return (a.x>b.x?a.x:b.x)>c.x?(a.x>b.x?a.x:b.x):c.x;} //-----------------------------------------------------------------public int yMayor() {return (a.y>b.y?a.y:b.y)>c.y?(a.y>b.y?a.y:b.y):c.y;} //----------------------------------------------------------------- } public String toString() { return "{("+a.x+","+a.y+")("+b.x+","+b.y+") ("+c.x+","+c.y+")}"; } 336 Código Fuente C.4.4 Paquete objcuenca Las siguientes clases son generadas por el utilitario JPUP de Oracle a partir de los tipos creados con CREATE TYPE. Estas clases sierven de interfaz entre los tipos de Oracle y las clases de java. Para cada tipo Objeto, le corresponden dos clases java, una para acceder a las instancias del tipo y otra para acceder a referencias del tipos. Para los tipos creados a partir de un tipo primitivo, solamente es generada la clase para acceder a instancias: • AporExtracOra.java , AporExtracOraRef.java • AportanteOra.java, AportanteOraRef.java • ArrayCaudalOra.java • ArrCaudalesOra.java, ArrCaudalesOraRef.java, • ArrPuntosOra.java • CanalOra.java, CanalOraRef.java • CaptAguaPotOra.java, CaptAguaPotOraRef.java • CentralHidroOra.java, CentralHidroOraRef.java • CuadradoOra.java, CuadradoOraRef.java • CuencaOra.java, CuencaOraRef.java • ElipseOra.java, ElipseOraRef.java • EmbalseOra.java, EmbalseOraRef.java • ExtraccionOra.java, ExtraccionOraRef.java • FlujoOra.java, FlujoOraRef.java • GrafCentHidroOra.java, GrafCentHidroOraRef.java • GrafCuencaOra.java, GrafCuencaOraRef.java • GrafEmbOra.java, GrafEmbOraRef.java • GrafFlujoOra.java, GrafFlujoOraRef.java • GrafNodoOra.java, GrafNodoOraRef.java • HoyaInterOra.java, HoyaInterOraRef.java • NodoOra.java, NodoOraRef.java • PolilineaOra.java, PolilineaOraRef.java • PuntoOra.java, PuntoOraRef.java • RegimenNaturalOra.java, RegimenNaturalOraRef.java • RioOra.java, RioOraRef.java • SalidaEmbOra.java, SalidaEmbOraRef.java 337 Código Fuente • TramoOra.java, TramoOraRef.java • TrianguloOra.java, TrianguloOraRef.java Acada una de estas clases, le agregamos una línea de código para indicar que pertenecen al paquete objcuenca. Por ser muy extensas y poco claras, sólo mostraremos dos de estas clases: AporExtracOra.java, AporExtracOraRef.java. El paquete objcuenca consta de 58 clases, donde 4 de ellas no son generadas por las utilidades de Oracle. Estas son: • BDObjCuenca.java • GuiEmbalse.java • GuiFlujo.java • GuiNodo.java Estas clases si son mostradas. C.4.4.1 Clase: AporExtracOra package objcuenca;//linea agregada manualmente /*@lineinfo:filename=AporExtracOra*/ /*@lineinfo:user-code*/ /*@lineinfo:1^1*/ import import import import import import import import import import import java.sql.SQLException; Oracle.jdbc.driver.OracleConnection; Oracle.jdbc.driver.OracleTypes; Oracle.sql.CustomDatum; Oracle.sql.CustomDatumFactory; Oracle.sql.Datum; Oracle.sql.STRUCT; Oracle.jpub.runtime.MutableStruct; sqlj.runtime.ref.DefaultContext; sqlj.runtime.ConnectionContext; java.sql.Connection; public class AporExtracOra implements CustomDatum, CustomDatumFactory { public static final String _SQL_NAME = "APOR_EXTRAC"; public static final int _SQL_TYPECODE = OracleTypes.STRUCT; /*@lineinfo:generated-code*//*@lineinfo:18^3*/ // // // ************************************************************ SQLJ context declaration: ************************************************************ static class _Ctx 338 Código Fuente extends sqlj.runtime.ref.ConnectionContextImpl implements sqlj.runtime.ConnectionContext { public _Ctx(java.sql.Connection conn) throws java.sql.SQLException { super(profiles, conn); } public _Ctx(java.lang.String url, java.lang.String user, java.lang.String password, boolean autoCommit) throws java.sql.SQLException { super(profiles, url, user, password, autoCommit); } public _Ctx(java.lang.String url, java.util.Properties info, boolean autoCommit) throws java.sql.SQLException { super(profiles, url, info, autoCommit); } public _Ctx(java.lang.String url, boolean autoCommit) throws java.sql.SQLException { super(profiles, url, autoCommit); } public _Ctx(sqlj.runtime.ConnectionContext other) throws java.sql.SQLException { super(profiles, other); } public static _Ctx getDefaultContext() { if (defaultContext == null) { java.sql.Connection conn = sqlj.runtime.RuntimeContext.getRuntime().getDefaultConnection(); if (conn != null) { try { defaultContext = new _Ctx(conn); } catch (java.sql.SQLException e) { } } } return defaultContext; } public static void setDefaultContext(_Ctx ctx) { defaultContext = ctx; } private static _Ctx defaultContext = null; 339 Código Fuente public static java.lang.Object getProfileKey(sqlj.runtime.profile.Loader loader, java.lang.String profileName) throws java.sql.SQLException { return profiles.getProfileKey(loader, profileName); } private static final sqlj.runtime.ref.ProfileGroup profiles = new sqlj.runtime.ref.ProfileGroup(); public static sqlj.runtime.profile.Profile getProfile(java.lang.Object profileKey) { return profiles.getProfile(profileKey); } } // ************************************************************ /*@lineinfo:user-code*//*@lineinfo:18^26*/ _Ctx _ctx; static int[] _sqlType = { 2, 2002, 2002, 2002 }; static CustomDatumFactory[] _factory = new CustomDatumFactory[4]; static { _factory[1] = CanalOra.getFactory(); _factory[2] = TramoOra.getFactory(); _factory[3] = CentralHidroOra.getFactory(); } MutableStruct _struct; static final AporExtracOra _AporExtracOraFactory = new AporExtracOra(); public static CustomDatumFactory getFactory() { return _AporExtracOraFactory; } /* constructors */ public AporExtracOra() { _struct = new MutableStruct(new Object[4], _sqlType, _factory); try { _ctx = new _Ctx(DefaultContext.getDefaultContext()); } catch (Exception e) { 340 Código Fuente } } _ctx = null; public AporExtracOra(ConnectionContext c) throws SQLException { _struct = new MutableStruct(new Object[4], _sqlType, _factory); _ctx = new _Ctx(c == null ? DefaultContext.getDefaultContext() : c); } public AporExtracOra(Connection c) throws SQLException { _struct = new MutableStruct(new Object[4], _sqlType, _factory); _ctx = new _Ctx(c); } /* CustomDatum interface */ public Datum toDatum(OracleConnection c) throws SQLException { _ctx = new _Ctx(c); return _struct.toDatum(c, _SQL_NAME); } /* CustomDatumFactory interface */ public CustomDatum create(Datum d, int sqlType) throws SQLException { if (d == null) return null; AporExtracOra o = new AporExtracOra(); o._struct = new MutableStruct((STRUCT) d, _sqlType, _factory); o._ctx = new _Ctx(((STRUCT) d).getConnection()); return o; } /* shallow copy method: give object same attributes as argument */ void shallowCopy(AporExtracOra d) throws SQLException { _struct = d._struct; } /* accessor methods */ public java.math.BigDecimal getTipo() throws SQLException { return (java.math.BigDecimal) _struct.getAttribute(0); } public void setTipo(java.math.BigDecimal tipo) throws SQLException { _struct.setAttribute(0, tipo); } public CanalOra getCa() throws SQLException { return (CanalOra) _struct.getAttribute(1); } public void setCa(CanalOra ca) throws SQLException { _struct.setAttribute(1, ca); } 341 Código Fuente public TramoOra getTr() throws SQLException { return (TramoOra) _struct.getAttribute(2); } public void setTr(TramoOra tr) throws SQLException { _struct.setAttribute(2, tr); } public CentralHidroOra getCh() throws SQLException { return (CentralHidroOra) _struct.getAttribute(3); } public void setCh(CentralHidroOra ch) throws SQLException { _struct.setAttribute(3, ch); } public java.math.BigDecimal demanda ( java.math.BigDecimal caudal, java.math.BigDecimal m) throws SQLException { AporExtracOra __jPt_temp = this; java.math.BigDecimal __jPt_result; /*@lineinfo:generated-code*//*@lineinfo:127^5*/ // // // // // // // // ************************************************************ #sql [_ctx] { BEGIN :__jPt_result := :__jPt_temp.DEMANDA( :caudal, :m); END; }; ************************************************************ { sqlj.runtime.ConnectionContext __sJT_connCtx = _ctx; if (__sJT_connCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_CONN_CTX(); sqlj.runtime.ExecutionContext __sJT_execCtx = __sJT_connCtx.getExecutionContext(); if (__sJT_execCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_EXEC_CTX(); AporExtracOra __sJT_2 = __jPt_temp; java.math.BigDecimal __sJT_3 = caudal; java.math.BigDecimal __sJT_4 = m; synchronized (__sJT_execCtx) { sqlj.runtime.profile.RTStatement __sJT_stmt = __sJT_execCtx.registerStatement(__sJT_connCtx, AporExtracOra_SJProfileKeys.getKey(0), 0); try { __sJT_stmt.setObject(2, __sJT_2); __sJT_stmt.setBigDecimal(3, __sJT_3); __sJT_stmt.setBigDecimal(4, __sJT_4); __sJT_execCtx.executeUpdate(); __jPt_result = __sJT_stmt.getBigDecimal(1); 342 Código Fuente } } // } finally { __sJT_execCtx.releaseStatement(); } ************************************************************ /*@lineinfo:user-code*//*@lineinfo:133^5*/ return __jPt_result; } public void entrada ( java.math.BigDecimal caudal, java.math.BigDecimal m) throws SQLException { AporExtracOra __jPt_temp = this; /*@lineinfo:generated-code*//*@lineinfo:143^5*/ // // // // // // // // ************************************************************ #sql [_ctx] { BEGIN :__jPt_temp.ENTRADA( :caudal, :m); END; }; ************************************************************ { sqlj.runtime.ConnectionContext __sJT_connCtx = _ctx; if (__sJT_connCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_CONN_CTX(); sqlj.runtime.ExecutionContext __sJT_execCtx = __sJT_connCtx.getExecutionContext(); if (__sJT_execCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_EXEC_CTX(); AporExtracOra __sJT_1 = __jPt_temp; java.math.BigDecimal __sJT_2 = caudal; java.math.BigDecimal __sJT_3 = m; synchronized (__sJT_execCtx) { sqlj.runtime.profile.RTStatement __sJT_stmt = __sJT_execCtx.registerStatement(__sJT_connCtx, AporExtracOra_SJProfileKeys.getKey(0), 1); try { __sJT_stmt.setObject(1, __sJT_1); __sJT_stmt.setBigDecimal(2, __sJT_2); __sJT_stmt.setBigDecimal(3, __sJT_3); __sJT_execCtx.executeUpdate(); 343 Código Fuente __jPt_temp = ((AporExtracOra)(__sJT_stmt.getObject(1, AporExtracOra.class))); } finally { __sJT_execCtx.releaseStatement(); } } } // ************************************************************ /*@lineinfo:user-code*//*@lineinfo:149^5*/ shallowCopy(__jPt_temp); } public java.math.BigDecimal salida ( java.math.BigDecimal m) throws SQLException { AporExtracOra __jPt_temp = this; java.math.BigDecimal __jPt_result; /*@lineinfo:generated-code*//*@lineinfo:159^5*/ // // // // // // // ************************************************************ #sql [_ctx] { BEGIN :__jPt_result := :__jPt_temp.SALIDA( :m); END; }; ************************************************************ { sqlj.runtime.ConnectionContext __sJT_connCtx = _ctx; if (__sJT_connCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_CONN_CTX(); sqlj.runtime.ExecutionContext __sJT_execCtx = __sJT_connCtx.getExecutionContext(); if (__sJT_execCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_EXEC_CTX(); AporExtracOra __sJT_2 = __jPt_temp; java.math.BigDecimal __sJT_3 = m; synchronized (__sJT_execCtx) { sqlj.runtime.profile.RTStatement __sJT_stmt = __sJT_execCtx.registerStatement(__sJT_connCtx, AporExtracOra_SJProfileKeys.getKey(0), 2); try { __sJT_stmt.setObject(2, __sJT_2); __sJT_stmt.setBigDecimal(3, __sJT_3); __sJT_execCtx.executeUpdate(); __jPt_result = __sJT_stmt.getBigDecimal(1); } 344 Código Fuente } } // finally { __sJT_execCtx.releaseStatement(); } ************************************************************ /*@lineinfo:user-code*//*@lineinfo:164^5*/ return __jPt_result; } public String tovarchar2 () throws SQLException { AporExtracOra __jPt_temp = this; String __jPt_result; /*@lineinfo:generated-code*//*@lineinfo:173^5*/ // // // // // // ************************************************************ #sql [_ctx] { BEGIN :__jPt_result := :__jPt_temp.TOVARCHAR2(); END; }; ************************************************************ { sqlj.runtime.ConnectionContext __sJT_connCtx = _ctx; if (__sJT_connCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_CONN_CTX(); sqlj.runtime.ExecutionContext __sJT_execCtx = __sJT_connCtx.getExecutionContext(); if (__sJT_execCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_EXEC_CTX(); AporExtracOra __sJT_2 = __jPt_temp; synchronized (__sJT_execCtx) { sqlj.runtime.profile.RTStatement __sJT_stmt = __sJT_execCtx.registerStatement(__sJT_connCtx, AporExtracOra_SJProfileKeys.getKey(0), 3); try { __sJT_stmt.setObject(2, __sJT_2); __sJT_execCtx.executeUpdate(); __jPt_result = __sJT_stmt.getString(1); } finally { __sJT_execCtx.releaseStatement(); } } } 345 Código Fuente // ************************************************************ /*@lineinfo:user-code*//*@lineinfo:177^5*/ return __jPt_result; } }/*@lineinfo:generated-code*/class AporExtracOra_SJProfileKeys { private static AporExtracOra_SJProfileKeys inst = null; public static java.lang.Object getKey(int keyNum) throws java.sql.SQLException { if (inst == null) { inst = new AporExtracOra_SJProfileKeys(); } return inst.keys[keyNum]; } private final sqlj.runtime.profile.Loader loader = sqlj.runtime.RuntimeContext.getRuntime().getLoaderForClass(getClass()); private java.lang.Object[] keys; private AporExtracOra_SJProfileKeys() throws java.sql.SQLException { keys = new java.lang.Object[1]; keys[0] = AporExtracOra._Ctx.getProfileKey(loader, "objcuenca/AporExtracOra_SJProfile0"); } } C.4.4.2 Clase: AporExtracOraRef package objcuenca; //linea agregada manualmente import import import import import import import import java.sql.SQLException; Oracle.jdbc.driver.OracleConnection; Oracle.jdbc.driver.OracleTypes; Oracle.sql.CustomDatum; Oracle.sql.CustomDatumFactory; Oracle.sql.Datum; Oracle.sql.REF; Oracle.sql.STRUCT; public class AporExtracOraRef implements CustomDatum, CustomDatumFactory { public static final String _SQL_BASETYPE = "APOR_EXTRAC"; public static final int _SQL_TYPECODE = OracleTypes.REF; REF _ref; static final AporExtracOraRef _AporExtracOraRefFactory = new AporExtracOraRef(); public static CustomDatumFactory getFactory() { 346 Código Fuente } return _AporExtracOraRefFactory; /* constructor */ public AporExtracOraRef() { } /* CustomDatum interface */ public Datum toDatum(OracleConnection c) throws SQLException { return _ref; } /* CustomDatumFactory interface */ public CustomDatum create(Datum d, int sqlType) throws SQLException { if (d == null) return null; AporExtracOraRef r = new AporExtracOraRef(); r._ref = (REF) d; return r; } public AporExtracOra getValue() throws SQLException { return (AporExtracOra) AporExtracOra.getFactory().create( (Datum) _ref.getValue(), OracleTypes.REF); } } public void setValue(AporExtracOra c) throws SQLException { _ref.setValue((STRUCT) c.toDatum(_ref.getConnection())); } 347 Código Fuente C.4.4.3 Clase: BDObjCuenca package objcuenca; import java.sql.*; import Oracle.jdbc2.*; import Oracle.sql.*; import Oracle.jdbc.driver.*; import Oracle.jdbc.oracore.*; import Oracle.sqlj.runtime.Oracle; import Oracle.sqlj.runtime.util.*; import consultorSql.ConectorJDBC; import java.util.Vector; import java.util.Dictionary; import objcuenca.*; public class BDObjCuenca extends ConectorJDBC { /** * Constructor Generico. */ public BDObjCuenca() { super(); } /** * Constructor con parametros de coneccion. */ public BDObjCuenca(String url, String driverName, String user, String passwd) { super(url,driverName,user,passwd); } /********************************************************************* * Obtiene desde la base de datos un objeto del tipo CuencaOraRef a * partir del codigo del objeto al que referencia. */ public CuencaOraRef getCuencaRef(String codigo) throws Exception { PreparedStatement st= getConnection().prepareStatement( "SELECT ref(c) FROM tbl_cuenca c where c.codigo=(?)") ; st.setObject(1,codigo,OracleTypes.VARCHAR) ; st.executeQuery(); OracleResultSet rs = (OracleResultSet) st.getResultSet() ; rs.next() ; CuencaOraRef cuencaRef= new CuencaOraRef(); cuencaRef._ref =rs.getREF(1); return cuencaRef; } /** * Obtiene desde la base de datos un objeto del tipo RioOraRef * del codigo del objeto al que referencia. */ public RioOraRef getRioRef(String codigo) 348 Código Fuente { try { PreparedStatement st= getConnection().prepareStatement( "SELECT ref(r) FROM tbl_rio r where r.codigo=(?)") ; st.setObject(1,codigo,OracleTypes.VARCHAR) ; st.executeQuery(); OracleResultSet rs = (OracleResultSet) st.getResultSet() ; rs.next() ; RioOraRef rioRef= new RioOraRef(); rioRef._ref =rs.getREF(1); return rioRef; } catch (Exception err) { err.printStackTrace(System.err); return null; } } /** * Obtiene desde la base de datos un objeto del tipo CuencaOra a * partir de su codigo */ public CuencaOra getCuenca(String codigo) throws Exception { CuencaOraRef cuencaRef=getCuencaRef(codigo); return cuencaRef.getValue(); } /** * Obtiene los nodos de la cuenca. y los debuelve * en un vector. Se entrega un NodoOraRef */ public Vector getAllNodoOraRef(CuencaOra c) throws Exception { Vector nodos=new Vector(); PreparedStatement st= getConnection().prepareStatement( " SELECT ref(n) " + " FROM tbl_nodo n"+ " where n.myCuenca = "+ " (SELECT ref(c) FROM tbl_cuenca c where c.codigo=(?))" ); st.setObject(1,c.getCodigo(),OracleTypes.VARCHAR) ; st.executeQuery(); OracleResultSet rs = (OracleResultSet) st.getResultSet() ; while(rs.next()) { GuiNodo guiNodoRef=new GuiNodo(); guiNodoRef._ref=rs.getREF(1); guiNodoRef.loadFigura(); nodos.add(guiNodoRef) ; 349 Código Fuente } } return nodos; /** * Obtiene los embalses de la cuenca. y los debuelve * en un vector. Se entrega un EmbalseOraRef */ public Vector getAllEmbalseOraRef(CuencaOra c) throws Exception { Vector embalses=new Vector(); PreparedStatement st= getConnection().prepareStatement( " SELECT ref(e) " + " FROM tbl_emb e"+ " where e.myNodo.myCuenca.codigo = ?") ; st.setObject(1,c.getCodigo(),OracleTypes.VARCHAR) ; st.executeQuery(); OracleResultSet rs = (OracleResultSet) st.getResultSet() ; while(rs.next()) { GuiEmbalse emb=new GuiEmbalse(); emb._ref=rs.getREF(1); emb.loadFigura(); embalses.add(emb) ; } return embalses; } /** * Obtiene los Flujos de la y los debuelve * en un vector. Se entrega un FlujoOraRef */ public Vector getAllFlujosOraRef(CuencaOra c) throws Exception { Vector vector=new Vector(); PreparedStatement st= getConnection().prepareStatement( " SELECT ref(f) " + " FROM tbl_flujo f"+ " where f.nodo_ini.myCuenca.codigo = ?"+ " or f.nodo_fin.myCuenca.codigo = ?") ; st.setObject(1,c.getCodigo(),OracleTypes.VARCHAR) ; st.setObject(2,c.getCodigo(),OracleTypes.VARCHAR) ; st.executeQuery(); OracleResultSet rs = (OracleResultSet) st.getResultSet() ; while(rs.next()) { GuiFlujo figura=new GuiFlujo(); figura._ref=rs.getREF(1); figura.loadFigura(); vector.add(figura) ; 350 Código Fuente } } return vector; /** * obtiene todas las figuras de la cuenca. * */ public Vector getAllFigRef(CuencaOra c) throws Exception { Vector vector=getAllNodoOraRef(c); vector.addAll(getAllEmbalseOraRef(c)); vector.addAll(getAllFlujosOraRef(c)); return vector; } /** * Inserta una cuenca a la base de datos */ public void Insertar(CuencaOra c) throws SQLException { PreparedStatement st= getConnection().prepareStatement( " INSERT INTO tbl_cuenca" + " VALUES (cuenca(?,?))" ); st.setObject(1,c.getCodigo(),OracleTypes.VARCHAR) ; st.setObject(2,c.getNombre(),OracleTypes.VARCHAR) ; st.executeQuery(); } /** * Inserta un rio a la base de datos. */ public void Insertar(RioOra c) throws SQLException { PreparedStatement st= getConnection().prepareStatement( " INSERT INTO tbl_rio" + " VALUES (rio(?,?))" ); st.setObject(1,c.getCodigo(),OracleTypes.VARCHAR) ; st.setObject(2,c.getNombre(),OracleTypes.VARCHAR) ; st.executeQuery(); } /** * Eliminar cuenca. */ public void eliminar(CuencaOra c) throws SQLException 351 Código Fuente { } PreparedStatement st= getConnection().prepareStatement( " DELETE FROM tbl_cuenca c" + " WHERE c.codigo=?" ); st.setObject(1,c.getCodigo().trim(),OracleTypes.VARCHAR) ; st.executeQuery(); /** * Eliminar Rio */ public void eliminar(RioOra c) throws SQLException { PreparedStatement st= getConnection().prepareStatement( " DELETE FROM tbl_rio r" + " WHERE r.codigo=?" ); st.setObject(1,c.getCodigo().trim(),OracleTypes.VARCHAR) ; st.executeQuery(); } /** * Insertar un Canal * * */ public GuiFlujo insertar(FlujoOra flujo) throws SQLException { OracleCallableStatement ocs= (OracleCallableStatement) getConnection().prepareCall("{ ? = call Insert_Flujo(?) }"); } ocs.registerOutParameter(1,OracleTypes.REF,"FLUJO"); ocs.setCustomDatum(2,flujo); ocs.execute(); GuiFlujo guiFlujo=new GuiFlujo(); guiFlujo._ref =ocs.getREF(1); return guiFlujo; } 352 Código Fuente C.4.4.4 Clase: GuiEmbalse package objcuenca; import import import import import import import objcuenca.*; java.awt.Dimension; javax.swing.JOptionPane; java.sql.SQLException; java.math.*; cuencamaule.frmEmbalse; figuras.*; public class GuiEmbalse extends EmbalseOraRef implements Graficable { private int zoom=100; private GrafEmbOra graf; private String Codigo; private frmEmbalse frm; private boolean alerta=false; public GuiEmbalse() { } /**----------------------------------------------------------------* Carga la figura que reprecenta al embaslse */ public void loadFigura() { try { int zoomAct=zoom; EmbalseOra em=this.getValue(); //la figura almacenada en la base de datos, esta al 100% zoom=100; graf=em.getGraf(); Codigo=em.getCodigo(); this.alerta=alertaEmb(); // restauro el valor del zoom, // y se ajusta la figura que estaba al100% setZoom(zoomAct); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); } } // implementacion metodos de la interfaz Graficable /** * Cambia el valor del zoom con el pasado como parametro */ public void setZoom(int zoom) { 353 Código Fuente try { TrianguloOra tr=graf.getTr() ; PuntoOra a=tr.getA() ; PuntoOra b=tr.getB() ; PuntoOra c=tr.getC() ; double X;double Y; int actual=getZoom(); //aplicando el zoom al punto a X = a.getX().doubleValue() ; Y = a.getY().doubleValue() ; X=(X * zoom)/actual; Y=(Y * zoom)/actual; a.setX(new BigDecimal(X)); a.setY(new BigDecimal(Y)); //aplicando el zoom al punto b X = b.getX().doubleValue() ; Y = b.getY().doubleValue() ; X=(X * zoom)/actual; Y=(Y * zoom)/actual; b.setX(new BigDecimal(X)); b.setY(new BigDecimal(Y)); //aplicando el zoom al punto c X = c.getX().doubleValue() ; Y = c.getY().doubleValue() ; X=(X * zoom)/actual; Y=(Y * zoom)/actual; c.setX(new BigDecimal(X)); c.setY(new BigDecimal(Y)); tr.setA(a); tr.setB(b); tr.setC(c) ; graf.setTr(tr); this.zoom=zoom; } } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); } /********************************************************** * Entrega el Zoom actual * */ public int getZoom() { return zoom; 354 Código Fuente } private boolean alertaEmb() { try { ArrCaudalesOra curAler=this.getValue().getCurvaAlerta(); ArrCaudalesOra caudal =this.getValue().getCaudales(); boolean colorCambiado=false; for(int i=1;i<=6;i++) { if(curAler.getcaudal(new BigDecimal(i)).compareTo( caudal.getcaudal(new BigDecimal(i)))==1) { return true; } } } catch (Exception ex) { JOptionPane.showMessageDialog(null,"Error en el embalse : " +ex.getMessage()) ; } return false; } /*********************************************************** * Dibuja un triangulo que reprecenta al embalse * */ public void dibujar(Dibujante d) { try { if(alerta){d.setColorLinea(java.awt.Color.red);} TrianguloOra tr=graf.getTr() ; d.drawTriangulo(tr) ; PuntoOra poA=tr.getA() ; PuntoOra poB=tr.getB() ; PuntoOra poC=tr.getC() ; Punto a=new Punto( poA.getX().intValue(),poA.getY().intValue()); Punto b=new Punto( poB.getX().intValue(),poB.getY().intValue()); Punto c=new Punto( poC.getX().intValue(),poC.getY().intValue()); a.x=c.x; int an = (int) b.distanciaA(c); int alturaTriangulo = (int) a.distanciaA(c) ; int al = alturaTriangulo/2; int x=b.x; int y=b.y-(alturaTriangulo/2); 355 Código Fuente //las constantes 3 y 22 furon obtenidas empiricamente. int tamfuente=an*3/22; if(Codigo.length() >0) { d.drawCenteredString(Codigo ,x,y,an, al,tamfuente); d.drawCenteredString("F Ee R Er G", b.x,b.y,an,al*2/3,tamfuente); } if(alerta){d.setColorLinea(java.awt.Color.black);} } catch (Exception err) { JOptionPane.showMessageDialog(null,err.getMessage()); } } /*********************************************************** * Despliega el dialogo para modificar los atributos del * nodo * */ public void dlgAtributos() { try { PuntoOra poA=graf.getTr().getA(); frm=new frmEmbalse(this); frm.setLocation(poA.getX().intValue() , poA.getY().intValue()); frm.pack() ; frm.show() ; } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); } } /************************************************************ * Entrega la dimencion minima para el contenedor * del embalse * */ public Dimension dimensionMinima() { try { int x=graf.getTr().getC().getX().intValue() ; int y=graf.getTr().getC().getY().intValue() ; return (new Dimension(x,y)); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); 356 Código Fuente } return null; } /************************************************************ * Entrega Verdadero si el punto pasado por parametro * percenece al triangulo o falso si no pertenece. */ public boolean pertenece(Punto p) { try { TrianguloOra tr=graf.getTr() ; PuntoOra poA=tr.getA() ; PuntoOra poB=tr.getB() ; PuntoOra poC=tr.getC() ; Punto a=new Punto( poA.getX().intValue(),poA.getY().intValue() ); Punto b=new Punto( poB.getX().intValue(),poB.getY().intValue() ); Punto c=new Punto( poC.getX().intValue(),poC.getY().intValue() ); Punto inicio,fin,direccion; int coorx,cnt=0,np=3; Punto q[]={a,b,c}; for(int i=0;i<np;i++) { inicio=p.restar(q[i%np]); fin=p.restar(q[(i+1)%np]); if((inicio.y * fin.y) <=0) { if((inicio.y!=0) || (fin.y!=0)) { direccion = inicio.restar(fin); coorx = (inicio.x * direccion.y inicio.y * direccion.x); if(direccion.y<0) coorx*=-1; if(coorx==0) {cnt =1; break;} if(coorx > 0) { cnt++; if((inicio.y < 0 && fin.y==0)|| (inicio.y==0 && fin.y<0)) cnt --; } } } } return ((cnt %2)!=0); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); return false; } } } 357 Código Fuente C.4.4.5 Clase: GuiFlujo package objcuenca; import objcuenca.*; import java.awt.Dimension; import javax.swing.JOptionPane; import java.sql.SQLException; import java.math.*; import cuencamaule.*; import figuras.*; public class GuiFlujo extends FlujoOraRef implements Graficable { private int zoom=100; private GrafCuencaOra graf; private frmFlujo frm; private boolean alerta=false; /** * Constructor por defecto. */ public GuiFlujo() { } //Carga la figura que reprecenta al flujo public void loadFigura() { try { //Como vamos a leer la figura desde la base de datos, debemos //ajustarla al valor del zoom actual. Para esto guardo el zoom. int zoomAct=zoom; //la figura almacenada en la base de datos, esta al 100% zoom=100; graf=this.getValue().getGraf(); // restauro el valor del zoom, y se ajusta la figura que // estaba al100% this.alerta=alertaFlujo(); setZoom(zoomAct); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } 358 Código Fuente // ajuste de Zoom para una figura del tipo GrafCentHidroOra private void setZoomCentHidro(int zoom) { try { GrafCentHidroOra gch=graf.getGch(); PolilineaOra pl1=graf.getGch().getPl1(); PolilineaOra pl2=graf.getGch().getPl2(); //ajustando zoom polilineas int cont=0; PolilineaOra pl=pl1; while(cont!=2) { int actual=getZoom(); int largo=pl.getNpuntos().intValue(); double X,Y; PuntoOra a; int i; for(i=1;i<=largo;i++) { a=pl.getp(new BigDecimal(i)); X = a.getX().doubleValue() ; Y = a.getY().doubleValue() ; X=(X * zoom)/actual; Y=(Y * zoom)/actual; a.setX(new BigDecimal(X)); a.setY(new BigDecimal(Y)); pl.setp(a,new BigDecimal(i)); } pl=pl2; cont++; } gch.setPl1(pl1); gch.setPl2(pl2); CuadradoOra cuad=graf.getGch().getCuad(); PuntoOra a=graf.getGch().getCuad().getA() PuntoOra b=graf.getGch().getCuad().getB() PuntoOra c=graf.getGch().getCuad().getC() PuntoOra d=graf.getGch().getCuad().getD() //ajustando puntos del cuadrado. PuntoOra p=a ; cont=0; while(cont != 4) { double X = p.getX().doubleValue() ; double Y = p.getY().doubleValue() ; int actual=getZoom(); X=(X * zoom)/actual; Y=(Y * zoom)/actual; p.setX(new BigDecimal(X)); p.setY(new BigDecimal(Y)); cont++; switch (cont) ; ; ; ; 359 Código Fuente { case 1: p=b; break; case 2: p=c; break; case 3: p=d; break; } } cuad.setA(a); cuad.setB(b); cuad.setC(c); cuad.setD(d); gch.setCuad(cuad); graf.setGch(gch); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } // aplica el zoom a un GrafFlujoOra private void setZoomGrafFlujo(int zoom) { try { int actual=getZoom(); int largo=graf.getGfl().getPl().length(); double X,Y; PuntoOra a; for(int i=0;i<largo;i++) { a=graf.getGfl().getPl().getElement(i) ; X = a.getX().doubleValue() ; Y = a.getY().doubleValue() ; X=(X * zoom)/actual; Y=(Y * zoom)/actual; a.setX(new BigDecimal(X)); a.setY(new BigDecimal(Y)); graf.getGfl().getPl().setElement(a,i) ; } } } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } 360 Código Fuente // implementacion metodos de la interfaz Graficable public void setZoom(int zoom) { try { if ((graf != null)&&(graf.getTipo() != null)) { switch (graf.getTipo().intValue()) { case 1: setZoomGrafFlujo(zoom); break; case 2: setZoomCentHidro(zoom); break; } this.zoom=zoom; } } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } /********************************************************** * Entrega el Zoom actual * */ public int getZoom() { return zoom; } /*********************************************************** * Dibuja un flujo. */ private void dibujarFlujo(Dibujante d) { try { ArrPuntosOra arrp=graf.getGfl().getPl(); switch (graf.getGfl().getEstilo().intValue()) { case 1: //linea simple d.drawPolilinea(arrp) ; break; case 2: //linea doble d.drawPolilineaDoble(arrp) ; break; 361 Código Fuente case 3: //gruesa d.drawPolilineaGruesa(arrp) ; break; } } catch (Exception err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } /********************************************************* * Dibuja una Central Hidroelectrica. */ private void dibujarCentHidro(Dibujante d) { try { GrafCentHidroOra ch=graf.getGch(); d.drawCuadrado(ch.getCuad()); d.drawPolilinea(ch.getPl1()); d.drawPolilinea(ch.getPl2().getLospuntos()); } catch (Exception err) { err.printStackTrace(System.err) ; JOptionPane.showMessageDialog(null,err.getMessage()); } } /*********************************************************** * Dibuja el flujo correspondiente. * */ public void dibujar(Dibujante d) { if(alerta){d.setColorLinea(java.awt.Color.red);} try { if((graf!=null)&&(graf.getTipo()!=null)) { switch (graf.getTipo().intValue()) { case 1: dibujarFlujo(d); break; case 2: dibujarCentHidro(d); int ax=graf.getGch().getCuad().getA().getX().intValue(); 362 Código Fuente } } int ay=graf.getGch().getCuad().getA().getY().intValue(); int bx=graf.getGch().getCuad().getB().getX().intValue(); int by=graf.getGch().getCuad().getB().getY().intValue(); int cx=graf.getGch().getCuad().getC().getX().intValue(); int cy=graf.getGch().getCuad().getC().getY().intValue(); int dx=graf.getGch().getCuad().getD().getX().intValue(); int dy=graf.getGch().getCuad().getD().getY().intValue(); int al=Math.abs(cy - ay); int an=Math.abs(bx - ax); int x=ax ; int y=ay ; int tamfuente=an*9/24; d.drawCenteredString("C" ,x,y,an, al,tamfuente); break; } catch (Exception err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } if(alerta){d.setColorLinea(java.awt.Color.black);} } /*********************************************************** * Despliega el dialogo para modificar los atributos del * flujo * */ public void dlgAtributos() { try { FlujoOra fl=this.getValue(); switch (fl.getTipo().intValue()) { case 1://aportante if(fl.getAp().getTipo().intValue()==1) { frm=new frmHoyaInter(this); break; } else { if(fl.getAp().getTipo().intValue()==2) { frm=new frmRegNat(this); break; } else { if(fl.getAp().getTipo().intValue()==3) { 363 Código Fuente } } frm=new frmSalidaEmb(this); break; } break; case 2://extracción if(fl.getEx().getTipo().intValue() == 1 ) { frm=new FrmCapAguaPot(this); break; } case 3://aportante y extractor if(fl.getApex().getTipo().intValue() ==1 ) {//si es un canal. frm=new FrmCanal(this); break; } else { if(fl.getApex().getTipo().intValue() ==2) {//si es un tramo. frm=new FrmTramo(this); break; } else { if(fl.getApex().getTipo().intValue()==3) {// si es una central hidroelectrica. frm=new FrmCentralHidro(this); break; } } } default: frm=new frmFlujo(); break; } frm.pack() ; frm.show() ; } catch (Exception err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; } } 364 Código Fuente /** * Entrega la dimension minima para el contenedor de los * puntos en arr */ private Dimension dimensionMinimaGFlujo() { try { int x=0; int px=0; int y=0; int py=0; int largo=graf.getGfl().getPl().length(); for (int i = 0; i < largo; i++) { px=graf.getGfl().getPl().getElement(i).getX().intValue(); py=graf.getGfl().getPl().getElement(i).getY().intValue(); x=x>px?x:px; y=y>py?y:py; } return new Dimension(x,y); } catch (Exception ex) { JOptionPane.showMessageDialog(null,ex.getMessage()); ex.printStackTrace(System.err) ; return new Dimension(0,0); } } private Dimension dimensionMinima(PolilineaOra fl) { try { int x=0; int px=0; int y=0; int py=0; int largo=fl.getLospuntos().length(); for (int i = 0; i < largo; i++) { px=fl.getLospuntos().getElement(i).getX().intValue(); py=fl.getLospuntos().getElement(i).getY().intValue(); x=x>px?x:px; y=y>py?y:py; } return new Dimension(x,y); } catch (Exception ex) { JOptionPane.showMessageDialog(null,ex.getMessage()); ex.printStackTrace(System.err) ; return new Dimension(0,0); } } 365 Código Fuente /** * Entrega la dimension minima para el contenedor la central * hidroelectrica */ private Dimension dimensionMinimaCH() { try { GrafCentHidroOra gch=graf.getGch() ; Dimension d1=dimensionMinima(gch.getPl1()); Dimension d2=dimensionMinima(gch.getPl2()); PuntoOra p=gch.getCuad().getD(); Dimension d3=new Dimension( p.getX().intValue(),p.getY().intValue()); int x=Math.max(d1.width , Math.max(d2.width,d3.width)); int y=Math.max(d1.height , Math.max(d2.height,d3.height)); return new Dimension(x,y); } catch (Exception ex) { JOptionPane.showMessageDialog(null,ex.getMessage()); ex.printStackTrace(System.err) ; return new Dimension(0,0); } } /************************************************************ * Entrega la dimencion minima para el contenedor * del nodo * */ public Dimension dimensionMinima() { try { Dimension d=new Dimension(0,0); if((graf!=null)&&(graf.getTipo()!=null)) { switch (graf.getTipo().intValue()) { case 1: d=dimensionMinimaGFlujo(); break; case 2: d=dimensionMinimaCH(); break; } } return d; } catch (SQLException err) 366 Código Fuente { } JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; return new Dimension(0,0); } private boolean pertenece(ArrPuntosOra arr,Punto p) { try { int largo=arr.length(); PuntoOra[] vertice=new PuntoOra[largo]; for (int i = 0; i < largo; i++) { vertice[i]=arr.getElement(i); } for (int i=0;i<(vertice.length-1);i++) { Punto a=new Punto( vertice[i].getX().intValue(),vertice[i].getY().intValue()); Punto b=new Punto( vertice[i+1].getX().intValue(),vertice[i+1].getY().intValue()); if (!this.colineales(a,b,p)) {return false;} else { if (a.x!=b.x) { return ((a.x <= p.x) && (p.x <= b.x)) ||((a.x >=p.x)&&(p.x>=b.x)); } else { return ((a.y <= p.y) && (p.y <= b.y)) ||((a.y >=p.y)&&(p.y>=b.y)); } } } return false; } catch (Exception ex) { JOptionPane.showMessageDialog(null,ex.getMessage()); ex.printStackTrace(System.err) ; return false; } } 367 Código Fuente /**--------------------------------------------------------------* p1,p2 linea de p1 a p2 * p3 el punto a consultar. * el mouse no es tan presiso, por eso la comparación * no es == a "0"(exacto) */ private boolean colineales(Punto a,Punto b,Punto c) { return (Math.abs((a.x * b.y) - (a.y * b.x) + (a.y * c.x) - (a.x * c.y) + (b.x * c.y) - (b.y*c.x))<300); } private boolean pertenece(GrafCentHidroOra gch,Punto p) { try { PuntoOra a=gch.getCuad().getA(); PuntoOra b=gch.getCuad().getB(); PuntoOra c=gch.getCuad().getC(); PuntoOra d=gch.getCuad().getD(); int vecX[]={a.getX().intValue(),b.getX().intValue(), c.getX().intValue(),d.getX().intValue() }; int vecY[]={a.getY().intValue(),b.getY().intValue(), c.getY().intValue(),d.getY().intValue() }; Poligono pol=new Poligono(vecX,vecY,4); if (pol.pertenece(p)) {return true;} else if (pertenece(gch.getPl1().getLospuntos(),p)){return true;} else if (pertenece(gch.getPl2().getLospuntos(),p)){return true;} return false; } catch (Exception ex) { JOptionPane.showMessageDialog(null,ex.getMessage()); ex.printStackTrace(System.err) ; return false; } } 368 Código Fuente /************************************************************ * Entrega Verdadero si el punto pasado por parametro * percenece a la figura, sino retorna falso. * */ public boolean pertenece(Punto p) { try { boolean resp=false; if((graf!=null)&&(graf.getTipo()!=null)) { switch (graf.getTipo().intValue()) { case 1: resp=pertenece(graf.getGfl().getPl(),p); break; case 2: resp=pertenece(graf.getGch(),p); break; } } return resp; } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); err.printStackTrace(System.err) ; return false; } } /** * Indica si el tipo de flujo no cumple con las condiciones impuestas */ private boolean alertaFlujo() { try { FlujoOra flujo=this.getValue(); int tipoFlujo=flujo.getTipo().intValue(); int tipoExtrac; try {tipoExtrac =flujo.getEx().getTipo().intValue();} catch (NullPointerException ex) {tipoExtrac=-1;} int tipoApex; try {tipoApex=flujo.getApex().getTipo().intValue();} catch (NullPointerException ex) { tipoApex=-1;} if((tipoFlujo==2)&&(tipoExtrac==1)) 369 Código Fuente { ArrCaudalesOra caudales=flujo.getEx().getCa().getCaudales(); ArrCaudalesOra caudalEx= flujo.getEx().getCa().getCaudalDeExtrac(); for(int i=1;i<=6;i++) { //si algun valor de caudales, es menor que el caudal de //extracción. if(caudales.getcaudal(new BigDecimal(i)).compareTo( caudalEx.getcaudal(new BigDecimal(i)))==-1) { return true;} } } else { if((tipoFlujo==3)&&(tipoApex==3)) { ArrCaudalesOra cau=flujo.getApex().getCh().getCaudales(); double caudal; double caudalMinimoGeneracion= flujo.getApex().getCh().getCaudalMin().doubleValue(); for(int i=1;i<=6;i++) { caudal=cau.getcaudal(new BigDecimal(i)).doubleValue(); if (caudal<caudalMinimoGeneracion) { return true; } } } } } } catch (Exception ex) { JOptionPane.showMessageDialog(null,"error en flujo: "+ ex.getMessage()); } return false; } 370 Código Fuente C.4.4.6 Clase: GuiNodo package objcuenca; import import import import import import import objcuenca.*; java.awt.Dimension; javax.swing.JOptionPane; java.sql.SQLException; java.math.*; cuencamaule.frmNodo; figuras.*; public class GuiNodo extends NodoOraRef implements Graficable { private int zoom=100; private ElipseOra graf; private String Codigo; private frmNodo frm; public GuiNodo() { } //Carga la figura que reprecenta al nodo localmente public void loadFigura() { try { //Como vamos a leer la figura desde la base de datos, //debemos ajustarla //al valor del zoom actual. Para esto guardo el zoom. int zoomAct=zoom; NodoOra no=this.getValue(); //la figura almacenada en la base de datos, esta al 100% zoom=100; graf=no.getGraf().getElip(); Codigo=no.getCodigo(); // restauro el valor del zoom, y se ajusta la figura //que estaba al100% setZoom(zoomAct); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); } } // implementacion metodos de la interfaz Graficable public void setZoom(int zoom) { try { PuntoOra c=graf.getCentro(); 371 Código Fuente double double double double alto = graf.getAlto().doubleValue() ; ancho = graf.getAncho().doubleValue() ; X = c.getX().doubleValue() ; Y = c.getY().doubleValue() ; int actual=getZoom(); X=(X * zoom)/actual; Y=(Y * zoom)/actual; ancho=(ancho * zoom)/actual; alto =(alto * zoom)/actual; c.setX(new BigDecimal(X)); c.setY(new BigDecimal(Y)); graf.setCentro(c) ; graf.setAlto(new BigDecimal(alto)) ; graf.setAncho(new BigDecimal(ancho)); this.zoom=zoom; } } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); } /********************************************************** * Entrega el Zoom actual * */ public int getZoom() { return zoom; } /*********************************************************** * Dibuja una elipse que reprecenta al nodo * */ public void dibujar(Dibujante d) { try { d.drawElipse(graf); int an=graf.getAncho().intValue() ; int al=graf.getAlto().intValue() ; int x=graf.getCentro().getX().intValue() ; int y=graf.getCentro().getY().intValue() ; x-=an/2; y-=al/2; int tamfuente=an*10/44; if(Codigo.length() >0) { d.drawCenteredString(Codigo ,x,y,an, al,tamfuente); 372 Código Fuente } } catch (Exception err) { JOptionPane.showMessageDialog(null,err.getMessage()); } } /*********************************************************** * Despliega el dialogo para modificar los atributos del * nodo * */ public void dlgAtributos() { try { PuntoOra centro=graf.getCentro(); frm=new frmNodo(this); frm.setLocation(centro.getX().intValue(), centro.getY().intValue()); frm.pack() ; frm.show() ; } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); } } /************************************************************ * Entrega la dimencion minima para el contenedor * del nodo * */ public Dimension dimensionMinima() { try { int x=graf.getCentro().getX().intValue()+ (graf.getAncho().intValue()/2); int y=graf.getCentro().getY().intValue()+ (graf.getAlto().intValue()/2); return (new Dimension(x,y)); } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); return null; } } 373 Código Fuente /************************************************************ * Entrega Verdadero si el punto pasado por parametro * percenece a la elipse o falso si no pertenece. * */ public boolean pertenece(Punto p) { try { PuntoOra centro=graf.getCentro() ; Punto c=new Punto(centro.getX().intValue(),centro.getY().intValue()); int an=graf.getAncho().intValue() /2; int al=graf.getAlto().intValue() /2; if ((((Math.pow((p.x-c.x),2))/(Math.pow(an,2)))+ ((Math.pow((p.y-c.y),2))/(Math.pow(al,2))))<=1) {return true;} else {return false;} } catch (SQLException err) { JOptionPane.showMessageDialog(null,err.getMessage()); return false; } } } 374 Bibliografía Bibliografía [1] Carlos Batini, Stefano Ceri, Shamkant B. Navathe. Diseño conceptual de Bases de datos, un enfoque de entidades-interrelaciones. Addison-Wesley/Diaz de Santos 1994 [2] Elisa Bertino, Lorenzo Martino. Sistemas de bases de datos orientadas a objetos, conceptos y arquitecturas. Addison-Wesley/Diaz de Santos, 1995 [3] Luis A. Lobos, Mauricio J. Cardemil, Paola M. Terrazas. “Diseño e Implementación de una Base de datos Objeto Relacional”. En: Actas del XIX International Conference of the Chilean Computer Science Society, VII Encuentro Chileno de computación, I congreso Chileno de Educación superior en computación III Workshop Chileno en sistemas Distribuídos, Campeonato Sudamericano de Programación ACM-Sede Chile. Talca – Chile, noviembre, 1999. [4] Grady Booch. Análisis y Diseño Orientado a Objetos con Aplicaciones. AddisonWesley/Diaz de Santos, 1996 [5] Timothy Budd. Introducción a la Programación orientada a Objeto. Addison- Wesley Iberoamericana, 1994. [6] Paul Dorsey, Joseph R. Hudicka. Oracle 8. Diseño de Bases de datos con UML. Osborne McGraw-Hill 1999. [7] Andrew Eisenberg, Jim Melton. “Standars In Practice”. En la revista Sigmod Record(http://www.acm.org/sigmod/record/) volumen 27 número 3, septiembre de 1998. [8] Andrew Eisenberg, Jim Melton. “SQL:1999, formerly know as SQL3”. En la revista Sigmod Record(http://www.acm.org/sigmod/record/) volumen 28 número 1, marzo de 1999. [9] Andrew Eisenberg, Jim Melton. “SQL Standardization: the Next Steps”. En revista Sigmod Record(http://www.acm.org/sigmod/record/) volumen la 29 número 1, marzo del 2000. [10] Ramez Elmasri, Shamkant B. Navathe. Sistemas de bases de datos, conceptos fundamentales. Addison-Wesley Iberoamericana, 1997. [11] Craig Larman. Applying UML and patterns: an introduction to object-oriented analysis and design. Prentice Hall 1998 [12] Patrick Naughton. Manual de Java. Osborne/McGraw-Hill, 1996 [13] Mario Piattini. “SABD Orientados a Objetos”. En: revista algoritmo http://www.algoritmodigital.com, abril de 1995 [14] Mario Piattini. “El futuro de las Bases de Datos”. En: Tutorial del XIX International 376 Bibliografía Conference of the Chilean Computer Science Society, VII Encuentro Chileno de computación, I congreso Chileno de Educación superior en computación III Workshop Chileno en sistemas Distribuídos, Campeonato Sudamericano de Programación ACM-Sede Chile. Talca – Chile, noviembre, 1999. [15] Mario Piattini. “SQL3: Futuro estándar de bases de datos (I)”. En: revista algoritmo http://www.algoritmodigital.com, Octubre de 1995 [16] Mario Piattini. “SQL3: Futuro estándar de bases de datos (II)”. En: revista algoritmo http://www.algoritmodigital.com, Noviembre de 1995 [17] Mario Piattini. “SQL3: Futuro estándar de bases de datos (y III)”. En: revista algoritmo http://www.algoritmodigital.com, Diciembre de 1995 [18] Mario Piattini. “SQL/MM:Estándar para bases de datos multimedia”. En: revista algoritmo http://www.algoritmodigital.com, Marzo de 1996 [19] Mario Piattini. “ODMG-93. El otro estándar para SABD Orientados a Objeto”. En: revista algoritmo http://www.algoritmodigital.com, Abril de 1996. [20] Roger S. Pressman. Ingeniería del Software un enfoque práctico, cuarta edición. McGraw-Hill 1998. [21] Antonio Quirós. “La persistencia de los objetos: un principio de la OOP (I)”. En: revista algoritmo http://www.algoritmodigital.com [22] Antonio Quirós. “La persistencia de los objetos: un principio de la OOP (II)”. En: revista algoritmo http://www.algoritmodigital.com [23] Carlos A. Sabino. Cómo hacer una tesis y toda clase de escritos, edición ampliada. Editorial Lumen Hvmanitas, 1998 [24] Jeffrey Ulman. A First Course in Database System. Prentice Hall 1997 [25] ANSI X3H2-99-462/WG3:SAF-003, (ANSI/ISO Working Draft) Framework (SQL/Framework), agosto 1999. En: ftp://jerry.ece.umassd.edu/SC32/WG3 [26] ANSI X3H2-99-463/WG3:SAF-004, (ANSI/ISO Working Draft) Foundation (SQL/Foundation), agosto 1999. En: ftp://jerry.ece.umassd.edu/SC32/WG3 [27] Rational Software et al. “UML Notation Guide” en UML resource Center de Rational Software. En: http://www.rational.com/uml/index.jsp [28] Gilberto Gutiérrez R. “Modelamiento de Sistemas de Información Geográfica bajo un enfoque de Orientación a Objetos. Tesis para optar al grado de Magíster en Ciencias de la Computación de la Universidad de Chile. [29] Enrique Kaliski y Luis Arrau. “Apuntes del curso de capacitación en modelación hidrológica”. Documento perteneciente al la Dirección General de Aguas del Ministerio de Obras Públicas de Chile. 377 Bibliografía [30] Oracle. “Oracle 8i Applications Developer’s Guide – Fundamentts”. Número del documento A68003-01 en: http://technet.Oracle.com/docs [31] Oracle. “Oracle 8i Concepts”. Número del documento A67781-01 en: http://technet.Oracle.com/docs [32] Oracle. “Oracle 8i PL/SQL User’s Guide and Reference”. Número del documento A67842-01 en : http://technet.Oracle.com/docs [33] Oracle. “Oracle 8i Java Developer’s Guide”. Número del documento A64681-01 en : http://technet.Oracle.com/docs [34] Oracle. “Oracle 8i Jpublisher User’s Guide”. Número del documento A68027-01 en: http://technet.Oracle.com/docs [35] Oracle . “Oracle 8i Spatial. User’s Guide and reference”. Número del documento A67295-01 en: http://technet.Oracle.com/docs [36] Oracle. “Oracle8i SQLJ Developer’s Guide and Reference”. Número del documento A64684-01 en: http://technet.Oracle.com/docs [37] Rational Software et al. “UML Summary” en UML resource Center de Rational Software. En: http://www.rational.com/uml/index.jsp [38] Rational Software et al. “UML Semantics” en UML resource Center de Rational Software. En: http://www.rational.com/uml/index.jsp [39] James Rumbaugh, Ivar Jacobson, Grady Booch. El Lenguaje Unificado de Modelado. Manual de referencia. Addison Wesley, 2000. [40] Mario Piattini. “El futuro de las bases de datos(I). Evolución y nuevos retos”. En: revista algoritmo http://www.algoritmodigital.com, abril de 1997. [41] Oracle. “Oracle8i SQL Reference”. Número del documento A67779-01 en: http://technet.Oracle.com/docs [42] Oracle. “Java Stored Procedures Developer’s Guide”. Número del documento A64686-01 en: http://technet.Oracle.com/docs [43] Oracle. “Oracle8i JDBC Developer’s Guide and Reference”. Número del documento A64685-01 en: http://technet.Oracle.com/docs [44] Oracle. “Oracle Call Interface Programer’s Guide”. Número del documento A76975-01 en: http://technet.Oracle.com/docs [45] Martin Fowler. UML Gota A Gota. Editorial Addison Wesley Longman de México, 1999 [46] Jim Melton, Alan R. Simon. SQL:1999 Understanding Relational Language Components. Morgan Kaufmann Publishers 2001 [47] ANSI X3H2-99-269/WG3:RTM-006, (ANSI-ISO Working Draft) Persistent Stored 378 Bibliografía Modules (SQL/PSM), agosto 1999. En: ftp://sqlstandards.org/SC32/WG3/Progression_Documents/Informal_working_drafts/wdpsm-1999-07.pdf [48] ISO WG3:YYJ-007/H2-2001-144, (ISO-ANSI Working Draft) Temporal (SQL/Temporal), Mayo 2001. En: http://www.jtc1sc32.org/sc32/jtc1sc32.nsf/Attachments/5A5731749C3013238 8256A5B0044557A/$FILE/32N0651.PDF [49] ISO WG3:YYJ-012/H2-2001-149, (ISO-ANSI Working Draft) XML-Related Specifications (SQL/XML), Mayo 2001. En : ftp://sqlstandards.org/SC32/WG3/Progression_Documents/Informal_working_dr afts/wd-xml-2001-06.pdf 379