M2 - Bases de dades / Apunts UF4 : BDOR1
Contingut
Tipos de datos objeto
- Un tipo de dato objeto es un tipo de dato compuesto definido por el usuario. Representa una estructura de datos así como funciones y procedimientos para manipular datos.
- Las variables que formen la estructura de datos de un tipo de dato objeto reciben el nombre de atributos (que se corresponde con sus propiedades). Las funciones y procedimientos del tipo de dato objeto se denominan métodos(que se corresponde con sus acciones).
- Aunque los atributos son públicos, es decir, visibles desde otros programas cliente, los programas deberían manipular los datos únicamente a través de los métodos (funciones y procedimientos) que se hayan declarado en el tipo objeto, en vez de asignar u obtener sus valores directamente. Esto es debido a que los métodos pueden hacer un chequeo de los datos de manera que se mantenga un estado apropiado en los mismos.
- Durante la ejecución, la aplicación creará instancias de un tipo objeto, es decir, referencias a objetos reales con valores asignados en sus atributos.
Definición de tipos de objeto
- La estructura de la definición o declaración de un tipo de objeto está dividida en una especificación y un cuerpo. La especificación define el interfaz de programación, donde se declaran los atributos así como las operaciones (métodos) para manipular los datos. En el cuerpo se implementa el código fuente de los métodos.
- En la especificación de un tipo de objeto, todos los atributos se deben declarar antes que los métodos. Si la especificación de un tipo de objeto sólo declara atributos, no es necesario que declarar el cuerpo.No se pueden declarar atributos en el cuerpo.
- Todas las declaraciones realizadas en la especificación del tipo de objeto son públicas, es decir, visibles fuera del tipo de objeto.
- No se pueden declarar en la especificación constantes (CONSTANTS), excepciones (EXCEPTIONS), cursores (CURSORS) o tipos (TYPES).
- Para definir un objeto en Oracle debes utilizar la sentencia CREATE TYPE que tiene el siguiente formato:
CREATE TYPE nombre_tipo AS OBJECT (
Declaración_atributos
Declaración_métodos);
- Para reemplazar un objeto existente:
CREATE OR REPLACE TYPE nombre_tipo AS OBJECT
- Eliminar el tip de objeto:
DROP TYPE nombre_tipo;
Ejemplo:
DROP TYPE Usuario;
Declaración de atributos
- El tipo de dato que puede almacenar un determinado atributo puede ser cualquiera de los tipos de Oracle excepto los siguientes:
- - LONG y LONG RAW .
- - ROWID y UROWID .
- - Los tipos específicos PL/SQL BINARY_INTEGER (y sus subtipos), BOOLEAN, PLS_INTEGER, RECORD, REF, CURSOR , %TYPE , y %ROWTYPE.
- - Los tipos definidos dentro de un paquete PL/SQL.
- No se pueden inicializar los atributos usando el operador de asignación, ni la cláusula DEFAULT , ni asignar la restricción NOT NULL.
- El tipo de dato de un atributo puede ser otro tipo de objeto, por lo que la estructura de datos puede ser tan complicada como sea necesario.
Ejemplo:
CREATE OR REPLACE TYPE Usuario AS OBJECT (
login VARCHAR2(10),
nombre VARCHAR2(30),
f_ingreso DATE,
credito NUMBER
);
/
- Se pueden modificar los atributos de un tipo de objeto utilizando la sentencia ALTER TYPE . Si se desean añadir nuevos atributos se añadirá la cláusula ADD ATTRIBUTE seguida de la lista de nuevos atributos con sus correspondientes tipos de dato. Utilizando MODIFY ATTRIBUTE se podrán modificar los atributos existentes, y para eliminar atributos se dispone de manera similar de DROP ATTRIBUTE .
Ejemplos
ALTER TYPE Usuario DROP ATTRIBUTE f_ingreso;
ALTER TYPE Usuario ADD ATTRIBUTE (apellidos VARCHAR2(40), localidad VARCHAR2(50));
ALTER TYPE Usuario
ADD ATTRIBUTE cp VARCHAR2(5),
MODIFY ATTRIBUTE nombre VARCHAR2(35);
Definición de métodos
- Un método es un subprograma que declaras en la especificación de un tipo de objeto usando las palabras clave MEMBER o STATIC .
- El nombre no puede ser el mismo que el de tipo de objeto ni el de ninguno de sus atributos.
- Los métodos tienen dos partes: una especificación y un cuerpo.
- En la especificación o declaración se debe encontrar el nombre del método, una lista opcional de parámetros, y, en el caso de las funciones, un tipo de dato de retorno.
Ejemplo:
CREATE OR REPLACE TYPE Usuario AS OBJECT (
login VARCHAR2(10),
nombre VARCHAR2(30),
f_ingreso DATE,
credito NUMBER,
MEMBER PROCEDURE incrementoCredito(inc NUMBER)
);
/
- En el cuerpo se debe indicar el código que se debe ejecutar para realizar una determinada tarea cuando el método es invocado.
Ejemplo:
CREATE OR REPLACE TYPE BODY Usuario AS
MEMBER PROCEDURE incrementoCredito(inc NUMBER) IS
BEGIN
credito := credito + inc;
END incrementoCredito;
END;
/
- Por cada especificación de método que se indique en el bloque de especificación del tipo de objeto, debe existir su correspondiente cuerpo del método, o bien, el método debe declararse como NOT INSTANTIABLE , para indicar que el cuerpo del método se encontrará en un subtipo de ese tipo de objeto. Además, debes tener en cuenta que las cabeceras de los métodos deben coincidir exactamente en la especificación y en el cuerpo.
- El tipo de dato de un parámetro no puede tener restricciones de tamaño. El tipo de datos puede ser cualquiera de los empleados por Oracle salvo los indicados anteriormente para los atributos. Las mismas restricciones se aplican para los tipos de retorno de las funciones.
- El código fuente de los métodos no sólo puede escribirse en el lenguaje PL/SQL. También con otros lenguajes de programación como Java o C.
- Puedes usar la sentencia ALTER TYPE para añadir, modificar o eliminar métodos de un tipo de objeto existente, de manera similar a la utilizada para modificar los atributos de un tipo de objeto.
Parámetro SELF
- Un parámetro especial que puedes utilizar con los métodos MEMBER es el que recibe el nombre SELF. Este parámetro hace referencia a una instancia (objeto) del mismo tipo de objeto. Aunque no lo declares explícitamente, este parámetro siempre se declara automáticamente.
- El tipo de dato correspondiente al parámetro SELF será el mismo que el del objeto original. En las funciones MEMBER , si no declaras el parámetro SELF , su modo por defecto se toma como IN . En cambio, en los procedimientos MEMBER , si no se declara, se toma como IN OUT . Ten en cuenta que no puedes especificar el modo OUT para este parámetro SELF , y que los métodos STATIC no pueden utilizar este parámetro especial.
- Si se hace referencia al parámetro SELF dentro del cuerpo de un método, realmente se está haciendo referencia al objeto que ha invocado a dicho método. Por tanto, si utilizas SELF.nombre_atributo o SELF.nombre_método , estarás utilizando un atributo o un método del mismo objeto que ha llamado al método donde se encuentra utilizado el parámetro SELF.
Ejemplo:
MEMBER PROCEDURE setNombre(Nombre VARCHAR2) IS
BEGIN
/* El primer elemento (SELF.Nombre) hace referencia al atributo del tipo de objeto mientras que
el segundo (Nombre) hace referencia al parámetro del método */
SELF.Nombre := Nombre;
END setNombre;
Sobrecarga
- Al igual que ocurre con los subprogramas empaquetados, los métodos pueden ser sobrecargados, es decir, puedes utilizar el mismo nombre para métodos diferentes siempre que sus parámetros formales sean diferentes (en cantidad o tipo de dato).
- Cuando se hace una llamada a un método, se comparan los parámetros actuales con los parámetros formales de los métodos que se han declarado, y se ejecutará aquél en el que haya una coincidencia entre ambos tipos de parámetros.
- Ten en cuenta que no es válida la sobrecarga de dos métodos cuyos parámetros formales se diferencian únicamente en su modo, así como tampoco en funciones que se diferencien únicamente en el valor de retorno.
Ejemplos:
MEMBER PROCEDURE setNombre(Nombre VARCHAR2)
MEMBER PROCEDURE setNombre(Nombre VARCHAR2, Apellidos VARCHAR2)
- No es válido crear un nuevo método en el que se utilicen los mismos tipos de parámetros formales aunque se diferencien los nombres de los parámetros:
MEMBER PROCEDURE setNombre(Name VARCHAR2, Surname VARCHAR2)
Métodos Constructores
- Cada tipo de objeto tiene un método constructor, que se trata de una función con el mismo nombre que el tipo de objeto y que se encarga de inicializar los atributos y retornar una nueva instancia de ese tipo de objeto.
- Oracle crea un método constructor por defecto para cada tipo de objeto declarado, cuyos parámetros formales coinciden en orden, nombres y tipos de datos con los atributos del tipo de objeto.
- También puedes declarar tus propios métodos constructores, reescribiendo ese método declarado por el sistema, o bien, definiendo un nuevo método con otros parámetros. Una de las ventajas de crear un nuevo método constructor personalizado es que se puede hacer una verificación de que los datos que se van a asignar a los atributos son correctos (por ejemplo, que cumplen una determinada restricción).
- Si deseas reemplazar el método constructor por defecto, debes utilizar la sentencia CONSTRUCTOR FUNCTION seguida del nombre del tipo de objeto en el que se encuentra (recuerda que los métodos constructores tienen el mismo nombre que el tipo de objeto). A continuación debes indicar los parámetros que sean necesarios de la manera habitual. Por último, debes indicar que el valor de retorno de la función es el propio objeto utilizando la cláusula RETURN SELF AS RESULT.
- Puedes crear varios métodos constructores siguiendo las restricciones indicadas para la sobrecarga de métodos.
Ejemplo:
CONSTRUCTOR FUNCTION Usuario(login VARCHAR2, credito NUMBER)
RETURN SELF AS RESULT
CREATE OR REPLACE TYPE BODY Usuario AS
CONSTRUCTOR FUNCTION Usuario(login VARCHAR2, credito NUMBER)
RETURN SELF AS RESULT
IS
BEGIN
IF (credito >= 0) THEN
SELF.credito := credito;
ELSE
SELF.credito := 0;
END IF;
RETURN;
END;
END;
Utilización de objetos
En los siguientes apartados vas a ver cómo puedes declarar variables que permitan almacenar objetos, darle unos valores iniciales a sus atributos, acceder al contenido de dichos atributos en cualquier momento, y llamar a los métodos que ofrece el tipo de objeto utilizado.
Declaración de objetos
- Una vez que el tipo de objeto ha sido definido, éste puede ser utilizado para declarar variables de objetos de ese tipo en cualquier bloque PL/SQL, subprograma o paquete. Ese tipo de objeto lo puedes utilizar como tipo de dato para una variable, atributo, elemento de una tabla, parámetro formal, o resultado de una función, de igual manera que se utilizan los tipos de datos habituales como VARCHAR o NUMBER .
Ejemplo:
u1 Usuario;
- En la declaración de cualquier procedimiento o función, incluidos los métodos del mismo tipo de objeto o de otro, se puede utilizar el tipo de dato objeto definido para indicar que debe pasarse como parámetro un objeto de dicho tipo en la llamada. Por ejemplo pensemos en un procedimiento al que se le debe pasar como parámentro un objeto del tipo Usuario:
PROCEDURE setUsuario(u IN Usuario)
- La llamada a este método se realizaría utilizando como parámetro un objeto, como el que podemos tener en la variable declarada anteriormente:
setUsuario(u1);
- De manera semejante una función puede retornar objetos:
FUNCTION getUsuario(codigo INTEGER) RETURN Usuario
- Los objetos se crean durante la ejecución del código como instancias del tipo de objeto, y cada uno de ellos puede contener valores diferentes en sus atributos.
- El ámbito de los objetos sigue las mismas reglas habituales en PL/SQL, es decir, en un bloque o subprograma los objetos son creados (instanciados) cuando se entra en dicho bloque o subprograma y se destruyen automáticamente cuando se sale de ellos. En un paquete, los objetos son instanciados en el momento de hacer referencia al paquete y dejan de existir cuando se finaliza la sesión en la base de datos.
Inicialización de objetos
- Para crear o instanciar un objeto de un determinado tipo de objeto, debes hacer una llamada a su método constructor. Esto lo puedes realizar empleando la instrucción NEW seguido del nombre del tipo de objeto como una llamada a una función en la que se indican como parámetros los valores que se desean asignar a los atributos inicialmente. En una asignación también puedes optar por hacer eso mismo omitiendo la palabra NEW .
- El orden de los parámetros debe coincidir con el orden en el que están declarados los atributos, así como los tipos de datos. El formato sería como el siguiente:
variable_objeto := NEW Nombre_Tipo_Objeto (valor_atributo1, valor_atributo2, ...);
Por ejemplo, en el caso del tipo de objeto Usuario:
u1 := NEW Usuario('luitom64', 'LUIS ', 'TOMAS BRUÑA', '24/10/07', 100);
- En ese momento se inicializa el objeto. Hasta que no se inicializa el objeto llamando a su constructor, el objeto tiene el valor NULL .
- Es habitual inicializar los objetos en su declaración.
u1 Usuario := NEW Usuario('luitom64', 'LUIS ', 'TOMAS BRUÑA', '24/10/07', 100);
- La llamada al método constructor se puede realizar en cualquier lugar en el que se puede hacer una llamada a una función de la forma habitual. Por ejemplo, la llamada al método constructor puede ser utilizada como parte de una expresión.
- Los valores de los parámetros que se pasan al constructor cuando se hace la llamada, son asignados a los atributos del objeto que está siendo creado. Si la llamada es al método constructor que incorpora Oracle por defecto, debes indicar un parámetro para cada atributo, en el mismo orden en que están declarados los atributos. Ten en cuenta que los atributos, en contra de lo que ocurre con variables y constantes, no pueden tener valores por defecto asignados en su declaración. Por tanto, los valores que se desee que tengan inicialmente los atributos de un objeto instanciado deben indicarse como parámetros en la llamada al método constructor.
- Existe la posibilidad de utilizar los nombres de los parámetros formales en la llamada al método constructor, en lugar de utilizar el modelo posicional de los parámetros. De esta manera no es obligatorio respetar el orden en el que se encuentran los parámetros reales respecto a los parámetros formales, que como se ha comentado antes coincide con el orden de los atributos.
DECLARE
u1 Usuario;
BEGIN
u1 := NEW Usuario('user1', -10);
/* Se mostrará el crédito como cero, al intentar asignar un crédito negativo */
dbms_output.put_line(u1.credito);
END;
/
Acceso a los atributos de objetos
- Para hacer referencia a un atributo de un objeto debes utilizar el nombre de dicho atributo, utilizando el punto para acceder al valor que contiene o bien para modificarlo. Antes debe ir precedido del objeto cuyo atributo deseas conocer o modificar.
nombre_objeto.nombre_atributo
- Por ejemplo, la consulta del valor de un atributo puede utilizarse como parte de una asignación o como parámetro en la llamada a una función:
unNombre := usuario1.nombre;
dbms_output.put_line(usuario1.nombre);
- La modificación del valor contenido en el atributo puede ser similar a la siguiente:
usuario1.nombre:= 'Nuevo Nombre';
- Los nombres de los atributos pueden ser encadenados, lo que permite el acceso a atributos de tipos de objetos anidados. Por ejemplo, si el objeto sitio1 tiene un atributo del tipo de objeto Usuario, se accedería al atributo del nombre del usuario con:
sitio1.usuario1.nombre
- Si se utiliza en una expresión el acceso a un atributo de un objeto que no ha sido inicializado, se evalúa como NULL . Por otro lado, si se intenta asignar valores a un objeto no inicializado, éste lanza una excepción ACCESS_INTO_NULL .
- Para comprobar si un objeto es NULL se puede utilizar el operador de comparación IS se obtiene el valor TRUE si es así.
- De manera similar, al intentar hacer una llamada a un método de un objeto que no ha sido inicializado, se lanza una excepción NULL_SELF_DISPATCH . Si se pasa como parámetro de tipo IN, los atributos del objeto NULL se evalúan como NULL , y si el parámetro es de tipo OUT o IN OUT lanza una excepción al intentar modificar el valor de sus atributos.
Llamada a los métodos de los objetos
- Al igual que las llamadas a subprogramas, puedes invocar a los métodos de un tipo de objetos utilizando un punto entre el nombre del objeto y el del método. Los parámetros reales que se pasen al método se indicarán separados por comas, entre paréntesis, después del nombre del método.
usuario1.setNombreCompleto('Juan', 'García Fernández');
- Si el método no tiene parámetros, se indicará la lista de parámetros reales vacía (sólo con los paréntesis), aunque se pueden omitir los paréntesis.
credito := usuario1.getCredito();
- Las llamadas a métodos pueden encadenarse, en cuyo caso, el orden de ejecución de los métodos es de derecha a izquierda. Se debe tener en cuenta que el método de la izquierda debe retornar un objeto del tipo correspondiente al método de la derecha.
- Por ejemplo, si dispones de un objeto sitio1 que tiene declarado un método getUsuario el cual retorna un objeto del tipo Usuario, puedes realizar con ese valor retornado una llamada a un método del tipo de objeto Usuario:
sitio1.getUsuario.setNombreCompleto('Juan', 'García Fernández');
- Los métodos MEMBER son invocados utilizando una instancia del tipo de objeto:
nombre_objeto.metodo()
- En cambio, los métodos STATIC se invocan usando el tipo de objeto, en lugar de una de sus instancias:
nombre_tipo_objeto.metodo()
Tablas de objetos
- Después de haber visto que un grupo de objetos se puede almacenar en memoria mediante colecciones, vas a ver en este apartado que también se pueden almacenar los objetos en tablas de igual manera que los tipos de datos habituales de las bases de datos.
- Los tipos de datos objetos se pueden usar para formar una tabla exclusivamente formado por elementos de ese tipo, o bien, para usarse como un tipo de columna más entre otras columnas de otros tipos de datos.
- En caso de que desees crear una tabla formada exclusivamente por un determinado tipo de dato objeto, (tabla de objetos) debes utilizar la sentencia CREATE TABLE junto con el tipo de objeto de la siguiente manera:
CREATE TABLE NombreTabla OF TipoObjeto;
- Siendo NombreTabla el nombre que deseas dar a la tabla que va a almacenar los objetos del tipo TipoObjeto. Por ejemplo, para crear la tabla UsuariosObj, que almacene objetos del tipo Usuario:
CREATE TABLE UsuariosObj OF Usuario;
- Debes tener en cuenta que si una tabla hace uso de un tipo de objeto, no podrás eliminar ni modificar la estructura de dicho tipo de objeto. Por tanto, desde el momento en que el tipo de objeto sea utilizado en una tabla, no podrás volver a definirlo.
- Para poder crear este ejemplo previamente debes tener declarado el tipo de objeto Usuario, que se ha utilizado en apartados anteriores.
- Al crear una tabla de esta manera, estamos consiguiendo que podamos almacenar objetos del tipo Usuario en una tabla de la base de datos, quedando así sus datos persistentes mientras no sean eliminados de la tabla. Anteriormente hemos instanciado objetos que se han guardado en variables, por lo que al terminar la ejecución, los objetos, y la información que contienen, desaparecen. Si esos objetos se almacenan en tablas no desaparecen hasta que se eliminen de la tabla en la que se encuentren.
- Cuando se instancia un objeto con el fin de almacenarlo en una tabla, dicho objeto no tiene identidad fuera de la tabla de la base de datos. Sin embargo, el tipo de objeto existe independientemente de cualquier tabla, y puede ser usado para crear objetos en cualquier modo.
- Las tablas que sólo contienen filas con objetos, reciben el nombre de tablas de objetos.
- En la siguiente imagen se muestra el contenido de la tabla que incluye dos filas de objetos de tipo Usuario. Observa que los atributos del tipo de objeto se muestran como si fueran las columnas de la tabla:
Tablas con columnas tipo objeto
- Puedes usar cualquier tipo de objeto, que hayas declarado previamente, para utilizarlo como un tipo de dato de una columna más en una tabla de la base de datos. Así, una vez creada la tabla, puedes utilizar cualquiera de las sentencias SQL para insertar un objeto, seleccionar sus atributos y actualizar sus datos.
- Para crear una tabla en el que alguno de sus columnas sea un tipo de objeto, simplemente debes hacerlo como si fuera una columna como las que has utilizado hasta ahora, pero en el tipo de dato debes especificar el tipo de objeto.
- Por ejemplo, podemos crear una tabla que contenga, entre otras columnas, una columna de objetos del tipo Usuario que hemos utilizado anteriormente.
CREATE TABLE Gente (
dni VARCHAR2(10),
unUsuario Usuario,
partidasJugadas SMALLINT
);
- Como puedes comprobar en la siguiente imagen, los datos del campo unUsuario se muestran como integrantes de cada objeto Usuario, a diferencia de la tabla de objetos que has visto en el apartado anterior. Ahora todos los atributos del tipo de objeto Usuario no se muestran como si fueran varias columnas de la tabla, sino que forman parte de una única columna.
Uso de la sentencia Select
- De manera similar a las consultas que has realizado sobre tablas sin tipos de objetos, puedes utilizar la sentencia SELECT para obtener datos de las filas almacenadas en tablas de objetos o tablas con columnas de tipos de objetos.
- El uso más sencillo sería para mostrar todas las filas contenidas en la tabla:
SELECT * FROM NombreTabla;
- Como puedes apreciar en la imagen, la tabla que forme parte de la consulta puede ser una tabla de objetos (como la tabla UsuariosObj), o una tabla que contiene columnas de tipos de objetos (como la tabla Gente).
- En las sentencias SELECT que utilices con objetos, puedes incluir cualquiera de las cláusulas y funciones de agrupamiento que has aprendido para la sentencia SELECT que has usado anteriormente con las tablas que contienen columnas de tipos básicos. Por ejemplo, puedes utilizar:
SUM , MAX , WHERE , ORDER , JOIN , etc.
- Es habitual utilizar alias para hacer referencia al nombre de la tabla. Observa, por ejemplo, la siguiente consulta, en la que se desea obtener el nombre y los apellidos de los usuarios que tienen algo de crédito:
SELECT u.nombre, u.apellidos FROM UsuariosObj u WHERE u.credito > 0
- Si se trata de una tabla con columnas de tipo objeto, el acceso a los atributos del objeto se debe realizar indicando previamente el nombre asignado a la columna que contiene los objetos:
SELECT g.unUsuario.nombre, g.unUsuario.apellidos FROM Gente g;
Inserción de objetos
- Evidentemente, no te servirá de nada una tabla que pueda contener objetos sino conocemos la manera de insertar objetos en la tabla.
- La manera que tienes para ello es la misma que has utilizado para introducir datos de cualquier tipo habitual en las tablas de la base de datos: usando la sentencia INSERT de SQL.
- En las tablas habituales, cuando querías añadir una fila a una tabla que tenía un campo VARCHAR le suministrabas a la sentencia INSERT un dato de ese tipo. Pues si la tabla es de un determinado tipo de objetos, o si posee un campo de un determinado tipo de objeto, tendrás que suministrar a la sentencia INSERT un objeto instanciado de su tipo de objeto correspondiente.
- Por tanto, si queremos insertar un Usuario en la tabla Gente que hemos creado en el apartado anterior, previamente debemos crear el objeto o los objetos que se deseen insertar. A continuación podremos utilizarlos dentro de la sentencia INSERT como si fueran valores simplemente.
DECLARE
u1 Usuario;
u2 Usuario;
BEGIN
u1 := NEW Usuario('luitom64', 'LUIS', 'TOMAS BRUNA', '24/10/2007', 50);
u2 := NEW Usuario('caragu72', 'CARLOS', 'AGUDO SEGURA', '06/07/2007', 100);
INSERT INTO UsuariosObj VALUES (u1);
INSERT INTO UsuariosObj VALUES (u2);
END;
- De una manera más directa, puedes crear el objeto dentro de la sentencia INSERT directamente, sin necesidad de guardar el objeto previamente en una variable:
INSERT INTO UsuariosObj VALUES (Usuario('luitom64', 'LUIS', 'TOMAS BRUNA', '24/10/2007', 50));
- Podrás comprobar los resultados haciendo una consulta SELECT sobre la tabla de la manera habitual:
SELECT * FROM UsuariosObj;
- De manera similar puedes realizar la inserción de filas en tablas con columnas de tipo objeto. Por ejemplo, para la tabla Gente que posee entre sus columnas, una de tipo objeto Usuario, podríamos usar el formato de instanciar el objeto directamente en la sentencia INSERT , o bien, indicar una variable que almacena un objeto que se ha instanciado anteriormente:
INSERT INTO Gente VALUES ('22900970P',Usuario('luitom64','LUIS','TOMAS BRUNA','24/10/2007', 50), 54);
INSERT INTO Gente VALUES ('62603088D', u2, 21);
Modificación de objetos
- Si deseas modificar un objeto almacenado en una tabla tan sólo tienes que utilizar las mismas sentencias SQL que disponías para modificar registros de una tabla. ¿Recuerdas la sentencia UPDATE? Ahora puedes volver a utilizarla para modificar también los objetos de la tabla, de igual manera que cualquier otro tipo de dato.
- Hay una pequeña diferencia en la forma de especificar los nombre de los campos afectados, en función del tipo de tabla: según sea una tabla de objetos, o bien una tabla con alguna columna de tipo objeto.
- Si se trata de una tabla de objetos, se hará referencia a los atributos de los objetos justo detrás del nombre asignado a la tabla. Sería algo similar al formato siguiente:
UPDATE NombreTabla
SET NombreTabla.atributoModificado = nuevoValor
WHERE NombreTabla.atributoBusqueda = valorBusqueda;
- Continuando con el ejemplo empleado anteriormente, vamos a suponer que deseas modificar los datos de un determinado usuario. Por ejemplo, modifiquemos el crédito del usuario identificado por el login 'luitom64', asignándole el valor 0.
UPDATE UsuariosObj
SET UsuariosObj.credito = 0
WHERE UsuariosObj.login = 'luitom64';
- Es muy habitual abreviar el nombre de la tabla con un alias:
UPDATE UsuariosObj u
SET u.credito = 0
WHERE u.login = 'luitom64';
- Pero no sólo puedes cambiar el valor de un determinado atributo del objeto. Puedes cambiar un objeto por otro como puedes ver en el siguiente ejemplo, en el que se sustituye el usuario con login 'caragu72' por otro usuario nuevo.
UPDATE UsuariosObj u SET u = Usuario('juaesc82', 'JUAN', 'ESCUDERO LARRASA', '10/04/2011', 0)
WHERE u.login = 'caragu72';
- Si se trata de una tabla con columnas de tipo objeto, se debe hacer referencia al nombre de la columna que contiene los objetos:
UPDATE NombreTabla
SET NombreTabla.colObjeto.atributoModificado = nuevoValor
WHERE NombreTabla.colObjeto.atributoBusqueda = valorBusqueda;
- A continuación puedes ver un ejemplo de actualización de datos de la tabla que se había creado con una columna del tipo de objeto Usuario. Recuerda que a la columna en la que se almacenaban los objetos de tipo Usuario se le había asignado el nombre unUsuario:
UPDATE Gente g
SET g.unUsuario.credito = 0
WHERE g.unUsuario.login = 'luitom64';
- O bien, puedes cambiar todo un objeto por otro, manteniendo el resto de los datos de la fila sin modificar, como en el siguiente ejemplo, donde los datos de DNI y partidasJugadas no se cambia, sólo se cambia un usuario por otro.
UPDATE Gente g
SET g.unUsuario = Usuario('juaesc82', 'JUAN', 'ESCUDERO LARRASA', '10/04/2011', 0)
WHERE g.unUsuario.login = 'caragu72';
Borrado de objetos
- Por supuesto, no nos puede faltar una sentencia que nos permita eliminar determinados objetos almacenados en tablas. Al igual que has podido comprobar en las operaciones anteriores, tienes a tu disposición la misma sentencia que has podido utilizar en las operaciones habituales sobre tablas. En este caso de borrado de objetos deberás utilizar la sentencia DELETE.
- El modo de uso de DELETE sobre objetos almacenados en tablas es muy similar al utilizado hasta ahora:
DELETE FROM NombreTablaObjetos;
- Recuerda que si no se indica ninguna condición, se eliminarán todos los objetos de la tabla, por lo que suele ser habitual utilizar la sentencia DELETE con una condición detrás de la cláusula WHERE . Los objetos o filas de la tabla que cumplan con la condición indicada serán los que se eliminen.
DELETE FROM NombreTablaObjetos WHERE condición;
- Observa el siguiente ejemplo en el que se borrarán de la tabla UsuariosObj, que es una tabla de objetos, los usuarios cuyo crédito sea 0. Observa que se utiliza un alias para el nombre de la tabla:
DELETE FROM UsuariosObj u WHERE u.credito = 0;
- De manera similar se puede realizar el borrado de filas en tablas en las que alguna de sus columnas son objetos. Puedes comprobarlo con el siguiente ejemplo, donde se utiliza la tabla Gente, en la que una de sus columnas (unUsuario) es del tipo de objeto Usuario que hemos utilizado en otros apartados anteriores.
DELETE FROM Gente g WHERE g.unUsuario.credito = 0;
- Esta sentencia, al igual que las anteriores, se puede combinar con otras consultas SELECT, de manera que en vez de realizar el borrado sobre una determinada tabla, se haga sobre el resultado de una consulta, o bien que la condición que determina las filas que deben ser eliminadas sea también el resultado de una consulta. Es decir, todo lo aprendido sobre las operaciones de manipulación de datos sobre las tablas habituales, se puede aplicar sobre tablas de tipos de objetos, o tablas con columnas de tipos de objetos.
Consultas con la función VALUE
Cuando tengas la necesidad de hacer referencia a un objeto en lugar de alguno de sus atributos, puedes utilizar la función VALUE junto con el nombre de la tabla de objetos o su alias, dentro de una sentencia SELECT . Puedes ver a continuación un ejemplo de uso de dicha función para hacer inserciones en otra tabla (Favoritos) del mismo tipo de objetos:
INSERT INTO Favoritos SELECT VALUE(u) FROM UsuariosObj u WHERE u.credito >= 100;
Esa misma función VALUE puedes utilizarla para hacer comparaciones de igualdad entre objetos, por ejemplo, si deseamos obtener datos de los usuarios que se encuentren en las tablas Favoritos y UsuariosObj.
SELECT u.login FROM UsuariosObj u JOIN Favoritos f ON VALUE(u)=VALUE(f);
Observa la diferencia en el uso cuando se hace la comparación con una columna de tipo de objetos. En ese caso la referencia que se hace a la columna (g.unUsuario) permite obtener directamente un objeto, sin necesidad de utilizar la función VALUE .
SELECT g.dni FROM Gente g JOIN Favoritos f ON g.unUsuario=VALUE(f);
Usando la cláusula INTO podrás guardar en variables el objeto obtenido en las consultas usando la función VALUE. Una vez que tengas asignado el objeto a la variable podrás hacer uso de ella de cualquiera de las formas que has visto anteriormente en la manipulación de objetos. Por ejemplo, puedes acceder a sus atributos, formar parte de asignaciones, etc.
En el siguiente ejemplo se realiza una consulta de la tabla UsuariosObj para obtener un determinado objeto de tipo Usuario. El objeto resultante de la consulta se guarda en la variable u1. Esa variable se utiliza para mostrar en pantalla el nombre del usuario, y para ser asignada a una segunda variable, que contendrá los mismos datos que la primera.
DECLARE
u1 Usuario;
u2 Usuario;
BEGIN
SELECT VALUE(u) INTO u1 FROM UsuariosObj u WHERE u.login = 'luitom64';
dbms_output.put_line(u1.nombre);
u2 := u1;
dbms_output.put_line(u2.nombre);
END;
Referencias a objetos.
El paso de objetos a un método resulta ineficiente cuando se trata de objeto de gran tamaño, por lo que es más conveniente pasar un puntero a dicho objeto, lo que permite que el método que lo recibe pueda hacer referencia a dicho objeto sin que sea necesario que se pase por completo. Ese puntero es lo que se conoce en Oracle como una referencia (REF).
Al compartir un objeto mediante su referencia, los datos no son duplicados, por lo que cuando se hace cualquier cambio en los atributos del objeto, se producen en un único lugar.
Cada objeto almacenado en una tabla tiene un identificador de objeto que identifica de forma única al objeto guardado en una determinada fila y sirve como una referencia a dicho objeto.
Las referencias se crean utilizando el modificador REF delante del tipo de objeto, y se puede usar con variables, parámetros, campos, atributos, e incluso como variables de entrada o salida para sentencias de manipulación de datos en SQL.
CREATE OR REPLACE TYPE Partida AS OBJECT (
codigo INTEGER,
nombre VARCHAR2(20),
usuarioCreador REF Usuario
);
/
DECLARE
u_ref REF Usuario;
p1 Partida;
BEGIN
SELECT REF(u) INTO u_ref FROM UsuariosObj u WHERE u.login = 'luitom64';
p1 := NEW Partida(1, 'partida1', u_ref);
END;
/
Hay que tener en cuenta que sólo se pueden usar referencias a tipos de objetos que han sido declarados previamente. Siguiendo el ejemplo anterior, no se podría declarar el tipo Partida antes que el tipo Usuario, ya que dentro del tipo Partida se utiliza una referencia al tipo Usuario. Por tanto, primero debe estar declarado el tipo Usuario y luego el tipo Partida.
El problema surge cuando tengamos dos tipos que utilizan referencias mutuas. Es decir, un atributo del primer tipo hace referencia a un objeto del segundo tipo, y viceversa. Esto se puede solucionar haciendo una declaración de tipo anticipada. Se realiza indicando únicamente el nombre del tipo de objeto que se detallará más adelante:
CREATE OR REPLACE TYPE tipo2;
/
CREATE OR REPLACE TYPE tipo1 AS OBJECT (
tipo2_ref REF tipo2
/*Declaración del resto de atributos del tipo1*/
);
/
CREATE OR REPLACE TYPE tipo2 AS OBJECT (
tipo1_ref REF tipo1
/*Declaración del resto de atributos del tipo2*/
);
/
Debes tener en cuenta que no se puede acceder directamente a los atributos de un objeto referenciado que se encuentre almacenado en una tabla. Para ello, puedes utilizar la función DEREF.
Esta función toma una referencia a un objeto y retorna el valor de ese objeto.
Vamos a verlo en un ejemplo suponiendo que disponemos de las siguientes variables declaradas:
u_ref REF Usuario;
u1 Usuario;
Si u_ref hace referencia a un objeto de tipo Usuario que se encuentra en la tabla UsuariosObj , para obtener información sobre alguno de los atributos de dicho objeto referenciado, hay que utilizar la función DEREF.
Esta función se utiliza como parte de una consulta SELECT, por lo que hay que utilizar una tabla tras la cláusula FROM . Esto puede resultar algo confuso, ya que las referencias a objetos apuntan directamente a un objeto concreto que se encuentra almacenado en una determinada tabla. Por tanto, no debería ser necesario indicar de nuevo en qué tabla se encuentra. Realmente es así. Podemos hacer referencia a cualquier tabla en la consulta, y la función DEREF nos devolverá el objeto referenciado que se encuentra en su tabla correspondiente.
La base de datos de Oracle ofrece la tabla DUAL para este tipo de operaciones. Esta tabla es creada de forma automática por la base de datos, es accesible por todos los usuarios, y tiene un solo campo y un solo registro. Por tanto, es como una tabla comodín.
SELECT DEREF(u_ref) INTO u1 FROM Dual;
dbms_output.put_line(u1.nombre);
Por tanto, para obtener el objeto referenciado por una variable REF , debes utilizar una consulta sobre cualquier tabla, independientemente de la tabla en la que se encuentre el objeto referenciado. Sólo existe la condición de que siempre se obtenga una solo fila como resultado. Lo más cómodo es utilizar esa tabla DUAL. Aunque se use esa tabla comodín, el resultado será un objeto almacenado en la tabla UsuariosObj.