Tema 8. Bases de datos orientadas a objetos. Juan Ignacio Rodrı́guez de León Resumen El paradigma de la programación orientada a objetos. Necesidad de tipos complejos de datos. El modelo de datos orientado a objetos. Lenguajes orientados a objetos. Lenguajes de programación persistentes. Sistemas C++ persistentes, sistemas Java persistentes Índice 1. Orientación a objetos 1.1. Los objetos . . . . . . . . . 1.2. Clases de objetos . . . . . 1.3. polimorfismo . . . . . . . 1.4. sobrecarga de operadores 1.5. Herencia . . . . . . . . . . 1.6. Herencia múltiple . . . . . 1.7. Identidad de los objetos . 1.8. Continentes de objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Lenguajes orientados a objetos 3 3 4 6 6 6 7 8 9 9 3. Lenguajes de programación persistentes 3.1. Persistencia de los objetos . . . . . . . . . . . . . . . . . . . . 3.2. Identidad de los objetos y punteros a memoria . . . . . . . . 3.3. Almacenamiento y acceso a los objetos persistentes . . . . . 10 11 12 13 4. Bases de datos relacionales orientadas a objetos 4.1. Relaciones anidadas . . . . . . . . . . . . . 4.2. Tipos de datos complejos . . . . . . . . . . . 4.2.1. Colecciones . . . . . . . . . . . . . . 4.2.2. Objetos de gran tamaño (LOB) . . . 4.2.3. Tipos estructurados . . . . . . . . . . 4.2.4. Constructores . . . . . . . . . . . . . 4.3. Herencia . . . . . . . . . . . . . . . . . . . . 4.3.1. Herencia de tipos . . . . . . . . . . . 4.3.2. Herencia de tablas . . . . . . . . . . 13 13 14 14 14 14 14 15 15 15 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ÍNDICE 4.4. Referencias . . . . . . . . . . . . . . . . . . . 4.5. Consultas con tipos complejos . . . . . . . . 4.5.1. Acceso a datos estructurados . . . . 4.5.2. Expresiones de ruta . . . . . . . . . . 4.5.3. Atributos de tipo colección . . . . . 4.6. Funciones y procedimientos . . . . . . . . . 4.6.1. Funciones y procedimientos en SQL 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 16 16 16 16 17 17 1 ORIENTACIÓN A OBJETOS 1. 3 Orientación a objetos Los conceptos de la programación orientada a objetos tienen origen en Simula 67, un lenguaje diseñado para hacer simulaciones, creado por OleJohan Dahl y Kristen Nygaard del Centro de Cómputo Noruego en Oslo. Fueron refinados más tarde en Smalltalk, que fue desarrollado en Simula en el Xerox PARC, pero diseñado para ser un sistema completamente dinámico en el cual los objetos se podrı́an crear y modificar “en marcha” en lugar de tener un sistema basado en programas estáticos. La programación orientada a objetos introduce nuevos conceptos, que a veces no son más que nombres nuevos aplicados a conceptos antiguos, ya conocidos. Entre ellos destacan los siguientes: Objetos entidades complejas provistas de datos (propiedades, atributos) y comportamiento (funcionalidad, programas, métodos). Corresponden a los objetos reales del mundo que nos rodea. Clases conjuntos de objetos que comparten propiedades y comportamiento. Método es un código ejecutable asociado a un objeto (o a una clase de objetos), cuya ejecución se desencadena mediante un ”mensaje”. Mensaje una comunicación dirigida a un objeto, que le ordena que ejecute uno de sus métodos con ciertos parámetros. Propiedad, atributo o variable datos asociados a un objeto o a una clase de objetos. Herencia las clases no están aisladas, sino que se relacionan entre sı́, formando una jerarquı́a de clasificación. Los objetos heredan las propiedades y el comportamiento de todas las clases a las que pertenecen. Encapsulamiento cada objeto está aislado del exterior, es un módulo natural, y la aplicación entera se reduce a una agregación de objetos. El aislamiento protege a los datos asociados a un objeto de su modificación por quien no tenga derecho a acceder a ellos, eliminando efectos secundarios e interacciones. Polimorfismo métodos diferentes, asociados a objetos distintos, pueden compartir el mismo nombre, aunque el comportamiento del método varı́e según el objeto al que se aplica. 1.1. Los objetos Hablando en general, los objetos se corresponden con las entidades del modelo E-R. El paradigma orientado a objetos está basado en el encapsu- 1 ORIENTACIÓN A OBJETOS 4 lamiento de los datos y del código relacionados con cada objeto en una sola unidad cuyo contenido no es visible desde el exterior. Conceptualmente, todas las interacciones entre cada objeto y el resto del sistema se realizan mediante mensajes. Por tanto, la interfaz entre cada objeto y el resto del sistema se define mediante un conjunto de mensajes permitidos. En general, cada objeto está asociado con: Un conjunto de variables o atributos que contiene los datos del objeto; las variables se corresponden con los atributos del modelo E-R. Un conjunto de mensajes a los que responde; cada mensaje puede no tener parámetros, tener uno o varios. Un conjunto de métodos, cada uno de los cuales es código que implementa un mensaje; el método devuelve un valor como respuesta al mensaje. El término mensaje en un entorno orientado a objetos no implica el uso de mensajes fı́sicos; por el contrario, hace referencia al intercambio de solicitudes entre los objetos, independientemente de los detalles concretos de su implementación. Se utiliza a veces la expresión invocar a un método para denotar el hecho de enviar un mensaje a un objeto y la ejecución del método correspondiente. La principal razón de diferenciar las dos acciones es obtener la capacidad de modificar la definición de un objeto, sin afectar al resto del sistema. Esta es una de las mayores ventajas de la programación orientada a objetos Los atributos derivados de las entidades del modelo E-R pueden expresarse en el modelo orientado a objetos como mensajes sólo de lectura. Por ejemplo, el atributo derivado antigüedad de una entidad empleado puede expresarse como el mensaje antigüedad de un objeto empleado. El método que implemente los mensajes, puede determinar la antigüedad restando la fecha-alta del empleado de la fecha actual. 1.2. Clases de objetos En una base de datos hay muchos objetos similares. Por similar se entiende que responden a los mismos mensajes, utilizan los mismos métodos y tienen atributos del mismo nombre y tipo. Serı́a un derroche definir por separado cada uno de estos objetos. Por tanto, los objetos parecidos se agrupan para formar una clase. Cada uno de estos objetos se denomina ejemplar o instancia de su clase. Todos los objetos de una clase comparten una definición y un comportamiento común, y se diferencian sólo en los valores asignados a sus atributos. 1 ORIENTACIÓN A OBJETOS 5 El concepto de clase del modelo orientado a objetos se corresponde con el concepto de entidad del modelo E-R. Algunos ejemplos de clases en la base de datos bancaria serı́an empleados, clientes, cuentas y préstamos. El siguiente listado define una clase Empleado, en pseudo-código. clase Empleado: string nombre string apellidos string dirección date fecha-alta int sueldo def sueldo-anual(self): return self.sueldo * 14 def nombre-completo(self): return self.nombre + ’ ’ + self.apellidos def antigüedad(self): return hoy() - self.fecha_alta def set-dirección(self, newdir): self.dirección = newdir La representación en UML de esta clase serı́a ası́: Empleado string nombre string apellidos string dirección date fecha-alta int sueldo int sueldo-anual() string nombre-completo() date antigüedad() set-dirección(string) La definición muestra las variables y los mensajes a los que responden los objetos de la clase. Cada objeto de la clase empleado contiene los Atributos nombre, apellidos y dirección, todas cadenas de caracteres; fechaalta, que es una fecha, y sueldo, que es un entero. Se define tras mensajes: sueldo-anual, nombre-completo y antigüedad. Obsérvese que el mensaje set-dirección utiliza un parámetro que especifica el nuevo valor de dirección. 1 ORIENTACIÓN A OBJETOS 1.3. 6 polimorfismo En programación orientada a objetos se denomina polimorfismo a la capacidad del código de un programa para ser utilizado con diferentes tipos de datos u objetos. También se puede aplicar a la propiedad que poseen algunas operaciones de tener un comportamiento diferente dependiendo del objeto (o tipo de dato) sobre el que se aplican. 1.4. sobrecarga de operadores Algunos lenguajes de programación permiten un tipo especial de métodos que permiten la sobrecarga de operadores. Estos métodos otorgan la capacidad de transformar los operadores de un lenguaje como por ejemplo los operadores +, -, *, /, etc de forma que se redefinen para poder ser utilizados con los objetos de nuestra clase. Mediante esta técnica podemos sumar dos objetos creados por nosotros, o un objeto y un entero, en vez de limitarnos a sumar números enteros o reales. Por ejemplo, si definiéramos una clase complex para trabajar con números complejos en un lenguaje que no soporta sobrecarga de operadores, tendrı́amos que implementar un método suma, y el código que implementa la suma quedarı́a ası́: a = complex(1, 3.4) b = complex(-2, 0.54) a.suma(b) Sin embargo, en un lenguaje con sobrecarga de operadores, redefinirı́amos el operador + para nuestra clase de complejos, y el código final quedarı́a: a = complex(1, 3.4) b = complex(-2, 0.54) a = a + b 1.5. Herencia Los esquemas de las bases de datos orientadas a objetos suelen necesitar gran número de clases. Frecuentemente, sin embargo, varias de las clases son parecidas entre sı́. Son parecidas porque definen iguales atributos y métodos. No son idénticas porque cada clase define, además, atributos y/o métodos que no comparte con las demás. Serı́a conveniente definir una representación de los atributos y métodos comunes en un solo lugar. Esto puede hacerse creando una nueva clase, que contendrá solo las caracterı́sticas comunes, y redefiniendo las clases originales como especializaciones de la nueva clase. 1 ORIENTACIÓN A OBJETOS 7 Las clases especializadas adquieren una dependencia de herencia con respecto a la clase general, ya que heredan los atributos y métodos definidos en esta. Aparece por tanto el concepto de jerarquı́a de clases, que es parecido al de especialización del modelo entidad-relación. Las relaciones entre una clase más especifica, o clase derivada, con respecta a su clase genérica o superclase, siempre son de especialización, es decir , si la clase A deriva de una superclase B, lo que queremos decir es que A es un tipo particular de B. Como ejemplo de estas relaciones, se podrı́an definir las clases Persona, Empleado y Cajero, donde un Empleado es un tipo especial de Persona, y Cajero es un tipo especial de Empleado. Una ventaja importante de la herencia en los sistemas orientados a objetos es el concepto de posibilidad de sustitución: cualquier método de una clase dada A puede ser invocado con cualquier objeto perteneciente a cualquier subclase de A. De igual forma, los atributos definidos en la superclase son utilizables en cualquiera de sus derivadas. Si la clase Persona define el atributo Nombre, las clases Empleado y Cajero también las definen implı́citamente, por la herencia. La herencia propicia ası́ la reutilización del código, dado que no hace falta volver a escribir los mensajes, métodos y funciones para los objetos de las clases derivadas. 1.6. Herencia múltiple En la mayor parte de los casos una organización de clases con estructura de árbol resulta adecuada para describir las aplicaciones; en la organización con estructura de árbol, cada clase puede tener a lo sumo una superclase. Sin embargo, hay situaciones que no pueden representarse bien en una jerarquı́a de clases con estructura de árbol. La herencia múltiple permite a las clases heredar variables y métodos de múltiples superclases. La relación entre clases y subclases se representa mediante un grafo acı́clico dirigido en vez de un árbol. Cuando se utiliza la herencia múltiple existe una posible ambigüedad, ya que se puede heredar la misma variable o el mismo método de más de una superclase. Por ejemplo, la clase estudiante puede tener un variable dept que identifica el departamento del estudiante y la clase profesor puede tener análogamente una variable dept que identifica el departamento de profesor. La clase ayudante-profesor heredarı́a ambas definiciones. Existen varias formas de evitar esta ambigüedad: Incluir las dos variables, dándoles nombres diferentes para distinguirlas Escoger sólo una, según el orden en que se declararon las clases. 1 ORIENTACIÓN A OBJETOS 8 Obligar al usuario a seleccionar de manera explı́cita una de las opciones Considerar esta situación como un error. Diferentes implementaciones han elegido cada una de estas opciones. Una solución aun más drástica es impedir la herencia múltiple, como en Java. 1.7. Identidad de los objetos Los objetos de las bases de datos orientadas a objetos suelen corresponder a entidades del sistema modelado por la base de datos. Las entidades conservan su identidad aunque algunas de sus propiedades cambien con el tiempo. De manera parecida, los objetos deben conservar su identidad aunque los valores de las variables o las definiciones de los métodos cambien total o parcialmente con el tiempo. Este concepto de identidad no se aplica a las tuplas de las bases de datos relacionales. En los sistemas relacionales las tuplas de una relación sólo se distinguen por los valores que contienen. La identidad de los objetos es un concepto de identidad más potente que el que suele hallarse en los lenguajes de programación o en los modelos de datos no orientados a objetos. A continuación se ilustran varios ejemplos de identidad. Valor Se utiliza un valor de datos como identidad. Esta forma de identidad se utiliza en los sistemas relacionales. Nombre Se utiliza como identidad un nombre proporcionado por el usuario. Esta forma de identidad suele utilizarse para los archivos en los sistemas de archivos. Incorporada Se incluye el concepto de identidad en el modelo de datos o en el lenguaje de programación y no hace falta que el usuario proporcione ningún identificador. Esta forma de identidad se utiliza en los sistemas orientados a objetos. Cada objeto recibe del sistema de manera automática un identificador en el momento en que se crea. Los identificadores de los objetos son únicos; es decir, cada objeto tiene un solo identificador y no hay dos objetos que tengan el mismo identificador. Los identificadores de los objetos no tienen por qué estar en una forma con la que los seres humanos se encuentren cómodos; pueden ser números grandes, por ejemplo. La posibilidad de guardar el identificador de un objeto como un campo de otro objeto es más importante que tener un nombre que resulte fácil de recordar. Utilizar un identificador de un objeto como atributo de otro se denomina referenciar un objeto. 2 LENGUAJES ORIENTADOS A OBJETOS 1.8. 9 Continentes de objetos Se pueden utilizar las referencias entre objetos para modelar diferentes conceptos del mundo real. Uno de estos objetos es el de continentes de objetos, que consiste en crear objetos compuestos o complejos, constituidos por objetos más simples. Puede haber varios niveles de continentes. Esta situación crea entre los objetos una jerarquı́a de continentes. Los enlaces entre las clases deben interpretarse en esta estructura como “es parte de” en lugar de la interpretación “es una especialización de” de los enlaces de una jerarquı́a de herencias. En ciertas aplicaciones un objeto puede estar incluido en varios objetos. En esos casos la relación de continentes se representa mediante un grafo. 2. Lenguajes orientados a objetos Hasta el momento se han explicado los conceptos básicos de la programación orientada a objetos en un nivel abstracto. Para poder utilizarlos en la práctica en un sistema de bases de datos hay que concretarlos en algún lenguaje. Esto puede realizarse de dos maneras: 1. Los conceptos de la programación orientada a objetos se utilizan únicamente como herramientas de diseño y se codifican, por ejemplo, en una base de datos relacional. Se sigue este enfoque cuando se utilizan los diagramas entidad-relación para modelar los datos y luego se convierten de manera manual en un conjunto de relaciones. 2. Los conceptos de la programación orientada a objetos se incorporan en un lenguaje que se utiliza para trabajar con la base de datos. Con este enfoque hay varios lenguajes posibles en los que se pueden integrar los conceptos: Una opción es extender un lenguaje para el tratamiento de datos, como SQL, añadiendo tipos complejos y las demás caracterı́sticas de la programación orientada a objetos. Los sistemas que proporcionan extensiones orientadas a objetos a los sistemas relacionales se denominan sistemas relacionales orientados a objetos. Otra opción es tomar un lenguaje de programación orientado a objetos y extenderlo para que trabaje con las bases de datos. Estos lenguajes se denominan lenguajes de programación persistente. Estudiaremos estas dos opciones en el resto de este tema. 3 3. LENGUAJES DE PROGRAMACIÓN PERSISTENTES 10 Lenguajes de programación persistentes Los lenguajes de las bases de datos trabajan directamente con datos que son persistentes, es decir, los datos siguen existiendo una vez que el programa que los creó ha concluido. Las relaciones de las bases de datos y las tuplas de las relaciones son ejemplos de datos persistentes. Por el contrario, los únicos datos persistentes con los que los lenguajes de programación tradicionales trabajan directamente son los archivos. La manera tradicional de realizar las interfaces de las bases de datos con los lenguajes de programación tradicionales consiste en incorporar o embeber el código SQL dentro del lenguaje de programación. Los lenguajes de programación persistente son lenguajes de programación extendidos para el tratamiento de datos persistentes. Los lenguajes de programación persistente pueden distinguirse de los lenguajes con SQL embebido de al menos dos maneras: 1. En los lenguajes incorporados el sistema de tipos del lenguaje anfitrión suele ser diferente del sistema de tipos del lenguaje para el tratamiento de los datos. Los programadores son responsables de las conversiones de tipos entre el lenguaje anfitrión y SQL. Exigir que los programadores ejecuten esta tarea presenta varios inconvenientes: a) El código para la conversión entre objetos y tuplas opera fuera del sistema de tipos orientado a objetos y, por tanto, tiene más posibilidades de presentar errores no detectados. b) La conversión entre el formato orientado a objetos y el formato relacional de las tuplas necesita gran cantidad de código. El código de conversión, junto con el código para cargar y descargar los datos de la base de datos puede suponer un porcentaje significativo del total necesario para la aplicación Por el contrario, en los lenguajes de programación persistente, el lenguaje de consulta se halla totalmente integrado con el lenguaje anfitrión y ambos comparten el mismo sistema de tipos. Los objetos se pueden crear y guardar en la base de datos sin ningún tipo explı́cito ni cambios de formato; los cambios necesarios se realizan de manera transparente. 2. Los programadores que utilizan lenguajes de consulta incorporados son responsables de la escritura de código explı́cito para la búsqueda de los datos de la base de datos en la memoria. Si se realizan actualizaciones, los programadores deben escribir código de manera explı́cita para volver a guardar los datos actualizados en la base de datos. Por el contrario, en los lenguajes de programación persistentes los programadores pueden trabajar con datos persistentes sin tener que 3 LENGUAJES DE PROGRAMACIÓN PERSISTENTES 11 escribir de manera explı́cita código para buscarlos en la memoria o para volver a guardarlos en el disco. Se han propuesto versiones persistentes de los lenguajes de programación como Pascal. En los últimos años han recibido mucha atención las versiones persistentes de los lenguajes orientados a objetos como C++, Java y Smalltalk. Sin embargo, los lenguajes de programación persistentes presentan ciertos inconvenientes. Dado que los lenguajes de programación suelen ser potentes resulta relativamente sencillo cometer errores de programación que dañen las bases de datos. Además, la complejidad de los lenguajes hace que la optimización automática de alto nivel, como la reducción de E/S de disco, resulte más difı́cil. A continuación se describen varios aspectos que cualquier lenguaje de programación persistente debe abordar. 3.1. Persistencia de los objetos En los lenguajes de programación orientados a objetos estos son transitorios, desaparecen cuando se termina el programa, Si se desea transformar uno de estos lenguajes en un lenguaje para la programación de bases de datos, el primer paso consiste en proporcionar una manera de hacer persistentes a los objetos. Se han propuesto varios enfoques. Persistencia por clases El enfoque más sencillo, pero el menos conveniente, consiste en declarar que una clase es persistente. Todos los objetos de la clase son, por tanto, persistentes de manera predeterminada. Todos los objetos de las clases no persistentes son transitorios. Este enfoque no es flexible, porque no permite disponer en una misma clase tanto de objetos transitorios como de objetos persistentes. En muchos sistemas de bases de datos orientados a objetos, la declaración de que una clase es persistente se debe interpretar mejor como “que pueden ser persistentes”. Persistencia por creación En este enfoque se introduce una sintaxis nueva para crear los objetos persistentes mediante la extensión de la sintaxis para la creación de los objetos transitorios. Por tanto, los objetos son persistentes o transitorios en función de la manera de crearlos. Este enfoque se sigue en varios sistemas de bases de datos orientados a objetos. Persistencia por marcas Una variante del enfoque anterior es marcar los objetos como persistentes después de haberlos creado. Todos los objetos se crean como transitorios, pero, si un objeto tiene que persistir más allá de la ejecución del programa, se le marca de manera explı́cita. 3 LENGUAJES DE PROGRAMACIÓN PERSISTENTES 12 A diferencia del enfoque anterior, la decisión sobre la persistencia o la transitoriedad se retrasa hasta después de la creación del objeto. Persistencia por alcance Uno o varios objetos se declaran objetos persistentes (objetos raı́z) de manera explı́cita. Todos los demás objetos serán persistentes si (y sólo si) son alcanzables desde el objeto raı́z mediante una secuencia de una o más referencias. Este esquema tiene la ventaja de que resulta sencillo hacer que sean persistentes estructuras de datos completas con sólo declarar como persistente la raı́z de las mismas. Sin embargo, el sistema de bases de datos sufre la carga de tener que seguir las cadenas de referencias para detectar los objetos que son persistentes, lo que puede resultar costoso. 3.2. Identidad de los objetos y punteros a memoria El concepto de la identidad de los objetos tiene una relación interesante con los punteros de los lenguajes de programación. Una manera sencilla de conseguir una identidad intrı́nseca es utilizar los punteros a las ubicaciones fı́sicas de almacenamiento. En concreto, en muchos lenguajes orientados a objetos como C++, los identificadores de los objetos son en realidad punteros internos de la memoria. Sin embargo, la asociación de los objetos con ubicaciones fı́sicas de almacenamiento puede variar con el tiempo. Hay varios grados de permanencia de las identidades: Dentro de los procedimientos La identidad sólo persiste durante la ejecución de un único procedimiento. Un ejemplo de la identidad dentro de los procedimientos son las variables locales del interior de los procedimientos. Dentro de los programas La identidad sólo persiste durante la ejecución de un único programa o una única consulta. Un ejemplo de la identidad dentro de los programas son las variables globales de los lenguajes de programación. Los punteros de la memoria principal o de la memoria virtual sólo ofrecen identidad dentro de los programas. Entre programas La identidad persiste de una ejecución del programa a otra. Los punteros a los datos del sistema de archivos del disco ofrecen identidad entre los programas, pero pueden cambiar si se modifica la manera en que los datos se guardan en el sistema de archivos. Persistente La identidad no sólo persiste entre las ejecuciones del programa sino también entre las reorganizaciones estructurales de los datos. Es la forma persistente de la identidad necesaria para los sistemas orientados a objetos. 4 BASES DE DATOS RELACIONALES ORIENTADAS A OBJETOS 3.3. 13 Almacenamiento y acceso a los objetos persistentes Hay varias maneras de hallar los objetos de la base de datos. Uno de los enfoques consiste en dar nombres a los objetos, igual que se hace con los archivos. Este enfoque resulta útil con un número de objetos relativamente pequeño, pero no resulta práctico para millones de objetos. Un segundo enfoque consiste en exponer los identificadores de los objetos o los punteros persistentes de los objetos, que pueden guardarse de manera externa. A diferencia de los nombres, estos punteros no tienen por qué ser fáciles de recordar y pueden ser incluso punteros fı́sicos dentro de la base de datos. Un tercer enfoque es guardar las colecciones de objetos y permitir que los programas iteren sobre las mismas para hallar los objetos deseados. Las colecciones de objetos pueden a su vez modelarse como objetos de un tipo colección. Los tipos de colecciones incluyen los conjuntos, los multiconjuntos, listas, etcétera. Un caso especial de colección son las extensiones de clases, que son la colección de todos los objetos pertenecientes a una clase. Si hay una extensión de clase, siempre que se cree un objeto de la clase, el mismo se inserta en la extensión de clase de manera automática, y siempre que se borre un objeto, éste se eliminará de la extensión de clase. La mayor parte de los sistemas de bases de datos orientados a objetos permiten las tres maneras de acceso a los objetos persistentes. Todos los objetos tienen identificadores. Generalmente sólo se da nombre a las extensiones de las clases y a otros objetos de tipo colección y, quizás, a determinados objetos seleccionados, pero normalmente no se nominan la mayorı́a de los objetos. 4. Bases de datos relacionales orientadas a objetos Los modelos de datos relacionales orientados a objetos extienden el modelo de datos relacional proporcionando un sistema de tipos más ricos y complejos y añadiendo la programación orientada a objetos. Los lenguajes de consulta relacionales como SQL también necesitan ser extendidos para trabajar con el sistema de tipos enriquecido. 4.1. Relaciones anidadas El modelo relacional anidado es una extensión del modelo relacional en la que los dominios pueden ser atómicos o de relación. Por tanto, el valor de las tuplas de los atributos puede ser una relación, y las relaciones pueden guardarse en otras relaciones. Los objetos complejos, por tanto, pueden representarse mediante una única tupla de las relaciones anidadas. 4 BASES DE DATOS RELACIONALES ORIENTADAS A OBJETOS 4.2. 4.2.1. 14 Tipos de datos complejos Colecciones Los conjuntos son ejemplares de los tipos colección. Otros ejemplares son los arrays y los multiconjuntos (es decir, colecciones sin orden donde un elemento puede aparecer varias veces). Las siguientes definiciones de atributos ilustran la declaración de un array: array-autores varchar(20) array [10] array-autores es un array de hasta 10 nombres de autor. Se puede acceder a los elementos del array especificando el ı́ndice del array, por ejemplo, array-autores[1]. 4.2.2. Objetos de gran tamaño (LOB) Muchas aplicaciones actuales de bases de datos necesitan almacenar atributos grandes (del orden de varios Kbytes), tales como la fotografı́a de una persona, o muy grandes (del orden de varios Mbytes o incluso Gbytes), tales como imágenes médicas de alta resolución o clips de vı́deo. SQL:1999 proporciona por tanto nuevos tipos de datos para objetos de gran tamaño para datos de caracteres (clob) y binarios (blob). Las letras “lob” en estos tipos de datos son acrónimos de “Large OBject” (objeto grande). Los objetos grandes se usan normalmente en aplicaciones externas, y tiene poco sentido extraerlos completamente en SQL. En su lugar, una aplicación conseguirı́a un “localizador” de un objeto grande y lo usarı́a para manipularlo desde el lenguaje anfitrión. 4.2.3. Tipos estructurados Los tipos estructurados permiten la representación directa de atributos compuestos de los diagramas E-R. Un tipo estructurado puede tener métodos definidos sobre él. Los métodos se declaran como parte de la definición de tipos de un tipo estructurado. 4.2.4. Constructores Hay que definir funciones constructoras para crear valores de tipos estructurados. En SQL-1999 y en muchos otros lenguajes se utiliza una función con el mismo nombre que un tipo estructurado como función constructora. De manera predeterminada, cada tipo estructurado tiene un constructor sin argumentos, que establece los atributos a sus valores predefinidos. Cualquiera otra función constructora tiene que crearse explı́citamente. Puede haber más de una constructora para el mismo tipo estructurado; 4 BASES DE DATOS RELACIONALES ORIENTADAS A OBJETOS 15 aunque tengan el mismo nombre, sólo tienen que ser distinguibles por el número de argumentos y sus tipos. 4.3. Herencia La herencia puede hallarse en el nivel de los tipos o en el nivel de las tablas. En primer lugar se considerará la herencia de los tipos y después en el nivel de las tablas. 4.3.1. Herencia de tipos Los tipos derivados heredan los atributos de superclase. Los métodos también se heredan por sus subtipos, al igual que los atributos. Sin embargo, un subtipo puede redefinir el efecto de un método declarándolo de nuevo. Esto se conoce como sobreescritura (overriding) del método. 4.3.2. Herencia de tablas Las subtablas en SQL:1999 se corresponden con la noción del modelo E-R de la especialización y la generalización. Por tanto, cada atributo presente en una supertabla debe estar también presente en las subtablas. Además, cuando se declaran subtablas derivadas de una supertabla, cada tupla presente en una subtabla también está presentes en la supertabla. En principio, serı́a posible la herencia múltiple tanto en tipos como en tablas, pero ANSI SQL:1999 no lo soporta Las subtablas pueden guardarse de manera eficiente –sin réplica de todos los campos heredados– de una de las dos siguientes formas: Cada tabla almacena la clave primaria (que se puede heredar de una tabla padre) y los atributos definidos localmente. Los atributos heredados (aparte de la clave primaria) no hace falta guardarlos y pueden obtenerse mediante una reunión con la supertabla basada en la clave primaria. Cada tabla almacena todos los atributos heredados y definidos localmente. Cuando se inserta una tupla se almacena sólo en la subtabla en la que se inserta y su presencia se infiere en cada supertabla. El acceso a todos los atributos de una tupla es más rápido, dado que no se requiere una reunión. 4.4. Referencias Los lenguajes orientados a objetos proporcionan la posibilidad de hacer referencia a los objetos. El atributo de un tipo puede ser una referencia a 4 BASES DE DATOS RELACIONALES ORIENTADAS A OBJETOS 16 un objeto de un tipo especificado. Este concepto es equivalente al de clave externa. 4.5. Consultas con tipos complejos En este apartado se presenta una extensión del lenguaje de consulta SQL para trabajar con los tipos complejos. 4.5.1. Acceso a datos estructurados Se hace referencia al nombre del atributo compuesto utilizando una notación con un punto. Se verá mejor con un ejemplo sencillo: averiguar el tı́tulo y el nombre de la editorial de cada documento. La consulta siguiente lleva a cabo esa tarea: select tı́tulo, editorial.nombre from libros Obsérvese que se hace referencia al campo nombre del atributo compuesto editorial. 4.5.2. Expresiones de ruta Las referencias se desreferencian en SQL:1999 con el sı́mbolo ->. Considérese otra vez la tabla departamentos. Se puede usar la siguiente consulta para hallar los nombres y direcciones de los directores de todos los departamentos. select director-$>$nombre, director-$>$dirección \\ from departamentos Una expresión como “director->nombre” se denomina una expresión de ruta. Dado que directores una referencia a una tupla de la tabla persona, el atributo nombre en la consulta anterior es el atributo nombre de la tupla de la tabla persona. 4.5.3. Atributos de tipo colección Los arrays son el único tipo colección soportado por SQL:1999 Una expresión que se evalúe a una colección puede aparecer en cualquier lugar en que aparezca un nombre de relación, tal como en la cláusula from, como ilustran los siguientes ejemplos (Se usa la tabla Libros definida en el libro) Si se desea hallar todos los documentos que tienen las palabras “base de datos” entre sus palabras clave se puede utilizar la consulta siguiente: 4 BASES DE DATOS RELACIONALES ORIENTADAS A OBJETOS 17 select tı́tulo from libros where ’base de datos’ in (unnest(lista-palabras-clave)) Obsérvese que se ha usado el operador unnest(lista-palabras-clave) en una posición en la que SQL sin relaciones anidadas habrı́a exigido una subexpresión select-from-where. La transformación de una relación anidada en una forma con menos (o sin) atributos de tipo relación se denomina desanidamiento. El proceso inverso de transformar una relación 1FN en una relación anidada se denomina anidamiento. Si se sabe que un libro en particular tiene tres autores, se podrı́a escribir: select array-autores[1], array-autores[2], array-autores[3] from libros where tı́tulo= ’Fundamentos de bases de datos’ 4.6. Funciones y procedimientos SQL:1999 permite la definición de funciones, procedimientos y métodos. Se pueden definir mediante el componente procedimental de SQL:1999 o mediante un lenguaje de programación como Java, C o C++. Algunos sistemas de bases de datos soportan sus propios lenguajes procedimentales, tales como PL/SQL en Oracle y TransactSQL en SQLServer de Microsoft. Éstos incorporan una parte procedimental parecida a SQL:1999, pero hay diferencias tanto en la sintaxis como en la semántica 4.6.1. Funciones y procedimientos en SQL Supóngase que se desea una función que, dado un libro, devuelva el recuento del número de autores usando el esquema 4FN. Se puede definir la función ası́: create function recuento-autores(tı́tulo varchar(20)) returns integer begin declare recuento-a integer; select count(autor) into recuento-a from autores where autores.tı́tulo = tı́tulo return recuento-a; end La función anterior se puede utilizar en una consulta que devuelva los tı́tulos de todos los libros que tengan más de un autor: 4 BASES DE DATOS RELACIONALES ORIENTADAS A OBJETOS 18 select tı́tulo from libros where recuento-autores(tı́tulo) > 1 Las funciones son particularmente útiles con tipos de datos especializados tales como las imágenes y los objetos geométricos. Las funciones se pueden escribir en un lenguaje externo, como C o Java. Algunos sistemas de bases de datos también soportan funciones que devuelven relaciones, es decir, multiconjuntos de tuplas, aunque tales funciones no se soportan en SQL:1999. Los métodos se pueden ver como funciones asociadas a tipos estructurados. Tienen un primer parámetro implı́cito denominado self, que se establece al valor del tipo estructurado sobre el que se invoca el método. Ası́, el cuerpo del método puede referirse a un atributo a del valor usando self.a. El método también puede actualizar estos atributos. SQL:1999 también soporta procedimientos. Los procedimientos se pueden invocar desde un procedimiento SQL o desde SQL embebido con la instrucción call: SQL:1999 permite que más de un procedimiento o función compartan el mismo nombre, siempre que el número de los argumentos sea diferente, o que las que tengan el mismo número difieran al menos en el tipo de un argumento. El nombre, junto con el número y tipo de los argumentos, se usa para identificar el procedimiento.